且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

更新时间:2021-10-11 02:32:22

文章目录

Mybatis中的延迟加载

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

一对一进行延迟加载

一对一的情况下一般是采用立即加载,但为了好理解延迟加载在这里就进行一下演示

在之前 账户和用户的表中,我们一个账户只能是归一个用户所有,所以我们采用查询账户的方式来演示延时加载

首先进行准备工作:

  1. 实现根据id来查询用户的功能<select id="findById" resultType="com.gegege.domain.User" parameterType="java.lang.Integer"> select * from user where id = #{uid}; </select>
  2. 在Account中添加私有成员变量 private User user2;并提供get和set方法
  3. Account的Test方法

首先我们在IAccountDao.xml添加一个resultMap

<resultMap id="accountusermap" type="com.gegege.domain.Account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <association property="user2" column="uid" javaType="com.gegege.domain.User" select="com.gegege.dao.IUserDao.findById"></association>
</resultMap>

这里要注意的是<association property="user2" column="uid" javaType="com.gegege.domain.User" select="com.gegege.dao.IUserDao.findById"></association>

其中property 对应的我们在准备工作第二步中创建的变量

column对应的是要传递给select映射的参数

select 对应要调用的select映射的ID

javaType对应延迟加载的javabean

这时更改findall对应的select语句

    <select id="FindAll" resultMap="accountusermap">
        select * from account
    </select>

在这时运行会发现 语句还是一次性执行完

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

https://mybatis.net.cn/configuration.html#settings网站中找到

要更改这两个属性值才可以实现延迟加载

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

于是我们更改sqlmapconfig.xml

在里添加以下内容即可实现延迟加载

<settings>
        <!--开启mybatis全局进行延迟加载的开关-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

那么我们删掉遍历输出的语句试一下

    @Test
    public void FindAllTest() {
        List<Account> list=dao.FindAll();
    }

发现只执行了单表上的内容

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

此时一对一的延迟加载便完成了

一对多的延迟加载

user的javabean中要有private List<Account> list;以便实现一对多

将IUserDao.xml改为

    <resultMap id="selectUserAccount" type="com.gegege.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        <collection property="list" column="id"  ofType="com.gegege.domain.Account" select="com.gegege.dao.IAccountDao.findByUid">
        </collection>
    </resultMap>
    <sql id="select*"> select * from user</sql>
    <select id="findAll" resultMap="selectUserAccount">
        SELECT * FROM user
    </select>

注意其中select="com.gegege.dao.IAccountDao.findByUid"

那么我们下一步要做的就是在IAccountDao.java和IAccountDao.xml添加findByUid方法

<mapper namespace="com.gegege.dao.IAccountDao">

    <sql id="select_account"> select * from account</sql>
    <resultMap id="accountusermap" type="com.gegege.domain.Account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <association property="user2" column="uid" javaType="com.gegege.domain.User" select="com.gegege.dao.IUserDao.findById"></association>
    </resultMap>
    <select id="FindAll" resultMap="accountusermap">
        select * from account
    </select>

    <select id="findByUid" resultType="com.gegege.domain.Account">
        select * from account where uid = #{uid}
    </select>

</mapper>
List<Account> findByUid(int uid);

最后别忘了更改sqlmapconfig.xml

在里添加以下内容即可实现延迟加载

<settings>
        <!--开启mybatis全局进行延迟加载的开关-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

缓存机制:

就是将用户经常查询的数据的结果的一个保存,保存到一个内存中(缓存就是内存中的一个对象),用户在查询的时候就不用到数据库文件中查询(磁盘),从而减少与数据库的交付次数提高了响应速度,解决了并发系统的西能问题

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

一级缓存

我们用以下代码测试一下一级缓存

    /**
     * 测试一级缓存
     */
    @Test
    public void testFirstLevelCache() {
        User user1 = dao.findById(41);
        System.out.println(user1);
        User user2 = dao.findById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

发现只执行了一次sql语句

还有user1和user2两个对象的地址是相同的,那么证明这个这个对象是从缓存中拿出来的

那么对于重要数据是不能保存在缓存里的

所以我们要清空缓存,清空缓存有两种方式

    /**
     * 测试一级缓存清空
     * 已知在关闭session会清空一级缓存
     */
    @Test
    public void testFirstLevelCacheClean() {
        User user1 = dao.findById(41);
        System.out.println(user1);
        session.close();
        session=sqlSessionFactory.openSession();
        dao=session.getMapper(IUserDao.class);
        User user2 = dao.findById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }
    /**
     * 测试一级缓存清空
     * 已知在关闭session会清空一级缓存
     */
    @Test
    public void testFirstLevelCacheClean() {
        User user1 = dao.findById(41);
        System.out.println(user1);
        //清空cache
        session.clearCache();
        User user2 = dao.findById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }

这两个程序执行的结果是这样的

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

这样执行的就是两遍sql语句

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

    /**
     * 测试一级缓存清空
     * 已知在关闭session会清空一级缓存
     */
    @Test
    public void testFirstLevelCacheClean() {
        User user1 = dao.findById(41);
        System.out.println(user1);
        //清空cache
        User user=new User();
        int num=new Random().nextInt();
        user.setId(42);
        user.setUsername("dd"+ num);
        user.setAddress("s" + num);
        user.setBirthday(new Date());
        user.setSex("s");
        dao.upDateUser(user);
        User user2 = dao.findById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }

在两个查询中间插入一个更新操作,这时执行程序就会有这样的结果

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

二级缓存

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

先写测试类

public class cache2_test {
    private InputStream in;
    private SqlSessionFactoryBuilder sessionFactoryBuilder;
    private SqlSessionFactory factory1;
    @Before
    public void before() throws IOException {
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建工具
        sessionFactoryBuilder=new SqlSessionFactoryBuilder();
        factory1 = sessionFactoryBuilder.build(in);
    }
    @After
    public void after() throws IOException {
        in.close();
    }

    /**
     * 测试二级缓存
     */
    @Test
    public void testTwoLevelCache() {
        SqlSession session1 = factory1.openSession();
        IUserDao dao1 = session1.getMapper(IUserDao.class);
        User user1 = dao1.findById(41);
        System.out.println(user1);
        session1.close();

        SqlSession session2 = factory1.openSession();
        IUserDao dao2 =session2.getMapper(IUserDao.class);
        User user2 = dao2.findById(41);
        System.out.println(user2);
        session2.close();

        System.out.println(session1==session2);
    }

}

运行发现仍然执行了两遍sql语句,这表示两次数据都是从数据库里存取的

这是因为二级缓存默认是不开启的

首先在sqlmapconfig.xml配置一下setting (因为此配置默认是true此配置可以省略)

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

然后在IUserDao.xml里添加这两个标签

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

此时运行时结果:

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

我们可以发现这时只执行了一次sql语句,证明二级缓存生效了

但我们还可以发现,两个对象的地址不一样,所以对比结果为false,这是因为mybatis中二级缓存的存取方式是吧数据散装进行存储,而不是直接存储对象

注解开发

注解开发比较方便简单,在公司项目中也用的越来越多了,所以有必要学习注解开发

注解开发——单表的增删改查

创建maven项目

创建javabean的类和Iuserdao的接口

创建SqlMapConfig.xml,jdbcConfig.properties,log4j.properties

要注意对应dao接口的资源库路径下不能有对应的xml文件,否则会报错

在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"></properties>
    <!--配置别名-->
    <typeAliases>
        <package name="com.mybatis.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>
    <!--指定带有注解的接口所在位置-->
    <mappers>
        <package name="com.mybatis.dao"/>
    </mappers>
</configuration>

在IUserDao中插入以下内容:

package com.mybatis.dao;

import com.mybatis.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

/**
 * mybatis的四种注解
 * @Select @Install @Update @Delete
 */
public interface IUserDao {
    /**
     *查询所有用户
     * @return
     */
    @Select(value = "select * from user")
    List<User> selectAll();

    /**
     * 插入用户
     * @param user
     */
    @Insert(value = "insert into user (username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday})")
    void install(User user);

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

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

    /**
     * 获取数据个数
     * @return
     */
    @Select(value = "select count(*) from user ")
    int getNumber();
    
    /**
     * 根据ID查询
     * @return
     */
    @Select(value = "select * from user where id=#{id}")
    User selectById(int id);

    /**
     * 模糊查询
     * @param username
     * @return
     */
    @Select(value = "select * from user where username Like #{username}")
    List<User> findUserByName(String username);
}

创建测试类进行测试


public class mybatisAnno_test {
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession sqlSession;
    private IUserDao dao;
    @Before
    public void init() throws IOException {
        //获取字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //根据字节构建sqlsessionfactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //根据sqlsessionfactory生产session
        sqlSession = factory.openSession(true);
        //根据sqlsession获取Dao代理对象
        dao = sqlSession.getMapper(IUserDao.class);
    }
    @After
    public void end() throws IOException {
        //释放资源
        in.close();
        sqlSession.close();
    }

    /**
     * 测试查询
     */
    @Test
    public void testfind() {
        //用代理对象执行方法
        List<User> users = dao.selectAll();
        users.forEach(System.out::println);
    }

    /**
     * 测试插入
     */
    @Test
    public void testinsert() {
        User user=new User();
        user.setAddress("jijijijij");
        user.setBirthday(new Date());
        user.setUsername("吉良吉影");
        user.setSex("男");
        dao.install(user);
    }

    /**
     * 测试更新
     */
    @Test
    public void testUpdate() {
        User user=new User();
        user.setId(55);
        user.setAddress("aaaaa");
        user.setBirthday(new Date());
        user.setUsername("空条承太郎");
        user.setSex("男");
        dao.updateUser(user);
    }

    /**
     * 测试删除
     */
    @Test
    public void testdelete() {
        dao.deleteUser(56);
    }

    /**
     * 测试获取个数
     */
    @Test
    public void testGetNumber() {
        System.out.println(dao.getNumber());
    }
    
    /**
     * 测试根据id查询
     */
    @Test
    public void testFindById() {
        User user = dao.selectById(55);
        System.out.println(user);
    }
    
    /**
     * 测试模糊查询
     */
    @Test
    public void testFindByName() {
        //用代理对象执行方法
        List<User> users = dao.findUserByName("%王%");
        users.forEach(System.out::println);
    }
}

此时所有测试便都可以正常执行

注解开发——建立表与实体类的对应关系

先删除除了查询之外的函数及抽象类

重新更改javabean中的参数,使其与数据库表中不对应

public class User implements Serializable {
    private int userid;
    private String username;
    private String useraddress;
    private String usersex;
    private Date userbirthday;

    public int getUserid() {
        return userid;
    }

    public void setUserid(int userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

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

    public String getUseraddress() {
        return useraddress;
    }

    public void setUseraddress(String useraddress) {
        this.useraddress = useraddress;
    }

    public String getUsersex() {
        return usersex;
    }

    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }

    public Date getUserbirthday() {
        return userbirthday;
    }

    public void setUserbirthday(Date userbirthday) {
        this.userbirthday = userbirthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "userid=" + userid +
                ", username='" + username + '\'' +
                ", useraddress='" + useraddress + '\'' +
                ", usersex='" + usersex + '\'' +
                ", userbirthday=" + userbirthday +
                '}';
    }
}

在IUserDao中添加@Results, @Result,@ResultMap

/**
 * mybatis的四种注解
 * @Select @Install @Update @Delete
 */
public interface IUserDao {

    /**
     *查询所有用户
     * @return
     */
    @Select(value = "select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,column = "ID" ,property = "userid"),
            @Result(column = "username" ,property = "username"),
            @Result(column = "birthday", property = "userbirthday"),
            @Result(column = "sex",property = "usersex"),
            @Result(column = "address",property = "useraddress")
    })
    List<User> selectAll();


    /**
     * 根据ID查询
     * @return
     */
    @Select(value = "select * from user where id=#{id}")
    @ResultMap(value = { "userMap"})
    User selectById(int id);

    /**
     * 模糊查询
     * @param username
     * @return
     */
    @Select(value = "select * from user where username Like #{username}")
    @ResultMap(value = { "userMap"})
    List<User> findUserByName(String username);
}

注解开发——多表查询:

一对一查询或者叫多对一 (一个账户只属于一个用户)

创建account的javabean

注意其中有user变量

public class Account implements Serializable {
    private int id;
    private int uid;
    private double money;

    private User user;

    public int getId() {
        return id;
    }

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

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}'+user;
    }
}

再创建IAccountDao.java

public interface IAccountDao {
    @Select(value = "select * from Account")
    @Results(id = "accountMap",value = {
            @Result(id = true,column = "id",property = "id"),
            @Result(column = "uid",property = "uid"),
            @Result(column = "money",property = "money"),
            @Result(column = "uid",property = "user",one = @One(select = "com.mybatis.dao.IUserDao.selectById",fetchType = FetchType.EAGER))
    })
    List<Account> findAll();
}

需要注意@Result(column = "uid",property = "user",one = @One(select = "com.mybatis.dao.IUserDao.selectById",fetchType = FetchType.EAGER))

这句代码:

property 为javabean里的容器名

column的参数为要传递的形参参数

one是指mybatis里一对一(多对一)的查找

one注解里的 select是要填写全限定类名及方法名

fetchType 里 EAGER代表立即加载,LAZY是延迟加载或者叫懒加载

这时创建一个test运行即可看见结果

public class MybatisAccount_test {
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession sqlSession;
    private IAccountDao dao;
    @Before
    public void init() throws IOException {
        //获取字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //根据字节构建sqlsessionfactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //根据sqlsessionfactory生产session
        sqlSession = factory.openSession(true);
        //根据sqlsession获取Dao代理对象
        dao = sqlSession.getMapper(IAccountDao.class);
    }
    @After
    public void end() throws IOException {
        //释放资源
        in.close();
        sqlSession.close();
    }

    /**
     * 测试查询
     */
    @Test
    public void testfind() {
        //用代理对象执行方法
        List<Account> users = dao.findAll();
        users.forEach(System.out::println);
    }
}

运行结果:

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

一对多查询——一个用户对应多个账户

为IAccountDao添加抽象类

    /**
     * 根据用户id查询账户
     * @param uid
     * @return
     */
    @Select(value = "select * from Account where uid=#{uid}")
    List<Account> findByUid(int uid);

更改User的javabean,增加账户的list

private List<Account> accounts = new ArrayList<Account>();

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

增加下面这句参数

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

    /**
     *查询所有用户
     * @return
     */
    @Select(value = "select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,column = "ID" ,property = "userid"),
            @Result(column = "username" ,property = "username"),
            @Result(column = "birthday", property = "userbirthday"),
            @Result(column = "sex",property = "usersex"),
            @Result(column = "address",property = "useraddress"),
            @Result(column = "id",property = "accounts",many = @Many(select = "com.mybatis.dao.IAccountDao.findByUid",fetchType = FetchType.LAZY))
    })
    List<User> selectAll();

这句注解的意思与上面一对一的意思类似,只是把one换成了more(一对一换成了多对一)

立即加载换成了懒加载

然后运行,即可执行成功

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

注解开发——缓存配置

使用下面的代码进行测试:


public class mybatisAnno_test {
    private InputStream in;
    private SqlSessionFactory factory;
    @Before
    public void init() throws IOException {
        //获取字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //根据字节构建sqlsessionfactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //根据sqlsessionfactory生产session
    }
    @After
    public void end() throws IOException {
        //释放资源
        in.close();
    }

    /**
     * 测试根据id查询
     */
    @Test
    public void testFindById() {
        SqlSession sqlSession = factory.openSession(true);
        IUserDao dao = sqlSession.getMapper(IUserDao.class);
        User user = dao.selectById(42);
        System.out.println(user);
        sqlSession.close();

        SqlSession sqlSession2 = factory.openSession(true);
        IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
        User user2 = dao2.selectById(42);
        System.out.println(user2);
        sqlSession2.close();

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

}

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

可以看到他其实是进行了两次查询,这证明此时并没有用到二级缓存

这时我们修改sqlmapconfig.xml此配置可以省略,因为默认值也为true

 <!--配置缓存-->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

然后在IUserDao类中添加注解@CacheNamespace(blocking = true)

@CacheNamespace(blocking = true)
public interface IUserDao {

    /**
     *查询所有用户
     * @return
     */
    @Select(value = "select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,column = "ID" ,property = "userid"),
            @Result(column = "username" ,property = "username"),
            @Result(column = "birthday", property = "userbirthday"),
            @Result(column = "sex",property = "usersex"),
            @Result(column = "address",property = "useraddress"),
            @Result(column = "id",property = "accounts",many = @Many(select = "com.mybatis.dao.IAccountDao.findByUid",fetchType = FetchType.LAZY))
    })
    List<User> selectAll();
}

此时再次运行原来的方法,就可以看到开启了二级缓存的效果:

Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发