Mybatis延迟加载和缓存策略

Mybatis延迟加载和缓存策略

1. 延迟加载

​ 延迟加载就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.

**好处:**先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

**缺点:**因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

1.1 一对一案例

一个账户只属于一个用户

  • 数据库表

account账户表

字段类型
accountIdint(11)
UIDint(11)
MONEYdouble

user表

字段类型
idint(11)
usernamevarchar(32)
birthdaydatetime
sexchar(1)
addressvarchar(256)
  • Account 与 User实体类
public class Account {
	private int accountId;
	private int uid;
	private double money;
	// 一个账户对应一个用户
	private User user;
}

public class User {
	private int id;
	private String username;
	private Date birthday;
	private String sex;
	private String address;
	// 一个用户,对应多个账户
	private List<Account> accounts;
}
  • IAccountDao 和 IUserDao 接口
public interface IUserDao {  
    /**
     * 通过id查找用户
     */
    User findUserById(int id);
    
}

public interface IAccountDao {
    /**
     * 查询全部账户(需要查询账户的用户)
     */
    List<Account> findAll();
    
}
  • 映射文件配置

对IUserDao.java接口进行映射

<? xml version ="1.0" encoding ="UTF- - 8" ?>
<!DOCTYPE mapper
		PUBLIC " "- - //mybatis.org//DTD Mapper 3.0//EN"
		"http://mybatis.org/dtd/mybatis- -3 3- - mapper.dtd" > 
< mapper namespace ="com.itheima.dao.IUserDao" >
	< select id ="findById" resultType ="user">
		select * from user where id=#{id}
	</ select>
</ mapper>

对IAccountDao.java接口进行映射

<? xml version ="1.0" encoding ="UTF- - 8" ?>
<!DOCTYPE mapper
PUBLIC " "- - //mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis- -3 3- - mapper.dtd" > >
<mapper namespace="com.itheima.dao.IAccountDao">
<resultMap id="accountResultMap" type="account">
    <!--1. 账户信息映射-->
    <id property="accountId" column="accountId"></id>
    <result property="uid" column="uid"></result>
    <result property="money" column="money"></result>
    <!--2. 延时加载的方式,查询账户的用户-->
    <!--select 延时加载的语句 id(名称空间.方法)-->
    <!--column="uid" 当然表外键字段-->
    <association property="user" javaType="user" column="uid"
      select="com.itheima.dao.IUserDao.findById"></association>
</resultMap>
<select id="findAccounts" resultMap ="accountResultMap">
 	  SELECT * FROM account
</select>
</mapper>
1.2 延迟加载的方案
  • 方式一: 在SqlMapConfig.xml 中配置开启懒加载支持

注意:SqlMapConfig配置标签是有顺序的,顺序如下

1 properties 属性
2 settings 配置全局参数
3 typeAliases 类型别名
4 typeHandlers 类型处理器
5 objectFactory 对象工厂
6 plugins 插件
7 environments 环境集合属性对象
8 databaseIdProvider 多数据库支持
9 mappers 映射器
<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>
  • 方式二:修改 IAccountDao.xml 映射文件,通过 association、 collection 实现一对一及一对多映射。association、 collection 具备延迟加载功能。
 <resultMap id="accountResMap" type="account">
        <result property="accountId" column="accountId"></result>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
     <!-- fetchType=“lazy” 开启懒加载 -->
        <association property="user" column="uid" javaType="user" fetchType="lazy" select="com.zmysna.dao.IUserDao.findUserById">
        </association>
 </resultMap>
1.3 测试
public class RelativeTest {

    private SqlSession sqlSession;

    //准备工作,加载配置文件并创建SqlSession
    @Before
    public void before() {
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        sqlSession = factory.openSession();
    }

    //提交事务并关闭SqlSession
    @After
    public void after() {
        sqlSession.commit();
        sqlSession.close();
    }

    //一对一延迟加载测试
    @Test
    public void o2oTest() {
        //创建dao层映射对象
        IAccountDao mapper = sqlSession.getMapper(IAccountDao.class);
        //调用方法得到所有账户
        List<Account> accounts = mapper.findAll();
        //遍历并打印
        accounts.forEach((account)->{
            System.out.println(account.getAccountId());
            System.out.println(account.getUser().getAddress());
        });
    }
}
1.4 结果

调用findAll()方法时不执行SQL语句,延迟加载,访问accounts时才执行SQL语句

###2. Mybatis缓存策略

​ 像大多数的持久化框架一样, Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。Mybatis中有一级缓存和二级缓存。

  • 一级缓存是基于 SqlSessoion 的缓存,一级缓存的内容不能跨 sqlsession。由 mybatis自动维护。

  • 二级缓存是基于映射文件的缓存,缓存范围比一级缓存更大。不同的 sqlsession 可以访问二级缓存的内容。哪些数据放入二级缓存需要自己指定。

2.1 一级缓存测试

​ 一级缓存默认存在,不由用户控制,范围是同一个SqlSession对象。

  • 使用注解开发的IUserDao接口,不用写配置文件
public interface IUserDao {
    @Select("select * from user where id = #{uid}")
    User findUserById(int id);
}
  • 测试类
public class AnnoTest {
    private SqlSession sqlSession;
    private IUserDao userDao;
    private InputStream in;

    @After
    public void after() throws IOException {
        sqlSession.commit();
        sqlSession.close();
        in.close();
    }

    @Before
    public void before() throws IOException {
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        sqlSession = new SqlSessionFactoryBuilder().build(in).openSession();
        userDao = sqlSession.getMapper(IUserDao.class);
    }
    
     @Test
    public void findUserById(){
        //两次查询同一条数据
        System.out.println(userDao.findUserById(46));
        System.out.println(userDao.findUserById(46));
    }
}
  • 结果

    两次查询只执行一次SQL语句,第二次查询使用了一级缓存的数据。

2.2 二级缓存

​ 二级缓存是mapper映射级别的缓存,多个SqlSession取操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

2.2.1 二级缓存说明

  • sqlSession1 去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
  • sqlSession2 去查询与 sqlSession1 相同的用户信息, 首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
  • 如果 SqlSession3 去执行相同 mapper 映射下 sql,执行 commit 提交, 将会清空该 mapper 映射下的二级缓存区域的数据
2.2.2 二级缓存测试
  1. 开启二级缓存(可选)

在SqlMapConfig下面开启二级缓存

<settings>
    <!--开启二级缓存,默认开启,这里不配也行-->
	<setting name="cacheEnabled" value="true"></setting>
</settings>
  1. 配置Mapper
 <?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.itheima.dao.IUserDao">

        <!--当前映射配置加入二级缓存(会自动把这里的所有的映射配置中查询的结果放入二级缓存)-->
        <cache/>

        <!--根据id查询-->
        <!--useCache="true" 表示是否使用二级缓存,默认为true-->
        <select id="findById" resultType="user" useCache="true">
            SELECT * FROM USER WHERE id=#{id}
        </select>
    </mapper>
  1. 实体类实现序列化接口
public class User implements Serializable{
    private int id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    private List<Account> accounts;
}
  1. 测试缓存
// 测试缓存
@Test
public void testCache() throws Exception{
    SqlSession session1 = factory.openSession();
    IUserDao userDao = session1.getMapper(IUserDao. class);
    // 主键查询
    User user1 = userDao.findById(41);
    System. out .println(user1); //发送 select..
    session1.close();
    SqlSession session2 = factory.openSession();
    IUserDao userDao2 = session2.getMapper(IUserDao. class);
    User user2 = userDao2.findById(41); //没有发送查询,说明使用了使用二级缓存的数据
    System. out .println(user2);
    session2.close();
}

使用不同的sqlSession去实现二级缓存测试,第二次查询时使用缓存,结果如下。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值