MyBatis 一对多关系使用 @Many 注解

MyBatis 一对多关系使用 @Many 注解

一对多关系

在讨论使用 MyBatis 实现一对多查找之前,有必要在数据表和数据类设计层次上明确一对多关系。
假设一个用户有多个账户。在最简单的情况下,用户表仅包含 user_id 和 user_name 列,而账户表则包含 account_id、account_name 和 user_id 外键,其中 *_id 列为各个表的主键。
现在来看数据类的设计。一个用户拥有多个账号,自然想到 User 类中包含一个 Account 列表。但是,Account 类中能包含 User 对象吗?
如果 Account 中包含 User 对象,这意味着用户的信息有泄露的可能(如果用户表还包含其他隐私信息)。比如,当我们只需要知道用户的某个账号时,却附赠了用户的一系列其他信息。另外这种互相包含的关系也容易导致写出无限递归的查询语句。Account 要为 User 赋值,而 User 中包含 Account 列表,Account 列表中的元素要为 User 赋值……
这里给出感觉合理的数据表和数据类模型。

# users table
CREATE TABLE `users` (
  `user_id` int NOT NULL,
  `user_name` varchar(45) NOT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
// user 类,忽略 setter 和 getter 
public class User {
    private Integer userId;
    private String userName;
    private List<Account> accounts;
}
# accounts table
CREATE TABLE `accounts` (
  `account_id` int NOT NULL,
  `account_name` varchar(45) NOT NULL,
  `user_id` int NOT NULL,
  PRIMARY KEY (`account_id`),
  KEY `user_id_idx` (`user_id`),
  CONSTRAINT `user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
// account 类,忽略 setter 和 getter 
public class Account {
    private Integer accountId;
    private String accountName;
    private Integer userId;
}

MyBatis 一对多查询

查询的主要难度在查询 User 上,其关键是理解 @Many 注解相关的参数。
先把简单的 AccountDao 搞了。

@Mapper
public interface AccountDao {
    @Results(id = "accountResultMap", value = {
            @Result(property = "accountId", column = "account_id", id = true),
            @Result(property = "accountName", column = "account_name"),
            @Result(property = "userId", column = "user_id")
    })
    @Select(value = "SELECT * FROM accounts WHERE account_id = #{id}")
    public Account findAccountById(@Param("id") Integer id);

    @ResultMap(value = "accountResultMap")
    @Select(value = "SELECT * FROM accounts WHERE user_id=#{id}")
    public List<Account> findAccountByUserId(@Param("id") Integer id);
}

注意:这里我们定义了一个 findAccountByUserId 查询函数。因为 User 类在填充 Account 列表时,是用 user_id 进行查询的。
再看 UserDao。

@Mapper
public interface UserDao {
    @Results(id = "userResultMap", value = {
            @Result(property = "userId", column = "user_id", id = true),
            @Result(property = "userName", column = "user_name"),
            @Result(property = "accounts", column = "user_id", javaType = List.class,
                    many = @Many(select = "com.sugarian.dao.AccountDao.findAccountByUserId"))
    })
    @Select(value = "SELECT * FROM users WHERE user_id=#{id}")
    public User findUserById(@Param("id") Integer id);
}

注意下面抽取的部分

@Result(property = "userName", column = "user_name"),
            @Result(property = "accounts", column = "user_id", javaType = List.class,
                    many = @Many(select = "com.sugarian.dao.AccountDao.findAccountByUserId"))
    })

User 中的 accounts 是存放 Account 的列表,其在表结构中是没有对应的列,所以它对应的这条 @Result 实际上是再次执行了一次 sql 索引,然后将值填充到 accounts 中。另外一点,请看 column 是 user_id,这实际上就是以 user_id 作为参数,findAccountByUserId 用进行索引。
我们可以看看调试工具给出的信息:

2022-11-25 23:39:09,552 [DEBUG] com.sugarian.dao.UserDao.findUserById - ==>  Preparing: SELECT * FROM users WHERE user_id=?
2022-11-25 23:39:09,552 [DEBUG] com.sugarian.dao.UserDao.findUserById - ==> Parameters: 1(Integer)
2022-11-25 23:39:09,553 [DEBUG] com.sugarian.dao.AccountDao.findAccountByUserId - ====>  Preparing: SELECT * FROM accounts WHERE user_id=?
2022-11-25 23:39:09,553 [DEBUG] com.sugarian.dao.AccountDao.findAccountByUserId - ====> Parameters: 1(Integer)
2022-11-25 23:39:09,556 [DEBUG] com.sugarian.dao.AccountDao.findAccountByUserId - <====      Total: 3
2022-11-25 23:39:09,556 [DEBUG] com.sugarian.dao.UserDao.findUserById - <==      Total: 1

明显执行了两次 sql 查询。对于第二个查询,它从第一个查询结果中取出其中的 user_id 列,作为查询的参数。
其他参数如 javaType 指定返回类型。@Many 中的参数 select 指定第二次查询用的函数。

总结

也算是掌握了一个技术细节,由于自己没细看官方文档,所以浪费了很多时间,下次还是耐心看文档吧。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值