尚硅谷-MyBatis3.5.7-非零基础-笔记篇-03

面试最爱问这些,缓存的作用和机制啦,特别是MyBatis还有跟Redis结合的部分!潸然泪下!希望多年以后,我看到我写的这些笔记,能很“不屑”地说一句,“诶呀,不过就是balabala”哈哈哈

MyBatis的缓存机制 

MyBatis的一级缓存

一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。

使一级缓存失效的四种情况:
1) 不同的 SqlSession 对应不同的一级缓存
2) 同一个 SqlSession 但是查询条件不同
3) 同一个 SqlSession 两次查询期间执行了任何一次增删改操作
4) 同一个 SqlSession 两次查询期间手动清空了缓存

 一级缓存也称为本地缓存,第一篇笔记debug的时候,如果有印象,应该见过localCache这个变量,当MyBatis设置完参数和参数值映射关系,准备执行SQL时,当查询二级缓存和一级缓存都没有命中时,就会直接从数据库查询了,查询返回的结果,会先放到一级缓存中,然后再返回。

localCache.putObject(key, EXECUTION_PLACEHOLDER) 先在缓存中占个位置

try查询返回结果list前,先执行finally,把这个占的位置拿出来removeObject

localCache.putObject(key, list),正式把查询结果放到本地缓存中

key就是hashCode + 查询的sql id + sql语句 + 参数

 当sqlSession flush或close后,sqlSession中的localCache就会被清空。

本地缓存不能被关闭,但可以调用clearCache()清空本地缓存,或改变缓存的作用域(比如二级缓存若开启的话,一级缓存的东西就会放到二级缓存了)我还以为是这个意思呢

mybatis3.1之后可以配置本地缓存的作用域:mybatis.xml

MyBatis利用本地缓存机制(Local Cache)防止循环引用(循环引用)和加速重复嵌套查询。

属性localCacheScope,可选项SESSION | STATEMENT,默认是SESSION,会缓存一个sqlSession中执行的所有查询,但如果是STATEMENT,官方的说法是本地会话仅在语句执行上,对同一个sqlSession的不同调用,将不会共享数据。

总的来说:在update、insert、delete、flushCache=“true”、commit、rollback、LocalCacheScope.STATEMENT等情况下,一级缓存就都会被清空。

演示一级缓存的功力~~~

相同sqlSession、相同mapper对象或者不同mapper对象都会使用一级缓存:

演示一级缓存失效的几种情况

1) 不同的SqlSession对应不同的一级缓存

2) 同一个SqlSession但是查询条件不同

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

注意!!!这里增删改不一定是这个mapper,任意mapper有增删改都会使本地缓存刷新掉。 

4) 同一个SqlSession两次查询期间手动清空了缓存

MyBatis的二级缓存

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

在尚硅谷2018年发布的教程课件中,也有说法二级缓存是namespace级别的,也就是Mapper级别的,这个看怎么理解,二级缓存和一级缓存其实是有交叉的,并不是包含的关系,但namespace一致的情况下,二级缓存包含了所有sqlSession的本地缓存,这么理解,是不是就是sqlSessionFactory级别的?

为了更好地扩展功能,也可以通过实现cache接口自定义二级缓存。

二级缓存开启的条件:
a> 在核心配置文件中,设置全局配置属性 cacheEnabled="true" ,默认为 true ,不需要设置
b> 在映射文件中设置标签 <cache />

二级缓存(second level cache),全局作用域缓存,默认不开启,需手动配置

二级缓存只有在sqlSession关闭或提交之后才会生效

 演示一下二级缓存的功力~~~

<!--设置全局配置属性cacheEnabled="true",默认为true,不需要设置-->
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

在映射文件中设置标签<cache/>或<cache></cache> 

 

于是不出意外的话,就出意外了;感受到了二级缓存,但命中率怎么都是0!!! 

原因就在获取sqlSession的这个工具类!!!这个时候,就可以理解上面说的二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存。而这里工具类中,每次调用sqlSession,sqlSessionFactory都是全新的,上面两个SQL根本就是在两个sqlSessionFactory中,也就是涉及两个二级缓存。

public class SqlSessionUtil {
    public static SqlSession getSqlSession() {
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        return sqlSession;
    }
}

 因此,我们不能再使用这个工具类去获取sqlSession了。

这里可以看到Cache Hit Ratio [com.coffeeship.mapper.UserMapper]: 0.5,命中了

这里为什么是0.5呢,因为第一次查询算一次,第二次本身是一次,这样就是0.5,再查一次,命中率就是2/3了。

然后为什么这里返回两个对象是false呢,那是因为二级缓存的配置中,我使用的都是默认的配置,比方说readOnly,默认是false,会返回缓存对象的拷贝。

这里我们可以试着更改一下二级缓存配置:

<cache readOnly="true"></cache>

 这里返回的就是缓存对象的相同实例,因此两个对象就是一样的。

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

 这里千万注意!!!又感觉2018那个教程说的二级缓存是namespace级别的,是对的了。这里我尝试在EmpMapper名称空间进行增加数据操作,发现UserMapper那还是用到了二级缓存。

 但UserMapper名称空间发生增加数据操作后,虽然命中率仍然是0.5,但还是从数据库去查询数据了,也就导致user1和user2是不同的对象,但user2和user3是同一个对象实例。

 

二级缓存的属性设置

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是持久层框架,跟那些专门做缓存的框架或技术不能比,为了更好地提升数据库查询效率,就要提升缓存的地位,就需要“敞开国门,取其精华”了~~~

这里整合的第三方缓存是用于优化二级缓存的,不是替换一级缓存的。

添加依赖

<!-- Mybatis EHCache整合包 -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.1</version>
</dependency>
<!-- slf4j日志门面的一个具体实现 -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

各jar包角色

mybatis-ehcache :Mybatis和EHCache的整合包

ehcache :EHCache核心包

slf4j-api :SLF4J日志门面包

logback-classic :支持SLF4J门面接口的一个具体实现

创建配置文件ehcache.xml

<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!-- 磁盘保存路径 -->
    <diskStore path="D:\010 Java\13 MyBatis\EHCache"/>
    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="true"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

这里就直接附上尚硅谷课件总结的这些属性及作用了~ 

 

注意配置文件名称都是固定的,像logback.xml、log4j.xml这种都是一样,为什么呢?可以这么想,我们在使用到这些框架的时候,都没有哪个位置去指定配置文件的位置,那自然就有一个默认位置,比方说类路径下,也必须有一个默认的名称,这些固定好了,约定俗成了,那就不需要去指定配置文件位置了。而你像UserMapper.xml,这种咱们自定义的xml文件,我就需要在sql映射文件中去指定它的路径,对不对。嘿嘿,我是这么理解的,欢迎指正!

设置二级缓存的类型

在sql映射文件中设置

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

加入logback日志

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

创建logback的配置文件logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT"
              class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志输出的格式 -->
            <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
        </encoder>
    </appender>
    <!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="DEBUG">
        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
        <appender-ref ref="STDOUT"/>
    </root>
    <!-- 根据特殊需求指定局部日志级别 -->
    <logger name="com.coffeeship.mapper" level="DEBUG"/>
</configuration>

那来show一下EHCache的功力~~~

public class MyBatisTest {
    @Test
    public void testEHCache() {

        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        User user1 = mapper1.getUserById(6);
        sqlSession1.close();//关闭sqlSession,将本地缓存刷到二级缓存
        SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
        //模拟一次增删改操作
//        EmpMapper mapper = sqlSession2.getMapper(EmpMapper.class);
//        mapper.insertEmp(new Emp(null, "coffees", 18, "coffee@163.com", new Dept(1, "开发部")));
        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        mapper2.insertUser(new User(null, "second", "cache", 66, "男", "cache@163.com"));
        User user2 = mapper2.getUserById(6);
        User user3 = mapper2.getUserById(6);
        System.out.println(user1 == user2);
        System.out.println(user2 == user3);
    }
}

这里日志就相当丰富了,最后结果跟上面MyBatis二级缓存效果是一样的。

"D:\Program Files\Java\jdk-1.8\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\IntelliJ IDEA 2022.3.3\lib\idea_rt.jar=54955:D:\Program Files\IntelliJ IDEA 2022.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\IntelliJ IDEA 2022.3.3\lib\idea_rt.jar;D:\Program Files\IntelliJ IDEA 2022.3.3\plugins\junit\lib\junit5-rt.jar;D:\Program Files\IntelliJ IDEA 2022.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\Java\jdk-1.8\jre\lib\charsets.jar;D:\Program Files\Java\jdk-1.8\jre\lib\deploy.jar;D:\Program Files\Java\jdk-1.8\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk-1.8\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk-1.8\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk-1.8\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk-1.8\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk-1.8\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk-1.8\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk-1.8\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk-1.8\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk-1.8\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk-1.8\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk-1.8\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk-1.8\jre\lib\javaws.jar;D:\Program Files\Java\jdk-1.8\jre\lib\jce.jar;D:\Program Files\Java\jdk-1.8\jre\lib\jfr.jar;D:\Program Files\Java\jdk-1.8\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk-1.8\jre\lib\jsse.jar;D:\Program Files\Java\jdk-1.8\jre\lib\management-agent.jar;D:\Program Files\Java\jdk-1.8\jre\lib\plugin.jar;D:\Program Files\Java\jdk-1.8\jre\lib\resources.jar;D:\Program Files\Java\jdk-1.8\jre\lib\rt.jar;D:\Java\MyBatis-MyBatisPlus\mybatis-projects\mybatis-ehcache\target\classes;D:\maven\repository\org\mybatis\caches\mybatis-ehcache\1.2.1\mybatis-ehcache-1.2.1.jar;D:\maven\repository\net\sf\ehcache\ehcache\2.10.6\ehcache-2.10.6.jar;D:\maven\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\maven\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\maven\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;D:\maven\repository\org\mybatis\mybatis\3.5.7\mybatis-3.5.7.jar;D:\maven\repository\junit\junit\4.13.2\junit-4.13.2.jar;D:\maven\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\maven\repository\mysql\mysql-connector-java\5.1.3\mysql-connector-java-5.1.3.jar;D:\maven\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.coffeeship.test.MyBatisTest,testEHCache
16:55:26,405 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
16:55:26,405 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
16:55:26,405 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/D:/Java/MyBatis-MyBatisPlus/mybatis-projects/mybatis-ehcache/target/classes/logback.xml]
16:55:26,442 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
16:55:26,443 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
16:55:26,447 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
16:55:26,461 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
16:55:26,461 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
16:55:26,462 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [com.coffeeship.mapper] to DEBUG
16:55:26,462 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
16:55:26,462 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@67b467e9 - Registering current configuration as safe fallback point
[16:55:26.464] [DEBUG] [main] [org.apache.ibatis.logging.LogFactory] [Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.]
[16:55:26.467] [DEBUG] [main] [org.apache.ibatis.io.VFS] [Class not found: org.jboss.vfs.VFS]
[16:55:26.467] [DEBUG] [main] [org.apache.ibatis.io.JBoss6VFS] [JBoss 6 VFS API is not available in this environment.]
[16:55:26.467] [DEBUG] [main] [org.apache.ibatis.io.VFS] [Class not found: org.jboss.vfs.VirtualFile]
[16:55:26.468] [DEBUG] [main] [org.apache.ibatis.io.VFS] [VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment.]
[16:55:26.468] [DEBUG] [main] [org.apache.ibatis.io.VFS] [Using VFS adapter org.apache.ibatis.io.DefaultVFS]
[16:55:26.468] [DEBUG] [main] [org.apache.ibatis.io.DefaultVFS] [Find JAR URL: file:/D:/Java/MyBatis-MyBatisPlus/mybatis-projects/mybatis-ehcache/target/classes/com/coffeeship/pojo]
[16:55:26.468] [DEBUG] [main] [org.apache.ibatis.io.DefaultVFS] [Not a JAR: file:/D:/Java/MyBatis-MyBatisPlus/mybatis-projects/mybatis-ehcache/target/classes/com/coffeeship/pojo]
[16:55:26.488] [DEBUG] [main] [org.apache.ibatis.io.DefaultVFS] [Reader entry: User.class]
[16:55:26.489] [DEBUG] [main] [org.apache.ibatis.io.DefaultVFS] [Listing file:/D:/Java/MyBatis-MyBatisPlus/mybatis-projects/mybatis-ehcache/target/classes/com/coffeeship/pojo]
[16:55:26.489] [DEBUG] [main] [org.apache.ibatis.io.DefaultVFS] [Find JAR URL: file:/D:/Java/MyBatis-MyBatisPlus/mybatis-projects/mybatis-ehcache/target/classes/com/coffeeship/pojo/User.class]
[16:55:26.489] [DEBUG] [main] [org.apache.ibatis.io.DefaultVFS] [Not a JAR: file:/D:/Java/MyBatis-MyBatisPlus/mybatis-projects/mybatis-ehcache/target/classes/com/coffeeship/pojo/User.class]
[16:55:26.490] [DEBUG] [main] [org.apache.ibatis.io.DefaultVFS] [Reader entry: ����   4 S]
[16:55:26.491] [DEBUG] [main] [org.apache.ibatis.io.ResolverUtil] [Checking to see if class com.coffeeship.pojo.User matches criteria [is assignable to Object]]
[16:55:26.501] [DEBUG] [main] [org.apache.ibatis.datasource.pooled.PooledDataSource] [PooledDataSource forcefully closed/removed all connections.]
[16:55:26.501] [DEBUG] [main] [org.apache.ibatis.datasource.pooled.PooledDataSource] [PooledDataSource forcefully closed/removed all connections.]
[16:55:26.501] [DEBUG] [main] [org.apache.ibatis.datasource.pooled.PooledDataSource] [PooledDataSource forcefully closed/removed all connections.]
[16:55:26.501] [DEBUG] [main] [org.apache.ibatis.datasource.pooled.PooledDataSource] [PooledDataSource forcefully closed/removed all connections.]
[16:55:26.517] [DEBUG] [main] [net.sf.ehcache.config.ConfigurationFactory] [Configuring ehcache from ehcache.xml found in the classpath: file:/D:/Java/MyBatis-MyBatisPlus/mybatis-projects/mybatis-ehcache/target/classes/ehcache.xml]
[16:55:26.517] [DEBUG] [main] [net.sf.ehcache.config.ConfigurationFactory] [Configuring ehcache from URL: file:/D:/Java/MyBatis-MyBatisPlus/mybatis-projects/mybatis-ehcache/target/classes/ehcache.xml]
[16:55:26.518] [DEBUG] [main] [net.sf.ehcache.config.ConfigurationFactory] [Configuring ehcache from InputStream]
[16:55:26.525] [DEBUG] [main] [net.sf.ehcache.config.BeanHandler] [Ignoring ehcache attribute xmlns:xsi]
[16:55:26.526] [DEBUG] [main] [net.sf.ehcache.config.BeanHandler] [Ignoring ehcache attribute xsi:noNamespaceSchemaLocation]
[16:55:26.526] [DEBUG] [main] [net.sf.ehcache.config.DiskStoreConfiguration] [Disk Store Path: D:\010 Java\13 MyBatis\EHCache]
[16:55:26.533] [DEBUG] [main] [net.sf.ehcache.CacheManager] [Creating new CacheManager with default config]
[16:55:26.534] [DEBUG] [main] [net.sf.ehcache.util.PropertyUtil] [propertiesString is null.]
[16:55:26.539] [DEBUG] [main] [net.sf.ehcache.config.ConfigurationHelper] [No CacheManagerEventListenerFactory class specified. Skipping...]
[16:55:26.875] [DEBUG] [main] [net.sf.ehcache.Cache] [No BootstrapCacheLoaderFactory class specified. Skipping...]
[16:55:26.875] [DEBUG] [main] [net.sf.ehcache.Cache] [CacheWriter factory not configured. Skipping...]
[16:55:26.875] [DEBUG] [main] [net.sf.ehcache.config.ConfigurationHelper] [No CacheExceptionHandlerFactory class specified. Skipping...]
[16:55:26.887] [DEBUG] [main] [net.sf.ehcache.DiskStorePathManager] [Using diskstore path D:\010 Java\13 MyBatis\EHCache]
[16:55:26.887] [DEBUG] [main] [net.sf.ehcache.DiskStorePathManager] [Holding exclusive lock on D:\010 Java\13 MyBatis\EHCache\.ehcache-diskstore.lock]
[16:55:26.888] [DEBUG] [main] [net.sf.ehcache.store.disk.DiskStorageFactory] [Failed to delete file com%002ecoffeeship%002emapper%002e%0055ser%004dapper.data]
[16:55:26.888] [DEBUG] [main] [net.sf.ehcache.store.disk.DiskStorageFactory] [Failed to delete file com%002ecoffeeship%002emapper%002e%0055ser%004dapper.index]
[16:55:26.891] [DEBUG] [main] [net.sf.ehcache.store.disk.DiskStorageFactory] [Matching data file missing (or empty) for index file. Deleting index file D:\010 Java\13 MyBatis\EHCache\com%002ecoffeeship%002emapper%002e%0055ser%004dapper.index]
[16:55:26.891] [DEBUG] [main] [net.sf.ehcache.store.disk.DiskStorageFactory] [Failed to delete file com%002ecoffeeship%002emapper%002e%0055ser%004dapper.index]
[16:55:26.917] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Pass-Through Statistic: LOCAL_OFFHEAP_SIZE]
[16:55:26.917] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Pass-Through Statistic: LOCAL_OFFHEAP_SIZE_BYTES]
[16:55:26.917] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Pass-Through Statistic: WRITER_QUEUE_LENGTH]
[16:55:26.917] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Pass-Through Statistic: REMOTE_SIZE]
[16:55:26.917] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Pass-Through Statistic: LAST_REJOIN_TIMESTAMP]
[16:55:26.922] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Operation Statistic: OFFHEAP_GET]
[16:55:26.923] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Operation Statistic: OFFHEAP_PUT]
[16:55:26.923] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Operation Statistic: OFFHEAP_REMOVE]
[16:55:26.924] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Operation Statistic: XA_COMMIT]
[16:55:26.924] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Operation Statistic: XA_ROLLBACK]
[16:55:26.924] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Operation Statistic: XA_RECOVERY]
[16:55:26.925] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Operation Statistic: CLUSTER_EVENT]
[16:55:26.925] [DEBUG] [main] [net.sf.ehcache.statistics.extended.ExtendedStatisticsImpl] [Mocking Operation Statistic: NONSTOP]
[16:55:26.928] [DEBUG] [main] [net.sf.ehcache.Cache] [Initialised cache: com.coffeeship.mapper.UserMapper]
[16:55:26.928] [DEBUG] [main] [net.sf.ehcache.config.ConfigurationHelper] [CacheDecoratorFactory not configured for defaultCache. Skipping for 'com.coffeeship.mapper.UserMapper'.]
[16:55:26.971] [DEBUG] [main] [com.coffeeship.mapper.UserMapper] [Cache Hit Ratio [com.coffeeship.mapper.UserMapper]: 0.0]
[16:55:26.974] [DEBUG] [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction] [Opening JDBC Connection]
[16:55:27.092] [DEBUG] [main] [org.apache.ibatis.datasource.pooled.PooledDataSource] [Created connection 379478400.]
[16:55:27.097] [DEBUG] [main] [com.coffeeship.mapper.UserMapper.getUserById] [==>  Preparing: select * from t_user where id = ?]
[16:55:27.115] [DEBUG] [main] [com.coffeeship.mapper.UserMapper.getUserById] [==> Parameters: 6(Integer)]
[16:55:27.124] [DEBUG] [main] [com.coffeeship.mapper.UserMapper.getUserById] [<==      Total: 1]
[16:55:27.125] [DEBUG] [main] [net.sf.ehcache.store.disk.Segment] [put added 0 on heap]
[16:55:27.126] [DEBUG] [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction] [Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@169e6180]]
[16:55:27.128] [DEBUG] [main] [org.apache.ibatis.datasource.pooled.PooledDataSource] [Returned connection 379478400 to pool.]
[16:55:27.128] [DEBUG] [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction] [Opening JDBC Connection]
[16:55:27.128] [DEBUG] [main] [org.apache.ibatis.datasource.pooled.PooledDataSource] [Checked out connection 379478400 from pool.]
[16:55:27.128] [DEBUG] [main] [com.coffeeship.mapper.UserMapper.insertUser] [==>  Preparing: insert into t_user values(null, ?, ?, ?, ?, ?)]
[16:55:27.129] [DEBUG] [main] [com.coffeeship.mapper.UserMapper.insertUser] [==> Parameters: second(String), cache(String), 66(Integer), 男(String), cache@163.com(String)]
[16:55:27.133] [DEBUG] [com%002ecoffeeship%002emapper%002e%0055ser%004dapper.data] [net.sf.ehcache.store.disk.Segment] [fault removed 0 from heap]
[16:55:27.133] [DEBUG] [com%002ecoffeeship%002emapper%002e%0055ser%004dapper.data] [net.sf.ehcache.store.disk.Segment] [fault added 0 on disk]
[16:55:27.137] [DEBUG] [main] [com.coffeeship.mapper.UserMapper.insertUser] [<==    Updates: 1]
[16:55:27.138] [DEBUG] [main] [com.coffeeship.mapper.UserMapper] [Cache Hit Ratio [com.coffeeship.mapper.UserMapper]: 0.5]
[16:55:27.138] [DEBUG] [main] [com.coffeeship.mapper.UserMapper.getUserById] [==>  Preparing: select * from t_user where id = ?]
[16:55:27.138] [DEBUG] [main] [com.coffeeship.mapper.UserMapper.getUserById] [==> Parameters: 6(Integer)]
[16:55:27.138] [DEBUG] [main] [com.coffeeship.mapper.UserMapper.getUserById] [<==      Total: 1]
[16:55:27.139] [DEBUG] [main] [com.coffeeship.mapper.UserMapper] [Cache Hit Ratio [com.coffeeship.mapper.UserMapper]: 0.6666666666666666]
false
true

MyBatis逆向工程

本质是一个代码生成器

 MDG代码生成器

官方文档地址:https://mybatis.org/generator/

官方工程地址:Releases · mybatis/generator · GitHub

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

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

Java实体类 、Mapper接口 、Mapper映射文件

添加依赖和插件

<!-- 依赖MyBatis核心包 -->
<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<build>
    <!-- 构建过程中用到的插件 -->
    <plugins>
        <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.0</version>
            <!-- 插件的依赖 -->
            <dependencies>
                <!-- 逆向工程的核心依赖 -->
                <dependency>
                    <groupId>org.mybatis.generator</groupId>
                    <artifactId>mybatis-generator-core</artifactId>
                    <version>1.3.2</version>
                </dependency>
                <!-- 数据库连接池 -->
                <dependency>
                    <groupId>com.mchange</groupId>
                    <artifactId>c3p0</artifactId>
                    <version>0.9.2</version>
                </dependency>
                <!-- MySQL驱动 -->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>5.1.8</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

清新简洁版 

创建逆向工程的配置文件

注意,配置文件名称不能修改,就是generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!--
    targetRuntime: 执行生成的逆向工程的版本
    MyBatis3Simple: 生成基本的CRUD(清新简洁版)
    MyBatis3: 生成带条件的CRUD(奢华尊享版)
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3Simple">
        <!-- 数据库的连接信息 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:13306/mybatis"
                        userId="root"
                        password="qts0922">
        </jdbcConnection>
        <!-- javaBean的生成策略-->
        <javaModelGenerator targetPackage="com.coffeeship.pojo"
                            targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="com.coffeeship.mapper"
                         targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.coffeeship.mapper" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="t_emp" domainObjectName="Emp"/>
        <table tableName="t_dept" domainObjectName="Dept"/>
    </context>
</generatorConfiguration>

执行MBG插件的generate目标  

 执行成功后,查看工程目录:以下都是代码生成器为我们生成的

创建MyBatis核心配置文件

逆向工程不能创建全局配置文件,咱们自己创建一下,然后就可以进行数据库操作了。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="jdbc.properties"/>
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <typeAliases>
        <package name="com.coffeeship.pojo"/>
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <package name="com.coffeeship.mapper"/>
    </mappers>
</configuration>

感受一下自动生成的美妙~

清晰简洁版 基本的增删改查有~

 

以其中一个为例,没有问题,就是反向生成实体类的时候,没有生成对应的toString方法。

这里尚硅谷老师说我们以后主要用的还是奢华尊享版哈哈哈

奢华尊享版

除了基本的增删改查外,还有带条件的CRUD操作。

 新建一个模块测试,或者把之前自动生成的实体类、mapper接口、xml文件那些都手动清理一下,传说,如果在已有这些文件的基础上执行逆向工程,会造成配置文件的追加,而不是修改。

生成后,发现实体类都多了XxxExample类,然后Mapper接口也多了很多XxxByExample方法。 

 

QBC查询风格

QBC设定规则的查询风格: 

在实体类中生成toString(),再写一个mybatis的全局配置文件,即可开始丝滑般测试:

1.EmpExample相当于Emp实体类的规则类,selectByExample()传入规则类对象,即可查询对应规则的结果,并且example调用addXxx()之类的规则方法,返回对象还是自身,便可链式调用。

在实体类中生成无参构造和有参构造,测试一下两个更新方法的区别:

updateByPrimaryKey:有啥设啥

updateByPrimaryKeySelective:有选择地设参,有选择就是体现在传参是否是null

MyBatis分页插件

添加依赖

<dependencies>
    <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.2.0</version>
    </dependency>
</dependencies>

全局配置文件添加支持插件

注意全局配置文件中标签的顺序,不过顺序错了也会有提示,安心安心~

<plugins>
    <!--设置分页插件-->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

分页插件的使用

index:当前页的起始索引 =(pagenum -1)*pagesize

pagesize:每页显示的条数

pagenum:当前页的页码

如何获取分页信息,方式一:如上述PageHelper.startPage()返回一个Page对象,直接打印即可

Page{count=true, pageNum=2, pageSize=4, startRow=4, endRow=8, total=17, pages=5, reasonable=false, pageSizeZero=false}

[User{id=8, username='admin8', password='123456', age=18, sex='女', email='zh091699@163.com'}, User{id=9, username='coffee', password='123456', age=25, sex='女', email='123456@163.com'}, User{id=10, username='coffee10', password='123456', age=25, sex='女', email='123456@163.com'}, User{id=11, username='admin', password='123456', age=22, sex='女', email='admin@163.com'}]

方式二:PageInfo<User> pageInfo = new PageInfo<>(users, 3);

PageInfo{pageNum=2, pageSize=4, size=4, startRow=5, endRow=8, total=17, pages=5, list=Page{count=true, pageNum=2, pageSize=4, startRow=4, endRow=8, total=17, pages=5, reasonable=false, pageSizeZero=false}

[User{id=8, username='admin8', password='123456', age=18, sex='女', email='zh091699@163.com'}, User{id=9, username='coffee', password='123456', age=25, sex='女', email='123456@163.com'}, User{id=10, username='coffee10', password='123456', age=25, sex='女', email='123456@163.com'}, User{id=11, username='admin', password='123456', age=22, sex='女', email='admin@163.com'}], prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=3, navigateFirstPage=1, navigateLastPage=3, navigatepageNums=[1, 2, 3]}

 需要注意的是,上述只有在查询语句执行后才会有详细信息,PageInfo是强制在查询后创建,需要传入关键参数之一就是查询返回结果;Page对象在查询后创建,信息显示更全。

后端开发中,分页插件使用的场景非常多,让我们一起掌握它们吧!!!加油

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值