mybatis延迟加载、缓存及逆向工程

1,myibatis延迟加载

存在多表关联查询时,不需要一下子把全部表都拿出来的时候,可以采用延迟加载,先取出单表,等需要时再取出其他表,这样可以大幅提高数据库性能。

相关table可自行建立,目录结构如下:

mybatis延迟加载、缓存及逆向工程_第1张图片

(1)延迟加载测试方法:

// 延迟加载测试
    @Test
    public void testfindOrdersUserLazyLoad(){
        SqlSession session = MybatisUtil.getSqlSession();
        OrdersMapper mapper = session.getMapper(OrdersMapper.class);
        List list = mapper.findOrdersUserLazyLoad();
        
        for (Orders orders : list) {
            System.out.println(orders.getUser());
        }
    }

(2)findOrdersUserLazyLoad相关的OrdersMapper.xml配置:


   


   
       
       
       
       
       
       
       
                    select="com.mybatis.mapper.UserMapper.findById"
            column="user_id" >
       

   

延迟加载配置的核心就在于association里的配置,property表示要将查询的结果注入到那个属性中,由于它是个集合,需要javaType表示集合里元素的类型,coLumn:指定关联查询的列 。

(3)设置总的配置文件的延迟加载属性

   
       
       
   

(4)结果展示

List list = mapper.findOrdersUserLazyLoad();  这一步时仅会发出请求查询orders:

[DEBUG] - ==>  Preparing: SELECT * FROM orders 
[DEBUG] - ==> Parameters: 
[DEBUG] - <==      Total: 3

System.out.println(orders.getUser());   到了这一步时,由于要调用user时,所以会去查找users,之后的循环没有发出sql:
[DEBUG] - ==>  Preparing: select * from user where id = ? 
[DEBUG] - ==> Parameters: 1(Integer)
[DEBUG] - <==      Total: 1
[DEBUG] - ==>  Preparing: select * from user where id = ? 
[DEBUG] - ==> Parameters: 10(Integer)
[DEBUG] - <==      Total: 1

每根据一个user_id进行一次查询是否效率很低?

 

(5)aggressiveLazyLoading属性

如果在setting中加入 

当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。

这句话有点绕,实际效果如下说明:

List list = mapper.findOrdersUserLazyLoad();  此时只执行一个sql:

[DEBUG] - ==>  Preparing: SELECT * FROM orders 
[DEBUG] - ==> Parameters: 
[DEBUG] - <==      Total: 3

for (Orders orders : list) {  到了这一步也没有发出sql

System.out.println(orders.getUser());  在方法体里,每一次循环再发出一次sql(这种方式让延迟加载按需执行)

[DEBUG] - ==>  Preparing: select * from user where id = ? 
[DEBUG] - ==> Parameters: 1(Integer)
[DEBUG] - <==      Total: 1

但是测试的时候发现aggressiveLazyLoading设为true和false发出的sql是一样的,发出sql顺序相同,没发现区别,对懒加载也没影响。可能是同时存在两个懒属性时,调用其中一个,是否会加载其他懒属性的一个区分。

 

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

一级缓存SqlSession级别的缓存,同一数据可以保存在同一个

二级缓存是mapper级别,多个SqlSession去操作同一个Mapper的sql语句,不用重复发送sql请求。

一级缓存比较简单,同一条sql,查了一次就不会重复查,不用配置,当其他语句更新了数据之后,通过commit更新后再查即可即可。

不过测试的过程很有意思,如下代码:

@Test
    public void testFindUser(){
        SqlSession session = MybatisUtil.getSqlSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        
//        SqlSession session2 = MybatisUtil.getSqlSession();
//        UserMapper mapper2 = session2.getMapper(UserMapper.class);
        User user = mapper.findById(1);
//        User user2 = mapper2.findById(1);
        
        /*
         * 执行了(更新,插入,删除),会自动清空sqlsession的一级缓存,
         * 目的:为了避免脏读,保持缓存中数据是最新的
         */
        User userrUser = new User();
        userrUser.setId(1);
        userrUser.setUsername("aaaaaaa");
        mapper.updateUser(userrUser);
        // 执行了更新,插入,删除操作后,需要执行 commit  提交
//        session.commit();
        
        User user1 = mapper.findById(1);
        
        System.out.println(user);
        System.out.println(user1);
        session.close();
    }

发现即使session没有commit,虽然只发出了一条sql,但是user1依然改变了username,很神奇。是不是说mybatis有一套机制避免脏读?

二级缓存同事在SqlMapConfig.xml和相关mapper.xml设置二级缓存即可。

SqlMapConfig.xml:


       
       

UserMapper.xml:


   

或者:   (ehcache是一个分布式缓存框架)

测试方法:

@Test
    public void testErJiFindUser(){
        SqlSession session = MybatisUtil.getSqlSession();
        UserMapper mapper = session.getMapper(UserMapper.class);

        SqlSession session2 = MybatisUtil.getSqlSession();
        UserMapper mapper2 = session2.getMapper(UserMapper.class);
        
        SqlSession session3 = MybatisUtil.getSqlSession();
        UserMapper mapper3 = session3.getMapper(UserMapper.class);
        
        User user = mapper.findById(1);
        System.out.println(user.getUsername());
        // 执行关闭以后,才会将数据缓存到二级缓存中
        session.close();
        
        User user2 = mapper2.findById(1);
        System.out.println(user2.getUsername());
        session2.close();
        
        /*
         * 执行了(更新,插入,删除),会自动清空sqlsession的一级缓存,
         * 目的:为了避免脏读,保持缓存中数据是最新的
         */
//        User userrUser = new User();
//        userrUser.setId(1);
//        userrUser.setUsername("aaaaaaa");
//        int i = mapper3.updateUser(userrUser);
//        System.out.println(i);
//        // 执行了更新,插入,删除操作后,需要执行 commit  提交
//        session3.commit();
//        
    }

没开启二级缓存,则会发出三条select语句。

开启二级缓存后

[DEBUG] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@223f3642]
[DEBUG] - ==>  Preparing: select * from user where id = ? 
[DEBUG] - ==> Parameters: 1(Integer)
[DEBUG] - <==      Total: 1
王五
[DEBUG] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@223f3642]
[DEBUG] - Closing JDBC Connection [

可以看出,这时只发出了一条sql。这里启用二级缓存的xml生成了一个HashMap,总的配置文件为何也要配置二级缓存?估计是为了整体管理

在statement中设置useCache=false可以禁用当前select语句的二级缓存: