1、MyBatis延迟加载
1.1、一对一延迟加载
- 延迟加载,都是针对在多表查询情况下,对关联对象的操作。
用户和订单从面向对象的角度来说就是关联对象,当只需要订单数据,暂时不需要用户数据的时候,就不应该去查询用户表,啥时候用到用户数据,啥时候查询。
- 一对一延迟加载
- 对关联查询的SQL语句进行拆分
- association标签:
- column属性:关联查询的外键列名
- select属性:指定另一个< select >标签的id值
IOrdersMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bdit.mapper.IOrdersMapper">
<select id="findAllOrders" resultMap="ordersUserResult">
SELECT o.id,o.user_id,o.number,o.createTime,o.note FROM t_orders o
</select>
<resultMap id="ordersUserResult" type="orders">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createTime" property="createTime"/>
<result column="note" property="note"/>
<!--
association联合标签,实现手动映射
property 属性:封装的pojo对象
javaType属性:封装的pojo对象的类型
一对一延迟加载:
column属性:外键列
select属性:指定另一个查询语句的id值
-->
<association property="user" javaType="user" column="user_id" select="com.bdit.mapper.IUserMapper.findById">
<id column="id" property="id"/>
<result column="username" property="username"/>
</association>
</resultMap>
<!-- <select id="queryUserByUserId" parameterType="int" resultType="user">-->
<!-- select u.id,u.username,u.email,u.source,u.remark from t_user u where id=#{user_id}-->
<!-- </select>-->
</mapper>
mybatis-config.xml 开启延迟加载配置
<settings>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--在mybatis中,默认情况下只要调用了equals,clone,hashCode,toString这几个方法,都会对对象进行完全加载
lazyLoadTriggerMethods用来指定方法,按需加载,多个方法之间用逗号隔开。
-->
<setting name="lazyLoadTriggerMethods" value="getUser"/>
</settings>
package com.bdit;
import com.bdit.mapper.IOrdersMapper;
import com.bdit.pojo.Orders;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UserOrdersTest {
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@Before
public void init() throws IOException {
InputStream inputStream= Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
sqlSessionFactory= sqlSessionFactoryBuilder.build(inputStream);
sqlSession=sqlSessionFactory.openSession();
}
@After
public void destory(){
sqlSession.commit();
sqlSession.close();
}
/**
* 订单查询用户 ,一对一
*/
@Test
public void testOrdersListUser(){
IOrdersMapper ordersMapper=sqlSession.getMapper(IOrdersMapper.class);
List<Orders> ordersList=ordersMapper.findAllOrders();
for(Orders order:ordersList){
System.out.println(order);
System.out.println("---------------"+order.getUser());
}
}
}
1.2、一对多延迟加载
根据用户查询订单,一对多,延迟加载,拆分SQL语句
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bdit.mapper.IUserMapper">
<select id="findById" parameterType="int" resultType="user">
select *
from t_user where id=#{id};
</select>
<!--一对多 用户查询订单 延迟加载-->
<select id="queryUserOrderList" resultMap="userOrdersResult">
SELECT u.id,u.username,u.email,u.source,u.remark FROM t_user u
</select>
<resultMap id="userOrdersResult" type="user">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="email" column="email"/>
<result property="source" column="source"/>
<result property="remark" column="remark"/>
<!--
collection标签
property属性:封装对应的属性名
ofType属性:指定集合中的泛型类型
-->
<collection property="ordersList" ofType="orders" column="id" select="queryOrdersByUserId">
<!-- <id column="order_id" property="id"/>-->
<!-- <result column="user_id" property="userId"/>-->
<!-- <result column="number" property="number"/>-->
<!-- <result column="createTime" property="createTime"/>-->
<!-- <result column="note" property="note"/>-->
</collection>
</resultMap>
<select id="queryOrdersByUserId" parameterType="int" resultType="orders">
select o.id ,o.user_id as userId,o.number,o.createTime,o.note from t_orders o where user_id=#{user_id}
</select>
</mapper>
/**
* 用户查询订单 一对多
*/
@Test
public void queryUserOrdersList(){
IUserMapper userMapper=sqlSession.getMapper(IUserMapper.class);
List<User> list=userMapper.queryUserOrderList();
for(User user:list){
System.out.println(user);
for(Orders orders:user.getOrdersList()){
System.out.println(orders);
}
}
}
2、MyBatis缓存机制
2.1、缓存机制
- 几乎所有的ORM框架都提供了缓存机制。
- MyBatis框架包含了一个非常强大的缓存特性,它可以很方便的配置和定制。缓存可以极大地提升查询效率。
- MyBatis框架中定义了两极缓存
- 一级缓存
- 二级缓存
- 默认情况下,只有一级缓存是开启的,一级缓存就是SqlSession级别的缓存
- 二级缓存需要手动开启和配置,它是基于namespace级别的缓存
- 为了提高扩展性,MyBaits定义了缓存接口,可以整合第三方的二级缓存插件。
2.2、一级缓存
一级缓存是SqlSession级别的缓存,只要SqlSession没有flush或close,它就有效。
2.2.1、 证明一级缓存的存在
/**
* 证明一级缓存的存在,也就是sqlSession级别的缓存
*/
@Test
public void queryUserById(){
IUserMapper userMapper=sqlSession.getMapper(IUserMapper.class);
User user1= userMapper.findById(9);
System.out.println(user1);
//手动清空缓存
//sqlSession.clearCache();
User user2=userMapper.findById(9);
System.out.println(user2);
}
通过测试可以发现,我们虽然执行了两次查询,但是只执行了一次SQL语句,这就是SqlSession的一级缓存的作用。缓存中如果存在相同条件的数据,直接获取缓存中已存在的;如果缓存中没有符合条件的数据,就会执行SQL语句查询数据库。
2.2.2、 一级缓存的分析
一级缓存是SqlSession范围的缓存,当调用SqlSession的修改、添加、删除、commit、close方法时,就会清空一级缓存。
第一次查询用户id为9的用户信息,先去缓存中查找是否有id为9的用户信息,如果没有,从数据库中查询用户信息,得到用户信息,并且会将用户信息存储到一级缓存中。如果SqlSession去执行commit操作(执行插入、修改、删除),就会清空SqlSession中的一级缓存,这样做的目的是为了让缓存中的数据永远都是最新的,避免脏读。
2.3、二级缓存
二级缓存是mapper映射级别的缓存,多个sqlSession去操作同一个Mapper映射的SQL语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
2.3.1 二级缓存的开启和关闭
Step1:在mybatis-config.xml文件中开启二级缓存,默认也是开启的,这一步可以省略
<setting name="cacheEnabled" value="true"/>
Step2:在UserMapper.xml中开启针对当前mapper映射文件的二级缓存
<!--
cache标签表示当前这个Mapper映射将使用二级缓存,开启二级缓存
-->
<cache/>
Step3: 在statement上配置useCache属性
<!--
cache标签表示当前这个Mapper映射将使用二级缓存,开启二级缓存
-->
<cache/>
<!--
useCache="true"代表当前这个statement要使用二级缓存,如果不使用二级缓存就设置成false或者不写
-->
<select id="findById" parameterType="int" resultType="user" useCache="true">
select *
from t_user where id=#{id};
</select>
IUserMapper userMapper=sqlSession.getMapper(IUserMapper.class);
User user1= userMapper.findById(9);
System.out.println(user1);
sqlSession.commit();
sqlSession.close();
//手动清空缓存
//sqlSession.clearCache();
SqlSession sqlSession1=sqlSessionFactory.openSession();
IUserMapper userMapper2=sqlSession1.getMapper(IUserMapper.class);
User user2=userMapper2.findById(9);
System.out.println(user2);
sqlSession1.commit();
sqlSession1.close();
2.3.2 二级缓存的注意事项
当我们使用MyBatis中的二级缓存时,所缓存的对象一定要实现序列化接口
在实际开发中MyBatis的二级缓存应用很少