资讯专栏INFORMATION COLUMN

通过项目逐步深入了解Mybatis(四)

kuangcaibao / 823人阅读

摘要:相关阅读通过项目逐步深入了解一通过项目逐步深入了解二通过项目逐步深入了解三本项目所有代码及文档都托管在地址延迟加载什么是延迟加载可以实现高级映射使用实现一对一及一对多映射,具备延迟加载功能。一级缓存是级别的缓存。

相关阅读:

1、通过项目逐步深入了解Mybatis<一>

2、通过项目逐步深入了解Mybatis<二>

3、通过项目逐步深入了解Mybatis<三>

本项目所有代码及文档都托管在 Github地址:https://github.com/zhisheng17/mybatis

延迟加载 什么是延迟加载?

resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
需求:
如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。

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

打开延迟加载开关

在mybatis核心配置文件中配置:

lazyLoadingEnabled、aggressiveLazyLoading

设置项 描述 允许值 默认值
lazyLoadingEnabled 全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。 true false false
aggressiveLazyLoading 当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。 true false true

        
        
使用 association 实现延迟加载

需求:查询订单并且关联查询用户信息

Mapper.xml

需要定义两个 mapper 的方法对应的 statement。

1、只查询订单信息

SQL 语句: select * from orders

在查询订单的 statement 中使用 association 去延迟加载(执行)下边的 statement (关联查询用户信息)


    

2、关联查询用户信息

通过上面查询订单信息中的 user_id 来关联查询用户信息。使用 UserMapper.xml 中的 findUserById

SQL语句:select * from user where id = user_id

上边先去执行 findOrdersUserLazyLoading,当需要去查询用户的时候再去执行 findUserById ,通过 resultMap的定义将延迟加载执行配置起来。也就是通过 resultMap 去加载 UserMapper.xml 文件中的 select = findUserById

延迟加载的 resultMap

    
        
        
        
        
        
        
        
        
        
    
OrderMapperCustom.java
public List findOrdersUserLazyLoading() throws Exception;
测试代码:
@Test
    public void testFindOrdersUserLazyLoading() throws Exception
    {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //创建OrdersMapperCustom对象,mybatis自动生成代理对象
        OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
        //查询订单信息
        List list = ordersMapperCustom.findOrdersUserLazyLoading();
        //遍历所查询的的订单信息
        for (Orders orders : list)
        {
            //查询用户信息
            User user = orders.getUser();
            System.out.println(user);
        }
        sqlSession.close();
    }
测试结果:

整个延迟加载的思路:

1、执行上边mapper方法(findOrdersUserLazyLoading),内部去调用cn.zhisheng.mybatis.mapper.OrdersMapperCustom 中的 findOrdersUserLazyLoading 只查询 orders 信息(单表)。

2、在程序中去遍历上一步骤查询出的 List,当我们调用 Orders 中的 getUser 方法时,开始进行延迟加载。

3、延迟加载,去调用 UserMapper.xml 中 findUserbyId 这个方法获取用户信息。

思考:

不使用 mybatis 提供的 association 及 collection 中的延迟加载功能,如何实现延迟加载??

实现方法如下:

定义两个mapper方法:

1、查询订单列表

2、根据用户id查询用户信息

实现思路:

先去查询第一个mapper方法,获取订单信息列表

在程序中(service),按需去调用第二个mapper方法去查询用户信息。

总之:

使用延迟加载方法,先去查询 简单的 sql(最好单表,也可以关联查询),再去按需要加载关联查询的其它信息。

一对多延迟加载

上面的那个案例是一对一延迟加载,那么如果我们想一对多进行延迟加载呢,其实也是很简单的。

一对多延迟加载的方法同一对一延迟加载,在collection标签中配置select内容。

延迟加载总结:

作用:

当需要查询关联信息时再去数据库查询,默认不去关联查询,提高数据库性能。
只有使用resultMap支持延迟加载设置。

场合:

当只有部分记录需要关联查询其它信息时,此时可按需延迟加载,需要关联查询时再向数据库发出sql,以提高数据库性能。

当全部需要关联查询信息时,此时不用延迟加载,直接将关联查询信息全部返回即可,可使用resultType或resultMap完成映射。

查询缓存 什么是查询缓存?

mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。

mybaits提供一级缓存,和二级缓存。

一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

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

为什么要用缓存?

如果缓存中有数据就不用从数据库中获取,大大提高系统性能。

一级缓存

工作原理

第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。

得到用户信息,将用户信息存储到一级缓存中。

如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。

一级缓存测试

Mybatis 默认支持一级缓存,不需要在配置文件中配置。

所以我们直接按照上面的步骤进行测试:

//一级缓存测试
    @Test
    public void  testCache1() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //创建UserMapper对象,mybatis自动生成代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //查询使用的是同一个session
        //第一次发起请求,查询Id 为1的用户信息
        User user1 = userMapper.findUserById(1);
        System.out.println(user1);
        //第二次发起请求,查询Id 为1的用户信息
        User user2 = userMapper.findUserById(1);
        System.out.println(user2);
        sqlSession.close();
    }

通过结果可以看出第二次没有发出sql查询请求,

所以我们需要在中间执行 commit 操作

//如果sqlSession去执行commit操作(执行插入、更新、删除),
// 清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
//更新user1的信息,
user1.setUsername("李飞");
//user1.setSex("男");
//user1.setAddress("北京");
userMapper.updateUserById(user1);
//提交事务,才会去清空缓存
sqlSession.commit();

测试

一级缓存应用

正式开发,是将 mybatis 和 spring 进行整合开发,事务控制在 service 中。

一个 service 方法中包括很多 mapper 方法调用。

service{

     //开始执行时,开启事务,创建SqlSession对象

     //第一次调用mapper的方法findUserById(1)

     //第二次调用mapper的方法findUserById(1),从一级缓存中取数据

     //方法结束,sqlSession关闭

}

如果是执行两次service调用查询相同的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。

二级缓存

原理

首先开启mybatis的二级缓存。

sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。

如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。

sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。

UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。

每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。

开启二级缓存

mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存

在 SqlMapConfig.xml 开启二级开关


然后在你的 Mapper 映射文件中添加一行: ,表示此 mapper 开启二级缓存。

调用 pojo 类实现序列化接口

二级缓存需要查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操作(因为二级缓存数据存储介质多种多样,在内存不一样),注意如果存在父类、成员pojo都需要实现序列化接口。

public class Orders implements Serializable
public class User implements Serializable

测试

//二级缓存测试
    @Test
    public void testCache2() throws Exception
    {
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();


        //创建UserMapper对象,mybatis自动生成代理对象
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        //sqlSession1 执行查询 写入缓存(第一次查询请求)
        User user1 = userMapper1.findUserById(1);
        System.out.println(user1);
        sqlSession1.close();


        //sqlSession3  执行提交  清空缓存
        UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
        User user3 = userMapper3.findUserById(1);
        user3.setSex("女");
        user3.setAddress("山东济南");
        user3.setUsername("崔建");
        userMapper3.updateUserById(user3);
        //提交事务,清空缓存
        sqlSession3.commit();
        sqlSession3.close();
        
        //sqlSession2 执行查询(第二次查询请求)
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        User user2 = userMapper2.findUserById(1);
        System.out.println(user2);
        sqlSession2.close();
   }

结果

useCache 配置

在 statement 中设置 useCache=false 可以禁用当前 select 语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。