Mybatis学习总结

1、第一个Mybatis程序

思路:搭建环境->导入Mybatis->编写代码->测试

1.1搭建环境

搭建数据库

create database `Mybatis`;
use `mybatis`;

create table `user`(
id int not null,
name varchar(30) default null,
pwd varchar(30) default null,
primary key(`id`)
)  ENGINE=INNODB default CHARSET=utf8;

insert into `user`(`id`, `name`, `pwd`)
values(1, 'qwer', '123456'),
(2, 'asdf', '123456'),
(3, 'zxcv', '123456');

新建项目

  •   1.新建一个普通的maven项目
  •   2.删除src目录
  •   3.导入maven依赖
<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

1.2创建一个模块

  • 编写mybatis 核心文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
    <environments default="development">
  
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/Mybatis?useSSL=true&amp;useUnicode=true&amp;charterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root123"/>
            </dataSource>
        </environment>
    </environments>
</configuration>
  • 编写mybatis 工具类
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static{//初始加载
        try {
            //获取SqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //有了 SqlSessionFactory,可以从中获得 SqlSession 的实例。
    // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。openSession
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
 }

1.3 编写代码

  • 实现类
public class User {
    private int id;
    private String name;
    private String pwd;
    public User() {
    }
    public User(int id, String name, String pwd) {

        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPwd() {
        return pwd;
    }
    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
}
  • dao接口
public interface UserDao {
    List<User> getUserList();
}
  • 接口实现 由DaoImpl转换为了Mapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.beibei.dao.UserDao">

    <select id="getUserList" resultType="com.beibei.pojo.User">
        select * from mybatis.user;
    </select>
</mapper>

1.4测试

Junit测试

public class UserDemoTest {
    @Test
    public void test(){
        //获得sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行sql
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> userList = userDao.getUserList();
        for (User user :userList) {
            System.out.println(user);
        }
        //关闭sqlsession
        sqlSession.close();

    }
}

注意点:
错误一:org.apache.ibatis.binding.BindingException: Type interface com.beibei.dao.UserDao is not known to the MapperRegistry.
MapperRegistry是什么?
解决方法:mybatis配置文件中添加

<!--每一个mapper.xml都需要在Mybatis核心配置文件中注册  斜杆不是.-->
    <mappers>
        <mapper resource="com/beibei/dao/UserMapper.xml"/>
    </mappers>

错误二:
java.lang.ExceptionInInitializerError
Could not find resource com.beibei.dao.UserMapper.xml
解决方法:内部pom.xml添加

<!--解决资源放在内部,导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

生命周期和作用域

SqlSessionFactoryBuilder->sqlSessionFactory->SqlSession
SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量):不占用过多资源
SqlSessionFactory 最佳作用域是应用作用域:等同于连接池过多创建影响性能,单例模式
SqlSession 实例不是线程安全的,将 SqlSession 放在一个和 HTTP 请求相似的作用域中,每次收到 HTTP 请求,就可以打开一个SqlSession,返回一个响应后,就关闭它。sqlSession.close();

2. CRUD

  1.namespace

namespace中的包名要和Dao/mapper接口的包名保持一致。

  2.select/insert/update/delete

选择,查询语句:

  • id:对应namespace方法名;
  • resultType:Sql语句执行返回值!
  • parameterType:参数类型

1.编写接口

public interface UserDao {
    List<User> getUserList();
    User getUserByID(int id);
    int addUser(User user);
    int updateUser(User user);
    int deleteUser(int id);
}

2.编写mapper中的sql语句

<select id="getUserList" resultType="com.beibei.pojo.User">
        select * from mybatis.user;
    </select>

    <select id="getUserByID" resultType="com.beibei.pojo.User" parameterType="int">
        select * from mybatis.user where id = #{id};
    </select>
    
    <insert id="addUser" parameterType="com.beibei.pojo.User">
        insert into mybatis.user(id, name, pwd) values (#{id}, #{name}, #{pwd});
    </insert>

    <update id="updateUser" parameterType="com.beibei.pojo.User">
        update mybatis.user
        set name=#{name},
            pwd=#{pwd}
        where id=#{id};
    </update>

    <delete id="deleteUser" parameterType="id">
        delete from mybatis.user where id = #{id}
    </delete>

3.测试 ==注意事务的提交==

@Test
    public void testSelectbyID(){
        //获得sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行sql
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        User user = userDao.getUserByID(1);
            System.out.println(user);
        //关闭sqlsession
        sqlSession.close();
    }

    @Test
    public void testAddUser(){
        //获得sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行sql
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        int res = userDao.addUser(new User(4, "hanhan", "123456"));
        System.out.println(res);
        //提交事务
        sqlSession.commit();
        //关闭sqlsession
        sqlSession.close();

    }

    @Test
    public void testUpdateUser(){
        //获得sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行sql
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        int res = userDao.updateUser(new User(4, "hehe", "123123"));
        System.out.println(res);
        //提交事务
        sqlSession.commit();
        //关闭sqlsession
        sqlSession.close();

    }
    @Test
    public void testDeleteUser(){
        //获得sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行sql
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        int res = userDao.deleteUser(4);
        System.out.println(res);
        //提交事务
        sqlSession.commit();
        //关闭sqlsession
        sqlSession.close();
    }

4.Map妙用

对于字段过多的情况,由于pojo对象的构造方法难以分属性创建构造方法,在全属性构造方法下就必须传所有参数,可以使用Map规避这个问题,只需传指定参数即可。

dao:

int updateUserByMap(Map<String, Object> map);

daomapper:

<update id="updateUserByMap" parameterType="map">
        update mybatis.user
        set name=#{username}
        where id=#{id};
    </update>

test:

@Test
    public void testUpdateUserByMap(){
        //获得sqlsession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDao User = sqlSession.getMapper(UserDao.class);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("id", 4);
        map.put("username", "meng");
        int i = User.updateUserByMap(map);
        System.out.println(i);
        sqlSession.commit();
        sqlSession.close();
    }

5.模糊查询

1.Java代码执行的时候,传递通配符%%

List<User> userlist = mapper.getUserLike("%李%");

2.在sql中拼接使用通配符

select * from mybatis.user where name like "%"#{value}"%";

3.配置解析

1.核心配置文件

mybatis-config.xml

properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
    environment(环境变量)
        transactionManager(事务管理器)
        dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
  • environments(环境配置)
    尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>
  1. 默认使用的环境 ID(比如:default="development")。
  2. 每个 environment 元素定义的环境 ID(比如:id="development")。
  3. 事务管理器的配置(比如:type="JDBC")。
    • 存在JDBC和MANAGED 两者,MANAGED一般不用
  4. 数据源的配置(比如:type="POOLED")
    • UNPOOLED 无连接池,性能依赖于数据库
    • POOLED 存在连接池,减少连接响应时间,可进一步配置
    • JNDI EJB 或应用服务器这类容器中使用

2.properties(属性)

通过properties来实现配置文件引用
编写db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/Mybatis?useSSL=true&useUnicode=true&charterEncoding=UTF-8
username=root
password=root123

mybatis-config.xml中进行引用

<properties resource="db.properties"/>

3.类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

若有注解,则别名为其注解值。

@Alias("author")
public class Author {
    ...
}

4.settings(设置)

核心配置:
cacheEnabled:全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认true
lazyLoadingEnabled: 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。默认false
logImpl:指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
完整的配置元素:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

5.其他配置,基本使用不到

  • 类型处理器(typeHandlers)
  • objectFactory(对象工厂)
  • plugins(插件)

6.其他配置,基本使用不到

MapperRegistry:注册绑定Mapper文件
方式一:

<!--每一个mapper.xml都需要在Mybatis核心配置文件中注册-->
    <mappers>
        <mapper resource="com/beibei/dao/UserMapper.xml"/>
    </mappers>

方式二:使用class文件绑定注册

<mappers>
        <mapper class="com.beibei.dao.UserDao"/>
    </mappers>

注意点:
接口和Mapper.xml文件名称必须一致
接口和Mapper.xml文件必须在一个包下

方式三:通过包引入

<mappers>
        <package name="com.beibei.daoo"/>
 </mappers>

注意点:
接口和Mapper.xml文件名称必须一致
接口和Mapper.xml文件必须在一个包下

7.ResultMap结果映射集

当pojo对象与数据库对象字段名称不相同时,无法正确查询结果,此时需要使用ResultMap 进行映射

<resultMap id="UserMap" type="com.beibei.pojo.User">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="pwd" column="pasword"/>
</resultMap>
    
<select id="getUserByID" resultMap="UserMap">
        select * from mybatis.user where id = #{id};
</select>

不进行resultType返回,使用resultMap进行对象返回
property是数据库字段名
column是pojo属性

4.日志

如果数据库操作出现异常,需要通过日志进行更方便的排错
在mybatis-config.xml中存在settings进行日志配置
logImpl 指定 MyBatis所用日志的具体实现,未指定时将自动查找。
核心2种,STDOUT_LOGGING 和常用的 LOG4J

4.1日志工厂

STDOUT_LOGGING标准的日志工厂实现,无需添加外部包,配置好可直接使用
配置项:

<!--settings放在properties和environments之间-->
    <!--标准的日志工厂实现-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

输出结果:

Opening JDBC Connection
Created connection 156545103.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@954b04f]
==>  Preparing: select * from mybatis.user where id = ?; 
==> Parameters: 1(Integer)
<==    Columns: id, name, pwd
<==        Row: 1, qwer, 123456
<==      Total: 1
User{id=1, name='qwer', password='123456'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@954b04f]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@954b04f]
Returned connection 156545103 to pool.

4.2log4j

优点:

  • 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
  • 日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等
  1. 导入log4j
<dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
</dependency>
  1. resources下编写log4j.properties
    当前配置为输出到文件和控制台
### 配置根 ###
#日志信息输出到console和file下
log4j.rootLogger = debug,console,file

### 设置输出sql的级别,其中logger后面的内容全部为jar包中所包含的包名 ###
log4j.logger.org.apache=dubug
log4j.logger.java.sql.Connection=dubug
log4j.logger.java.sql.Statement=dubug
log4j.logger.java.sql.PreparedStatement=dubug
log4j.logger.java.sql.ResultSet=dubug

### 配置输出到控制台 ###
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern =  %d{ABSOLUTE} %5p %c{1}:%L - %m%n

### 配置输出到文件 ###
log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.File = logs/log.log
log4j.appender.file.Append = true
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

3.配置log4j为mybatis日志实现

<!--settings放在properties和environments之间-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

4.测试运行

19:31:52,373 DEBUG LogFactory:105 - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
19:31:52,376 DEBUG LogFactory:105 - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
19:31:52,414 DEBUG PooledDataSource:353 - PooledDataSource forcefully closed/removed all connections.
19:31:52,414 DEBUG PooledDataSource:353 - PooledDataSource forcefully closed/removed all connections.
19:31:52,415 DEBUG PooledDataSource:353 - PooledDataSource forcefully closed/removed all connections.
19:31:52,415 DEBUG PooledDataSource:353 - PooledDataSource forcefully closed/removed all connections.
19:31:52,563 DEBUG JdbcTransaction:136 - Opening JDBC Connection
19:31:52,868 DEBUG PooledDataSource:424 - Created connection 793315160.
19:31:52,868 DEBUG JdbcTransaction:100 - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2f490758]
19:31:52,875 DEBUG getUserList:143 - ==>  Preparing: select * from mybatis.user; 
19:31:52,920 DEBUG getUserList:143 - ==> Parameters: 
19:31:52,952 DEBUG getUserList:143 - <==      Total: 4
User{id=1, name='qwer', password='123456'}
User{id=2, name='asdf', password='123456'}
User{id=3, name='zxcv', password='123456'}
User{id=4, name='meng', password='123123'}
19:31:52,955 DEBUG JdbcTransaction:122 - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2f490758]
19:31:52,961 DEBUG JdbcTransaction:90 - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@2f490758]
19:31:52,961 DEBUG PooledDataSource:381 - Returned connection 793315160 to pool.

5.log4j项目使用
使用类中引入
import org.apache.log4j.Logger;
具体测试使用

static Logger logger = Logger.getLogger(UserDemoTest.class);
    @Test
    public void  testLog4j(){
        logger.info("info: testLog4j");
        logger.debug("debug: testLog4j");
        logger.error("error: testLog4j");
    }

可以在输出文件logs.log看差异

[ INFO ] - 2020-08-30 19:46:07 info: testLog4j
[ DEBUG ] - 2020-08-30 19:46:07 debug: testLog4j
[ ERROR ] - 2020-08-30 19:46:07 error: testLog4j

5.分页实现

为什么需要分页

  • 减少数据处理量

5.1 limit 分页

limit语法:

mysql> SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15
//为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1: 
mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.
//如果只给定一个参数,它表示返回最大的记录行数目: 
mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行
//换句话说,LIMIT n 等价于 LIMIT 0,n。

测试:
创建接口:

List<User> getUserListByLimit(Map<String, Object> map);

mapper:

<resultMap id="UserMap" type="com.beibei.pojo.User">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="pwd" column="pasword"/>
</resultMap>

<select id="getUserListByLimit" parameterType="map" resultMap="UserMap">
    select * from mybatis.user limit #{startIndex}, #{pageSize};
</select>

测试调用:

@Test
    public void testSelectByLimit(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDao userdao = sqlSession.getMapper(UserDao.class);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("startIndex", 0);
        map.put("pageSize", 2);

        List<User> userList = userdao.getUserListByLimit(map);
        for (User user:userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

5.2.RowBounds 分页

不再使用sql实现分页,通过mybatis官方方法实现(不推荐)

接口:

List<User> getUserListByRowBounds();

mapper.xml

<select id="getUserListByRowBounds" resultMap="UserMap">
        select * from mybatis.user;
    </select>   

测试脚本
@Test
public void testSelectByRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
RowBounds rowBounds = new RowBounds(1,2 );
List<User> userList = sqlSession.selectList("com.beibei.dao.UserDao.getUserListByRowBounds", null, rowBounds);
for (User user: userList) {
System.out.println(user);
}
sqlSession.close();
}

5.3 其他插件方式

Mysql分页处理(PageHelper)

6 使用注解开发

**面向接口编程 **

  • 解耦
  • 定义(规范,约束)与实现(名实分离的原则)的分离。
  • 接口的本身反映了系统设计人员对系统的抽象理解

6.1使用注解开发

1.注解在接口上实现

    @Select("select * from mybatis.user")
    List<User> getUsers();

2.需要在核心配置中绑定接口

<!--mapper 绑定注解接口-->
<mappers>
    <mapper class="beibei.dao.UserDao"/>
</mappers>

3.测试

@Test
public void test(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    List<User> userList = mapper.getUsers();
    for (User user: userList ) {
        System.out.println(user);
    }
    sqlSession.close();
    
}

本质:反射来获取注解信息实现
底层:动态代理

6.2 注解开发CRUD
6.2.1 自动提交事务

public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession(true);//自动提交事务
    }

6.2.2 编写接口

@Insert("insert into mybatis.user(id, name, pwd) values(#{id}, #{name}, #{pwd})")
int addUser(@Param("id") int id, @Param("name") String name, @Param("pwd") String password);

@Update("update mybatis.user set name=#{name} where id = #{id}")
int updateUser(@Param("id") int id,  @Param("name") String name);

6.2.3 增加测试类

@Test
public void testAddUser(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    int i = mapper.addUser(5, "qwer", "123321");
    System.out.println(i);
}

@Test
public void testUpdateUser(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    int i = mapper.updateUser(5, "asdf");
    System.out.println(i);
}

6.3 #{} 和${} 区别

{}是预编译处理,对应JBDC中的PreparedStatement,可以防止依赖注入。

${}是字符串替换。不能防止依赖注入

7.Lombok

不用再getter setter 构造 toString ,注解即可完成
1.idea安装插件
2.导入依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
    <scope>provided</scope>
</dependency>

3.使用:

@ToString
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class User {
    private int id;
    private String name;
    private int age;
    private String birth;
    private Date birthday;
}

@Data:生成无参 get set toString equeals hashcode
@AllArgsConstructor:生成全参
@NoArgsConstructor:生成无参

7 复杂查询

7.1环境搭建

1.导入lombok
2.新建实体类Teacher、Student
3.建立Mapper接口
4.建立Mapper.xml文件
5.核心配置文件中绑定注册的Mapper接口或文件
6.测试查询是否成功

7.2 多对一,多表查询

方式一:按照查询嵌套处理
核心mapper配置如下

<select id="getAllStudent" resultMap="student-teacher">
    select * from mybatis.student;
</select>

<resultMap id="student-teacher" type="com.beibei.pojo.Student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!--查询结果映射到对应的子查询条件中-->
    <association property="teacher" column="tid" javaType="com.beibei.pojo.Teacher" select="getTeacher"/>
</resultMap>

<select id="getTeacher" resultType="com.beibei.pojo.Teacher">
    select * from mybatis.teacher where id = #{id};
</select>

方式二:按结果嵌套查询

<select id="getAllStudent2" resultMap="student-teacher2">
    select s.id sid, s.name sname,t.name tname  from student s, teacher t where t.id = s.tid;
</select>

<resultMap id="student-teacher2" type="com.beibei.pojo.Student">
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    <!--查询结果映射到对应的子查询条件中-->
    <association property="teacher" javaType="com.beibei.pojo.Teacher">
        <result property="name" column="tname"/>
    </association>
</resultMap>

7.3 一对多,集合查询

<select id="getTeacherbyid" resultMap="teacher-student">
    select t.id tid, t.name tname, s.id sid, s.name sname 
    from student s, teacher t 
    where t.id = s.tid and t.id = #{tid};
</select>

<resultMap id="teacher-student" type="com.beibei.pojo.Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <!--javatype指定属性的类型,集合中的泛型信息,使用oftype获取-->
    <collection property="students" ofType="com.beibei.pojo.Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
</resultMap>

8 动态sql

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

数据库搭建

CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量',
primary key(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;

pojo对象

@Data
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}

if判断动态语句

mapper

<insert id="addBlog" parameterType="com.beibei.pojo.Blog">
    insert into mybatis.blog(id, title, author, create_time, views)
    values (#{id}, #{title}, #{author}, #{createTime}, #{views});
</insert>

<select id="queryBlogIF" parameterType="map" resultType="com.beibei.pojo.Blog">
    select * from mybatis.blog where 1=1
    <if test="title != null">
        and title = #{title}
    </if>

    <if test="author != null">
        and author = #{author}
    </if>
</select>

测试

@org.junit.Test
public void testAddBlog(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Blog blog = new Blog();
    blog.setAuthor("QWER");
    blog.setCreateTime(new Date());
    blog.setTitle("QWEQWRT");
    blog.setViews(6213);
    blog.setId(IDutils.getIdrandom());
    int i = mapper.addBlog(blog);
    System.out.println(i);
    sqlSession.commit();
    sqlSession.close();
}
@org.junit.Test
public void testQueryBlogIF(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    map.put("author","QWER");
    List<Blog> blogs = mapper.queryBlogIF(map);
    for (Blog blog : blogs) {
        System.out.println(blog.toString());
    }
 //   sqlSession.commit();
    sqlSession.close();
}

choose(when otherwise)选择动态语句

和java的switch效果相同
mapper:

<select id="queryBlogChoose" parameterType="map" resultType="com.beibei.pojo.Blog">
    select * from mybatis.blog where 1=1
   <choose>
       <when test="title != null">
           and title = #{title}
       </when>
       
       <when test="title != null and author != null">
           and author = #{author}
       </when>

       <otherwise>
          and views > 100
       </otherwise>
   </choose>
</select>

test:

@org.junit.Test
public void testQueryBlogChoose(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();

    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    map.put("author","QWER");
    map.put("title", "QWEQWRT");
    List<Blog> blogs = mapper.queryBlogIF(map);
    for (Blog blog : blogs) {
        System.out.println(blog.toString());
    }
    //   sqlSession.commit();
    sqlSession.close();
}

trim (where, set)

where 是取消where 1=1 使用的,update中可能使用
trim 则是优化多于的and or ,等元素的
==set== 有使用的必要,优化update中可能存在的多于","
进行set相关的测试

<update id="updateBlogSet"  parameterType="map">
    update mybatis.blog
    <set>
        <if test="author != null">author = #{author},</if>
        <!-- 大于小于注意要转换,和尾巴的逗号 -->
        <if test="views &gt;= 1000">views = views+1000,</if>
    </set>
    where 1= 1 and author = "pgm"
</update>

测试:

@org.junit.Test
public void testUpdateBlogSet(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();

    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    map.put("author","darmon");
    map.put("title", "QWEQWRT");
    int i= mapper.updateBlogSet(map);
        System.out.println(i);
      sqlSession.commit();
    sqlSession.close();
}

foreach 集合类条件动态语句

你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号item 的值是本次迭代获取到的元素。==当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值==
mapper

<select id="queryBlogIn" parameterType="map" resultType="com.beibei.pojo.Blog">
    select * from mybatis.blog
    where 1=1 and author in
    <foreach collection="authors" item="author" index="index"
     open="(" separator="," close=")">
        #{author}
    </foreach>
</select>

测试用例

@org.junit.Test
public void testQueryBlogIn(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    ArrayList<String> authors = new ArrayList<String>();
    authors.add("pgm");
    authors.add("darmon");
    map.put("authors",authors);
    List<Blog> blogs = mapper.queryBlogIn(map);
    for (Blog blog : blogs) {
        System.out.println(blog.toString());
    }
    sqlSession.commit();
    sqlSession.close();
}

9.缓存机制

为了提升查询效率,引入了缓存机制
mybatis缓存共定义了2级缓存

一级缓存(本地缓存):
  • 默认情况开启
  • 作用于Sqlsession级别(sqlsession.close关闭便关闭)
  • 修改数据后缓存失效(全刷新,失效)
  • sqlsession.clearCache();//清理缓存
二级缓存:
  • namespace级别缓存(又叫mapper级别,任意访问mapper.xml的文件都能使用)
  • 主配置文件中必须开启缓存设置(默认开启)
<setting name="cacheEnabled" value="true"/>
  • 在mapper.xml中添加<cache/>既开启二级缓存
  • 缓存设置(FIFO 缓存(先进先出),每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的)
  • 默认使用LRU算法机制(最近最少使用)
 <cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>
  • 缓存策略包含
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象

存在多级缓存时,查询调用顺序是== 优先二级缓存 ==,==其次一级缓存==,==最后查数据库==
Mybatis执行流程

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351