在Java中处理对象关联

在Java中处理对象关联时,分两步骤:1. 查询数据库;2. 做Java中组装对象

对于数据库表
表clazz: id
表student: id, clazzId

在Java中有时需要班级获取班级里的所有学生,是一对多关联
有时学生获取班级信息,是多对一关联

一、一对多关联 OneToMany

将表数据转换为java的运行时数据
类clazz: id, students
类student: id

1.1 从数据库分别查询相关联的实体数据

List fathers = fatherRepo.findSomeList();
List fatherIds = fathers.stream().map(Father::getId).collect(toList);
List children = childRepo.findByFatherIdsIn(fatherIds);

注:oracle的in字句限制1000。MySQL限制了SQL语句的最大长度,可以认为对in语句没有限制。sqlserver对in语句没有限制。

1.2 子表数据挂载到父表对象的属性上

二次查询数据库获取父子表数据后,需要在java代码中,将子表数据挂载到父表对象的属性上。

java代码有三种实现方式:

  1. 双重for循环
for(father in fathers) {
  for(child in children) {
    if(child.parentId == father.id) {
      faher.children.add(child)
    }
  }
}
  1. lamda表达式形式的双重循环
fathers.each(father->{
  father.children.addAll(children.stream().filter(c -> c.parentId == father.id).collect())
})
  1. 借助Map
Map<ID,List<Child>> collect = children.stream().collect(Collectors.groupingBy(Child::getParentId))
fathers.each(father->{
  father.children.addAll(collect.getOrDefault(father.id, List.of()))
})

理论上方法3的两次循环时间复杂度为2n,方法1和2的时间复杂度为n^2.
方法2的编码方式最简洁。
方法1的编码方式最灵活,方便增减其他逻辑代码。另外,不存在方法2和3中,有单行代码过长的问题。

二、多对一关联ManyToOne

表clazz: id
表student: id, clazzId

类clazz: id
类student: id, clazz

  1. 循环使用findById查询数据库

无论是jpa还是MyBatis对findById都有很好的缓存。如下实现即可:

@Transaction(readonly)
List children = childRepo.findSomeChildren()
for(child in children){
  Father father = fatherRepo.findById(child.parentId)
  child.setFather(father)
}
  1. 使用findByIdIn避免循环查询数据库
    类比子表对象挂载至父表对象的思路,使用findByIdIn。避免循环查询数据库。

由于MyBatis和jpa等框架的缓存,方法1在一般情况下循环查询数据库的次数不会太多。编码实现简单。
方法2避免的循环查询数据库,编码实现比方法1复杂。
优先选择方式2。

posted @ 2023-07-06 10:29  豆苗稀  阅读(42)  评论(0编辑  收藏  举报