1.查询缓存
一级缓存:同一个SqlSession对象,MyBatis默认开启一级缓存
如果用同样的SqlSession对象查询相同的数据,则只会在第一次查询时向数据库发送Sql语句并将查询的结果放入到SqlSession中(作为缓存存在),后续再次查询该对象即可(即省略了数据库的访问)
// Connection - SqlSession操作Mybatis // conf.xml->reader Reader reader = Resources.getResourceAsReader("conf.xml"); // reader->sqlSession // 可以通过build的第二参数 指定数据库环境 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader, "development"); SqlSession session = sessionFactory.openSession(); StudentMapper studentMapper = session.getMapper(StudentMapper.class); Student student = studentMapper.queryStudentByStuno(2); Student student2 = studentMapper.queryStudentByStuno(2); //增删改操作... session.commit(); Student student3 = studentMapper.queryStudentByStuno(2); System.out.println(student.getStuName()); System.out.println(student2.getStuName()); System.out.println(student3.getStuName()); session.close(); 结果: DEBUG [main] - ==> Preparing: select * from student1 where stuno=2 DEBUG [main] - ==> Parameters: DEBUG [main] - <== Total: 1 DEBUG [main] - ==> Preparing: select * from student1 where stuno=2 DEBUG [main] - ==> Parameters: DEBUG [main] - <== Total: 1 ls ls ls
二级缓存:
Mybatis自带二级缓存:同一个namespace生成的XxxMapper对象
MyBatis默认关闭二级缓存,需要手工打开
回顾:namespace的值就是接口的全类名(包名、类名),通过接口可以产生代理对象(Mapper对象)
-->namespace决定了Mapper对象的产生
结论:只要产生的xxxMapper对象来自于同一个namespace,则这些对象共享二级缓存。
如果是同一个SqlSession对象进行多次相同的查询,则直接进入一级缓存查询
如果不是同一个SqlSession对象进行多次相同的查询(但是均来自于同一个namespace),则进入二级缓存查询
// Connection - SqlSession操作Mybatis // conf.xml->reader Reader reader = Resources.getResourceAsReader("conf.xml"); // reader->sqlSession // 可以通过build的第二参数 指定数据库环境 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader, "development"); //第一次查询 SqlSession session = sessionFactory.openSession(); StudentMapper studentMapper = session.getMapper(StudentMapper.class); Student student = studentMapper.queryStudentByStuno(2); //第二次查询 SqlSession session2 = sessionFactory.openSession(); StudentMapper studentMapper2 = session2.getMapper(StudentMapper.class); Student student2 = studentMapper2.queryStudentByStuno(2); System.out.println(student.getStuName()); System.out.println(student2.getStuName()); session.close();
session2.close(); 结果: DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 481511146. DEBUG [main] - Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@1cb346ea] DEBUG [main] - ==> Preparing: select * from student1 where stuno=2 DEBUG [main] - ==> Parameters: DEBUG [main] - <== Total: 1 DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 1060042118. DEBUG [main] - Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@3f2ef586] DEBUG [main] - ==> Preparing: select * from student1 where stuno=2 DEBUG [main] - ==> Parameters: DEBUG [main] - <== Total: 1 ls ls 原因:未开启二级缓存
手工开启二级缓存:
a./MyBatisProject3/src/conf.xml
<!--开启二级缓存 --> <setting name="cacheEnabled" value="true"/>
b.在具体的mapper.xml中声明开启
/MyBatisProject3/src/org/myy/mapper/studentMapper.xml
<!--声明此namespace开启二级缓存 --> <cache />
根据异常提示java.io.NotSerializableException: 可知,MyBatis缓存是将对象放入硬盘文件中
->准备缓存的对象,必须实现了序列化接口(如果开启的缓存namespace="org.myy.mapper.StudentMapper")
可知序列化对象为Student,因此需要将Student序列化(序列化Student类,以及Student的级联属性和父类)
public class Student implements Serializable
...
序列化:内存->硬盘
反序列化:硬盘->内存
触发将对象写入二级缓存的时机:SqlSession对象的close()方法
// Connection - SqlSession操作Mybatis // conf.xml->reader Reader reader = Resources.getResourceAsReader("conf.xml"); // reader->sqlSession // 可以通过build的第二参数 指定数据库环境 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader, "development"); //第一次查询 SqlSession session = sessionFactory.openSession(); StudentMapper studentMapper = session.getMapper(StudentMapper.class); Student student = studentMapper.queryStudentByStuno(2); session.close();//触发将对象写入二级缓存的时机 //第二次查询 SqlSession session2 = sessionFactory.openSession(); StudentMapper studentMapper2 = session2.getMapper(StudentMapper.class); Student student2 = studentMapper2.queryStudentByStuno(2); System.out.println(student.getStuName()); System.out.println(student2.getStuName()); session2.close();
结果:
DEBUG [main] - Cache Hit Ratio [org.myy.mapper.StudentMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 997850486.
DEBUG [main] - Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@3b79fd76]
DEBUG [main] - ==> Preparing: select * from student1 where stuno=2
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 1
DEBUG [main] - Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@3b79fd76]
DEBUG [main] - Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@3b79fd76]
DEBUG [main] - Returned connection 997850486 to pool.
DEBUG [main] - Cache Hit Ratio [org.myy.mapper.StudentMapper]: 0.5
ls
ls
注意:二级缓存的范围是同一个namespace,如果有多个xxMapper.xml的namespace值相同,则通过这些xxxMapper.xml产生的xxMapper对象仍然共享二级缓存
禁用:select标签中useCache="false"
<select id="queryStudentByStuno" parameterType="int" resultMap="studentMapping" useCache="false"> select * from student1 where stuno=${value} </select>
清理:a.与清理一级缓存的方法相同
commit();(一般执行增删改时,会清理掉缓存,设计的原因:是为了防止脏数据)
在二级缓存中,commit()不能时查询自身的commit。
commit会清理一级和二级缓存,但是清理二级缓存时,不能是查询自身的commit();
b.在select标签中增加属性flushCache="true"
<select id="queryStudentByStuno" parameterType="int" resultMap="studentMapping" useCache="true" flushCache="true"> select * from student1 where stuno=${value} </select>
命中率:1:0.0
2:0.5
3:0.6666666
4.0.75
DEBUG [main] - Cache Hit Ratio [org.myy.mapper.StudentMapper]: 0.0 DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 997850486. DEBUG [main] - Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@3b79fd76] DEBUG [main] - ==> Preparing: select * from student1 where stuno=2 DEBUG [main] - ==> Parameters: DEBUG [main] - <== Total: 1 DEBUG [main] - Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@3b79fd76] DEBUG [main] - Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@3b79fd76] DEBUG [main] - Returned connection 997850486 to pool. DEBUG [main] - Cache Hit Ratio [org.myy.mapper.StudentMapper]: 0.5 ls ls DEBUG [main] - Cache Hit Ratio [org.myy.mapper.StudentMapper]: 0.6666666666666666 DEBUG [main] - Cache Hit Ratio [org.myy.mapper.StudentMapper]: 0.75
三方提供的二级缓存:
ehcache、mecache
想要整合三方提供的二级缓存(或者自定义二级缓存),必须实现org.apache.ibatis.cache.Cache接口,该接口的默认实现类是PerpetCache
整合ehcache二级缓存:
a.添加jar包
Ehcache-core.jar
mybatis-Ehcache.jar
slf4j-api.jar
b.编写ehcache配置文件 Ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!-- diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下: user.home – 用户主目录 user.dir – 用户当前工作目录 java.io.tmpdir – 默认临时文件路径 当二级缓存的对象超过内存限制时(缓存对象的个数>maxElementsInMemory),存放入的硬盘文件 --> <diskStore path="D: estEhcache"/> <!-- defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。 --> <!-- name:缓存名称。 maxElementsInMemory:设置在内存中缓存对象的最大个数 maxElementsOnDisk:社渚在硬盘中最大缓存个数。 eternal:设置缓存是否永远过期,一但设置了,timeout将不起作用。 overflowToDisk:当内存中缓存的对象个数超过maxElementsInMemory的时候,是否转移到硬盘中 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.也就是对象存活时间无穷大。 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。 FIFO,first in first out,这个是大家最熟的,先进先出。 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 --> <defaultCache maxElementsInMemory="10000" maxElementsOnDisk="1000000" eternal="false" overflowToDisk="false" timeToIdleSeconds="100" timeToLiveSeconds="100" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"/> <cache name="cloud_user" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU"/> </ehcache>
c.开启EhCache二级缓存
/MyBatisProject3/src/Ehcache.xml
<cache type="org.mybatis.caches.ehcache.EhcacheCache"> <!--通过property覆盖Ehcache.xml中的值 --> <property name="maxElementsInMemory" value="1000"/> </cache>
2.逆向工程
表、类、接口、mapper.xml四者密切相关,因此当知道一个的时候,其他三个应该可以自动生成。
表->其他三个
a.mybatis-generaotr-core.jar、mybatis.jar、ojdbc.jarS
b.逆向工程的配置文件generator.xml
/MyBatisgenerator/src/generator.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> <context id="DB2Tables" targetRuntime="MyBatis3"> <commentGenerator> <!-- suppressAllComments属性值: true:自动生成实体类、SQL映射文件时没有注释 true:自动生成实体类、SQL映射文件,并附有注释 --> <property name="suppressAllComments" value="true" /> </commentGenerator> <!-- 数据库连接信息 --> <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:mldn" userId="scott" password="tiger"> </jdbcConnection> <!-- forceBigDecimals属性值: true:把数据表中的DECIMAL和NUMERIC类型, 解析为JAVA代码中的java.math.BigDecimal类型 false(默认):把数据表中的DECIMAL和NUMERIC类型, 解析为解析为JAVA代码中的Integer类型 --> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- targetProject属性值:实体类的生成位置 targetPackage属性值:实体类所在包的路径 --> <javaModelGenerator targetPackage="org.myy.entity" targetProject=".src"> <!-- trimStrings属性值: true:对数据库的查询结果进行trim操作 false(默认):不进行trim操作 --> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- targetProject属性值:SQL映射文件的生成位置 targetPackage属性值:SQL映射文件所在包的路径 --> <sqlMapGenerator targetPackage="org.myy.mapper" targetProject=".src"> </sqlMapGenerator> <!-- 生成动态代理的接口 --> <javaClientGenerator type="XMLMAPPER" targetPackage="org.myy.mapper" targetProject=".src"> </javaClientGenerator> <!-- 指定数据库表 --> <table tableName="Student1"> </table> <table tableName="studentCard"> </table> <table tableName="studentClass"> </table> </context> </generatorConfiguration>
/MyBatisgenerator/src/org/myy/test/Test.java
package org.myy.test; import java.io.File; import java.util.ArrayList; import java.util.List; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.config.Configuration; import org.mybatis.generator.config.xml.ConfigurationParser; import org.mybatis.generator.exception.XMLParserException; import org.mybatis.generator.internal.DefaultShellCallback; public class Test { public static void main(String[] args) throws Exception, XMLParserException { File file=new File("src/generator.xml");//配置文件 List<String> warnings=new ArrayList<String>(); ConfigurationParser cp=new ConfigurationParser(warnings); Configuration config=cp.parseConfiguration(file); DefaultShellCallback callback=new DefaultShellCallback(true); //逆向工程的核心类 MyBatisGenerator generator=new MyBatisGenerator(config,callback,warnings); generator.generate(null); } }