栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

MyBatis学习总结(四) MyBatis 延迟加载策略/MyBatis 一级缓存、二级缓存/MyBatis注解开发

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

MyBatis学习总结(四) MyBatis 延迟加载策略/MyBatis 一级缓存、二级缓存/MyBatis注解开发

一、 MyBatis 延迟加载策略

通过前面的学习,我们已经掌握了 MyBatis 中一对一(多对一)、一对多、多对多关系的配置及实现,可以实现对象的关联查询。实际开发过程中很多时候我们并不需要总是将关联的对象的信息也一并加载出来,此时就是我们所说的延迟加载。

举例:用户和账户
一个账户只能属于一个用户(多个账户也可以属于同一个用户):一对一(多对一)
一个用户可以有多个账户:一对多

在查询用户时,可能我就只是想查询用户信息,并不关心其账户,因此用户下的账户信息应该是什么时候使用什么时候才加载出来(延迟加载)。
在查询账户时,光是查出账户信息而不知道此账户属于哪个用户会很不直观,因此账户的所属用户信息应该是随着账户查询时一起立即查询出来(立即加载)。

(一)什么是延迟加载

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

  • 好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
  • 坏处:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

在对应的四种表关系中:

  • 一对一,多对一:通常情况下我们都是采用立即加载。
  • 一对多,多对多:通常情况下我们都是采用延迟加载。
(二)延迟加载的实现

在实现延迟加载之前,我们必须做一些配置。

进入 MyBatis 的官方文档,找到 settings 的说明信息:

设置名描述默认值
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。false
aggressiveLazyLoading开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。false (在 MyBatis 3.4.1 及之前的版本中默认为 true)

我们需要在 MyBatis 的核心配置文件中添加延迟加载的配置:

 
	
	

如果MyBatis版本是3.4.1以后就可以不添加aggressiveLazyLoading,因为默认是false。

1.一对一(多对一)实现延迟加载

案例:查询所有账户及其所属用户信息
用户表、账户表、用户实体类、账户实体类,可在前篇博客查看。

IAccountDao:

import com.fox.pojo.Account;
import java.util.List;

public interface IAccountDao {
    //查询所有账户及其所属用户信息
    List findAll();
}

IAccountDao.xml:






    
        
        
        
        
        
    
    
    
        select * from user where id=#{id};
    

测试类:

import com.fox.dao.IAccountDao;
import com.fox.pojo.Account;
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 AccountTest {
    private InputStream in = null;
    private SqlSession sqlSession = null;
    private IAccountDao accountDao = null;

    @Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.使用构建者创建工厂对象 SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.使用 SqlSessionFactory 生产 SqlSession 对象
        sqlSession = factory.openSession();
        //4.使用 SqlSession 创建 dao 接口的代理对象
        accountDao = sqlSession.getMapper(IAccountDao.class);
    }

    @After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
    public void destroy() throws IOException {
        sqlSession.commit();
        //释放资源
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll() {
        List accounts = accountDao.findAll();
    }
}


我们发现,因为本次只是将 Account对象查询出来放入 List 集合中,并没有涉及到 User对象,所以就没有发出 SQL 语句查询账户所关联的 User 对象的查询。

(三)一对多实现延迟加载

案例:查询所有用户及其所包含的账户信息
用户表、账户表、用户实体类、账户实体类,可在前篇博客查看。

IUserDao:

import com.fox.pojo.User;
import java.util.List;

public interface IUserDao {

    //查询所有用户以及所包含的账户信息
    List findAll();
}

IUserDao.xml:






    
    
        
        
        
        
        
        
        
    

    
    
        select * from account where uid=#{uid};
    

测试类:

import com.fox.dao.IUserDao;
import com.fox.pojo.User;
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 UserTest {

    private InputStream in = null;
    private SqlSession sqlSession =null;
    private IUserDao userDao = null;

    @Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.使用构建者创建工厂对象 SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.使用 SqlSessionFactory 生产 SqlSession 对象
        sqlSession = factory.openSession();
        //4.使用 SqlSession 创建 dao 接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
    public void destroy() throws IOException {
        sqlSession.commit();
        //释放资源
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll(){
        List users = userDao.findAll();
    }
}


同样的,我们发现并没有加载 Account 账户信息。

二、MyBatis 缓存
  • 什么是缓存?
    存在于内存中的临时数据。
  • 为什么使用缓存?
    减少和数据库的交互次数,提高执行效率。
  • 什么样的数据能使用缓存,什么样的数据不能使用?
    • 适用于缓存:
      (1)经常查询并且不经常改变的。
      (2)数据的正确与否对最终结果影响不大的。
    • 不适用于缓存:
      (1)经常改变的数据
      (2)数据的正确与否对最终结果影响很大的。
      例如:商品的库存,银行的汇率,股市的牌价。

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

MyBatis 中缓存分为一级缓存,二级缓存。

(一)MyBatis一级缓存 1.证明一级缓存的存在

User实体类:

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

}

IUserDao:

import com.fox.pojo.User;

public interface IUserDao {

    //根据id查询用户以及所包含的账户信息
    User findUserById(Integer id);
}

IUserDao.xml:







    
    
        select * from user where id=#{id};
    

    
        update user set username=#{username},address=#{address} where id=#{id};
    

测试类:

import com.fox.dao.IUserDao;
import com.fox.pojo.User;
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;

public class UserTest {

    private InputStream in = null;
    private SqlSession sqlSession =null;
    private IUserDao userDao = null;

    @Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.使用构建者创建工厂对象 SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.使用 SqlSessionFactory 生产 SqlSession 对象
        sqlSession = factory.openSession();
        //4.使用 SqlSession 创建 dao 接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
    public void destroy() throws IOException {
        sqlSession.commit();
        //释放资源
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll(){
        //根据id查询用户
        User user1 = userDao.findUserById(41);
        System.out.println(user1);
        //更新用户信息
        user1.setUsername("小何");
        user1.setAddress("广东");
        userDao.updateUser(user1);
        //再次查询id为41的用户
        User user2 = userDao.findUserById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }
}

(二)MyBatis二级缓存
  • 二级缓存是 Mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
  • 二级缓存实际上指的是MyBatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
  • 二级缓存的使用步骤:
    第一步:让MyBatis框架支持二级缓存(在MyBatis核心配置文件中配置)
    第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
    第三步:让当前的操作支持二级缓存(在select标签中配置)

案例:
让MyBatis框架支持二级缓存(在MyBatis核心配置文件中配置):


	
    

因为 cacheEnabled 的默认值就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为false 代表不开启二级缓存。

让当前的映射文件支持二级缓存(在IUserDao.xml中配置)并且让当前的操作支持二级缓存(在select标签中配置)useCache="true":