MyBatis - 系统化整理 (二, 完结, 2020年4月14日)

文章目录

  • 7 日志
    • 7.1 日志工厂
    • 7.2 STDOUT_LOGGING
    • 7.3 LOG4J
  • 8 分页
    • 8.1 通过 limit 分页
    • 8.2 RowBounds分页
    • 8.3 插件分页: PageHelper
  • 9 使用注解开发
    • 9.1 注解开发
    • 9.2 autoTransaction (自动提交事务)
    • 9.3 @Param
    • 9.4 #{} 还是 ${}
  • 10 复杂查询
    • 10.1 "多对一"
      • 测试1: 查询时嵌套处理
      • 测试2: 对结果处理
    • 10.2 一对多
    • 10.3 小结
      • 10.3.1 resultMap标签的结构
      • 10.3.2 其他注意点
  • 11 Dynamic SQL
    • 11.1 环境搭建
    • 11.2 IF
    • 11.3 choose when otherwise
    • 11.4 trim (where set)
    • 11.5 foreach
    • 11.6 SQL片段
      • 11.6.1 示例
      • 11.6.2 注意
  • 12 缓存
    • 12.1 MyBatis 缓存
    • 12.1 一级缓存
    • 12.2 二级缓存
      • **12.2.1 简介**
      • 12.2.2 **最简使用方式**
      • 12.2.3 较高级使用方式
      • 12.2.4 清除策略
    • 12.3 自定义二级缓存
  • 13 BUG Set
    • 13.1 漏掉 resultMap\Type
    • 13.2 POJO最好实现序列化接口
  • 14 MyBatis 详细执行流程 底层原理

7 日志

如果数据库操作出现异常需要排错, 日志是最好的助手!

7.1 日志工厂

官网列出的 ‘logImpl’ (日志实现), 无默认值, 具体选择哪一个在 settings 中设置:

  • SLF4J
  • LOG4J [掌握]
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING (标准日志输出) [掌握]
  • NO_LOGGING

那么, 要如何使用呢?

7.2 STDOUT_LOGGING

测试一下吧:

  1. 在mybatis-config.xml 中设置, 无需任何其他设置:

    
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    settings>
    
  2. 运行test输出结果:

    D:\Software\jdk1.8.0_71\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=56252:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\plugins\junit\lib\junit5-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\plugins\junit\lib\junit-rt.jar;D:\Software\jdk1.8.0_71\jre\lib\charsets.jar;D:\Software\jdk1.8.0_71\jre\lib\deploy.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\access-bridge-64.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\cldrdata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\dnsns.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jaccess.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jfxrt.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\localedata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\nashorn.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunec.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunjce_provider.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunmscapi.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunpkcs11.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\zipfs.jar;D:\Software\jdk1.8.0_71\jre\lib\javaws.jar;D:\Software\jdk1.8.0_71\jre\lib\jce.jar;D:\Software\jdk1.8.0_71\jre\lib\jfr.jar;D:\Software\jdk1.8.0_71\jre\lib\jfxswt.jar;D:\Software\jdk1.8.0_71\jre\lib\jsse.jar;D:\Software\jdk1.8.0_71\jre\lib\management-agent.jar;D:\Software\jdk1.8.0_71\jre\lib\plugin.jar;D:\Software\jdk1.8.0_71\jre\lib\resources.jar;D:\Software\jdk1.8.0_71\jre\lib\rt.jar;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-03\target\test-classes;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-03\target\classes;D:\Software-idea\MavenRepository\repo-3.6.1\org\mybatis\mybatis\3.5.4\mybatis-3.5.4.jar;D:\Software-idea\MavenRepository\repo-3.6.1\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;D:\Software-idea\MavenRepository\repo-3.6.1\junit\junit\4.12\junit-4.12.jar;D:\Software-idea\MavenRepository\repo-3.6.1\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.zhangcl.test.UserDaoTest,getUserById
    Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
    Class not found: org.jboss.vfs.VFS
    JBoss 6 VFS API is not available in this environment.
    Class not found: org.jboss.vfs.VirtualFile
    VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment.
    Using VFS adapter org.apache.ibatis.io.DefaultVFS
    Find JAR URL: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
    Not a JAR: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
    Reader entry: User.class
    Listing file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
    Find JAR URL: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo/User.class
    Not a JAR: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo/User.class
    Reader entry: ����   1 <
    Checking to see if class com.zhangcl.pojo.User matches criteria [is assignable to Object]
    PooledDataSource forcefully closed/removed all connections.
    PooledDataSource forcefully closed/removed all connections.
    PooledDataSource forcefully closed/removed all connections.
    PooledDataSource forcefully closed/removed all connections.
    Opening JDBC Connection
    Sat Apr 11 09:49:23 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
    Created connection 128359175.
    Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7a69b07]
    ==>  Preparing: select * from mybatis.user where id = ?; 
    ==> Parameters: 2(Integer)
    <==    Columns: id, name, pwd
    <==        Row: 2, 张三, 123456
    <==      Total: 1
    User{id=2, name='张三', password='123456'}
    Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7a69b07]
    Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7a69b07]
    Returned connection 128359175 to pool.
    
    Process finished with exit code 0
    

7.3 LOG4J

什么是LOG4J

  • Apache的开源项目, 可以通过它控制日志信息输出目的地: 控制台、文件、GUI组建

  • 可以精确控制每一条日志信息的输出格式

  • 可以精确控制每一条日志信息的输出级别

  • 可以通过配置文件来设置它们, 而不必要修改代码

测试一下吧:

  1. 导入Maven依赖

    
    <dependency>
        <groupId>log4jgroupId>
        <artifactId>log4jartifactId>
        <version>1.2.17version>
    dependency>
    
  2. 在 MyBatis 核心配置文件, 也就是mybatis-config.xml 中配置使用log4j.

    要注意顺序性或顺序性报错的提示

    
    <settings>
        
        
        
        <setting name="logImpl" value="LOG4J"/>
    settings>
    
  3. 在 resource 目录下新建LOG4J的配置文件, log4j.properties

    #将等级为DEBUG的日志信息输出到console和file两个目的地
    log4j.rootLogger=DEBUG,console,file
    
    #控制台输出的相关设置
    log4j.appender.console=org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target=System.out
    log4j.appender.console.Threshold=DEBUG
    log4j.appender.console.layout=org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
    
    #文件输出的相关配置
    log4j.appender.file=org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/zhangcl.log
    log4j.appender.file.MaxFileSize=10mb
    log4j.appender.file.Threshold=DEBUG
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}[%c]%m%n
    
    #日志输出级别
    log4j.logger.org.mybatis=DEBUG
    log4j.logger.java.sql=DEBUG
    log4j.logger.java.sql.Statement=DEBUG
    log4j.logger.java.sql.ResultSet=DEBUG
    log4j.logger.java.sql.PreparedStatement=DEBUG
    
  4. 单元测试, 输出结果:

    D:\Software\jdk1.8.0_71\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=61217:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\plugins\junit\lib\junit5-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\plugins\junit\lib\junit-rt.jar;D:\Software\jdk1.8.0_71\jre\lib\charsets.jar;D:\Software\jdk1.8.0_71\jre\lib\deploy.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\access-bridge-64.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\cldrdata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\dnsns.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jaccess.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jfxrt.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\localedata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\nashorn.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunec.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunjce_provider.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunmscapi.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunpkcs11.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\zipfs.jar;D:\Software\jdk1.8.0_71\jre\lib\javaws.jar;D:\Software\jdk1.8.0_71\jre\lib\jce.jar;D:\Software\jdk1.8.0_71\jre\lib\jfr.jar;D:\Software\jdk1.8.0_71\jre\lib\jfxswt.jar;D:\Software\jdk1.8.0_71\jre\lib\jsse.jar;D:\Software\jdk1.8.0_71\jre\lib\management-agent.jar;D:\Software\jdk1.8.0_71\jre\lib\plugin.jar;D:\Software\jdk1.8.0_71\jre\lib\resources.jar;D:\Software\jdk1.8.0_71\jre\lib\rt.jar;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-03\target\test-classes;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-03\target\classes;D:\Software-idea\MavenRepository\repo-3.6.1\log4j\log4j\1.2.17\log4j-1.2.17.jar;D:\Software-idea\MavenRepository\repo-3.6.1\org\mybatis\mybatis\3.5.4\mybatis-3.5.4.jar;D:\Software-idea\MavenRepository\repo-3.6.1\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;D:\Software-idea\MavenRepository\repo-3.6.1\junit\junit\4.12\junit-4.12.jar;D:\Software-idea\MavenRepository\repo-3.6.1\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.zhangcl.test.UserDaoTest,getUserById
    [org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
    [org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
    [org.apache.ibatis.io.VFS]-Class not found: org.jboss.vfs.VFS
    [org.apache.ibatis.io.JBoss6VFS]-JBoss 6 VFS API is not available in this environment.
    [org.apache.ibatis.io.VFS]-Class not found: org.jboss.vfs.VirtualFile
    [org.apache.ibatis.io.VFS]-VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment.
    [org.apache.ibatis.io.VFS]-Using VFS adapter org.apache.ibatis.io.DefaultVFS
    [org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
    [org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
    [org.apache.ibatis.io.DefaultVFS]-Reader entry: User.class
    [org.apache.ibatis.io.DefaultVFS]-Listing file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
    [org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo/User.class
    [org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo/User.class
    [org.apache.ibatis.io.DefaultVFS]-Reader entry: ����   1 <
    [org.apache.ibatis.io.ResolverUtil]-Checking to see if class com.zhangcl.pojo.User matches criteria [is assignable to Object]
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
    Sat Apr 11 12:50:59 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1484171695.
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5876a9af]
    [com.zhangcl.dao.UserMapper.getUserById]-==>  Preparing: select * from mybatis.user where id = ?; 
    [com.zhangcl.dao.UserMapper.getUserById]-==> Parameters: 2(Integer)
    [com.zhangcl.dao.UserMapper.getUserById]-<==      Total: 1
    User{id=2, name='张三', password='123456'}
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5876a9af]
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5876a9af]
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1484171695 to pool.
    
    Process finished with exit code 0
    
    

**简单使用, 需要注意: **

  1. 导包: 注意不是 java.util 的, 而是 org.apache.Logger 的.

  2. 声明Logger对象. 考虑对象作用域问题, 可以把声明语句提升到类成员级别

    static Logger logger = Logger.getLogger(UserDaoTest.class);
    
  3. 日志的级别, 常用三种级别:

    logger.info("info: 进入了testLog4j()");
    logger.debug("debug: 进入了testLog4j()");
    logger.error("error: 进入了testLog4j()");
    /*
    在日志file中输出效果如下: 
    
    [INFO][20-04-11[com.zhangcl.test.UserDaoTest]info: 进入了testLog4j()
    [DEBUG][20-04-11[com.zhangcl.test.UserDaoTest]debug: 进入了testLog4j()
    [ERROR][20-04-11[com.zhangcl.test.UserDaoTest]error: 进入了testLog4j()
    */
    

8 分页

为什么要分页?

  • 可减少多余的数据处理量

8.1 通过 limit 分页

是SQL层面的分页, 以下是基本语法:

#[syntax] select * from user limit startIndex, pageSize;
select * from user limit 0, 3;
select * from user limit 3;	#两者等效 

使用 MyBatis 测试一下:

  1. 接口: UserMapper.java

    /*查 User 分页*/
    List<User> getUserWithLimit(Map<String, Integer> map);
    
  2. 映射文件: UserMapper.xml. 这里使用了万能Map, 可以注意一下.

    
    <select id="getUserWithLimit" resultType="User" parameterType="map">
        SELECT * FROM user LIMIT #{startIndex}, #{pageSize};
    select>
    
  3. 单元测试: UserMapperTest.java

    /*查 User Limit*/
    @Test
    public void getUserWithLimit(){
        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("startIndex", 1);
        map.put("pageSize", 2);
        List<User> userList = userMapper.getUserWithLimit(map);
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }
    
  4. 结果输出: 测试通过

    D:\Software\jdk1.8.0_71\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=64465:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\plugins\junit\lib\junit5-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\plugins\junit\lib\junit-rt.jar;D:\Software\jdk1.8.0_71\jre\lib\charsets.jar;D:\Software\jdk1.8.0_71\jre\lib\deploy.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\access-bridge-64.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\cldrdata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\dnsns.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jaccess.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jfxrt.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\localedata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\nashorn.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunec.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunjce_provider.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunmscapi.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunpkcs11.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\zipfs.jar;D:\Software\jdk1.8.0_71\jre\lib\javaws.jar;D:\Software\jdk1.8.0_71\jre\lib\jce.jar;D:\Software\jdk1.8.0_71\jre\lib\jfr.jar;D:\Software\jdk1.8.0_71\jre\lib\jfxswt.jar;D:\Software\jdk1.8.0_71\jre\lib\jsse.jar;D:\Software\jdk1.8.0_71\jre\lib\management-agent.jar;D:\Software\jdk1.8.0_71\jre\lib\plugin.jar;D:\Software\jdk1.8.0_71\jre\lib\resources.jar;D:\Software\jdk1.8.0_71\jre\lib\rt.jar;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-03\target\test-classes;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-03\target\classes;D:\Software-idea\MavenRepository\repo-3.6.1\log4j\log4j\1.2.17\log4j-1.2.17.jar;D:\Software-idea\MavenRepository\repo-3.6.1\org\mybatis\mybatis\3.5.4\mybatis-3.5.4.jar;D:\Software-idea\MavenRepository\repo-3.6.1\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;D:\Software-idea\MavenRepository\repo-3.6.1\junit\junit\4.12\junit-4.12.jar;D:\Software-idea\MavenRepository\repo-3.6.1\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.zhangcl.test.UserDaoTest,getUserWithLimit
    [org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
    [org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
    [org.apache.ibatis.io.VFS]-Class not found: org.jboss.vfs.VFS
    [org.apache.ibatis.io.JBoss6VFS]-JBoss 6 VFS API is not available in this environment.
    [org.apache.ibatis.io.VFS]-Class not found: org.jboss.vfs.VirtualFile
    [org.apache.ibatis.io.VFS]-VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment.
    [org.apache.ibatis.io.VFS]-Using VFS adapter org.apache.ibatis.io.DefaultVFS
    [org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
    [org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
    [org.apache.ibatis.io.DefaultVFS]-Reader entry: User.class
    [org.apache.ibatis.io.DefaultVFS]-Listing file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
    [org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo/User.class
    [org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo/User.class
    [org.apache.ibatis.io.DefaultVFS]-Reader entry: ����   1 <
    [org.apache.ibatis.io.ResolverUtil]-Checking to see if class com.zhangcl.pojo.User matches criteria [is assignable to Object]
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
    Sat Apr 11 14:37:33 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1529060733.
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5b239d7d]
    [com.zhangcl.dao.UserMapper.getUserWithLimit]-==>  Preparing: SELECT * FROM user LIMIT ?, ?; 
    [com.zhangcl.dao.UserMapper.getUserWithLimit]-==> Parameters: 1(Integer), 2(Integer)
    [com.zhangcl.dao.UserMapper.getUserWithLimit]-<==      Total: 2
    User{id=2, name='张三', password='null'}
    User{id=3, name='李四', password='null'}
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5b239d7d]
    [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5b239d7d]
    [org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1529060733 to pool.
    
    Process finished with exit code 0
    
    

8.2 RowBounds分页

属于 Java 代码层面的分页, 只作为了解, 据说技术比较老. 以下为基本步骤:

  1. 接口. 注意这里没有参数

    /*查 User 分页2: rowBounds*/
    List<User> getUserWithRowBounds();
    
  2. Mapper.xml. 注意使用了 resultMap

    <resultMap id="UserMap" type="User">
        <result column="pwd" property="password">result>
    resultMap>
    
    
    <select id="getUserWithRowBounds" resultMap="UserMap">
        SELECT * FROM user;
    select>
    
  3. 单元测试

    /*分页查User2 用RowBounds*/
    @Test
    public void getUserWithRowBounds(){
        RowBounds rowBounds = new RowBounds(1, 2);
        List<User> userList = sqlSession.selectList("com.zhangcl.dao.UserMapper.getUserWithRowBounds", null, rowBounds);
        for (User u : userList) {
            System.out.println("分页2"+u);
        }
        sqlSession.close();
    }
    
  4. 结果通过.

8.3 插件分页: PageHelper

据说用法和 RowBounds 比较接近, 了解即可.

如果真要学用, 可以直接上官网, 中文并且内容少而简单.

9 使用注解开发

为什么 “面向接口编程” ?

根本目的: 解耦, 定义与实现分离. leader写接口, 码农写实现.

9.1 注解开发

基本步骤

  1. 在核心配置文件 mybatis-config.xml 中注册绑定接口类

    <mappers>
        
        <mapper class="com.zhangcl.dao.UserMapper">mapper>
    mappers>
    
  2. 编写接口, 并且编写相应注解+SQL

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

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

9.2 autoTransaction (自动提交事务)

SqlSession sqlSession = sqlSessionFactory.openSqlSession(true);

参数true: 如果传入参数true, 则以为这开启自动提交事务

9.3 @Param

作用: 可以承上启下改名.

SQL中column: pwd <=> @Param("pwd") String password <=> POJO 中属性名 private String password

使用原则:

  • 基本类型必加
    1. 只有一个参数可以不加, 但建议还是加上
    2. ‘基本类型’ 的表述包括 String
  • 引用类型不加
  • 多个基本类型参数时更加要使用 @Param 注解修饰基本数据的参数.

9.4 #{} 还是 ${}

推荐使用 #{} , 因为 ${} 不能防止 ‘SQL注入’

10 复杂查询

用到resultMap

10.1 “多对一”

理解: 多个学生共同一个老师

处理思路: 无非两种, 子查询 | 联表查询

测试1: 查询时嵌套处理

理解上有点像子查询. 以下是具体步骤:

  1. 准备数据库

    CREATE TABLE teacher(
        id INT(10) NOT NULL PRIMARY KEY,
        NAME VARCHAR(30) DEFAULT NULL
    )ENGINE=INNODB DEFAULT CHARSET=utf8;
    
    INSERT INTO teacher(id,NAME) VALUES(1, '秦老师');
    
    CREATE TABLE student(
        id INT(10) NOT NULL,
        NAME VARCHAR(30) DEFAULT NULL,
        tid INT(10) DEFAULT NULL,
        PRIMARY KEY(id),
        KEY fktid(tid),
        CONSTRAINT fktid FOREIGN KEY(tid) REFERENCES teacher(id)
    )ENGINE=INNODB DEFAULT CHARSET=utf8;
    
  2. 新建module, 引入lombok的依赖包

    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>1.18.12version>
    dependency>
    
  3. 创建POJO: Student 和 Teacher

    package com.zhangcl.pojo;
    import lombok.Data;
    @Data
    public class Teacher {
        private int id;
        private String name;
    }
    
    // ..........................
    package com.zhangcl.pojo;
    import lombok.Data;
    @Data
    public class Student {
        private int id;
        private String name;
        private Teacher teacher;
    }
    
  4. 创建Mapper.xml: StudentMapper.xml 和 TeacherMapper.xml. 在StudentMapper.xml编写需要的SQL.

    起初我只是非常单纯的将resultType设置如下, 后来出现问题, 是为后话.

    
    <select id="getStudent" resultType="Student">
        select * from student;
    select>
    
  5. 在mybatis-config.xml文件中设置绑定注册Mapper或者接口类. 这里我们先简单测试一下, 用接口, 所以这里只注册接口绑定.

    <mappers>
        
        
        <mapper class="com.zhangcl.dao.TeacherMapper">mapper>
        <mapper class="com.zhangcl.dao.StudentMapper">mapper>
    mappers>
    

    注意: 我们这次把两个文件放在resources目录下的对应的com.zhangcl.dao目录下, 最后target输出的mapper文件和mapper类居然在同一个文件夹中!

    异常: 之前用的resource + *.xml的表述, 结果出现Could not found …的异常, 换了之后就解决了

  6. 测试

    // 1
    @Test
    public void testGetStudent(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> studentList = mapper.getStudent();
        for (Student student : studentList) {
            System.out.println(student);
        }
        sqlSession.close();
    }
    

    运行结果: teacher的值都是null, 引出难点.

    Created connection 957465255.
    Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3911c2a7]
    ==>  Preparing: select S.id,S.name,T.name from student S, teacher T where S.tid = T.id; 
    ==> Parameters: 
    <==    Columns: id, name, name
    <==        Row: 1, 小明, 秦老师
    <==        Row: 2, 小红, 秦老师
    <==        Row: 3, 小张, 秦老师
    <==        Row: 4, 小李, 秦老师
    <==        Row: 5, 小王, 秦老师
    <==      Total: 5
    Student(id=1, name=小明, teacher=null)
    Student(id=2, name=小红, teacher=null)
    Student(id=3, name=小张, teacher=null)
    Student(id=4, name=小李, teacher=null)
    Student(id=5, name=小王, teacher=null)
    
  7. [重点] 针对null的情况, 引出重点, 对resultMap更复杂的配置

    
    <select id="getStudent" resultMap="StudentTeacher">
        select * from student;
    select>
    <resultMap id="StudentTeacher" type="Student">
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher">association>
    resultMap>
    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id=#{id}
    select>
    

    写错: 一开始我在teacher的where中写的#{tid}, 结果不行, 按照视频抄写成id, 暂时可以. 原理我也有点蒙蔽.

    注意: 这里查询老师的#{tid}可以随便写

    执行结果Teacher有点像嵌套查询, 如下所示:

    Created connection 961712517.
    Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@39529185]
    ==>  Preparing: select * from student; 
    ==> Parameters: 
    <==    Columns: id, name, tid
    <==        Row: 1, 小明, 1
    ====>  Preparing: select * from teacher where id=? 
    ====> Parameters: 1(Integer)
    <====    Columns: id, name
    <====        Row: 1, 秦老师
    <====      Total: 1
    <==        Row: 2, 小红, 1
    <==        Row: 3, 小张, 1
    <==        Row: 4, 小李, 1
    <==        Row: 5, 小王, 1
    <==      Total: 5
    Student(id=1, name=小明, teacher=Teacher(id=1, name=秦老师))
    Student(id=2, name=小红, teacher=Teacher(id=1, name=秦老师))
    Student(id=3, name=小张, teacher=Teacher(id=1, name=秦老师))
    Student(id=4, name=小李, teacher=Teacher(id=1, name=秦老师))
    Student(id=5, name=小王, teacher=Teacher(id=1, name=秦老师))
    Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@39529185]
    Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@39529185]
    Returned connection 961712517 to pool.
    

测试2: 对结果处理

先联表查询, 再处理结果

  1. 接口: StudentMapper.java

    /*2*/
    List<Student> getStudent2();
    
  2. Mapper文件: StudentMapper.xml

    两表联查, 查询出结果后映射给一个resultMap, 重点它的配置:

    注意:

    1. 标签是result, 而不是id
    2. 处理Student中的teacher属性时相当于嵌套
    
    <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid, s.name sname,t.id tid, t.name tname
        from student s, teacher t
        where s.tid = t.id;
    select>
    <resultMap id="StudentTeacher2" type="Student">
        <result column="sid" property="id">result>
        <result column="sname" property="name">result>
        <association property="teacher" javaType="Teacher">
            <result column="tname" property="name">result>
            <result column="tid" property="id">result>
        association>
    resultMap>
    
  3. 执行结果,

    逻辑异常: 一开始老师的id都为0, 实际应该是null, 没有结果. 后在association中配置tid之后就可以了

    Created connection 367746789.
    Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15eb5ee5]
    ==>  Preparing: select s.id sid, s.name sname,t.id tid, t.name tname from student s, teacher t where s.tid = t.id; 
    ==> Parameters: 
    <==    Columns: sid, sname, tid, tname
    <==        Row: 1, 小明, 1, 秦老师
    <==        Row: 2, 小红, 1, 秦老师
    <==        Row: 3, 小张, 1, 秦老师
    <==        Row: 4, 小李, 1, 秦老师
    <==        Row: 5, 小王, 1, 秦老师
    <==      Total: 5
    2Student(id=1, name=小明, teacher=Teacher(id=1, name=秦老师))
    2Student(id=2, name=小红, teacher=Teacher(id=1, name=秦老师))
    2Student(id=3, name=小张, teacher=Teacher(id=1, name=秦老师))
    2Student(id=4, name=小李, teacher=Teacher(id=1, name=秦老师))
    2Student(id=5, name=小王, teacher=Teacher(id=1, name=秦老师))
    Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15eb5ee5]
    Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@15eb5ee5]
    Returned connection 367746789 to pool.
    

10.2 一对多

理解: 一个老师拥有多个学生

测试1: 结果处理方式 (相当于联表查询)

  1. 修改POJO

    • Student.java
    @Data
    public class Student {
        private int id;
        private String name;
        private int tid;
    }
    
    • Teacher.java
    @Data
    public class Teacher {
        private int id;
        private String name;
        // 每个老师拥有多个学生
        private List<Student> student;
    }
    
  2. 接口: TeacherMapper.java

    /*2 获取指定老师名下的所有学生信息及老师信息, 返回值确实是一个teacher*/
    Teacher getTeacher2(@Param("tid") int id);
    
  3. TeacherMapper.xml

    
    <select id="getTeacher2" resultMap="TeacherStudent2">
        select s.id sid, s.name sname, t.name tname, t.id tid
        from student s, teacher t
        where s.tid=t.id and t.id=#{tid};
    select>
    <resultMap id="TeacherStudent2" type="Teacher">
        <result property="id" column="tid">result>
        <result property="name" column="tname">result>
        
        <collection property="student" javaType="ArrayList" ofType="Student">
            <result property="id" column="sid">result>
            <result property="name" column="sname">result>
            <result property="tid" column="tid">result>
        collection>
    resultMap>
    
  4. Tests.java

    /*2*/
    @Test
    public void testGetTeacher2(){
        InputStream inputStream = null;
        SqlSession sqlSession = null;
        try {
            inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession();
            TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
            Teacher teacher = mapper.getTeacher2(1);
            System.out.println(teacher);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }
    
    }
    

测试2: 按照查询嵌套 (相当于子查询)

  1. TeacherMapper.java 接口

    /*3 按照查询嵌套实现*/
    Teacher getTeacher3(@Param("tid") int id);
    
  2. TeacherMapper.xml

    注意,

    1. select中的SQL, 一开始我没想起来要写where clause
    2. 这里collection中的column指teacher表的id, 所以是id 而不是tid
    
    <select id="getTeacher3" resultMap="TeacherStudent3">
        select * from teacher where id = #{tid};
    select>
    <resultMap id="TeacherStudent3" type="Teacher">
        <collection property="student" javaType="List" ofType="Student" select="getStudentByTeacherId" column="id"/>
    resultMap>
    
    <select id="getStudentByTeacherId" resultType="Student">
        select * from student where tid = #{tid}
    select>
    
  3. 测试, 通过

10.3 小结

10.3.1 resultMap标签的结构

  • resultMap 标签
    • result - 基本类型, 名称不一致时
    • association - 引用类型 Java类, 多对一
      • javaType - Java中的类型, 如Student\ Teacher, 不写也没报错
    • collection - 集合, 一对多
      • javaType - Java的类型, 如List
      • ofType - 指定映射到List (集合)中的pojo (集合元素\泛型的具体类型)

10.3.2 其他注意点

  • “SQL复杂+resultMap容易理解” vs “SQL简单+resultMap难以理解”, 尽量选择前者.

  • 如果错误不容易排查, 可以借助日志, 推荐LOG4J

  • 避免"慢SQL", 要逐渐开始考虑性能

    • MySQL 引擎
    • InnoDB 底层
    • 索引及索引优化

11 Dynamic SQL

11.1 环境搭建

准备数据库

# 动态SQL
USE mybatis;
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 '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8

创建一个project: mybatis-08-dynamicsql

  1. 导入依赖

    <dependencies>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.12version>
        dependency>
    
    dependencies>
    
  2. 配置文件

    
    
    
    
    <configuration>
    
        
        <properties resource="db.properties">
            <property name="username" value="root"/>
            <property name="password" value="Ms_910613"/>
        properties>
    
        
        <settings>
            
            <setting name="logImpl" value="STDOUT_LOGGING"/>
            
            <setting name="mapUnderscoreToCamelCase" value="true"/>
        settings>
    
        
        <typeAliases>
            <package name="com.zhangcl.pojo"/>
        typeAliases>
    
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <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>
    
        <mappers>
    
    
    
    
    
    
            <package name="com.zhangcl.dao"/>
        mappers>
    
    
    configuration>
    
  3. 实体类

    package com.zhangcl.pojo;
    
    import lombok.Data;
    
    import java.util.Date;
    @Data
    public class Blog {
        private String id;
        private String title;
        private String author;
        private Date createTime;    // 注意: 属性名, 字段名问题
        private int views;
    }
    
    
  4. Mapper接口和.XML文件

    package com.zhangcl.dao;
    
    import com.zhangcl.pojo.Blog;
    
    public interface BlogMapper {
    
        /*插入数据*/
        int addBlog(Blog blog);
    }
    
    
    
    
    <mapper namespace="com.zhangcl.dao.BlogMapper">
        <insert id="addBlog" parameterType="Blog">
            insert into blog(id, title, author, create_time, views)
            values(#{id},#{title},#{author},#{createTime},#{views})
        insert>
    
    mapper>
    
  5. 工具类

    package com.zhangcl.util;
    
    import org.junit.Test;
    
    import java.util.UUID;
    
    /**
     * 用于生成唯一ID
     */
    public class IDUtils {
    
        public static String getId(){
            return UUID.randomUUID().toString().replace("-","");
        }
    
        @Test
        public void testGet(){
            System.out.println(IDUtils.getId());
        }
    
    }
    
    
    
    //##############################################
    package com.zhangcl.util;
    
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    // sqlSessionFactory --> sqlSession
    public class MybatisUtils {
        private static SqlSessionFactory sqlSessionFactory;
        static {
            try {
                String resource = "mybatis-config.xml"; //src/main/resources/
    //            System.out.println("打桩1" + resource);
                InputStream inputStream = Resources.getResourceAsStream(resource);
    //            System.out.println("打桩2" + inputStream);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //            System.out.println("打桩3" + sqlSessionFactory);
            } catch (IOException e) {
                System.out.println("工具类出错了!");
                e.printStackTrace();
            }
        }
    
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    }
    
    

11.2 IF

  1. BlogMapper.java

    List<Blog> queryBlogIF(Map map);
    
  2. BlogMapper.xml. 其中where 1=1是个一比较巧妙的用法, 避免了where关键字可能存在的尴尬处境

    
    <select id="queryBlogIF" parameterType="map" resultType="Blog">
        select * from blog where 1=1
        <if test="title != null">
            and title = #{title}
        if>
        <if test="author != null">
            and author = #{author}
        if>
    select>
    
  3. Tests.java

    /*1*/
    @Test
    public void queryBlogIF(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    
        Map map = new HashMap();
        map.put("title", "Java不难");
        List<Blog> blogs = mapper.queryBlogIF(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
    
    
        sqlSession.close();
    }
    

11.3 choose when otherwise

11.4 trim (where set)

11.5 foreach

11.6 SQL片段

11.6.1 示例

其实用where举例不合理


<sql id="NAME">
	WHERE ...
sql>


<select ...>
	select ... from ...
	<include refid="NAME">include>    
select>

11.6.2 注意

  • 最好基于单表来定义SQL片段
  • 不要引用where (以上例子不合理)

12 缓存

12.1 MyBatis 缓存

简介:

MyBatis默认定义了两级缓存: 一级缓存 | 二级缓存

顺序:

  1. 数据一定先进一级缓存, 如果二级开启, 则关闭一级后进二级

  2. 但查询时候, 是先去二级查, 再去一级查, 再去数据库

    以上可以通过日志看出轨迹: cache hit…

12.1 一级缓存

简介:

默认开启, 是SqlSession级别的缓存, 也称为本地缓存、会话缓存.

SqlSession级别: 与数据库同一次会话期间select到的数据都会放在这个一级缓存中.

测试:

秦老师做了演示. 在同一个SqlSession中查询两次相同的东西, 只查询了一次, 并且两次查询的是同一个引用对象.

缓存失效:

  • 同一个SqlSession中, 第一次查之后 (产生缓存), 此时对同表的另一条数据进行了更新, 居然也会导致首次查的缓存失效
  • 跨Mapper.xml
  • 手动清除: SqlSession.clearCache();

12.2 二级缓存

12.2.1 简介

简介

需要手动开启和配置的, 是nameSpace级别的缓存, 也称"全局缓存", 也可以自定义二级缓存

nameSpace级别: 每个mapper查出的数据, 会在各自对应的缓存 (map) 中 [个人理解]

基本工作机制:

  1. 会话中查询一条数据, 会放在当前会话的一级缓存中 (都会先进一级缓存)
  2. 如果这个会话关闭, 则一级缓存关闭, 则其中的数据被保存到二级缓存中. (二级缓存此时真正生效)
  3. 新的会话查询, 可以从这个二级缓存中获取数据

开启大致步骤

  1. MyBatis 核心配置文件: mybatis-config.xml 中

    <setting name="cacheEnable" value="true">setting>
    
  2. 将要使用缓存的某个 Mapper.xml 中

    <cache/>
    或者
    <cache
      eviction="FIFO"
      flushInterval="60000"
      size="512"
      readOnly="true"/>
    

12.2.2 最简使用方式

在SQL映射文件中添加以下代码即可

<cache/>

详解:

  • 映射文件中的所有 select 语句结果会被缓存
  • 所有 insert、update、delete 语句会刷新缓存
  • 会使用"最近最少使用"算法 (LRU, Least Recently Used) 来清除不需要的缓存
  • 不会定时刷新, 不涉及"刷新间隔"的概念
  • 最多会保存1024个引用
  • “读\写缓存”, 获取到的对象不共享, 可以安全地被调用者修改而不干扰其他调用者或线程的潜在修改

12.2.3 较高级使用方式

示例: 可以修改 cache 元素的属性, 实现比较 customized 的配置, 比如

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

详解:

  • FIFO 清除策略 (first in first out, 先进先出, 队列)
  • 60s 间隔刷新
  • 最多 512 个引用
  • “只读缓存” (readOnly), 对获取的对象修改可能会在不同线程的调用者中产生冲突

通用说明:

12.2.4 清除策略

有哪些清除策略可供使用?

  • LRU - 最近最少使用
  • FIFO - 先进先出
  • SOFT - 基于垃圾回收和软引用规则移除对象
  • WEAK - 更积极地基于依赖垃圾回收和弱引用规则移除对象

12.3 自定义二级缓存

课程以ehcache为例, 工作应该用redis

什么是自定义二级缓存?

实现MyBatis所定义的缓存接口Cache, 即可自定义二级缓存

13 BUG Set

13.1 漏掉 resultMap\Type

Caused by: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement ‘com.zhangcl.dao.BlogMapper.queryBlogIF’. It’s likely that neither a Result Type nor a Result Map was specified.

13.2 POJO最好实现序列化接口

14 MyBatis 详细执行流程 底层原理

  1. Resources 获取\加载全局核心配置文件
  2. 实例化 SqlSessionFactoryBuilder 建造器
    1. 解析配置文件流XMLConfigBuilder
    2. 所有的配置信息保存在Configuration
  3. 实例化 SqlSessionFactory
  4. transactional 事务管理器
  5. 创建 executor 执行器
  6. 创建 sqlSession 会话
  7. 实现 CRUD
    • 失败, 回 4
    • 成功, 到 8
  8. 提交事务
  9. 结束

changelog:

  • 2020年4月11日 凌晨

    先发布一次到blog

  • 2020年4月13日

    学完

你可能感兴趣的:(数据库,-,持久层)