SSM框架之Mybatis(7)延迟加载、缓存及注解

Mybatis(7)延迟加载、缓存及注解

1、延迟加载

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

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

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

2、Mybatis缓存

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

2.1、一级缓存

一级缓存是SqlSession级别的缓存,只要SqlSession没有flush、close或clearCache,那么缓存就会存在。

2.1.1、测试一级缓存的存在

测试方法

@Test
public void testFindOne(){
    //通过id查找用户
    User user1 = uesrdao.findById(48);
    System.out.println(user1.hashCode());

    //sqlSession.clearCache();

    User user2 = uesrdao.findById(48);
    System.out.println(user2.hashCode());

    System.out.println(user1 == user2);
}

运行结果:

Opening JDBC Connection
Created connection 278934944.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@10a035a0]
==> Preparing: select * from user where id=?
> Parameters: 48(Integer)
<
Total: 1
1832580921
1832580921
true

分析

上面的结果显示虽然我们进行了两次查询,但是只执行了一次数据库操作。这就是一级缓存在起作用,因为一级缓存的存在所以在第二次查询id = 48时,没有进行数据库的操作,而是直接从一级缓存中读取数据。

2.1.2、一级缓存

一级缓存是SqlSession级别的缓存,当调用SqlSession缓存的 添加,更新、删除、commit()、close()等操作时,会清空一级缓存。

第一次sqlsession对象执行查询操作id = 48时,先去缓存中查询是否存在有id=48的用户信息,没有就执行sql语句,然后从数据库中查询数据。

得到用户信息后就把用户信息储存在一级缓存中。

准确来说,存入一级缓存的是由查询语句返回的对象,而不是具体的数据

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

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

2.1.3、对清除缓存的操作

@Test
public void testFindOne(){
    //通过id查找用户
    User user1 = uesrdao.findById(48);
    System.out.println(user1.hashCode());

    sqlSession.clearCache();

    User user2 = uesrdao.findById(48);
    System.out.println(user2.hashCode());

    System.out.println(user1 == user2);
}

执行结果:

Opening JDBC Connection
Created connection 278934944.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@10a035a0]
==> Preparing: select * from user where id=?
> Parameters: 48(Integer)
<
Total: 1
1832580921
==> Preparing: select * from user where id=?
> Parameters: 48(Integer)
<
Total: 1
369241501
false

清空缓存后查询数据库的信息,查询了两次。

2.2、二级缓存

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

及可以认为二级缓存是依赖于SqlSessionFactory,SqlSessionFactory对象产生时,就存在,并且由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)在configuration标签中开启setting标签

<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

setting标签

设置名 描述 有效值 默认值
cacheEnabled 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 true | false true

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

第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)

在mapper标签中写

<mapper namespace="dao.IUserDao">
	<cache/>
</mapper>

cache标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。

第三步:让当前的操作支持二级缓存(在select标签中配置)

在mapper标签中写

<mapper namespace="dao.IUserDao">
	<cache/>
    
    <select id="findById" parameterType="int" resultType="domain.User" useCache="true">
        select * from user where id=#{id}
    </select>
</mapper>

将 UserDao.xml 映射文件中的select标签中设置 useCache=”true”代表当前这个 statement 要使用
二级缓存,如果不使用二级缓存可以设置为 false。
注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。

第四步:添加测试类

package test;

import dao.IUserDao;
import domain.QueryVo;
import domain.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.InputStream;
import java.util.Date;
import java.util.List;

public class CacheTest {
    /**
     * 测试mybatis的CRUD操作
     */
    InputStream in;
    SqlSessionFactory build;

    @Before
    public void init() throws Exception {
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        build = new SqlSessionFactoryBuilder().build(in);

    }

    @After
    public void destory() throws Exception {

        in.close();
    }
    @Test
    public void testFindOne(){
        SqlSession sqlSession1 = build.openSession();
        IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
        User user1 = dao1.findById(41);
        System.out.println(user1);
        sqlSession1.close();//一级缓存消失
        SqlSession sqlSession2 = build.openSession();
        IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
        User user2 = dao2.findById(41);
        System.out.println(user2);
        sqlSession2.close();
        System.out.println(user1 == user2);
        
    }
}

执行结果:

Opening JDBC Connection
Created connection 929776179.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@376b4233]
==> Preparing: select * from user where id=?
> Parameters: 41(Integer)
<
Total: 1
User{id=41, username=‘老王’, birthday=Tue Feb 27 17:47:08 CST 2018, sex=‘男’, address=‘北京’}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@376b4233]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@376b4233]
Returned connection 929776179 to pool.
Cache Hit Ratio [dao.IUserDao]: 0.5
User{id=41, username=‘老王’, birthday=Tue Feb 27 17:47:08 CST 2018, sex=‘男’, address=‘北京’}

false

经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二
次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。

然而两次的User对象却不相同,这是因为二级缓存存储的是对应的具体数据

{id=41, username=‘老王’, birthday=Tue Feb 27 17:47:08 CST 2018, sex=‘男’, address=‘北京’}

等到再次查询时才会new新的对象。

2.2.1、二级缓存的注意事项

当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化
方式来保存对象。

/**
*
* <p>Title: User</p>
* <p>Description: 用户的实体类</p>
*/
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}

3、Mybatis关于注解的开发

3.1、mybatis常用注解说明

@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态 SQL 映射
@CacheNamespace:实现注解二级缓存的使用

3.2、用注解实现CRUD操作

3.2.1、编写实体类

E:\java\idea\SSM\ssm01_mybatis_annotation\src\main\java\domain\User.java

package domain;

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

public class User implements Serializable {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    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 String getAddress() {
        return address;
    }

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

    public String getSex() {
        return sex;
    }

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

    public Date getBirthday() {
        return birthday;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}

3.2.2、编写主配置文件

E:\java\idea\SSM\ssm01_mybatis_annotation\src\main\resources\SqlMapConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--引入外部配置文件-->
    <properties resource="jdbcConfig.properties"/>
<!--配置别名-->
    <typeAliases>
        <package name="domain"/>
    </typeAliases>
    <!--配置环境-->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--配置dao接口所在位置-->
    <mappers>
        <package name="dao"/>
    </mappers>
</configuration>

3.2.3、编写持久化接口

E:\java\idea\SSM\ssm01_mybatis_annotation\src\main\java\dao\IUserDao.java

package dao;

import domain.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * 在mybatis中针对注解有四个注解
 *@Select()、@Insert()、@Update()、@Delete()
 */
@CacheNamespace(blocking = true)
public interface IUserDao {
    /**
     * 查询所有
     * @return
     */
    @Select(value = "select * from user")
    List<User> findAll();

    /**
     * 添加用户
     * @param user
     */
    @Insert("insert into user (username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday})")
    void addUser(User user);

    /**
     * 根据id删除指定用户
     * @param id
     */
    @Delete("delete from user where id = #{id}")
    void deleteUser(int id);

    /**
     * 更新用户数据
     * @param user
     */
    @Update("update user set username = #{username},address = #{address},sex = #{sex},birthday = #{birthday} where id = #{id}")
    void updateUser(User user);

    @Select("select * from user where id = #{id}")
    User findById(int id);

    /**
     * 模糊查询
     * @param name
     * @return
     */
    //@Select("select * from user where username like #{str}")
    @Select("select * from user where username like '%${value}%'")
    List<User> findByName(String name);

    /**
     *
     * @return
     */
    @Select("select count(id) from user")
    int findTotal();
}

3.2.4、编写相应测试类

package test;

import dao.IUserDao;
import domain.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.InputStream;
import java.util.Date;
import java.util.List;

public class AnnotationTest {
    InputStream in;
    SqlSessionFactory factory;
    SqlSession sqlSession;
    IUserDao userDao;
    @Before
    public void init() throws Exception {
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        factory = new SqlSessionFactoryBuilder().build(in);
        sqlSession = factory.openSession();
        userDao = sqlSession.getMapper(IUserDao.class);
    }
    @After
    public void destory() throws Exception {
        sqlSession.commit();
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll(){
        List<User> users = userDao.findAll();
        for (User user:users){
            System.out.println(user);
        }
    }

    @Test
    public void testAddUser(){
        User user = new User();
        user.setUsername("wf");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("中国");

        userDao.addUser(user);
    }

    @Test
    public void testDeleteUser(){
        userDao.deleteUser(49);
    }

    @Test
    public void testUpdateUser(){
        User user = new User();
        user.setId(50);
        user.setUsername("gx");
        user.setBirthday(new Date());
        user.setSex("女");
        user.setAddress("中国");

        userDao.updateUser(user);
    }

    @Test
    public void testFindById(){
        User user = userDao.findById(50);
        System.out.println(user);
    }

    @Test
    public void testFindByName(){
        //List<User> users = userDao.findByName("%王%");
        List<User> users = userDao.findByName("王");

        for (User user:users){
            System.out.println(user);
        }
    }

    @Test
    public void testFindTotal(){
        int a = userDao.findTotal();
        System.out.println(a);
    }
}

3.4、mybatis基于注解的二级缓存

3.4.1、在SqlMapConfig中开启二级缓存支持

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

3.4.2、在持久层接口中使用注解配置二级缓存

/**
*
* <p>Title: IUserDao</p>
* <p>Description: 用户的持久层接口</p>
*/
@CacheNamespace(blocking=true)//mybatis 基于注解方式实现配置二级缓存
public interface IUserDao {}
posted @ 2019-04-20 23:01  紫月冰凌  阅读(323)  评论(0编辑  收藏  举报