Mybatis 的注解开发-多表查询
# 210.Mybatis 的注解开发-多表查询
来讲解更多使用注解完成开发的步骤。
# 注解建立成员变量和列名的映射
之前我们类的成员变量和数据库列名是一致的,所以不用映射;而使用注解也支持建立映射。
我们修改其成员变量的名字,和数据库列名不一致:
public class User implements Serializable {
private Integer userId;
private String userName;
private String userAddress;
private String userSex;
private Date userBirthday;
}
2
3
4
5
6
7
注:自行生成 getter 和 setter。
我们可以使用 Results 注解来完成映射。
@Select("select * from user")
@Results(value = {
@Result(id=true, column = "id", property = "userId"),
@Result(column = "username", property = "userName"),
@Result(column = "address", property = "userAddress"),
@Result(column = "sex", property = "userSex"),
@Result(column = "birthday", property = "userBirthday")
})
List<User> findAll();
2
3
4
5
6
7
8
9
再次运行 findAll 方法,可以看到正常运行。
那么其他 CRUD 注解还需要自己写一份 Results 注解吗?不用的,我们可以给 Result 定义一个 id,然后其他注解可以应用这个 ID:
@Select("select * from user")
@Results(id = "userMap", value = {
@Result(id=true, column = "id", property = "userId"),
@Result(column = "username", property = "userName"),
@Result(column = "address", property = "userAddress"),
@Result(column = "sex", property = "userSex"),
@Result(column = "birthday", property = "userBirthday")
})
List<User> findAll();
2
3
4
5
6
7
8
9
其他注解就可以使用 ResultMap 来指定:注意 ResultMap 可以写多个,这里我们只用到了一个
@Select("select * from user where id=#{id}")
@ResultMap("userMap")
User findById(Integer id);
2
3
# 一对一查询
我们试着在查询账户的时候,同时查询用户信息,
新建 Account 类:
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
}
2
3
4
5
请读者自行生成 setter 和 getter。
新增 IAccountDao.java
接口
public interface IAccountDao {
@Select("select * from account")
List<Account> findAll();
}
2
3
4
新建测试类 AccountAnnoTest:
@Test
public void testFindAll(){
List<Account> accounts = iAccountDao.findAll();
for(Account account : accounts){
System.out.println(account);
}
}
2
3
4
5
6
7
可以正常打印;
新增 User 成员变量:
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
// 多对一(在Mybatis称为一对一)的映射
private User user;
}
2
3
4
5
6
7
8
修改 findAll 方法:
@Select("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.peterjxl.dao.IUserDao.findById", fetchType= FetchType.EAGER) )
})
List<Account> findAll();
2
3
4
5
6
7
8
one 表示一对一查询,selet 属性则是根据某个方法来查询,fetchType 表示延迟加载还是立即加载
修改测试方法:
@Test
public void testFindAll(){
List<Account> accounts = iAccountDao.findAll();
for(Account account : accounts){
System.out.print(account);
System.out.println(account.getUser());
}
}
2
3
4
5
6
7
8
可以看到能正常查询:
# 一对多查询
我们试着在查询用户的时候,查询其所有账户,也就是一对多
我们先在 IAcountDao 接口中新建方法:findAccountByUid
/**
* 根据用户ID查询账户
* @param uid
* @return
*/
@Select("select * from account where uid = #{userId}")
List<Account> findAccountByUid(Integer uid);
2
3
4
5
6
7
我们在 User 类中新建 Account 属性:
// 一对多关系映射,一个用户对应多个账户
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
2
3
4
5
6
7
8
9
10
然后在 IUserDao 中增加一对多查询配置:这里我们选择懒加载,关键是第 9 行
@Select("select * from user")
@Results(id = "userMap", value = {
@Result(id=true, column = "id", property = "userId"),
@Result(column = "username", property = "userName"),
@Result(column = "address", property = "userAddress"),
@Result(column = "sex", property = "userSex"),
@Result(column = "birthday", property = "userBirthday"),
@Result(column = "id", property = "accounts",
many = @Many(select = "com.peterjxl.dao.IAccountDao.findAccountByUid", fetchType = FetchType.LAZY))
})
List<User> findAll();
2
3
4
5
6
7
8
9
10
11
修改测试方法:
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for(User user : users){
System.out.print(user);
System.out.println(user.getAccounts());
}
}
2
3
4
5
6
7
8
可以看到,能正常查询,并且是延迟加载的模式
# 缓存的配置
我们修改测试方法,看看有无一级缓存:
@Test
public void testFindOne(){
User user = userDao.findById(41);
System.out.println(user);
User user2 = userDao.findById(41);
System.out.println(user2);
System.out.println( "user == user2 ? " + (user == user2));
}
2
3
4
5
6
7
8
9
10
运行结果:可以看到,一级缓存是存在的。
user == user2 ? true
我们新建一个测试类
public class SecondLevelCatchTest {
private InputStream in;
private SqlSessionFactory factory;
@Before
public void init() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(in);
}
@After
public void destroy() throws IOException {
in.close();
}
@Test
public void testFindOne(){
SqlSession session = factory.openSession();
IUserDao userDao = session.getMapper(IUserDao.class);
User user = userDao.findById(41);
System.out.println(user);
session.close(); //释放一级缓存
SqlSession session1 = factory.openSession(); //再次打开session
IUserDao userDao1 = session1.getMapper(IUserDao.class);
User user1 = userDao1.findById(41);
System.out.println(user1);
session1.close();
}
}
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
可以看到 testFindOne 会查询两次查询,因为我们没有配置二级缓存:
我们在 IUserDao 接口上,加一个注解即可开启二级缓存:blocking 默认是 false,我们改成 true
@CacheNamespace(blocking = true)
public interface IUserDao {
2
可以看到值查询了一次
# 源码
所有代码已上传到了 GitHub (opens new window) 和 Gitee (opens new window) 上,并且创建了分支 demo20,读者可以通过切换分支来查看本文的示例代码。