MyBatis框架使用指南(快速上手)

目录

介绍

mybatis核心配置文件: 

映射文件:

mybatis执行原理

mybatis获取参数的两种方式#{}和${}(重点) 

MyBatis获取Mapper方法的参数值的各种情况 

MyBatis针对表的各种查询 

MyBatis处理模糊查询

MyBtis处理批量删除

MyBatis根据动态的表名查询

MyBatis获取自增的主键值

数据库表字段名和实体类属性名的映射

一对一映射

多对一映射

解决方式一:使用自定义结果集映射resultMap解决上述问题(级联属性):

 解决方式二:在resulMap中使用association标签定义多对一映射关系

解决方式三:使用分步查询

​编辑

延迟加载

一对多映射

使用分步查询 

用一条sql语句完成查询

MyBatis实现动态SQL

if标签

where标签

trim标签 

 choose、when、otherwise一套标签

 foreach循环遍历

 foreach实现批量删除

foreach实现批量添加

SQL标签

MyBatis缓存

MyBatis的一级缓存

MyBatis的二级缓存

MyBatis缓存查询的顺序

整合第三方缓存EHCache(了解)

MyBatis的逆向工程 

创建逆向工程的步骤

奢华尊享版的逆向工程功能测试 

 MyBatis使用分页插件

分页插件使用步骤


介绍

最近学习了MyBaits框架,来记录一下我的学习体会。

首先MyBatis是持久层的一个框架,他封装了JDBC,几乎避免了手写JDBC的绝大部分代码,可以避免那些重复而繁多的JDBC代码。

MyBatis框架=mybatis核心jar包+配置文件,两个结合一起工作。

配置文件分为 核心配置文件 和 映射文件。

        核心配置文件(mybatis-config.xml):配置连接数据库的信息以及MyBatis的全局配置信息

        映射文件(XXXMaper.xml):主要写sql语句,一个操作数据库表的Mapper接口,就对应一个映射文件。

mybatis核心配置文件: 





    


    
    

    
    
        


        
        
    


    
    
        
        

            
            

            
            
                
                
                
                
                
            
        

        
        
            
            
                
                
                
                
            
        
    

    
    


        
        
    

映射文件:





    
     

mybatis执行原理

置文当我们调用Mapper接口中的方法时,Mybatis会找到核心配置文件,读取映射文件,然后找到当前Mapper接口对应的映射文件,然后根据方法名在映射文件中定位到唯一的sql语句进行执行。

1.我们在Mapper接口中定义好操作数据库表的方法,如:getUserByUserName(String username)

/**
 *
 * 操作数据库中User表的Mapper,相当于之前我们学的Dao接口。
 * 现在不用写实现类,MyBatis底层会使用代理模式创建Mapper接口的实现类对象然后返回,
 * 我们直接使用这个实现类对象调用接口中的方法即可,他会自动去找映射文件中对应的sql语句执行。
 *
 * @author shkstart
 * @create 2022-05-25 16:26
 */
public interface UserMapper {


    /**
     * 根据用户名查找用户信息
     * @return
     */
    User getUserByUserName(String username);

}

2.Mapper对应的映射文件UserMapper.xml



    
    

3.调用Mapper接口中的方法

    @Test
    public void testGetUserByUserName() throws IOException {

        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        UserMapper userMapper1 = new SqlSessionFactoryBuilder().build(is)
        .openSession(true).getMapper(UserMapper.class);

        // "张三"就是sql语句的参数
        User user =  userMapper.getUserByUserName("张三");
        System.out.println(user);
}

mybatis获取参数的两种方式#{}和${}(重点) 

  • ${}的本质就是字符串拼接,#{}的本质就是占位符赋值

  • #{} 对应的变量会自动加上单引号 ,${} 对应的变量不会加上单引号 
    • 字符串类型数据、日期类型数据、字段名等可以加单引号

我们底层的东西就先不深挖了,先学会用。

调用了Mapper接口中的 User getUserByUserName(String username); 方法后:

1、MyBatis会根据这个Mapper的全类名com.lin.mybatis.mapper.UserMapper找到它所对应的映射文件UserMapper.xml,

2、然后根据方法名getUserByUserName在映射文件中找到对应id为getUserByUserName的sql语句:

3、接着就是MyBatis会预编译上述找到的sql语句:

        a> "select * from t_user where name = ?"  (体现占位符)

        b> "select * from t_user where name =" + ${username} + "   (体现字符串拼接)

4、接着应该是MyBatis获取参数(这个参数就是调用Mapper中的方法的实参)填充占位符了

        a>select * from t_user where name = '张三'

        b>select * from t_user where name = '张三'

5、最后执行sql语句返回结果集

MyBatis获取Mapper方法的参数值的各种情况 

1.当Mapper方法中的参数为一个时,此时可以使用 ${}和#{} 以任意的名称(最好见名识意)获取参数的值,注意${}需要手动加单引号
可以用@param代替

 2.当Mapper接口中的方法的参数为多个时,调用方法时,MyBatis会将参数值放到一个Map集合中,可以通过 ${键}和#{键} 的方式来获取参数值。
  键有两种形式:
      a:arg0,arg1,.....
      b:param1,param2,...
  值(就是用户传过来的参数值):
     按照键的顺序一一对应: param1对应参数值1,param2对应参数值2,......

3.当Mapper接口方法的参数有多个时,可以将方法的参数设置为一个Map集合,调用方法时,这样就和第2种情况一样
可以通过 ${键}和#{键} 的方式来访问值。
 select * from t_user where name = #{username} and password = #{password}

4.当Mapper接口方法的参数是一个实体类类型时,调用此方法时,
 映射文件中的sql语句可以通过可以通过 ${属性值}和#{属性名} 的方式来访问属性值。

5.使用@Param注解来命名注解
 MyBatis会将@Param注解的值当成Map集合的key,参数值当成Map集合的value
 也会把param1,param2,....当成键
 两种.可以通过 ${键}和#{键} 的方式来获取参数值。

总结:可以只分为下面两种情况来处理
        实体类型参数和
        @param情况.

代码理解:

    @Test
    public void testGetUserByNameAndPwd(){
        UserMapper userMapper = SqlSessionUtil.getSqlSession().getMapper(UserMapper.class);

        User user =  userMapper.getUserByNameAndPwd("root","ni");
        System.out.println(user);
    }

    @Test
    public void testInsertUser(){
        UserMapper userMapper = SqlSessionUtil.getSqlSession().getMapper(UserMapper.class);

        User user = new User(23, "梨花", "lll", "女", 12);

        int count = userMapper.insertUser(user);
        System.out.println(count);
    }
public interface UserMapper {

    /**
     * 根据用户名和密码查询用户信息
        key        value
        uName      root
        password    ni
        
     * @return
     */
    User getUserByNameAndPwd(@Param("uName") String username,@Param("password") String pwd);

     /**
     * 增加用户

        key        value
        id          23
        name        梨花
        password    111
        sex         女
        age         12

     * @param user
     * @return
     */
    int insertUser(User user);

}

    
    

    
    
        insert into t_user values(null,#{name},#{password},#{sex},#{age})
    

MyBatis针对表的各种查询 

* 针对表的各种查询
* 1.若查询出来的结果集有多条记录:
*      a: 可以通过List集合来接收(List集合中每个元素是一个实体类类型)
*      b: 可以通过List集合来接收(List集合中每个元素是一个Map类型,Map中是每条记录的键值对)
*      [{password=ni, sex=男, name=root, id=2, age=12}, {password=root, sex=男, name=dfd, id=3, age=12}]
*      c: 使用@MapKey注解, @MapKey("id")以查询的记录中的某个唯一的字段为键,每条记录构成的Map为值。
*      {2={password=ni, sex=男, name=root, id=2, age=12}, 3={password=root, sex=男, name=dfd, id=3, age=12}}
*
* 注意:不能用实体类对象来接收,会报TooManyResultsException异常(多对一)
*
* 2.若查询出来的结果集只有一条记录
*      a: 可以通过实体类对象来接收
*      b: 也可以通过List集合来接收
*      c: 也可以通过Map集合来接收(字段名为键,字段值为值)

代码理解:

public interface SelectMapper {


    /**
     * 查询所有的用户信息,并将所有的用户封装进一个集合中(多条记录)
     * a: 可以通过List集合来接收(List集合中每个元素是一个实体类类型)
     * @return
     */
//    User getAllUser();错的,报TooManyResultsException异常
    List getAllUser();


     /**
     * 查询所有的用户信息,(多条记录)
     * b:可以通过List集合来接收(List集合中每个元素是一个Map类型,每个Map中是每条记录的键值对)
     * @return
     */
    List> getAllUserToListMap();

     /**
     * 查询所有的用户信息,结果转换为一个Map(多条记录)
     *  c: 使用@MapKey注解, 
        @MapKey("id")以查询的记录中的某个唯一的字段为Map的键,每条记录构成Map的值。
     * @return
     */
    @MapKey("id")
    Map getAllUserToMap();


    /**
     * 查询结果只有一条
     * 通过用户名查询用户信息,
     * a: 可以通过实体类对象来接收
     *
     * @return
     */
    User getUserByName(@Param("username") String username);

    /**
     * 查询结果只有一条
     * 通过Id查询用户信息,并将用户信息封装进一个List集合中返回
     * b: 也可以通过List集合来接收(List中只有一个实体类对象)
     *
     * @param id
     * @return
     */
    List getUserByIdToList(@Param("id")Integer id);


    /**
     * 查询结果只有一条
     * 通过Id查询用户信息,并将用户信息封装进一个map集合中返回
     * 
     * c: 也可以通过Map集合来接收(查询结果集的字段名为键,字段值为值)
     * @return
     */
    Map getUserByIdToMap(@Param("id")Integer id);

    /**
     * 查询用户数量
     * 查询结果集只有单行单列的情况
     * 
     * @return
     */
    int getNumOfUser();

}


    
    

    
    

    
    

     
    

    
    

    

        
    
    

        
    
    


MyBatis处理模糊查询

public interface LikeQueryMapper {

    /**
     * 使用模糊的用户名查询用户信息
     *
     * @param likeName
     * @return
     */
    List getUserByLikeName(@Param("likeName")String likeName);
}


    
    

其中select * from t_user where name like "%"#{likeName}"%" 最常用

MyBtis处理批量删除

    /**
     * 根据用户的ids批量删除多个用户
     *
     * @param ids
     * @return
     */
    int deleteUserByIds(@Param("ids")String ids);
    
    
        delete from t_user where id in(#{ids})
    
    @Test
    public void deleteUserByIdsTest(){
        SQLMapper mapper = SqlSessionUtil.getSqlSession().getMapper(SQLMapper.class);
        int count = mapper.deleteUserByIds("5,6,7");
        System.out.println(count);
    }

 运行截图:

 MyBatis框架使用指南(快速上手)_第1张图片

从以上现象可以看出,实现批量删除不能使用#{}的方式获取参数值,因为它会自动将参数值加上单引号,即会将delete from t_user where id in(#{ids}) 解析成delete from t_user where id in('5,6,7');  只有id为5,6,7的数据会被删除

而批量删除正确的sql语句应该是:delete from t_user where id in(5,6,7);

所以批量删除只能用${}的方式获取参数值,它不会自动加单引号。

    
    
        
        delete from t_user where id in(${ids})
    

执行结果:

MyBatis框架使用指南(快速上手)_第2张图片

MyBatis根据动态的表名查询

    /**
     * 自定义要查询的表,根据传来的表名进行查询
     *
     * @param tableName
     * @return
     */
    List getUserByTable(String tableName);
    
    
    @Test
    public void getUserByTableTest(){
        SQLMapper mapper = SqlSessionUtil.getSqlSession().getMapper(SQLMapper.class);

        List users = mapper.getUserByTable("t_user");
        System.out.println(users);
    }

运行结果:

可以看出来使用#{}的方式出错了,因为他会给要查询的表名加上单引号,

 select * from 't_user';

而我们的表名是不能加单引号,所以这个也只能用${}的方式获取参数值。

 
    

 运行结果:

MyBatis获取自增的主键值

这个是干嘛的? 

业务场景:

有一个班级信息表t_class(class_id,class_name);

有一个学生信息表t_student(student_id,stu_name,class_id);

1、要添加一个班级信息

2、获取新添加班级的主键值(MyBatis获取自增的主键值)

3、然后添加一个学生信息,同时指定学生所在的班级为刚刚添加的班级。(所以就需要获取刚刚添加班级的主键class_id)

使用:

    /**
     * 插入用户
     *
     * @param user
     * @return
     */
    int insertUser(User user);
    
    
        insert into t_user values(null,#{username},#{password},#{sex},#{age})
    
    @Test
    public void insertUserTest(){
        SQLMapper mapper = SqlSessionUtil.getSqlSession().getMapper(SQLMapper.class);

        User user = new User(null, "测试名", "234", "男", 23);

        // 插入用户信息,id为空,但是表里面id字段是自增的,会将自增的id赋给user
        int count = mapper.insertUser(user);
        System.out.println(user);
    }

运行结果:

 MyBatis框架使用指南(快速上手)_第3张图片

数据库表字段名和实体类属性名的映射

一对一映射

表:

MyBatis框架使用指南(快速上手)_第4张图片

 对应实体类:

MyBatis框架使用指南(快速上手)_第5张图片

 以下的查询sql,执行完毕后,会根据结果集中的字段名调对应的名字的属性名然后调该属性的set方法,将字段对应的值设置给属性。



    
    

 问题引出,但是由于属性和字段的命名规则不一样,所以可能会出现以下情况:

MyBatis框架使用指南(快速上手)_第6张图片

问题解决,倘若字段名和属性名不一致,可以通过以下两种方式处理字段和属性之间的映射关系:

1、给查询的字段名其别名,这个别名要和属性名一样



    
    

MyBatis框架使用指南(快速上手)_第7张图片

 2、在MyBatis核心配置文件中定义全局设置,

    
        
        
    


    
    

MyBatis框架使用指南(快速上手)_第8张图片

 3、使用resultMap自定义字段名和属性名的映射关系



    
    
        
        
    

    
    

多对一映射

多个员工对应一个部门

业务背景:

想要查询员工的信息,

// 员工表t_emp对应的实体类
public class Emp {
    private Integer eid;
    private String empName;
    private String sex;
    private Integer age;
    private String email;
    private Dept dept;// 员工所在的部门
}
    /**
     * 根据员工编号,查询所有的员工信息与其所在的部门信息
     *
     * @return
     */
    Emp getEmpAndDeptById(@Param("eid")Integer eid);
    
    
    

查询结果集:

MyBatis框架使用指南(快速上手)_第9张图片

 运行结果:

结果集的字段和实体类的属性映射失败,导致某些属性没有赋值。

解决方式一:使用自定义结果集映射resultMap解决上述问题(级联属性):

    

    

    
        
        
        
        
        
        
        
    

 MyBatis会根据自定义结果集映射将查询出来的结果集按照此套映射,将字段值设置给对应属性,比如:emp_name查询出来的值为 周杰伦,对应Emp的empName属性设置为 周杰伦;dept_name为B,对应Emp的dept的deptName设置为B。

MyBatis框架使用指南(快速上手)_第10张图片

 解决方式二:在resulMap中使用association标签定义多对一映射关系


    
        
        
        
        
        
        
            
            
        
    

        

MyBatis框架使用指南(快速上手)_第11张图片

    @Test
    public void getEmpAndDeptByIdTest(){
        EmpMapper mapper = SqlSessionUtil.getSqlSession().getMapper(EmpMapper.class);

        Emp emp = mapper.getEmpAndDeptById(2);
        System.out.println(emp);
    }

调用Mapper接口中的方法,会执行getEmpAndDeptById这个sql语句,然后根据empAndDeptResultMap2映射关系,处理结果集和实体类型Emp之间的对应关系。

解决方式三:使用分步查询

某个字段使用其他的查询结果赋值。

MyBatis框架使用指南(快速上手)_第12张图片

    /**
     * 根据员工id查询员工信息
     *
     * @param eid
     * @return
     */
    Emp getEmpById(Integer eid);
    
    
    
        
        
        
        
    

查询部门:



    
    

    
        
        
    

延迟加载

  • 延迟加载:只根据所需的数据执行对应的sql语句,不会执行不相关的sql语句。倘若没有开启延迟加载的话,某些sql语句之间产生关联了,然后所需的数据只需要执行其中的一条sql语句即可,但是由于没有开启延迟加载,就得把这些关联的sql语句全部都执行,然后获取所需的数据。

  • 分步查询的优点:可以实现延迟加载,但是必须在MyBatis核心配置文件中设置全局配置信息:

  • lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。(默认为false)

    • aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载。(默认为false)

    
        
        
        
    

 以下就是分步查询的配置,查询员工信息dept,员工所对应的部门信息需要由另一条查询语句来查询部门信息。

    
    
    
        
        
        
        
    

要员工的全部信息(包括部门信息):

    @Test
    public void getEmpByIdTest(){
        EmpMapper mapper = SqlSessionUtil.getSqlSession().getMapper(EmpMapper.class);

        Emp emp = mapper.getEmpById(4);
        System.out.println(emp);
    }

查询员工的全部信息,所以需要查询员工对应的部门信息,所以将关联的查询部门信息的sql语句一起执行了。 

MyBatis框架使用指南(快速上手)_第13张图片

只要员工的姓名信息: 

    @Test
    public void getEmpByIdTest(){
        EmpMapper mapper = SqlSessionUtil.getSqlSession().getMapper(EmpMapper.class);

        Emp emp = mapper.getEmpById(4);
        System.out.println(emp.getEmpName());
    }

从运行结果可以看出,只执行了查询员工信息的sql语句select * from t_emp where eid = ?

因为我们只需要员工的名字,所以不需要查询部门信息,因此延迟加载了互相关联的查询部门信息的sql语句。

只要员工的所在的部门信息

    @Test
    public void getEmpByIdTest(){
        EmpMapper mapper = SqlSessionUtil.getSqlSession().getMapper(EmpMapper.class);

        Emp emp = mapper.getEmpById(4);
        System.out.println(emp.getEmpName());
        System.out.println("-----------------");
        System.out.println(emp.getDept());
    }

MyBatis框架使用指南(快速上手)_第14张图片

fetchType:指定当前的查询是否使用延迟加载。(eager表示使用,lazy表示使用)
    
    
    
        
        
        
        
    
    @Test
    public void getEmpByIdTest(){
        EmpMapper mapper = SqlSessionUtil.getSqlSession().getMapper(EmpMapper.class);

        Emp emp = mapper.getEmpById(4);
        System.out.println(emp.getEmpName());
/*        System.out.println("-----------------");
        System.out.println(emp.getDept());*/
    }

可以看出没有延迟加载。 

MyBatis框架使用指南(快速上手)_第15张图片

一对多映射

一个部门有多个员工。

MyBatis框架使用指南(快速上手)_第16张图片

public interface DeptMapper {

    /**
     * 根据部门id查询部门信息
     *
     * @param did
     * @return
     */
    Dept getDeptById(Integer did);


}

使用分步查询 

    
    

    
        
        
        
        
        
    

emps属性需要由以下的sql语句来查询并赋值: 

    
    

用一条sql语句完成查询


    

    
    
        

        
            
            
            
            
            
        
    

MyBatis框架使用指南(快速上手)_第17张图片

MyBatis实现动态SQL

if标签

在我们的平时业务中,常常要用到多条件查询。

比如查询一个员工的信息,要根据用户填写的信息来查询,要根据员工的姓名、性别、年龄、邮箱等信息来查询员工信息。

public interface DynamicSqlMapper {

    /**
     * 根据员工的姓名、性别、年龄、邮箱等信息来查询员工信息。
     *
     * 将员工的姓名、性别、年龄、邮箱等信息封装成一个员工对象作为参数
     *
     * @param emp
     * @return
     */
    List getEmpByConditions(Emp emp);
}


    
    
    
    @Test
    public void getEmpByConditionsTest(){
        DynamicSqlMapper mapper = SqlSessionUtil.getSqlSession().getMapper(DynamicSqlMapper.class);

        // 用户可能在前端输入了要查询的员工信息
        
        // 然后后端,获取请求参数,获取要查询的员工信息
        String empName = "易烊千玺";
        int age = 21;
        String sex = "男";
        Emp emp = new Emp(null, empName, sex, age, null);

        List empList = mapper.getEmpByConditions(emp);
        System.out.println(empList);
    }

 也有可能只输入了性别和年龄:

    @Test
    public void getEmpByConditionsTest(){
        DynamicSqlMapper mapper = SqlSessionUtil.getSqlSession().getMapper(DynamicSqlMapper.class);

        // 用户可能在前端输入了要查询的员工信息

        // 然后后端,获取请求参数,获取要查询的员工信息


        // 也有可能只输入了年龄和性别
        Emp emp2 = new Emp(null, null, "男", 21, null);

        List empList = mapper.getEmpByConditions(emp2);
        System.out.println(empList);
    }

修改映射文件:



    
    
    

MyBatis框架使用指南(快速上手)_第18张图片

where标签

上面的if标签,我们可以看到where关键字是写死的,那么如果下面的if标签都不成立,那么where关键字就很多余。

基于以上问题引出where标签:

1、where标签一般和if标签一起使用

2、如果where标签中有内容(就是if标签成立)则会自动生成WHERE关键字和上面的sql语句拼接,而且会将最前面的and或or关键字去除掉。

3、如果where标签中没有内容(if标签都不成立),则不会生成WHERE关键字,体现动态性。

trim标签 

  • trim用于去掉或添加标签中的内容

  • 常用属性

    • prefix:在trim标签中的内容的前面添加某些内容

    • suffix:在trim标签中的内容的后面添加某些内容
    • prefixOverrides:在trim标签中的内容的前面去掉某些内容
    • suffixOverrides:在trim标签中的内容的后面去掉某些内容
  • 若trim中的标签都不满足条件,则trim标签没有任何效果,也就是只剩下select * from t_emp

 choose、when、otherwise一套标签

  • choose、when、otherwise相当于if...else if..else或者switch case default

  • 1.when至少要有一个,otherwise至多只有一个  

  • 2.when只会执行一个,如果有多个when都成立,那么谁在前面执行谁的

  • 3.when都不成立的话,则执行otherwise中的

    @Test
    public void getEmpByConditionsTest(){
        DynamicSqlMapper mapper = SqlSessionUtil.getSqlSession().getMapper(DynamicSqlMapper.class);

        // 用户可能在前端输入了要查询的员工信息

        // 然后后端,获取请求参数,获取要查询的员工信息

        // 也有可能只输入了年龄和性别
        Emp emp2 = new Emp(null, null, "男", 21, null);

        List empList = mapper.getEmpByConditions(emp2);
        System.out.println(empList);
    }

    

 foreach循环遍历

 foreach循环遍历输出,主要帮我们实现一些批量操作。

  • collection:要遍历的集合或数组
  • item:给集合或数组中的每一项元素起个变量名,代表集合或数组中的每一项元素
  • separator:指明遍历输出的每一项数据之间的分隔符
  • open:指明以什么开始遍历输出,设置foreach标签中的内容的开始符
  • close:指明以什么结束遍历输出,设置foreach标签中的内容的结束符

 foreach实现批量删除

    /**
     * 根据员工编号id构成的数组来删除员工
     *
     * @Param("eids") 到时候,mybatis会以@param注释的值为键,然后参数数组为值.
     *
     * @param eids
     * @return
     */
    int deleteEmpByEids(@Param("eids") Integer[] eids);
    
    
        delete from t_emp where eid in
        (
            
                #{eid}
            
        )
    
    
    
        delete from t_emp where eid in
            
                #{eid}
            
    
    @Test
    public void deleteEmpByEidsTest(){
        DynamicSqlMapper mapper = SqlSessionUtil.getSqlSession().getMapper(DynamicSqlMapper.class);

        int count = mapper.deleteEmpByEids(new Integer[]{6, 7, 8});
        System.out.println(count);
    }

    #{eid}

这一整个foreach标签,最后遍历输出的就是:(6,7,8)

 MyBatis框架使用指南(快速上手)_第19张图片

MyBatis框架使用指南(快速上手)_第20张图片

foreach实现批量添加

    /**
     * 批量添加员工
     *@Param("empList") 到时候, mybatis会会将empList集合添加到Map集合中,
     *      以@param注释的值为键,然后参数数组为值.
     *
     * @param empList
     * @return
     */
    int addEmpsByList(@Param("empList") List empList);
    

    
        insert into t_emp values
        
            (null,#{emp.empName},#{emp.sex},#{emp.age},#{emp.email},null)
        
    
    @Test
    public void addEmpsByListTest(){
        DynamicSqlMapper mapper = SqlSessionUtil.getSqlSession().getMapper(DynamicSqlMapper.class);

        // 要添加的员工集合
        List empList = new ArrayList<>();
        empList.add(new Emp(null,"a","男",12,"[email protected]"));
        empList.add(new Emp(null,"b","男",23,"[email protected]"));
        empList.add(new Emp(null,"c","男",25,"[email protected]"));

        int count = mapper.addEmpsByList(empList);
        System.out.println(count);
    }

SQL标签

  • sql片段,可以记录保存一段公共sql片段,在使用的地方通过include标签进行引入

    • sql片段:emp_name,sex,age,email
      引用:
emp_name,sex,age,email
    
        select * from t_emp where eid = #{eid}
    

同一个sqlSession: 

    @Test
    public void testCache1(){
        // 一个sqlSession
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();

        CacheMapper mapper1 = sqlSession.getMapper(CacheMapper.class);
        Emp emp1 = mapper1.getEmpByEid(1);
        System.out.println(emp1);

        CacheMapper mapper2 = sqlSession.getMapper(CacheMapper.class);
        Emp emp2 = mapper2.getEmpByEid(1);
        System.out.println(emp2);
    }

MyBatis框架使用指南(快速上手)_第21张图片

MyBatis框架使用指南(快速上手)_第22张图片

不同一个sqlSession:  

    @Test
    public void testCache2(){
        // 两个sqlSession
        SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
        SqlSession sqlSession2 = SqlSessionUtil.getSqlSession();

        CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
        Emp emp1 = mapper1.getEmpByEid(1);
        System.out.println(emp1);

        CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
        Emp emp2 = mapper2.getEmpByEid(1);
        System.out.println(emp2);
    }

MyBatis框架使用指南(快速上手)_第23张图片

MyBatis的二级缓存

  • 二级缓存是SqlSessionFactory(可以理解为一个数据库连接池)级别,通过一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;后面再通过同一个SqlSessionFactory创建的SqlSession进行查询,会从二级缓存中找,如果有相同的查询,则直接从缓存中取,没有则访问数据库;此后若再次执行相同的查询语句,结果就会从缓存中获取

  • 二级缓存开启的条件

    • 1. 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置
      2. 在映射文件中设置标签
      3. 二级缓存必须在SqlSession关闭或提交之后有效,只有Sqlsession关闭或提交之后,查询的数据才会被保存到二级缓存中,否则保存到一级缓存中。
      4. 查询的数据所转换的实体类类型必须实现序列化的接口。比如查询员工共信息,那么员工类就必须implement Serializable

    • 只有满足以上四个条件,二级缓存才能生效。

  • 二级缓存的相关配置

    • 在mapper配置文件中添加的cache标签可以设置一些属性

    • eviction属性:缓存回收策略

      • LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。

      • FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除他们。

      • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。

      • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

      • 默认的是 LRU

    • flushInterval属性:刷新间隔,就是清空缓存的时间间隔,单位毫秒

      • 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(增删改)时刷新清空缓存。

    • size属性:引用数目,正整数

      • 代表缓存最多可以存储多少个对象,太大容易导致内存溢出

    • readOnly属性:只读,true/false

      • true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。速度快。

      • false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false

MyBatis缓存查询的顺序

  • 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用

  • 如果二级缓存没有命中,再查询一级缓存

  • 如果一级缓存也没有命中,则查询数据库

  • SqlSession关闭之后,一级缓存中的数据会写入二级缓存

整合第三方缓存EHCache(了解)

由于我们的MyBatis是一个持久层框架,所以它在做缓存功能可能没有那么专业,所以我们可以用第三方的缓存来替换MyBatis的二级缓存,但是一级缓存是替换不了的。 

 第一步:添加依赖



	org.mybatis.caches
	mybatis-ehcache
	1.2.1



	ch.qos.logback
	logback-classic
	1.2.3

各个jar包的功能:

jar包名称 作用
mybatis-ehcache Mybatis和EHCache的整合包
ehcache EHCache核心包
slf4j-api SLF4J日志门面包
logback-classic 支持SLF4J门面接口的一个具体实现

第二步:创建EHCache的配置文件ehcache.xml

 名字必须叫`ehcache.xml`



    
    
    
    

第三步:设置二级缓存的类型

  • 在xxxMapper.xml文件中设置二级缓存类型

第四步:加入logback日志

  • 存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。创建logback的配置文件logback.xml,名字固定,不可改变



    
    
        
            
            
            [%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n
        
    
    
    
    
        
        
    
    
    

EHCache配置文件说明

属性名 是否必须 作用
maxElementsInMemory 在内存中缓存的element的最大数目
maxElementsOnDisk 在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal 设定缓存的elements是否永远不过期。 如果为true,则缓存的数据始终有效, 如果为false那么还要根据timeToIdleSeconds、timeToLiveSeconds判断
overflowToDisk 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
timeToIdleSeconds 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时, 这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
diskPersistent 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false
diskExpiryThreadIntervalSeconds 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s, 相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。 默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出

MyBatis的逆向工程 

  • 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的

  • 逆向工程:先创建数据库表,由框架负责根据数据库中的表,反向生成如下资源:

    • Java实体类

    • Mapper接口

    • Mapper映射文件

创建逆向工程的步骤

第一步:在项目下创建一个Maven模块

MyBatis框架使用指南(快速上手)_第24张图片

第二步:在Maven模块的pom.xml文件中添加依赖和插件

 
jar

	
	
		org.mybatis
		mybatis
		3.5.9
	
	
	
		junit
		junit
		4.13.2
		test
	
	
	
		mysql
		mysql-connector-java
		8.0.27
	
	
	
		log4j
		log4j
		1.2.17
	




	
	
		
		
			org.mybatis.generator
			mybatis-generator-maven-plugin
			1.3.0
			
			
				
				
					org.mybatis.generator
					mybatis-generator-core
					1.3.2
				
				
				
					com.mchange
					c3p0
					0.9.2
				
				
				
					mysql
					mysql-connector-java
					8.0.27
				
			
		
	

第三步:创建MyBatis的核心配置文件




    
    
        
    
    
        
            
            
                
                
                
                
            
        
    
    
        
    

第四步:创建逆向工程所需的配置文件

  • 文件名必须是:generatorConfig.xml




    
    
        
        
        
        
        
        
        
        
            
            
        
        
        
        
            
        
        
        
        
            
        
        
        
        
        
        

第五步:执行插件的generate目标进行逆向工程生成

MyBatis框架使用指南(快速上手)_第25张图片

 看到以下说明成功了:

MyBatis框架使用指南(快速上手)_第26张图片

如果出现报错:Exception getting JDBC Driver,可能是pom.xml中,数据库驱动配置错误

那你就要检查pom.xml文件中的dependencies标签中的mysql驱动要和mybatis-generator-maven-plugin插件中的mysql驱动的版本是否一致  

MyBatis框架使用指南(快速上手)_第27张图片

奢华尊享版的逆向工程功能测试 

  • selectByExample:按条件查询,需要传入一个example对象或者null;如果传入一个null,则表示没有条件,也就是查询所有数据

  • example.createCriteria().andXXX:创建条件对象,通过andXXX方法为SQL添加查询添加,每个条件之间是and关系

  • example.or().andXXX:将之前添加的条件通过or拼接其他条件

测试查询: 

    // 测试查询功能
    @Test
    public void testMBGOfQuery1() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

        // 查询所有的员工信息
        List empList = mapper.selectByExample(null);// 传入null表示条件为null即没有条件,就是查询所有的员工
        empList.forEach(emp -> System.out.println(emp));

    }

MyBatis框架使用指南(快速上手)_第28张图片

MyBatis框架使用指南(快速上手)_第29张图片

 代码:

    // 测试查询功能
    @Test
    public void testMBGOfQuery2() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

        // 根据条件查询员工信息
        EmpExample example = new EmpExample();// 用于条件查询
        // 创建要查询的条件
        // 1.这是添加与and条件
        example.createCriteria().andEmpNameEqualTo("易烊千玺").andAgeGreaterThanOrEqualTo(10);
        // 2.这是添加或or条件
        example.or().andEmpNameEqualTo("周杰伦");

        List empList = mapper.selectByExample(example);
        for (Emp emp : empList) {
            System.out.println(emp);
        }
    }

测试修改:

MyBatis框架使用指南(快速上手)_第30张图片

普通修改: 

    // 测试修改功能
    @Test
    public void testMBGOfUpdate1() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

        // updateByPrimaryKey()方法是普通的根据主键修改记录信息,如果其中有空的字段的话,还是会修改成null
        int count = mapper.updateByPrimaryKey(new Emp(1, "周杰伦", "男", null, "[email protected]", 5));
        System.out.println(count);
    }

MyBatis框架使用指南(快速上手)_第31张图片

选择性修改: 

    // 测试修改功能
    @Test
    public void testMBGOfUpdate2() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

        // updateByPrimaryKeySelective()方法是选择性的根据主键修改记录信息,如果其中有空的字段的话,则这个字段会被去除掉,不会修改为null
        int count = mapper.updateByPrimaryKeySelective(new Emp(1, "周杰伦", "男", null, "[email protected]", 5));
        System.out.println(count);
    }

MyBatis框架使用指南(快速上手)_第32张图片

 MyBatis使用分页插件

分页插件使用步骤

第一步:在pom.xml文件中添加依赖



	com.github.pagehelper
	pagehelper
	5.2.0

第二步:在mybatis核心配置文件中配置分页插件


	
	

第三步:在查询之前开启分页功能

开启分页功能:PageHelper.startPage(int pageNum, int pageSize);

pageNum是页码,pageSize是每页显示的记录数。

    // 测试分页插件功能
    @Test
    public void testPage1() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

        // 开启分页功能
        PageHelper.startPage(1,4);

        // 查询所有的员工信息
        List empList = mapper.selectByExample(null);// 传入null表示条件为null即没有条件,就是查询所有的员工
        empList.forEach(emp -> System.out.println(emp));

    }

MyBatis框架使用指南(快速上手)_第33张图片

 MyBatis框架使用指南(快速上手)_第34张图片

 第四步:在查询完分页所需的数据之后,获取和分页相关的信息

  • 在查询分页所需的数据,获取list集合之后,使用PageInfo pageInfo = new PageInfo<>(List list, intnavigatePages)获取分页相关数据

  • PageInfo对象包含了做分页功能相关的很多信息。

    • list:分页的数据

    • navigatePages:导航分页的页码数,就是分页导航条一次显示多少个页码

    // 测试分页插件功能
    @Test
    public void testPage1() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

        // 开启分页功能
        PageHelper.startPage(3, 4);

        // 查询所有的员工信息,这里会获取第三页然后每页4条的数据
        List empList = mapper.selectByExample(null);// 传入null表示条件为null即没有条件,就是查询所有的员工
        empList.forEach(emp -> System.out.println(emp));

        // 在查询完分页所需的数据后,获取和分页相关的信息(PageInfo包含了和分页功能相关的所有数据)
        PageInfo page = new PageInfo<>(empList,5);


        System.out.println("-------page相关---------");
        System.out.println(page);
    }

pageInfo:

常用数据:

  • pageNum:当前页的页码

  • pageSize:每页显示的条数

  • size:当前页显示的真实条数

  • total:总记录数

  • pages:总页数

  • prePage:上一页的页码

  • nextPage:下一页的页码

  • isFirstPage/isLastPage:是否为第一页/最后一页

  • hasPreviousPage/hasNextPage:是否存在上一页/下一页

  • navigatePages:导航分页的页码数

  • navigatepageNums:导航分页的页码,[1,2,3,4,5]

PageInfo{pageNum=3, pageSize=4, size=4, startRow=9, endRow=12, total=23, pages=6,

list=Page{count=true, pageNum=3, pageSize=4, startRow=8, endRow=12, total=23, pages=6, reasonable=false, pageSizeZero=false}[Emp{eid=12, empName='b', sex='男', age=23, email='[email protected]', did=null}, Emp{eid=13, empName='c', sex='男', age=25, email='[email protected]', did=null}, Emp{eid=14, empName='aa', sex='null', age=null, email='null', did=null}, Emp{eid=15, empName='aa', sex='null', age=null, email='null', did=null}],

prePage=2, nextPage=4, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=5, navigateFirstPage=1, navigateLastPage=5, navigatepageNums=[1, 2, 3, 4, 5]}

Mybatis日志输出

mybatis提供了很多种日志功能:

  • SLF4J
  • LOG4J 【掌握】
  • LOG4J2【Spring5要求掌握的】
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING【掌握】标准日志
  • NO_LOGGING

mybatis中具体使用哪个日志,在mybatis的核心配置文件中设定。

依赖:

log4j.properties配置:

#log4j.rootLogger = level, appenderName1, appenderName2, …
#appenderName:就是指定日志信息要输出到哪里。可以同时指定多个输出目的地,用逗号隔开。console是输出到控制台
#level:DEBUG < INFO < WARN < ERROR < FATAL,只会输出大于等于该级别的日志信息
log4j.rootLogger=debug,console


#log4j.appender.appenderName1=className. console上面设置日志输出到哪,org.apache.log4j.ConsoleAppender表示控制台、org.apache.log4j.FileAppender表示文件
log4j.appender.console=org.apache.log4j.ConsoleAppender
#设置日志输出的格式。org.apache.log4j.PatternLayout表示可定制化、org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
log4j.appender.console.layout=org.apache.log4j.PatternLayout
#ConversionPattern设置以怎样的格式输出日志信息。百度很多。
log4j.appender.console.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%t] %p %C{1} : %m%n


#log4j.logger.全类名=level 自定义指定某个类中的日志输出级别
log4j.logger.org.apache.zookeeper=WARN
log4j.logger.org.apache.dubbo.config.bootstrap.DubboBootstrap=WARN
log4j.logger.com.lin.module1.Module1Application=INFO

你可能感兴趣的:(SSM,数据库)