mybatis探讨之延迟加载和缓存

mybatis探讨之延迟加载和缓存

一、什么是延迟加载
1.延迟加载的观点

在mybatis举行多表查询时,并非所有的查询都需要立刻举行。例如在查询带有账户信息的用户信息时,我们们并不需要总是在加载用户信息时就一定要加载他的账户信息。这时就要用到延迟加载,所谓延迟加载就是在需要用到数据时才举行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。

2.延迟加载的利益和坏处

利益:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,由于查询单表要比关联查询多张表速率要快。

坏处:由于只有当需要用到数据时,才会举行数据库查询,这样在大批量数据查询时,由于查询事情也要消耗
时间,以是可能造成用户等待时间变长,造成用户体验下降。

3.什么时刻使用延迟加载

在对应的四种表关系(一对多,多对一,一对一,多对多)中:
一对多,多对多:通常情况下我们都是接纳延迟加载。
多对一,一对一:通常情况下我们都是接纳立刻加载。

二、一对一实现延迟加载

进阶案例的基础上,举行如下修改:

1.添加延迟加载的设置

在主设置文件SqlMapConfig.xml文件中,添加settings标签,可以参考mybatis官方文档

<settings>
            <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
</settings>
2.修改主从表对应关系的设置

将映射设置文件IAccountDao.xml文件中的resultMap标签中的association标签修改为如下:

<association property="user" javaType="domain.User"
             select="dao.IUserDao.findById" column="uid"/>

select的内容是: 要挪用的IUserDao中对应的 select 方式的 id ,column的内容是 : 要传递给 select 方式的参数在account数据表中对应的列名。

3.修改从表查询的SQL语句

在原来的findAllAccountsWithUser方式中,SQL语句直接将两个表举行笛卡尔积,若是不修改SQL语句,就会举行立刻查询。由于是延迟加载,以是此处只需查询account信息即可。在映射设置文件IAccountDao.xml中举行如下修改:

<!-- 以延迟加载的方式设置查询带有用户信息的账户信息 -->
<select id="findAllAccountsWithUser" resultMap="accountUserMap">
    select* from account
</select>
4.测试运行

1.接纳立刻加载方式的查询

历程:直接执行一条SQL语句就可以获得带有用户信息的账户信息

mybatis探讨之延迟加载和缓存

2.不修改测试函数举行查询

当我们完成上述设置之后,直接运行测试函数,会发现并没有实现延迟加载。每次查询账户时依旧对用户举行了查询:这是由于在测试函数当中,对查询到的account工具和user工具举行打印,相当每次查询都需要用到数据。而延迟加载是在需要用到数据时才举行加载,不需要用到数据时就不加载数据。以是每次都市查询用户信息。

/**
 * 测试以延迟加载的方式查询带有用户信息的账户信息
 *
 * @throws IOException
 */
@Test
public void testFindAllAccountsWithUser() throws IOException {
    List<Account> accounts = accountDao.findAllAccountsWithUser();
    for (Account account : accounts
    ) {
        System.out.println(account);//这里解释每次查询都需要加载完整数据
        System.out.println(account.getUser());
    }
}

mybatis探讨之延迟加载和缓存

注重:即便是注释掉System.out.println(account.getUser()); 每次查询账户信息时,也会查询出用户信息。这是为什么呢?由于account工具中包罗对user工具的引用,直接打印account工具,需要组织完整的account工具,也就需要从数据库查出user工具的属性,并通过反射赋值给account工具中的user。

3.修改测试函数举行查询

@Test
public void testFindAllAccountsWithUser() throws IOException {
    List<Account> accounts = accountDao.findAllAccountsWithUser();
    int i = 0;
    for (Account account : accounts
    ) {
        System.out.println(account.getMoney());
        if(i == 1)
            System.out.println(account.getUser());
        i++;
    }
}

mybatis探讨之延迟加载和缓存

当修改测试函数之后,每次遍历只需要打印出账户的金额信息,不需要完整的account工具,以是并没有对user表举行查询,而当计数变量i == 1时,需要打印user信息,这才对user表举行查询。

三、一对多实现延迟加载
1.修改主从表对应关系设置

将映射设置文件IUserDao.xml文件中的resultMap标签中的collection标签修改为如下:

<collection property="accounts" ofType="domain.Account"
select="dao.IAccountDao.findAccountsByUid" column="id"/>
2.添加查询方式

在IAccountDao接口中添加凭据用户id查询账户信息的方式:

/**
 * 凭据用户id查询账户信息
 * @param uid
 * @return
 */
List<Account> findAccountsByUid(Integer uid);
3.设置查询方式

在映射设置文件IAccountDao.xml接口中设置凭据用户id查询账户信息的查询方式:

<!-- 设置凭据用户id查询账户信息 -->
<select id="findAccountsByUid" parameterType="Integer" resultType="domain.Account">
    select * from account where uid = #{uid}
</select>
4.修改sql语句

在映射设置文件IUserDao.xml文件中举行如下修改:

<!-- 设置以延迟加载的方式查询带有账户信息的用户信息 -->
<select id="findUserWithAccounts" resultMap="userAccountsMap">
     select * from user
</select>
5.测试运行

在测试类MybatisTest中修改 testFindUserWithAccounts方式为:

@Test
public void testFindUserWithAccounts() {
    //6.执行操作
    List<User> users = userDao.findUserWithAccounts();
    int i = 0;
    for(User user : users) {
        System.out.println(user.getUsername());
        if(i == 3)
            System.out.println(user.getAccounts());
        i++;
    }
}

mybatis探讨之延迟加载和缓存

四、什么是缓存
1.缓存的观点

缓存就是在内存中存储的数据备份,当数据没有发生本质改变的时刻,我们就不让数据的查询去数据库举行操作,而去内存中取数据,这样就大大降低了数据库的读写次数,而且从内存中读数据的速率比去数据库查询要快一些,这样同时又提高了效率。

一篇文章搞懂到底什么是渲染流水线

2.缓存若何使用

在数据库中适用于缓存机制的数据包罗:经常查询而且不经常改变的数据和效果的准确与否对最终效果影响不大的数据。响应地,不适用于缓存机制的数据包罗:经常改变的数据和效果的准确与否对最终效果影响很大的数据,例如:商品的库存,银行的汇率,股市的牌价。

3.mybatis中的缓存机制

mybatis中的缓存凭据缓存的生命周期,可分为一级缓存和二级缓存。Mybatis默认开启一级缓存而关闭二级缓存。

mybatis探讨之延迟加载和缓存

五、mybatis中的一级缓存
1.什么是一级缓存

一级缓存指的是Mybatis中SqlSession工具的缓存,当我们执行查询之后,查询的效果会同时存入到SqlSession为我们提供一块区域中。该区域的结构是一个Map。当我们再次查询同样的数据,Mybatis首先会去sqlsession中查询是否,若是该缓存中有这个数据,就直接从缓存中读取数据,否则就去数据库举行查询。当SqlSession工具消逝(close)时,mybatis的一级缓存也就消逝了。

2.一级缓存的测试

在测试类MybatisTest中添加如下测试方式:

/**
 * 测试一级缓存
 */
@Test
public void testL1Cache() {

    User user1 = userDao.findById(41);
    System.out.println("第一次查询的用户:" + user1);

    User user2 = userDao.findById(41);
    System.out.println("第二次查询用户:" + user2);
    System.out.print("第一次和第二次是否是统一工具:");
    System.out.println(user1 == user2);

    sqlSession.clearCache();//清空缓存
    User user3 = userDao.findById(41);
    System.out.println("第三次查询用户:" + user3);
    System.out.print("第二次和第三次是否是统一工具:");
    System.out.println(user2 == user3);

    sqlSession.close();//close操作会清空缓存
    //再次获取 SqlSession 工具
    sqlSession = factory.openSession();
    userDao = sqlSession.getMapper(IUserDao.class);
    User user4 = userDao.findById(41);
    System.out.println("第四次查询用户:" + user4);
    System.out.print("第三次和第四次是否是统一工具:");
    System.out.println(user3 == user4);

}

为了更清楚地看到效果,在User类的toString方式返回的字符串中添加super.toString()方式。运行效果如下:

mybatis探讨之延迟加载和缓存

可以看到第二次查询时,是直接从缓存获取,并非查询数据库。因此第一次和第二次都是统一工具。而第三次查询由于清空缓存,以是是从数据库中查询,以是不是统一工具。第四次由于关闭sqlSession工具,缓存消逝,以是也是从数据库中查询。

3.一级缓存的剖析

一级缓存是 SqlSession 局限的缓存,当挪用 SqlSession 的修改,添加,删除,commit(),close()等方式时,就会清空一级缓存。

mybatis探讨之延迟加载和缓存

第一次提议查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,若是没有,从数据库查询用户信息。获得用户信息后,将用户信息存储到一级缓存中。若是 sqlSession 去执行 commit 操作(对数据库执行插入、更新、删除),就会清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,制止脏读。(换句话说,只要对数据库举行更新、删除、插入等操作,就会清空一级缓存,制止缓存中数据和数据库中数据不一致。)
第二次提议查询用户 id 为 1 的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。

六、mybatis中的二级缓存
1.什么是二级缓存

它指的是Mybatis中SqlSessionFactory工具的缓存。由统一个SqlSessionFactory工具建立的SqlSession共享其缓存。多个 SqlSession 去操作统一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

2.二级缓存实例

1.在主设置文件中开启全局二级缓存

二级缓存默认不开启,需要在主设置文件SqlMapConfig.xml文件中开启二级缓存的支持。

<settings>
    <!-- 开启二级缓存的支持 -->
    <setting name="cacheEnabled" value="true"/>
</settings>

2.指定要开启二级缓存的映射设置文件

在映射设置文件IUserDao.xml文件中添加:

<!-- cache标签仅用于指定要开启二级缓存的映射设置文件 -->
<cache></cache>

3.修改查询方式的设置

将 IUserDao.xml 映射设置文件中的select标签中设置 useCache=”true”代表当前这个 statement 要使用
二级缓存,若是不使用二级缓存可以设置为 false。

<!-- 设置凭据id查询用户 -->
<select id="findById" resultType="domain.User" useCache="true">
    select * from user where id = #{id};
</select>

注重:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。

4.测试

在测试类MybatisTest中添加测试方式:

/**
 * 测试二级缓存
 */
@Test
public void testL2Cache() {

    User user1 = userDao.findById(41);
    System.out.println("第一次查询的用户:" + user1);
    sqlSession.close(); //关闭一级缓存

    SqlSession sqlSession2 = factory.openSession();
    IUserDao userDao2 = sqlSession2.getMapper(IUserDao.class);
    User user2 = userDao2.findById(41);
    System.out.println("第二次查询用户:" + user2);
    System.out.print("第一次和第二次是否是统一工具:");
    System.out.println(user1 == user2);
    sqlSession2.close();

}

mybatis探讨之延迟加载和缓存

可以看到虽然只查询了一次,第二次确实是从二级缓存中读取数据,然则第一次查询获得的工具和第二次查询获得的工具并不一致。

3.二级缓存剖析

二级缓存中存放的工具的属性数据,而非工具数据。因此,即便从二级缓存读取数据,获得的工具也是不相同的。

mybatis探讨之延迟加载和缓存

在开启mybatis的二级缓存之后。若是sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,若是存在直接从缓存中取出数据。若是SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。

注重:当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保留工具。
————恢复内容竣事————

原创文章,作者:2d28新闻网,如若转载,请注明出处:https://www.2d28.com/archives/1531.html