Mybatis(四)

  • Mybatis中的延迟加载
    • 什么是延迟加载
    • 什么是立即加载
  • Mybatis中的缓存
    • 什么是缓存
    • 为什么使用缓存
    • 什么样的数据能使用缓存,什么样的数据不能使用
    • Mybatis中的一级缓存和二级缓存
  • Mybatis中的注解开发
    • 环境搭建
    • 单表CRUD操作(代理Dao方式)
    • 多表查询操作
    • 缓存的配置

Mybatis中的延迟加载

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

问题:在一对多中,当我们有一个用户,它有100个账单。在查询用户的时候,要不要把关联的张大宝查出来?在查询账单的时候,要不要把关联的用户查出来?

  • 在查询用户时,用户下的账单信息应该是,什么时候需要账户信息,就什么时候查询(一个用户多个账单)
  • 在查询账单时,账单的所属用户信息应该是随着账单查询,一起查询出来(一个账单只能对应一个用户)

延迟加载和立即加载:

  • 延迟加载:在真正使用数据时才发起查询,不用的时候不查询(也叫按需加载,懒加载)
  • 立即加载:不管用不用,只要一调用方法,马上发起查询

MyBatis中的延迟加载

MyBatis 延迟加载和立即加载

在对应的一对多,一对一(多对一),多对多这四种表关系中:

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

(很好理解的)

在之前的笔记中,我们使用了resultMap来实现一对一,一对多,多对多关系的操作。主要是通过 <association><collection> 实现一对一及一对多映射。其实**<association><collection>也具备延迟加载功能。**

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

视频讲解

在之前一对一的案例的基础上,我们进行如下操作:

首先在主配置文件(SqlMapConfig.xml)添加对<settings>的配置

1
2
3
4
5
6
<!--配置settings-->
<settings>
<!--开启Mybatis的支持延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

关于<settings>下的两个属性:

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

关于**fetchType 属性**:可选的。有效值为 lazyeager指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled,使用属性的值

  • lazy :延迟加载
  • eager:立即加载

具体可查看官网:设置(settings)关联

然后在持久层接口添加代码如下:

1
2
3
4
5
/**
* 查询所有账户信息(包括账户所对应的用户的信息,但是用户的信息采用延迟加载策略)
* @return
*/
List<Account> findAll();

接着在映射配置文件中添加代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<resultMap id="accountUserMap" type="top.qing.domain.Account">
<!--主键字段的对应-->
<id property="ID" column="ID"/>
<!--非主键字段的对应-->
<result property="UID" column="UID"/>
<result property="MONEY" column="MONEY"/>
<!--一对一的关系映射,配置封装user的内容-->
<!--<association>标签:一个复杂类型的关联,许多结果将包装成这种类型-->
<!--简单地说,用于指定从表的应用实体(这里当然就是user)的属性-->
<association property="user" column="UID"
javaType="top.qing.domain.User"
select="top.qing.dao.UserDao.findById">
<!--select属性:填写我们要调用的select映射的id(这里我们是根据id查询user,当然就填UserDao下的findById)-->
<!--column属性:填写我们要传递给select映射的参数(findById是根据id查询用户,所以我们当然要选择account表中的UID)-->
</association>
</resultMap>

<!--查询所有账户信息(包括账户所对应的用户的信息,但是用户的信息采用延迟加载策略)-->
<select id="findAll" resultMap="accountUserMap">
SELECT * FROM account
</select>

MyBatis有两种不同的方式加载关联

  • 嵌套Select查询:通过执行另外一个SQL映射语句来加载期望的复杂类型
  • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集

这里我们采用了嵌套Select查询来加载关联,而在之前的案例中我们采用的是嵌套结果映射来处理连接结果的重复子集从而实现加载关联

注意:这里由于我们在使用了**<association>标签中使用了column属性select属性**,所以在下面的findAll方法的SQL语句我们只需要写上SELECT * FROM account,最后依然可以查询到user表的相关内容(因为select属性中指定了UserDao的对应方法(id))

关于**<association>**标签的相关属性:

  • column:数据库中的列名,或者是列的别名,指定传递给嵌套Select查询语句的列名(要传递给select映射的参数)
  • select:用于加载复杂类型属性的映射语句的ID,它会从column属性指定的列中检索数据,作为参数传递给目标 select 语句(要调用的select映射的id)
  • fetchType:可选的,有效值为 lazy 和 eager(具体上面已经提到过了)

具体可查看官网:关联

最后在测试类中(执行代码):

1
2
3
4
5
6
7
8
    @Test
public void testFindAll(){
// 使用代理对象accountDao执行查询所有方法
List<Account> accounts = accountDao.findAll();
// for (Account account : accounts) {
// System.out.println(account);
// }
}

结果如下:

延迟加载配置成功

可以发现如果我们不输出user相关信息的话,就不会执行UserDao下的findById方法。这样我们一对一的延迟加载就配置成功了。

Mybatis一对多实现延迟加载

视频讲解

在之前一对多的案例基础上,我们进行如下改造:

首先要提前准备一下AccountDao,补充一个findAccountByUid方法。

在AccountDao下添加代码如下:

1
2
3
4
5
/**
* 根据用户id查询结果(一个账单集合,因为一个用户对应多个账单)
* @return
*/
List<Account> findAccountByUid(Integer userID);

在AccountDao.xml下添加代码如下:

1
2
3
4
<!--根据用户id查询结果(一个账单集合,因为一个用户对应多个账单)-->
<select id="findAccountByUid" resultType="top.qing.domain.Account">
SELECT * FROM account WHERE UID = #{uid}
</select>

接下来就可以进行Mybatis一对多的延迟加载了。

首先在主配置文件(SqlMapConfig.xml)添加对<settings>的配置

1
2
3
4
5
6
<!--配置settings-->
<settings>
<!--开启Mybatis的支持延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

相关说明上面已经提到过了

然后在持久层接口添加代码如下:

1
2
3
4
5
/**
* 查询所有用户(如果用户有账单,要把账单信息也查询出来,但是账单信息采用延迟加载)
* @return
*/
List<User> findAll();

接着在映射配置文件中添加代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<resultMap id="userAccountMap" type="top.qing.domain.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
<!--collection 是用于建立一对多中集合属性的对应关系,ofType 用于指定集合的元素数据类型
select 是用于指定查询账户的唯一标识(账户的 dao 全限定类名加上方法名称)
column 是用于指定使用哪个字段的值作为条件查询-->
<collection property="accounts" ofType="top.qing.domain.Account"
column="id" select="top.qing.dao.AccountDao.findAccountByUid">
</collection>
</resultMap>

<!--查询所有用户(如果用户有账单,要把账单信息也查询出来,但是账单信息采用延迟加载)-->
<select id="findAll" resultMap="userAccountMap">
SELECT * FROM user
</select>

<collection>标签在配置延迟加载的相关属性大体上和<association>是一样的,具体可以看上面的笔记。

  • select 属性用于指定查询的sql语句,填写的是该sql映射的id
  • column 属性用于指定select属性的sql语句的参数来源(上面的参数来自于use 的id列,所以就写成id 这一个字段名了)

具体可看官网:集合

最后在测试类中(执行代码):

1
2
3
4
5
6
7
8
    @Test
public void testFindAll() throws IOException {
// 使用刚刚创建的代理对象userDao执行查询所有方法
List<User> users = userDao.findAll();
// for (User user : users) {
// System.out.println(user);
// }
}

结果如下:

一对多延迟加载配置成功


Mybatis中的缓存

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

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

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

MyBatis缓存机制

mybatis的缓存机制(一级缓存二级缓存和刷新缓存)和mybatis整合ehcache

Mybatis一级缓存

视频讲解

在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的SQL,MyBatis提供了一级缓存的方案优化这部分场景,如果是相同的SQL语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能。具体执行过程如下图所示:

一级缓存:它指的是Mybatis中SqlSession对象的缓存。当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用**。当SqlSession对象消失时,mybatis的一级缓存也就消失了。**

一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

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

Mybatis二级缓存

视频讲解

在上文中提到的一级缓存中,其最大的共享范围就是一个SqlSession内部,如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,具体的工作流程如下所示:

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

二级缓存:它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

二级缓存的使用步骤:

  1. 让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
  2. 让当前的映射文件支持二级缓存(在UserDao.xml中配置)
  3. 让当前的操作支持二级缓存(在<select>标签中配置)

二级缓存


Mybatis中的注解开发

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

注意:在Mybatis中的用一个Dao接口中,不能同时使用xml和注解

Mybatis的常用注解说明:

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

MyBatis 注解(摘自MyBatis官方文档)

MyBatis 注解方式的基本用法

MyBatis 注解

使用Mybatis注解实现基本的CRUD

CRUD相关注解:

  • @Insert:实现新增
  • @Update:实现更新
  • @Delete:实现删除
  • @Select:实现查询

关于环境准备最开始的pom.xml以及resources相关的配置这里就不多说了。这里就将使用的方法一起给出吧:

  • 保存操作(增):saveUser
  • 更新操作(改):updateUser
  • 删除操作(删):deleteUser
  • 查询一个(查):findById
  • 模糊查询(查):findByName
  • 查询user表的总记录数(聚合函数):findTotal

首选创建User类,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package top.qing.domain;

import lombok.Data;

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

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

}

然后创建主配置文件SqlMapConfig.xml,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?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">
<!--mybatis的主配置文件-->
<configuration>
<!--配置properties-->
<properties>
<!--配置连接数据库的四个基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>

<!--配置settings-->
<settings>
<!--开启Mybatis的支持延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

<!--配置环境-->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<!--配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!--配置连接数据库的四个基本信息-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>

<!--指定带有注解的dao接口所在位置-->
<mappers>
<package name="top.qing.dao"/>
</mappers>

</configuration>

创建持久层接口代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package top.qing.dao;

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 top.qing.domain.User;

import java.util.List;

public interface UserDao {

/**
* 查询所有用户
* @return
*/
@Select(value = "SELECT * FROM `user`") /*当注解里只有一个属性时,可以省略(我们这里可以省略value=)*/
List<User> findAll();

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

/**
* 根据Name模糊查询名字中带有指定字符串的用户信息
* @return
*/
@Select("select * from user where username like #{uStr}")
List<User> findByName(String UserStr);

/**
* 查询记录总数
* @return
*/
@Select("select count(id) from user;")
Integer findTotal();


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


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


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

}

执行代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package top.qing;

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 top.qing.dao.UserDao;
import top.qing.domain.User;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

public class UserTest {

private InputStream inputStream;
private SqlSession session;
private UserDao userDao;

@Before
public void init(){
// 1.读取配置文件
inputStream = Resources.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml");
// 2.创建 SqlSessionFactory 的SqlSessionFactoryBuilder对象builder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 3.使用builder创建工厂对象 SqlSessionFactory
SqlSessionFactory factory = builder.build(inputStream);
// 4.使用 SqlSessionFactory对象factory 生产 SqlSession 对象
session = factory.openSession();
// 5.使用 SqlSession 创建 dao 接口的代理对象userDao
userDao = session.getMapper(UserDao.class);
}

@After
public void destroy() throws IOException {
// 提交事务(不然增删改不会生效)
session.commit();
// 7.释放资源
session.close();
inputStream.close();
}

@Test
public void testFindAll() throws IOException {
// 使用刚刚创建的代理对象userDao执行查询所有方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}

@Test
public void testSaveUser() throws IOException {
User user = new User();
user.setUsername("saveUser");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("中国");

// 使用useDao代理对象执行saveUser方法
userDao.saveUser(user);
}

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

// 使用useDao代理对象执行saveUser方法
userDao.updateUser(user);
}

@Test
public void testDeleteUser() throws IOException {
// 使用useDao代理对象执行saveUser方法
userDao.deleteUser(52);
}

@Test
public void testFindById() throws IOException {
// 使用useDao代理对象执行saveUser方法
User user = userDao.findById(58);
System.out.println(user);
}

@Test
public void testFindByName() throws IOException {
// 使用useDao代理对象执行saveUser方法
List<User> users = userDao.findByName("%王%"); // 注意这里要加占位符%以完成模糊查询
for (User user : users) {
System.out.println(user);
}
}

@Test
public void testFindTotal() throws IOException {
// 使用useDao代理对象执行saveUser方法
Integer total = userDao.findTotal();
System.out.println("总记录数为:"+total);
}
}

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

要实现复杂关系映射,之前我们是在映射文件中通过配置<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注解(一对一):代替了<association>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象

    @One注解属性介绍:

    • select:指定用来多表查询的sqlmapper
    • fetchType:会覆盖全局的配置参数lazyLoadingEnabled

    使用格式:@Result(column=" “,property=”",one=@One(select=""))

  • @Many注解(多对一):代替了<Collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象集合

    @One注解属性介绍:

    • select:指定用来多表查询的sqlmapper
    • fetchType:会覆盖全局的配置参数lazyLoadingEnabled

    注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType(一般为 ArrayList)但是注解中可以不定义

    使用格式:@Result(property="",column="",many=@Many(select=""))

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

需求:加载账户信息时并且加载该账户的用户信息,根据情况可实现延迟加载(注解方式实现)

首先创建Account实体类:

1
2
3
4
5
6
7
8
9
10
11
12
package top.qing.domain;

import lombok.Data;

import java.io.Serializable;

@Data
public class Account implements Serializable {
private Integer ID;
private Integer UID;
private Double MONEY;
}

然后创建AccountDao代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package top.qing.dao;

import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;
import top.qing.domain.Account;

import java.util.List;

public interface AccountDao {

/**
* 查询所有账户信息(包括账户所对应的用户的信息)
* @return
*/
@Select(value = "SELECT * FROM account")
@Results(id = "accountUserMap", value = {
@Result(id = true, property="ID", column="ID"),
@Result(property="UID", column="UID"),
@Result(property="MONEY", column="MONEY"),
@Result(property="user", column="UID",
one = @One(select = "top.qing.dao.UserDao.findById", fetchType = FetchType.LAZY))
})
List<Account> findAll();
}

在测试类中执行代码如下:

1
2
3
4
5
6
7
8
9
// 注意别忘了init和destroy方法
@Test
public void testFindAll(){
// 使用代理对象accountDao执行查询所有方法
List<Account> accounts = accountDao.findAll();
for (Account account : accounts) {
System.out.println(account);
}
}

结果如下:

成功将用户信息查询出来,且延迟加载配置成功

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

需求:查询用户信息时,也要查询他的账户列表(使用注解方式实现)

分析:一个用户具有多个账户信息,所以形成了用户(User)与账户(Account)之间的一对多关系。

首先要在User类中添加一个Account集合的属性(这里就不展示),然后在AccountDao添加一个findByUid方法:

1
2
3
4
5
 * 根据用户id查询账单集合(因为一个用户对应对个账单)
* @return
*/
@Select("SELECT * FROM account WHERE UID = #{uid}")
List<Account> findByUid(Integer userID);

然后修改UserDao中的findAll方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 查询所有用户(如果用户有账单,也要将账单信息查询出来)
* @return
*/
@Select(value = "SELECT * FROM `user`") /*当注解里只有一个属性时,可以省略(我们这里可以省略value=)*/
@Results(id = "userAccountMap", value = {
@Result(id = true, property="id", column="id"),
@Result(property="username", column="username"),
@Result(property="birthday", column="birthday"),
@Result(property="sex", column="sex"),
@Result(property="address", column="address"),
@Result(property = "accounts",column = "id",
many = @Many(select = "top.qing.dao.AccountDao.findByUid",
fetchType = FetchType.LAZY)
)

})
List<User> findAll();

执行代码如下:

1
2
3
4
5
6
7
8
@Test
public void testFindAll() throws IOException {
// 使用刚刚创建的代理对象userDao执行查询所有方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}

结果如下:

一对多查询成功且延迟加载配置成功

Mybatis基于注解的二级缓存

视频讲解