3

精简版

我有一个基本设置,其中用户表链接到角色表,角色表链接到权限。这些都是多对多关系。角色是一个动态实体,应用程序不感兴趣(仅用于视觉方面)。当我获取用户时,我想返回用户表中的数据,包括所有权限名称的列表。

为了澄清,这就是我想要解决方案做的事情:

解决方案概述

我设法在我的用户对象中获得了权限并返回它们,但是由于在调用原始查询后 hibernate 进行了额外的查询调用,因此效率低下。

详细版

首先让我给你一些关于如何链接实体以及代码是什么样子的信息。我的(简化的)数据库表结构如下所示:

数据库图

用户.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
}

角色.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
}

对.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 Specifications API 来加入表格:

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=?

这使我的通话效率非常低。在这种情况下,它是单个用户,但有多个用户时会累加。

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

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

  • 用于@SecondaryTable直接从我的用户实体中的右表定义列。我无法先将角色链接到用户,然后使用角色表中的字段来链接右表。所以最后我必须在我的 User 对象顶部添加 @SecondaryTable 注释,并在下面定义 Right 对象的列。

  • 在 User 实体中使用@Formula以将本机调用插入到查询中。这也不起作用,因为注释不了解如何将所有内容映射到权限列表中。

这里可能还有其他选项,或者我在实现上述选项时做了一些可怕的错误。但是现在我不知道要为我的问题找到解决方案。如果有人能告诉我,那就太好了。

在此先感谢,
罗宾

4

1 回答 1

3

您正在使用Root.joinwhich 仅出于查询目的加入表;加载实体中的惰性关联仍然不会被初始化。

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

使用给定的连接类型创建到指定集合值属性的提取连接。

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

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

于 2016-04-21T17:26:34.667 回答