第四天:mybatis的缓存和注解开发

第四天:mybatis的缓存和注解开发

mybatis框架 学习计划

共四天
第一天:mybatis入门
mybatis的概述
mybatis的环境搭建
mybatis入门案例
自定义mybatis框架(主要的目的是为了让大家了解mybatis中执行细节)
第二天:mybatis基本使用
mybatis的单表crud操作
mybatis的参数和返回值
mybatis的dao编写
mybatis配置的细节
几个标签的使用
第三天:mybatis的深入和多表
mybatis的连接池
mybatis的事务控制及设计的方法
mybatis的多表查询
一对多(多对一)
多对多
第四天:mybatis的缓存和注解开发
mybatis中的加载时机(查询的时机)
mybatis中的一级缓存和二级缓存
mybatis的注解开发
单表CRUD
多表查询

4.1 Mybatis 延迟加载策略(查询的时机)

​ 通过前面的学习,我们已经掌握了 Mybatis 中一对一,一对多,多对多关系的配置及实现,可以实现对象的关联查询。实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的账户信息。此时就是我们所说的延迟加载。

4.1.1 为什么要用延迟加载策略

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

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

实现需求

需求:
查询账户(Account)信息并且关联查询用户(User)信息。如果先查询账户(Account)信息即可满足要求,当我们需要查询用户(User)信息时再查询用户(User)信息。把对用户(User)信息的按需去查询就是延迟加载。
mybatis 第三天实现多表操作时,我们使用了 resultMap 来实现一对一,一对多,多对多关系的操作。主要是通过 associationcollection 实现一对一及一对多映射。associationcollection 具备延迟加载功能。

实际使用场景

在对应的四种表关系中:一对多,一对一,多对多
    一对多,多对多:通常情况下我们都是采用延迟加载。
    一对一:通常情况下我们都是采用立即加载。

4.1.2 使用 assocation 实现延迟加载(一对一)

​ 需求:
查询账户信息同时查询用户信息。

1. 编写账户实体类

/**
 * 账号实体类
 */
public class Account implements Serializable {

    private  Integer id;

    private Integer uid;

    private Double money;

    //从表实体应该包含一个主体实体的对象引用
    private User user;

// getter + setter 省略

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

2.账户的持久层 DAO 接口

public interface AccountDao {

    /**
     * 查询所有账号,同时还要获取到当前账号的所属用户信息
     * @return
     */
    List<Account> findAll();
}

3. 账户的持久层映射文件

    <!-- 定义account和user的resultMap-->
    <resultMap id="accountUserMap" type="org.example.domain.Account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 一对一的关系映射: 配置封装user的内容  配置延迟加载-->
        <association property="user" column="uid" javaType="org.example.domain.User" select="org.example.dao.UserDao.findById">
        </association>

    </resultMap>

    <!-- 查询所有账号-->
    <select id="findAll" resultMap="accountUserMap">
            select * from account
    </select>

select: 填写我们要调用的 select 映射的 id
column : 填写我们要传递给 select 映射的参数

4. 用户的持久层接口和映射文件

/**
 * 根据Id查询用户
 */
User findById(Integer id);

    <!-- 根据Id查询用户-->
    <select id="findById" parameterType="java.lang.Integer" resultType="org.example.domain.User">
    select * from  user where id = #{id}
    </select>

5. 编写测试只查账户信息不查用户信息。

public class AccountTest {

    private  InputStream in;
    private  SqlSession sqlSession;
    private AccountDao accountDao;

    @Before  // test方法执行之前执行
    public void init() throws IOException {
            //1.读取配置文件
             in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.创建 SqlSessionFactory 的构建者对象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

            //3.使用构建者创建工厂对象 SqlSessionFactory
            SqlSessionFactory factory = builder.build(in);

            //4.使用 SqlSessionFactory 生产 SqlSession 对象
            sqlSession = factory.openSession();

            //5.使用 SqlSession 创建 dao 接口的代理对象
            accountDao = sqlSession.getMapper(AccountDao.class);

    }

    @After // test方法执行之后执行
    public void destroy() throws IOException {
        //提交事务
        sqlSession.commit();
        //7.释放资源
        sqlSession.close();
        in.close();
    }

    /**
     * 查询所有账号
     */
    @Test
    public void testFindAll()  {
        List<Account> accounts = accountDao.findAll();
        for(Account account : accounts) {
           // System.out.println(account);
        }
    }
 
}

6 测试结果

DEBUG example.dao.AccountDao.findAll  - ==>  Preparing: select * from account 
DEBUG example.dao.AccountDao.findAll  - ==> Parameters: 
DEBUG example.dao.AccountDao.findAll  - <==      Total: 3
DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@612679d6]
DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@612679d6]
DEBUG source.pooled.PooledDataSource  - Returned connection 1629911510 to pool.

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

4.1.3 使用 Collection 实现延迟加载(一对多)

同样我们也可以在一对多关系配置的<collection>结点中配置延迟加载策略。
<collection>结点中也有 select 属性,column 属性。
需求:
完成加载用户对象时,查询该用户所拥有的账户信息

1. 在 User 实体类中加入List<Account>属性

/**
 * 用户的实体类
 */
public class User implements Serializable {

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

    // 一对多关系映射; 主表实体应该包含从表实体的集合引用
    private List<Account> accounts;
    // 多对多的关系映射: 一个用户可以具有多个角色
    private List<Role> roles;

// getter setter 省略

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

2. 编写用户和账户持久层接口的方法

/**
 * 查询所有用户操作
 * @return
 */
List<User> findAll();

/**
 *查询所有账户 根据用户id 
 * @return
 */
List<Account> findAllByUid(Integer uid);

3. 编写用户持久层映射配置

<!-- 定义User账号的resultMap -->
<resultMap id="userMap" type="org.example.domain.User">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="sex" column="sex"></result>
    <result property="birthday" column="birthday"></result>
    <result property="address" column="address"></result>
   
   <!--配置user对象中国accounts集合映射  延迟加载-->
    <collection property="accounts" column="id" ofType="org.example.domain.Account" select="org.example.dao.AccountDao.findAllByUid">

    </collection>

</resultMap>

<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
    select * from user
</select>

<collection>标签:
        主要用于加载关联的集合对象
select 属性:
        用于指定查询 account 列表的 sql 语句,所以填写的是该 sql 映射的 id
column 属性:
        用于指定 select 属性的 sql 语句的参数来源,上面的参数来自于 user 的 id 列,所以就写成 id 这一个字段名了

4. 编写账户持久层映射配置

<select id="findAllByUid" resultType="org.example.domain.Account">
    select * from account where uid= #{uid}
</select>

5. 测试只加载用户信息

 package org.example.test;


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.example.dao.UserDao;
import org.example.domain.Account;
import org.example.domain.QueryVo;
import org.example.domain.User;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import sun.nio.ch.Net;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 *  mybatis 入门案例
 */
public class UserTest {

    private  InputStream in;
    private  SqlSession sqlSession;
    private  UserDao userDao;

    @Before  // test方法执行之前执行
    public void init() throws IOException {
            //1.读取配置文件
             in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.创建 SqlSessionFactory 的构建者对象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

            //3.使用构建者创建工厂对象 SqlSessionFactory
            SqlSessionFactory factory = builder.build(in);

            //4.使用 SqlSessionFactory 生产 SqlSession 对象
            sqlSession = factory.openSession();

            //5.使用 SqlSession 创建 dao 接口的代理对象
            userDao = sqlSession.getMapper(UserDao.class);

    }

    @After // test方法执行之后执行
    public void destroy() throws IOException {
        //提交事务
        sqlSession.commit();
        //7.释放资源
        sqlSession.close();
        in.close();
    }


    @Test
    public void testFindAll()  {
        //6.使用代理对象执行查询所有方法
        List<User> users = userDao.findAll();
    }

}

6 测试结果:

测试结果如下:

DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
DEBUG source.pooled.PooledDataSource  - Created connection 1629911510.
DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@612679d6]
DEBUG rg.example.dao.UserDao.findAll  - ==>  Preparing: select * from user 
DEBUG rg.example.dao.UserDao.findAll  - ==> Parameters: 
DEBUG rg.example.dao.UserDao.findAll  - <==      Total: 10
DEBUG ansaction.jdbc.JdbcTransaction  - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@612679d6]
DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@612679d6]
DEBUG source.pooled.PooledDataSource  - Returned connection 1629911510 to pool.

我们发现并没有加载 Account 账户信息。

4.2 mybatis中的一级缓存和二级缓存

4.2.1 Mybatis 一级缓存

4.2.1.1证明一级缓存的存在

​ 一级缓存是 SqlSession级别的缓存,只要 SqlSession 没有 flushclose,它就存在。

1. 编写用户持久层 Dao 接口
public interface UserDao {
    /**
     * 根据 id 查询
     * @param userId
     * @return
     */
    User findById(Integer userId);
}
2. 编写用户持久层映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.IUserDao">
    <!-- 根据 id 查询 -->
    <select id="findById" resultType="UsEr" parameterType="int" useCache="true">
        select * from user where id = #{uid}
    </select>
</mapper>
3. 编写测试方法
@Test
public void testFindById() {
    User user = userDao.findById(41);
    System.out.println("第一次查询的用户:"+user);
    User user2 = userDao.findById(41);
    System.out.println("第二次查询用户:"+user2);
    System.out.println(user == user2);
}
4. 测试结果
DEBUG ansaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e720b71]
DEBUG g.example.dao.UserDao.findById  - ==>  Preparing: select * from user where id = ? 
DEBUG g.example.dao.UserDao.findById  - ==> Parameters: 49(Integer)
DEBUG g.example.dao.UserDao.findById  - <==      Total: 1
第一次查询的用户:org.example.domain.User@17d0685f
第二次查询的用户:org.example.domain.User@17d0685f
true

    我们可以发现,虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是 Mybatis 提供给我们的一级缓存在起作用了。因为一级缓存的存在,导致第二次查询 id 为 41 的记录时,并没有发出 sql 语句从数据库中查询数据,而是从一级缓存中查询。

4.2.1.2 一级缓存的分析

​ 一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查
询用户信息。
得到用户信息,将用户信息存储到一级缓存中。
如果 sqlSession去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。

4.2.2 Mybatis 二级缓存

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

​ 首先开启 mybatis 的二级缓存。
sqlSession1 去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
如果 SqlSession3 去执行相同 mapper 映射下 sql,执行 commit 提交,将会清空该 mapper 映射下的二级缓存区域的数据。
sqlSession2 去查询与 sqlSession1 相同的用户信息,首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

4.2.2.1 二级缓存的开启与关闭

第一步:在 SqlMapConfig.xml文件开启二级缓存
<settings>
    <!-- 开启二级缓存的支持 -->
    <setting name="cacheEnabled" value="true"/>
</settings>
因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为
false 代表不开启二级缓存。
第二步:配置相关的Mapper 映射文件
<cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.UserDao">
    <!--开启user支持二级缓存-->
    <cache/>
</mapper>
第三步:配置statement上面的useCache 属性
<!-- 根据 id 查询 -->
<select id="findById" resultType="org.example.domain.User" parameterType="int" useCache="true">
    select * from user where id = #{uid}
</select>
将 UserDao.xml 映射文件中的<select>标签中设置 useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。
注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。

4.2.2.2 二级缓存开启的注意事项

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

4.3 mybatis的注解开发

​ 这几年来注解开发越来越流行,Mybatis 也可以使用注解开发方式,这样我们就可以减少编写 Mapper 映射文件了。本次我们先围绕一些基本的 CRUD 来学习,再学习复杂映射关系及延迟加载。

4.3.1 mybatis 的常用注解说明

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

4.3.2 使用 Mybatis 注解实现基本 CRUD

​ 单表的 CRUD 操作是最基本的操作,前面我们的学习都是基于 Mybaits 的映射文件来实现的。

1. 编写实体类

public class User implements Serializable {

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

    // 一对多关系映射; 主表实体应该包含从表实体的集合引用
    private List<Account> accounts;
    // 多对多的关系映射: 一个用户可以具有多个角色
    private List<Role> roles;

    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;
    }

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

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

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

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

2. 使用注解方式开发持久层接口

public interface IUserDao {

    /**
     * 查询所有用户
     * @return
     */

    @Select("select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "sex",column = "sex"),
            @Result(property = "birthday",column = "birthday"),
            @Result(property = "address",column = "address")
    })
    List<User> findAll();

    /**
     * 保存用户信息
     * @param user
     * @return
     */
    @Insert("insert into user(username,sex,birthday,address) values(#{username},#{sex},#{birthday},#{address})")
    int saveUer(User user);

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

    /**
     * 根据ID删除用户
     * @param userId
     * @return
     */
    @Delete("delete from user where id = #{userId} ")
    int deleteUser(Integer userId);

    /**
     * 根据ID查询用户
     * @param userId
     * @return
     */
    @ResultMap("userMap")
    @Select("select * from user where id = #{userId} ")
    User findById(Integer userId);

    /**
     * 根据名称模糊查询用户
     * @param username
     * @return
     */
    @ResultMap("userMap")
    @Select("select * from user where username like concat('%',#{username},'%')")
    List<User> findUserByName(String username);

    /**
     * 查询总用户数
     * @return
     */
    @Select("select count(*) from user")
    int findTotalUser();

}
通过注解方式,我们就不需要再去编写 UserDao.xml 映射文件了。

3. 编写测试方法

public class IUserTest {

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

    @Before  // test方法执行之前执行
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建 SqlSessionFactory 的构建者对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        //3.使用构建者创建工厂对象 SqlSessionFactory
        SqlSessionFactory factory = builder.build(in);

        //4.使用 SqlSessionFactory 生产 SqlSession 对象
        sqlSession = factory.openSession();

        //5.使用 SqlSession 创建 dao 接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);

    }

    @After // test方法执行之后执行
    public void destroy() throws IOException {
        //提交事务
        sqlSession.commit();
        //7.释放资源
        sqlSession.close();
        in.close();
    }

    /**
     * 查询所有账号
     */
    @Test
    public void testFindAll()  {
        List<User> users = userDao.findAll();
        for(User user : users){
            System.out.println(user);
        }

    }

    @Test
    public void testSaveUser(){
        User user = new User();
        user.setUsername("唐老鸭");
        user.setSex("男");
        user.setBirthday(new Date());
        user.setAddress("唐宁街10号");
        int i = userDao.saveUer(user);
        System.out.println("受影响行数-> " + i);
    }

    @Test
    public void testUpdateUser(){
        User user = new User();
        user.setId(54);
        user.setUsername("唐老鸭AAA");
        user.setSex("男");
        user.setBirthday(new Date());
        user.setAddress("唐宁街10号");
        int i = userDao.updateUser(user);
        System.out.println("受影响行数-> " + i);
    }

    @Test
    public void testDeleteUser(){
        int i = userDao.deleteUser(56);
        System.out.println("受影响行数-> " + i);
    }

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

    @Test
    public void testFindByName(){
        List<User> users = userDao.findUserByName("唐");
        for(User user: users){
            System.out.println(user);
        }
    }

    @Test
    public void testFindTotla(){
        int total = userDao.findTotalUser();
        System.out.println("总用户数: " + total);
    }

}

4.3.3 使用注解实现复杂关系映射开发

​ 实现复杂关系映射之前我们可以在映射文件中通过配置<resultMap>来实现,在使用注解开发时我们需要借助@Results 注解,@Result 注解,@One 注解,@Many 注解。

@Results 注解
代替的是标签 <resultMap>
该注解中可以使用单个@Result注解,也可以使用@Result集合 @Results({@Result(),@Result()})或@Results(@Result())
@Resutl 注解
代替了 <id>标签和<result>标签
@Result 中 属性介绍:
**id** 是否是主键字段
**column** 数据库的列名
**property** 需要装配的属性名
**one** 需要使用的@One 注解(@Result(one=@One()))

**many** 需要使用的@Many 注解(@Result(many=@many()))
@One 注解(一对一)
代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
@One 注解属性介绍:
**select**
指定用来多表查询的 sqlmapper
**fetchType** 会覆盖全局的配置参数 lazyLoadingEnabled
使用格式:
@Result(column=" ",property="",one=@One(select=""))
@Many 注解(多对一)
代替了<Collection>标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。
注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType(一般为 ArrayList)但是注解中可以不定义;
使用格式:
@Result(property="",column="",many=@Many(select=""))

4.3.3.1使用注解实现一对一复杂关系映射及延迟加载

1 添加 User 实体类及 Account 实体类
public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一对多关系映射; 主表实体应该包含从表实体的集合引用
    private List<Account> accounts;


    // 省略getter,setter 和 toString 方法
    
    }
    
    public class Account implements Serializable {

    private  Integer id;

    private Integer uid;

    private Double money;

    //从表实体应该包含一个主体实体的对象引用
    private User user;
    
    // 省略getter,setter 和 toString 方法
}
    
2. 添加账户的持久层接口并使用注解配置
public interface IAccountDao {

    /**
     * 查询所有账号,并且获取每个账号的用户信息
     * @return
     */
    @Select("select * from account")
    @Results(id = "accountMap",value = {
            @Result(id = true,column = "id",property = "id"),
            @Result(property = "uid",column = "uid"),
            @Result(property = "money",column = "money"),
            @Result(property = "user",column = "uid",one = @One(select = "org.example.dao.IUserDao.findById",fetchType = FetchType.LAZY))
    })
    List<Account> findAll();
}
@One:
相当于<assocation>的配置
    select 属性:代表将要执行的 sql 语句
    fetchType 属性:代表加载方式,一般如果要延迟加载都设置为 LAZY 的值
3 添加用户的持久层接口并使用注解配置
public interface IUserDao {

    /**
     * 查询所有用户
     * @return
     */

    @Select("select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "sex",column = "sex"),
            @Result(property = "birthday",column = "birthday"),
            @Result(property = "address",column = "address"),
            @Result(property = "accounts",column = "id",many = @Many(select = "org.example.dao.IAccountDao.findAccountByUid",fetchType = FetchType.LAZY))
    })
    List<User> findAll();
    
        /**
     * 根据ID查询用户
     * @param userId
     * @return
     */
    @Select("select * from user where id = #{userId} ")
    User findById(Integer userId);
}
@Many:
相当于<collection>的配置
    select 属性:代表将要执行的 sql 语句
    fetchType 属性:代表加载方式,一般如果要延迟加载都设置为 LAZY 的值

4 测试一对一关联及延迟加载
public class IAccountTest {

    private  InputStream in;
    private  SqlSession sqlSession;
    private IAccountDao accountDao;

    @Before  // test方法执行之前执行
    public void init() throws IOException {
            //1.读取配置文件
             in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.创建 SqlSessionFactory 的构建者对象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

            //3.使用构建者创建工厂对象 SqlSessionFactory
            SqlSessionFactory factory = builder.build(in);

            //4.使用 SqlSessionFactory 生产 SqlSession 对象
            sqlSession = factory.openSession();

            //5.使用 SqlSession 创建 dao 接口的代理对象
            accountDao = sqlSession.getMapper(IAccountDao.class);

    }

    @After // test方法执行之后执行
    public void destroy() throws IOException {
        //提交事务
        sqlSession.commit();
        //7.释放资源
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll(){
        List<Account> accounts = accountDao.findAll();
        for(Account account : accounts){
            System.out.println("--------每个账号的信息--------");
            System.out.println(account);
            System.out.println("--------每个账号所属人员的信息--------");
            System.out.println(account.getUser());
        }
    }
}

4.3.3.2 使用注解实现一对多复杂关系映射

1 User 实体类加入List<Account>
/**
  * 用户实体类
  */
public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一对多关系映射; 主表实体应该包含从表实体的集合引用
    private List<Account> accounts;


    // 省略getter,setter 和 toString 方法
    
    }
    
/**
  * 账户实体类
  */
    public class Account implements Serializable {

    private  Integer id;

    private Integer uid;

    private Double money;

    //从表实体应该包含一个主体实体的对象引用
    private User user;
    
    // 省略getter,setter 和 toString 方法
}
2 编写用户的持久层接口并使用注解配置
public interface IUserDao {

    /**
     * 查询所有用户
     * @return
     */

    @Select("select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "sex",column = "sex"),
            @Result(property = "birthday",column = "birthday"),
            @Result(property = "address",column = "address"),
            @Result(property = "accounts",column = "id",many = @Many(select = "org.example.dao.IAccountDao.findAccountByUid",fetchType = FetchType.LAZY))
    })
    List<User> findAll();
}
@Many:
相当于<collection>的配置
    select 属性:代表将要执行的 sql 语句
    fetchType 属性:代表加载方式,一般如果要延迟加载都设置为 LAZY 的值
3 编写账户的持久层接口并使用注解配置
public interface IAccountDao {
    @Select("select * from account where uid = #{userId}")
    List<Account> findAccountByUid(Integer userId);
}
4 添加测试方法
public class IUserTest {

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

    @Before  // test方法执行之前执行
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建 SqlSessionFactory 的构建者对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        //3.使用构建者创建工厂对象 SqlSessionFactory
        SqlSessionFactory factory = builder.build(in);

        //4.使用 SqlSessionFactory 生产 SqlSession 对象
        sqlSession = factory.openSession();

        //5.使用 SqlSession 创建 dao 接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);

    }

    @After // test方法执行之后执行
    public void destroy() throws IOException {
        //提交事务
        sqlSession.commit();
        //7.释放资源
        sqlSession.close();
        in.close();
    }

    /**
     * 查询所有账号
     */
    @Test
    public void testFindAll()  {
        List<User> users = userDao.findAll();
        for(User user : users){
            System.out.println(user);
            for(Account account : user.getAccounts()){
                System.out.println(account);
            }
        }

    }
}

4.3.4 mybatis 基于注解的二级缓存

1 在 SqlMapConfig中开启二级缓存支持

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

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

/**
 * 用户的持久层接口
 */
@CacheNamespace(blocking=true)  //mybatis 基于注解方式实现配置二级缓存
public interface IUserDao {}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容