Spring + Hibernate:如何有效地链接两个 link 表并将结果数据包含在单个实体中? (用户角色权限)

Spring + Hibernate: How do I efficiently chain two link tables and include resulting data in single entity? (user-role-right)

短版

我有一个基本设置,其中用户 table 被 link 编辑到角色 table 并且角色 table 被 link 编辑到正确的。这些都是多对多关系。角色是一个动态实体,应用程序不感兴趣(仅针对视觉方面)。当我获取用户时,我想 return 用户 table 中的数据,包括所有权利的名称列表。

澄清一下,这就是我想要的解决方案:

我设法获得了我的用户对象和 return 它们的权限,但是由于在调用原始查询后休眠进行了额外的查询调用,所以效率很低。

详细版

首先让我向您提供一些有关如何 link 编辑实体以及代码是什么样子的信息。我的(简化的)数据库 table 结构如下所示:

User.java

@Entity
@Table(name = "user")
public class User {

  @Id
  @Column(name = "user_id", columnDefinition = "user_id")
  private Long userId;

  @Transient
  private List<String> rights

  @ManyToMany
  @JoinTable(
    name = "user_role",
    joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "user_id"),
    inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "role_id"))
  @JsonIgnore
  private List<Role> roles;

  //Getters and setters
}

Role.java

@Entity
@Table(name = "role")
public class Role {

  @Id
  @Column(name = "role_id", columnDefinition = "role_id")
  private Long roleId;

  @ManyToMany
  @JoinTable(
    name = "user_role",
    joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "role_id"),
    inverseJoinColumns = @JoinColumn(name = "user_id", referencedColumnName = "user_id"))
  @JsonIgnore
  private List<Employee> employees;

  @ManyToMany
  @JoinTable(
    name = "role_right",
    joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "role_id"),
    inverseJoinColumns = @JoinColumn(name = "right_id", referencedColumnName = "right_id"))
  @JsonIgnore
  private List<Right> rights;


  //Getters and setters
}

Right.java

@Entity
@Table(name = "right")
public class Right {

  @Id
  @Column(name = "right_id", columnDefinition = "right_id")
  private Long rightId;

  @Column(name = "name", columnDefinition = "name")
  private String name;

  @ManyToMany
  @JoinTable(
    name = "role_right",
    joinColumns = @JoinColumn(name = "right_id", referencedColumnName = "right_id"),
    inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "role_id"))
  @JsonIgnore
  private List<Role> roles;

  //Getters and setters
}

重要的是要知道我使用 Java 规范 API 加入 tables:

return (root, query, cb) -> {
  query.distinct(true);
  Join rolesJoin = root.join("roles", JoinType.LEFT);
  Join rightsJoin = rolesJoin.join("rights", JoinType.LEFT);
  return cb.conjunction();
};

这将创建正确的查询:

select <columns go here> 
from employee user0_
left outer join user_role roles1_ on user0_.user_id=roles1_.user_id
left outer join role role2_ on roles1_.role_id=role2_.role_id
left outer join role_right rights3_ on role2_.role_id=rights3_.role_id
left outer join right right4_ on rights3_.right_id=right4_.right_id

到目前为止,我觉得一切都很好。但是当我试图获取所有角色的名称时,那里执行了两个以上的查询(计算页面和原始查询)

//The original code uses lambda for this
for(Role role : user.getRoles()){
  for(Right right: role.getRights()){
    user.addRight(right.getName());
  }
}

额外查询如下所示:

select <column stuff> 
from role_right rights0_
inner join right right1_ on rights0_.right_id=right1_.right_id
where rights0_.role_id=?

这让我的通话效率很低。在这种情况下,它是单个用户,但如果有多个用户,它就会加起来。

有没有办法让单个查询将所有权限的名称放入用户实体中,而不添加额外的查询执行?

到目前为止我尝试过的事情:

这里可能还有其他选项,或者我在实施上述选项时犯了严重错误。但是现在我不知道该用什么方法来解决我的问题。如果有人能告诉我,那就太好了。

提前致谢,
罗宾

您正在使用 Root.join,它只是为了查询目的而连接表;加载实体中的惰性关联仍将不会被初始化。

据我所知,您的意图也是初始化惰性集合。为此,您必须使用 Root.fetch(在继承自 FetchParent 的接口方法中定义):

Create a fetch join to the specified collection-valued attribute using the given join type.

但是,您的意图不是一个好的做法;不要在一个查询中连接多个集合,否则查询结果集将在连接的集合之间以完整的笛卡尔积爆炸。您的结果集包含 <num of users> * <num of roles per user> * <num of rights per role> 行。因此,每个用户数据在生成的结果集中重复 <num of roles per user> * <num of rights per role> 次。

我发现最好和最直接的方法是在惰性关联上指定