MyBatis延迟加载和缓存
Posted zhangjinru123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis延迟加载和缓存相关的知识,希望对你有一定的参考价值。
一、项目创建
1、项目目录结构
2、数据库配置和上一篇的一样,这里不再描述。下面创建mybatis配置文件SqlMapConfig.xml
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 <!-- 加载属性文件 --> 7 <properties resource="db.properties"></properties> 8 9 <settings> 10 <!-- 打开延迟加载 的开关 --> 11 <setting name="lazyLoadingEnabled" value="true"/> 12 <!-- 将积极加载改为消极加载即按需要加载 --> 13 <setting name="aggressiveLazyLoading" value="false"/> 14 <!-- 开启二级缓存 --> 15 <setting name="cacheEnabled" value="true"/> 16 </settings> 17 18 <!-- 和spring整合后 environments配置将废除--> 19 <environments default="development"> 20 <environment id="development"> 21 <!-- 使用jdbc事务管理,事务控制由mybatis--> 22 <transactionManager type="JDBC" /> 23 <!-- 数据库连接池,由mybatis管理--> 24 <dataSource type="POOLED"> 25 <property name="driver" value="${jdbc.driver}" /> 26 <property name="url" value="${jdbc.url}" /> 27 <property name="username" value="${jdbc.username}" /> 28 <property name="password" value="${jdbc.password}" /> 29 </dataSource> 30 </environment> 31 </environments> 32 33 <mappers> 34 <mapper resource="sqlmap/User.xml"/> 35 <mapper resource="sqlmap/Orders.xml" /> 36 </mappers> 37 </configuration>
3、创建映射文件User.xml和Order.xml
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 6 <mapper namespace="com.zhang.dao.OrdersDao"> 7 <!-- 延迟加载的resultMap --> 8 <resultMap type="com.zhang.domain.Orders" id="OrdersUserLazyLoadingResultMap"> 9 <!--对订单信息进行映射配置 --> 10 <id column="id" property="id"/> 11 <result column="user_id" property="userId"/> 12 <result column="number" property="number"/> 13 <result column="createtime" property="createtime"/> 14 <result column="note" property="note"/> 15 16 <!-- 实现对用户信息进行延迟加载 17 select:指定延迟加载需要执行的statement的id(是根据user_id查询用户信息的statement) 18 要使用user.xml中findUserById完成根据用户id(user_id)用户信息的查询,如果findUserById不在本mapper中需要前边加namespace 19 column:订单信息中关联用户信息查询的列,是user_id 20 --> 21 <association property="user" javaType="com.zhang.domain.User" 22 select="com.zhang.dao.UserDao.findUserById" column="user_id"> 23 </association> 24 </resultMap> 25 26 <!-- 查询订单关联查询用户,用户信息需要延迟加载 --> 27 <select id="findOrdersUserLazyLoading" resultMap="OrdersUserLazyLoadingResultMap"> 28 SELECT * FROM orders 29 </select> 30 </mapper>
4、各种实体类这里也不再阐述,下面之间创建接口类OrderDao.java和UserDao.java
1 package com.zhang.dao; 2 3 import java.util.List; 4 5 import com.zhang.domain.Orders; 6 public interface OrdersDao { 7 //查询订单关联查询用户,用户信息是延迟加载 8 public List<Orders> findOrdersUserLazyLoading()throws Exception; 9 }
1 package com.zhang.dao; 2 3 import com.zhang.domain.User; 4 public interface UserDao { 5 6 //根据id查询用户信息 7 public User findUserById(int id) throws Exception; 8 9 //更新用户 10 public void updateUser(User user)throws Exception; 11 12 }
5、创建测试方法
1 package com.zhang.test; 2 3 import java.io.InputStream; 4 import java.util.List; 5 6 import org.apache.ibatis.io.Resources; 7 import org.apache.ibatis.session.SqlSession; 8 import org.apache.ibatis.session.SqlSessionFactory; 9 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 10 import org.junit.Before; 11 import org.junit.Test; 12 13 import com.zhang.dao.OrdersDao; 14 import com.zhang.dao.UserDao; 15 import com.zhang.domain.Orders; 16 import com.zhang.domain.User; 17 18 public class TestMy { 19 private SqlSessionFactory sqlSessionFactory; 20 21 // 此方法是在执行testFindUserById之前执行 22 @Before 23 public void setUp() throws Exception { 24 // 创建sqlSessionFactory 25 // mybatis配置文件 26 String resource = "SqlMapConfig.xml"; 27 // 得到配置文件流 28 InputStream inputStream = Resources.getResourceAsStream(resource); 29 // 创建会话工厂,传入mybatis的配置文件信息 30 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 31 } 32 33 34 @Test 35 public void findOrdersUserLazyLoading() throws Exception { 36 SqlSession sqlSession = sqlSessionFactory.openSession(); 37 OrdersDao ordersDao = sqlSession.getMapper(OrdersDao.class); 38 List<Orders> list=ordersDao.findOrdersUserLazyLoading(); 39 for (Orders orders : list) { 40 User user= orders.getUser(); 41 } 42 System.out.println(list.size()); 43 } 44 45 46 // 一级缓存测试 47 @Test 48 public void testCache1() throws Exception { 49 SqlSession sqlSession = sqlSessionFactory.openSession();// 创建代理对象 50 UserDao userDao = sqlSession.getMapper(UserDao.class); 51 // 下边查询使用一个SqlSession 52 // 第一次发起请求,查询id为1的用户 53 User user1 = userDao.findUserById(1); 54 System.out.println(user1); 55 // 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。 56 // 更新user1的信息 57 user1.setUsername("测试用户22"); 58 userDao.updateUser(user1); 59 //执行commit操作去清空缓存 60 sqlSession.commit(); 61 // 第二次发起请求,查询id为1的用户 62 User user2 = userDao.findUserById(1); 63 System.out.println(user2); 64 sqlSession.close(); 65 } 66 67 68 // 二级缓存测试 69 @Test 70 public void testCache2() throws Exception { 71 SqlSession sqlSession1 = sqlSessionFactory.openSession(); 72 SqlSession sqlSession2 = sqlSessionFactory.openSession(); 73 SqlSession sqlSession3 = sqlSessionFactory.openSession(); 74 UserDao userDao1 = sqlSession1.getMapper(UserDao.class); 75 // 第一次发起请求,查询id为1的用户 76 User user1 = userDao1.findUserById(1); 77 System.out.println(user1); 78 //这里执行关闭操作,将sqlsession中的数据写到二级缓存区域 79 sqlSession1.close(); 80 81 //使用sqlSession3执行commit()操作 82 UserDao userDao3 = sqlSession3.getMapper(UserDao.class); 83 User user = userDao3.findUserById(1); 84 user.setUsername("张明明"); 85 userDao3.updateUser(user); 86 //执行提交,清空UserMapper下边的二级缓存 87 sqlSession3.commit(); 88 sqlSession3.close(); 89 90 UserDao userDao2 = sqlSession2.getMapper(UserDao.class); 91 // 第二次发起请求,查询id为1的用户 92 User user2 = userDao2.findUserById(1); 93 System.out.println(user2); 94 sqlSession2.close(); 95 } 96 97 }
二、延迟加载
1、在order.xml文件中编写查询订单信息
在查询订单的statement中使用association去延迟加载(执行)下边的satatement(关联查询用户信息)关联查询用户信息:通过上边查询到的订单信息中user_id去关联查询用户信息。
在user.xml编写查询用户信息的映射
上边先去执行findOrdersUserLazyLoading,当需要去查询用户的时候再去执行findUserById,通过resultMap的定义将延迟加载执行配置起来。
2、延迟加载resultMap,使用association中的select指定延迟加载去执行的statement的id。
3、编写接口方法
4、测试:在程序中去遍历上一步骤查询出的List<Orders>,当我们调用Orders中的getUser方法时,开始进行延迟加载。延迟加载,去调用User.xml中findUserbyId这个方法获取用户信息。
5、延迟加载配置,mybatis默认没有开启延迟加载,需要在SqlMapConfig.xml中setting配置。
6、什么是延迟加载
resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
需求:如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。
延迟加载:先从单表查询、需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
三、查询缓存
1、什么是查询缓存
mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。mybaits提供一级缓存和二级缓存。
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
2、一级缓存工作原理
第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
3、一级缓存测试
mybatis默认支持一级缓存,不需要在配置文件去配置。
按照上边一级缓存原理步骤去测试
控制台信息可以看出只发起了一条SQL语句
更新数据
控制台信息可以看出只发起了两条SQL语句
4、二级缓存原理
sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。
如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。
sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。
UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。
每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。
5、开启二级缓存
mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。
在核心配置文件SqlMapConfig.xml中加入
在User.xml中开启二缓存,User.xml下的sql执行完成会存储到它的缓存区域(HashMap)
6、测试方法
控制台信息可以看出只发起了一条SQL语句
更新数据
控制台信息可以看出只发起了两条SQL语句
7. useCache配置
在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。
<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
总结:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。
8.刷新缓存
在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。
设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。
如下:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。
资源下载链接:http://download.csdn.net/detail/u013865056/9907254
以上是关于MyBatis延迟加载和缓存的主要内容,如果未能解决你的问题,请参考以下文章