【Mybatis】(一)


Mybatis简介

提供持久层框架包括SQL Maps和Data Access Objects(DAO).

SQL Maps提供数据库数据和java数据的映射关系,换句话说即是封装JDBC的过程。

Data Access Objects数据访问对象,通过JDBC访问数据库然后操作数据库中的数据

Mybatis特性

1) MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架

高级映射指的是数据库字段名和java属性名不一致时的映射,相反只能处理相同情况的映射称为普通映射

2) MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
3) MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java
Objects,普通的Java对象)映射成数据库中的记录
4) MyBatis 是一个 半自动的ORM(Object Relation Mapping)框架

和其他持久层技术的对比

①JDBC
SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
代码冗长,开发效率低
②Hibernate 和 JPA
操作简便,开发效率高
程序中的长难复杂 SQL 需要绕过框架
内部自动生产的 SQL,不容易做特殊优化
基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
反射操作太多,导致数据库性能下降

MyBatis
轻量级,性能出色
SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
开发效率稍逊于HIbernate,但是完全能够接受

搭建Mybatis

开发环境

IDE:idea 2020.2
构建工具:maven 3.6.3
MySQL版本:MySQL 5.0.12
MyBatis版本:MyBatis 3.5.7

创建Maven工程

a>打包方式:jar
b>引入依赖

    
        
            mysql
            mysql-connector-mxj-db-files
            5.0.12
        

        
            junit
            junit
            4.12
            test
        

        
        
            org.mybatis
            mybatis
            3.5.7
        
    

配置Mybatis核心配置文件

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>


    
    
        
            
            
                
                
                
                
            
        
    
    
    
        
    

创建mapper接口 UserMapper

mapper接口相当于以前的DAO,但是区别在于mapper仅仅是接口,不需要提供额外的实现类,因为mybatis里有面向接口编程,每当调用接口中的方法,mybatis就会自动执行相应的sql语句

public interface UserMapper {
    int insertUser();
}

创建同名映射文件 UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>


    
        insert into user(user, balance) values("2b", 1000)
    

1、映射文件的命名规则:
表所对应的实体类的类名+Mapper.xml
例如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml
因此一个映射文件对应一个实体类,对应一张表的操作
MyBatis映射文件用于编写SQL,访问以及操作表中的数据
MyBatis映射文件存放的位置是src/main/resources/mappers目录下
2、MyBatis中可以面向接口操作数据,要保证两个一致:
a>mapper接口的全类名和映射文件的命名空间(namespace)保持一致
b>mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致

在mybatis核心配置文件中引入映射文件

<?xml version="1.0" encoding="UTF-8" ?>


    
    
        
            
            
                
                
                
                
            
        
    
    
    
        
    

测试:调用mapper接口方法

    @Test
    public void testMybatis() throws IOException {
        //加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        //获取SqlSessionFactoryBuilder
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //构建SqlSessionFactory
        SqlSessionFactory sessionFactory = builder.build(resourceAsStream);
        //获取SqlSession
        SqlSession sqlSession = sessionFactory.openSession();
        //返回一个接口对应的实现类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int result = mapper.insertUser();
        sqlSession.commit();
        System.out.println("result = " + result);
    }

SqlSession是一个数据库与java程序的通话,底层实现了mapper的接口实现类

当mapper接口调用方法时,mybatis会到同名配置文件执行被调用方法的相应sql语句

由于mybatis核心配置文件中的 sql的执行本质是事务,因此需要进行事务的提交

设置事务自动提交
SqlSession sqlSession = sessionFactory.openSession(true);

对Mybatis功能的优化 添加log4j日志

添加依赖


log4j
log4j
1.2.17

加入log4j的配置文件

log4j的配置文件为log4j.xml,文件目录为src/main/resource下

<?xml version="1.0" encoding="UTF-8" ?>



















日志的级别

FATAL>ERROR>WARN>INFO>DEBUG

从左往右打印的内容会越来越详细

Mybatis实现增删改查

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>


    
        insert into user(user, balance) values("2b", 1000)
    

    

    

查询功能必须设置resultType或resultMap

resultType:设置默认的映射关系,要求数据库字段名与实体属性名一致

resultMap:设置自定义的映射关系

Mapper接口

public interface UserMapper {
    int insertUser();
    User getUserById();
    List getAllUser();
}

测试

    @Test
    public void testMybatis() throws IOException {
        //加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        //获取SqlSessionFactoryBuilder
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //构建SqlSessionFactory
        SqlSessionFactory sessionFactory = builder.build(resourceAsStream);
        //获取SqlSession
        SqlSession sqlSession = sessionFactory.openSession(true);
        //返回一个接口对应的实现类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.getUserById();
        System.out.println(user);
        List users =mapper.getAllUser();
        users.forEach(user1 -> System.out.println(user1));
    }

核心配置文件详解

核心配置文件中的标签必须按照固定的顺序:
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>


    
    
        
            
            
                
                
                
                
            
        
    
    
    
        
    

enviroments

default:表示存在多个数据库环境时,默认使用的数据库环境id

enviroment

①id:数据库环境的唯一表示

②transactionManager:设置事务管理方式。

属性:type=“JDBC”或者“MANAGED”。JDBC表示是原生的事务管理方式,事物的提交和回滚都需要手动进行。MANAGED:被管理,例如Spring

③dataSource:配置数据源

属性type=“POOLED”或者“UNPOOLED”或者“JNDI”。POOLED表示数据库连接池,UNPOOLED表示不使用数据库连接池,JNDI表示使用上下文的数据源

在Spring整合后,可以直接使用Spring的数据源

使用properties存储JDBC数据库连接信息

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
jdbc.username=root
jdbc.password=abc123

在配置文件中使用标签的形式以及${}获取properties文件的键值对

    
    
    
        
            
            
                
                
                
                
            
        
    

typeAliases package添加类型别名

    
        
    

属性type为被添加别名的类型,alias为添加的别名(不区分大小写

如没有声明alias属性,则默认别名为类名(仍不区分大小写

则在mybatis的范围内使用user及其大小写可以替换完整类名

<?xml version="1.0" encoding="UTF-8" ?>


    
        insert into user(user, balance) values("2b", 1000)
    

    

    

也可以使用标签aliases下的标签package,替换一个包内的所有类型名为默认别名

    
        
    

以包的形式添加mapper mappers>package

    
    

        
    

要求映射文件的包名要和mapper接口所在包名一致

映射文件名和mapper接口名一致

资源下的包名不能用.要用/

Mybatis获取参数值的两种方式

①单个字面量的参数

若mapper接口中的方法参数为单个的字面量类型
此时可以使用${}和#{}以任意的名称获取参数的值,注意${}需要手动加单引号

        
        select * from user where user = '${aaa}'

②多个字面量类型的参数

Mapper接口:

int insertUserByNameAndBalance(String user, double balance);

Mapper映射文件:

    
        insert into user(user, balance) values (#{param1}, #{arg1})
    

若mapper接口中的方法参数为多个时
此时MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1...为键,以参数为值;以param1,param2...为键,以参数为值; 因此只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号

③参数为Map集合类型的数据

若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号

mapper接口:

int insertUserByNameAndBalance(Map map);

mapper映射文件:

    
        insert into user(user, balance) values (#{user}, #{balance})
    

这时原来自定义的键值param和arg都不能再用了

④实体类型的参数

若mapper接口中的方法参数为实体类对象时
此时可以使用${}和#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加单引号

mapper接口

int insertUser(User user);

mapper映射文件

    
        insert into user(user, balance) values(#{user}, #{balance})
    

这里的属性名狭义上讲是实体类get、set方法的名称得到小写,而不是成员变量,要严格对应get、set方法的名称

⑤使用@Param标识参数

以通过@Param注解标识mapper接口中的方法参数
此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以
param1,param2...为键,以参数为值;只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号

mapper接口

int insertUser(@Param("username") String username, @Param("balance") double balance);

mapper映射文件

    
        insert into user(user, balance) values(#{username}, #{balance})
    
    
        insert into user(user, balance) values(#{param1}, #{param2})
    

这里相较于③map集合的方式,保留了param的键值对

Mybatis的各种查询功能

查询出的数据只有一条时,可以通过 ①实体类②list集合③map集合 接收

map集合形式为:{balance=1000, id=1, user=2b},这种方法方便将数据转换为json的形式

查询的数据有多条时,一定不能通过实体类对象接收,否则会报TooManyResultException错误

查询单个数据

mapper接口

int getCount();

mapper映射文件

    
Alias                               Mapped Type
_byte                               byte
_long                               long
_short                               short
_int                               int
_integer                               int
_double                               double
_float                               float
_boolean                               boolean
string                               String
byte                               Byte
long                               Long
short                               Short
int                               Integer
integer                               Integer
double                               Double
float                               Float
boolean                               Boolean
date                               Date
decimal                               BigDecimal
bigdecimal                               BigDecimal
object                               Object
map                               Map
hashmap                               HashMap
list                               List
arraylist                               ArrayList
collection                               Collection
iterator                               Iteratot 
collection
iterator  

Mybatis内置的别名

Map类型查询结果

单个数据结果转换为Map

map接口:

Map getUserIdToMap(@Param("id") int id);

map映射文件:

    
多个数据结果转换为Map

①通过List的形式

mapper接口

List> list = mapper.getUserIdToMap();

mapper配置文件


结果为map类型的list集合:

[{balance=1000, id=1, user=2b}, {balance=1000, id=2, user=9s}, {balance=1000, id=3, user=9s},

②通过@MapKey()的形式

mapper接口

    @MapKey("id")
    Map getUserIdToMap();

mapper映射文件

    

结果为map类型的map,键为多个map获取的某一属性值,值为获取的多个map集合:

{1={balance=1000, id=1, user=2b}, 2={balance=1000, id=2, user=9s}, 3={balance=1000, id=3, user=9s},

Mybatis特殊查询

模糊查询
批量删除 in

mapper接口

int deleteMore(@Param("ids") String ids);

mapper映射文件


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

in批量删除只能使用$,#的话会自动添加双引号导致sql语句不正确

动态获取表名

mapper接口

List seleceByTableName(@Param("tableName") String tableName);

mapper配置文件


    select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid};

测试结果

Emp{eid=2, empName='2B', sex='女', email='123@qq.com', age='22', dept=Dept{did=2, deptName='B'}}
通过association解决多对一映射

mapper接口

List getEmpAndDept2(String eid);

mapper配置文件


    
    
    
    
    
    
        
        
    


测试结果

Emp{eid=2, empName='2B', sex='女', email='123@qq.com', age='22', dept=Dept{did=2, deptName='B'}}
分布查询解决多对一的映射关系

mapper接口

List getEmpAndDeptStep1(@Param("eid") String eid);
Dept getEmpAndDeptStep2(String did);

mapper映射文件


    
    
    
    
    
    
    


select表示调用其他表对应的映射接口的方法


这里没有对dept映射做ResultMap处理,是因为mybatis核心配置中开启了下划线到驼峰的自动映射

结果

Emp{eid=2, empName='2B', sex='女', email='123@qq.com', age='22', dept=Dept{did=2, deptName='B'}}
分布查询的延迟加载 lazyLoadingEnabled

    
    

理解延迟加载:并不是开启后只会执行第一步的查询而不执行第二步,而是需要执行第二步的时候才去执行第二步

测试①:

Emp emp = empMapper.getEmpAndDeptStep1("2");
System.out.println(emp.getEmpName());

输出:

DEBUG 05-04 13:04:48,410 ==>  Preparing: select * from t_emp where eid = ?; (BaseJdbcLogger.java:137) 
DEBUG 05-04 13:04:48,437 ==> Parameters: 2(String) (BaseJdbcLogger.java:137) 
DEBUG 05-04 13:04:48,495 <==      Total: 1 (BaseJdbcLogger.java:137) 
2B

测试②:

Emp emp = empMapper.getEmpAndDeptStep1("2");
System.out.println(emp.getDept());

输出:

DEBUG 05-04 13:06:45,414 ==>  Preparing: select * from t_emp where eid = ?; (BaseJdbcLogger.java:137) 
DEBUG 05-04 13:06:45,446 ==> Parameters: 2(String) (BaseJdbcLogger.java:137) 
DEBUG 05-04 13:06:45,567 <==      Total: 1 (BaseJdbcLogger.java:137) 
DEBUG 05-04 13:06:45,569 ==>  Preparing: select * from t_dept where did = ?; (BaseJdbcLogger.java:137) 
DEBUG 05-04 13:06:45,569 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:137) 
DEBUG 05-04 13:06:45,571 <==      Total: 1 (BaseJdbcLogger.java:137) 
Dept{did=2, deptName='B'}

如上第二组测试中需要查询第二步SQL的时候,延迟加载才会去执行第二步

fetchType标签单独设置延迟加载

fetchType值为eager或者lazy



测试输出:

DEBUG 05-04 13:11:48,340 ==>  Preparing: select * from t_emp where eid = ?; (BaseJdbcLogger.java:137) 
DEBUG 05-04 13:11:48,365 ==> Parameters: 2(String) (BaseJdbcLogger.java:137) 
DEBUG 05-04 13:11:48,383 ====>  Preparing: select * from t_dept where did = ?; (BaseJdbcLogger.java:137) 
DEBUG 05-04 13:11:48,384 ====> Parameters: 2(Integer) (BaseJdbcLogger.java:137) 
DEBUG 05-04 13:11:48,385 <====      Total: 1 (BaseJdbcLogger.java:137) 
DEBUG 05-04 13:11:48,387 <==      Total: 1 (BaseJdbcLogger.java:137) 
2B

解决多对一的映射关系

”对一对应对象,对多对应集合“

collection解决多对一的映射关系

mapper接口

Dept getDeptAndEmp(@Param("did") String did);

mapper映射文件


    
    
    
        
        
        
        
        
    


collection标签用于解决多对一的映射关系,与association需要javatype获取对象类型不同,collection需要ofType获取集合内的对象类型

集合内对象不能再声明Dept类,防止套娃

通过分布查询解决多对一映射

mapper接口

Dept getDeptAndEMmpStep1(@Param("did") String did);
List getDeptAndEmpStep2(@Param("did") String did);

mapper映射文件


    
    
    



动态SQL

Mybatis框架的动态SQL技术是一种根据条件动态拼装SQL语句的功能,它存在的意义是为了解决动态拼接SQL语句字符串的痛点问题。

if标签

mapper接口

Emp getEmpByCondition(Emp emp);

mapper映射文件


使用恒成立条件1=1防止sql语句and前缀错误

测试输出

DEBUG 05-05 09:11:48,799 ==>  Preparing: select * from t_emp where 1 = 1 and emp_name = ? and sex = ? and email = ? (BaseJdbcLogger.java:137) 
DEBUG 05-05 09:11:48,824 ==> Parameters: 1A(String), 男(String), 123@qq.com(String) (BaseJdbcLogger.java:137) 
DEBUG 05-05 09:11:48,847 <==      Total: 1 (BaseJdbcLogger.java:137) 
Emp{eid=1, empName='1A', sex='男', email='123@qq.com', age='23', dept=null}

Process finished with exit code 0

where标签

mapper映射文件


当where标签中有内容时,会自动生成where关键字,并将内容前多余的and、or去掉

当where标签中没有内容时,where标签不会生成where关键字

where标签不能将内容后面的and、or去掉

trim标签

trim若标签中有内容时:

prefix|suffix:将trim标签中内容前面或者后面添加指定的内容

prefixOverrides|suffixOverrides:将trim标签中内容前面或者后面去掉指定的内容

若标签中没有内容:trim标签也没有任何的效果

mapper映射文件


输出

DEBUG 05-05 09:36:20,141 ==>  Preparing: select * from t_emp (BaseJdbcLogger.java:137) 
DEBUG 05-05 09:36:20,172 ==> Parameters:  (BaseJdbcLogger.java:137) 
DEBUG 05-05 09:36:20,196 <==      Total: 5 (BaseJdbcLogger.java:137) 
Emp{eid=1, empName='1A', sex='男', email='123@qq.com', age='23', dept=null}
Emp{eid=2, empName='2B', sex='女', email='123@qq.com', age='22', dept=null}
Emp{eid=3, empName='3S', sex='男', email='123@qq.com', age='23', dept=null}
Emp{eid=4, empName='9S', sex='女', email='123@qq.com', age='24', dept=null}
Emp{eid=5, empName='1T', sex='男', email='123@qq.com', age='22', dept=null}

choose:when、otherwise

when、otherwise相当于if if if ... else

foreach

属性collection表示需要循环的数组或集合,item为数组或集合中的每一个数据

separator表示循环体之间的分隔符

open|close可以在标签中的内容的开始和结束添加指定内容

foreach实现批量删除

mapper接口

int deleteMore(@Param("eids") Integer[] ids);

mapper映射文件


    delete from t_emp where eid in
    
        #{eid}
    

测试输出

DEBUG 05-05 10:11:59,303 ==>  Preparing: delete from t_emp where eid in ( ? , ? ) (BaseJdbcLogger.java:137) 
DEBUG 05-05 10:11:59,329 ==> Parameters: 6(Integer), 7(Integer) (BaseJdbcLogger.java:137) 
DEBUG 05-05 10:11:59,331 <==    Updates: 0 (BaseJdbcLogger.java:137) 
foreach实现批量添加

mapper接口

int insertMore(@Param("emps") List emps);

mapper配置文件


    insert into t_emp () values
    
        (null, #{emp.empName}, #{emp.sex}, #{emp.email},  1)
    

这里的include是引用sql片段

设置sql片段

eid, emp_name, sex, email, did

测试

@Test
public void testDynamicSQLMapper() {
    SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class)
      //Integer eid, String empName, String sex, String email, Integer did
    Emp emp1 = new Emp(null, "a1", "男", "123@qq.com");
    Emp emp2 = new Emp(null, "a2", "男", "123@qq.com");
    Emp emp3 = new Emp(null, "a3", "男", "123@qq.com");
    List emps = Arrays.asList(emp1, emp2, emp3);
    int result = mapper.insertMore(emps);
    System.out.println(result);
}

Mybatis缓存

缓存只适用于查询

Mybatis一级缓存

一级缓存是sqlSession级别,自动开启

令一级缓存失效的情况:

①不同的SqlSession对应不同的一级缓存

②同一个SqlSession但是查询条件不同

③同一个SqlSession两次查询期间执行了任何一次增删改操作

④同一个SqlSession两次查询期间手动清空了缓存,sqlsession.clearCache()

测试

SqlSession sqlSession = SqlSessionUtils.getSqlSession();
CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
List emps = mapper.getAll();
emps.forEach(emp -> System.out.println(emp));
SqlSession sqlSession1 = SqlSessionUtils.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
List emps1 = mapper1.getAll();
emps1.forEach(emp -> System.out.println(emp));

结果为两次sql查询

DEBUG 05-05 11:20:12,191 ==>  Preparing: select * from t_emp; (BaseJdbcLogger.java:137) 
DEBUG 05-05 11:20:12,213 ==> Parameters:  (BaseJdbcLogger.java:137) 
DEBUG 05-05 11:20:12,231 <==      Total: 11 (BaseJdbcLogger.java:137
DEBUG 05-05 11:20:12,297 ==>  Preparing: select * from t_emp; (BaseJdbcLogger.java:137) 
DEBUG 05-05 11:20:12,297 ==> Parameters:  (BaseJdbcLogger.java:137) 
DEBUG 05-05 11:20:12,300 <==      Total: 11 (BaseJdbcLogger.java:137) ) 

Mybatis二级缓存

二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询(即使SqlSession不同)的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取

二级缓存开启的条件:

a> 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置


    
    
    

b> 在mapper映射文件中设置标签cache


c> 二级缓存必须在SqlSession关闭或提交之后有效

d> 查询的数据所转换的实体类类型必须实现序列化的接口

即是使实体类继承Serializable接口

使二级缓存失效的情况:
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效

测试

@Test
public void testCacheTwo() throws IOException {
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = builder.build(resourceAsStream);
    SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
    SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
    CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
    List emps1 = mapper1.getAll();
    emps1.forEach(emp -> System.out.println(emp));
    sqlSession1.close();
    CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
    List emps2 = mapper2.getAll();
    emps2.forEach(emp -> System.out.println(emp));
    sqlSession2.close();
}

测试结果:

DEBUG 05-06 10:30:39,916 Cache Hit Ratio [com.hikaru.mybatis.mapper.CacheMapper]: 0.0 (LoggingCache.java:60) 
DEBUG 05-06 10:30:40,800 ==>  Preparing: select * from t_emp; (BaseJdbcLogger.java:137) 
DEBUG 05-06 10:30:40,822 ==> Parameters:  (BaseJdbcLogger.java:137) 
DEBUG 05-06 10:30:40,846 <==      Total: 11 (BaseJdbcLogger.java:13
7)

DEBUG 05-06 10:34:31,559 Cache Hit Ratio [com.hikaru.mybatis.mapper.CacheMapper]: 0.5 (LoggingCache.java:60) 

可以看到只查询了一次SQL,第二次缓存利用率为0.5

Mybatis二级缓存的相关配置

eviction属性:缓存回收策略

缓存是缓存到内存中的,故不能无限制地缓存,因此需要设定策略定期清除

LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
    FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
    SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
    WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
   默认的是 LRU。
flushInterval属性:刷新间隔,单位毫秒
    默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用增删改语句时刷新
size属性:引用数目,正整数
    代表缓存最多可以存储多少个对象,太大容易导致内存溢出
readOnly属性:只读,true/false
    true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
    false:读写缓存;会返回缓存对象的拷贝通过序列化)。这会慢一些,但是安全,因此默认是false

MyBatis缓存查询的顺序

先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据(因为二级缓存对应多个SQLSession,范围更大),可以拿来直接使用。
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存

第三方缓存EHCache

a>添加依赖



org.mybatis.caches
mybatis-ehcache
1.2.1



ch.qos.logback
logback-classic
1.2.3

b>各jar包功能

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

c>创建EHCache的配置文件ehcache.xml

<?xml version="1.0" encoding="utf-8" ?>






d>设置二级缓存的类型


e>加入logback日志

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

<?xml version="1.0" encoding="UTF-8"?>














f>EHCache配置文件说明

![](file://D:\资料\学习笔记\Java\Mybatis\1.png?msec=1651885414653)

Mybatis逆向工程

Simple版本(只有增删改查五个功能)

正向工程: 先创建实体类,再由框架生成数据库表。Hibernate支持正向工程

逆向工程: 先创建数据库表,然后由框架生成下面的资源:

①实体类

②Mapper接口

③Mapper映射文件

a 添加依赖和插件



    
    
        
        
            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.16
                
            
        
    

b 创建MyBatis核心文件

c 创建逆向工程的配置文件 generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>


    
    
        
        
        
        
        
            
            
        
        
        
            
        
        
        
            
        
        
        
        
        

其中enableSubPackages属性声明是否使用子包(设为false的话,com.hikaru.mybatis.pojo就会被当成一整个路径名而不是多个)

trimStrings属性可以将数据库字段名前后的空格去掉,方便生成资源

d>执行MBG插件的generate目标

![](file://D:\资料\学习笔记\Java\Mybatis\2.png?msec=1651885414566)

测试

DEBUG 05-06 12:23:44,119 ==>  Preparing: select eid, emp_name, age, sex, email, did from t_emp (BaseJdbcLogger.java:137) 
DEBUG 05-06 12:23:44,143 ==> Parameters:  (BaseJdbcLogger.java:137) 
DEBUG 05-06 12:23:44,167 <==      Total: 11 (BaseJdbcLogger.java:137) 
Emp{eid=1, empName='1A', age='23', sex='男', email='123@qq.com', did=1}
Emp{eid=2, empName='2B', age='22', sex='女', email='123@qq.com', did=2}

在执行过程中出现了数据库连接文件JBDC.properties以及log4j无法加载的问题,找了半天错,原来是target文件中没有这两个文件,在Maven中重新clean compile一下即可

高级版本(可实现条件CUID)

a 修改逆向工程核心配置文件的版本号


b 生成实体类Example,用于条件CURD 其余与Simple版本相同

c

测试查询、主键查询和条件查询
@Test
public void testGenerate() throws IOException {
    InputStream is = Resources.getResourceAsStream("mybatis-config.
    SqlSessionFactory sqlFactory = new SqlSessionFactoryBuilder().b
    SqlSession sqlSession = sqlFactory.openSession(true);
    EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
    //  List emps = mapper.selectByExample(null);
    //  emps.forEach(emp -> System.out.println(emp));
    EmpExample empExample = new EmpExample();
    empExample.createCriteria().andAgeLike("%21%");
    empExample.or().andEidEqualTo(3);
    empExample.or().andEidEqualTo(4);
    List emps = mapper.selectByExample(empExample);
    emps.forEach(emp -> System.out.println(emp));
}

输出SQL语句

Preparing: select eid, emp_name, age, sex, email, did from t_emp WHERE ( age like ? ) or( eid = ? ) or( eid = ? ) (BaseJdbcLogger.java:137) 
Parameters: %21%(String), 3(Integer), 4(Integer) (BaseJdbcLogger.java:137) 
Total: 2 (BaseJdbcLogger.java:137) 7) 
测试两种修改、删除
    @Test
    public void testGenerate2() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlFactory.openSession(true);

        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//        (Integer eid, String empName, String age, String sex, String email, Integer did) {
//        int result = mapper.updateByPrimaryKey(new Emp(1, "1A", null, "男", "456@qq.com", 2));
        int result = mapper.updateByPrimaryKeySelective(new Emp(1, "1A", null, "男", "456@qq.com", 2));
        System.out.println(result);

    }

直接修改会将null也跟新到数据库字段中,而selective选择性修改碰到null则不会对数据库字段做任何修改