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
}

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
}

右.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 を使用していることを知っておくことは重要です。

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

今まですべてが私にはよさそうに見えました。しかし、すべてのロールの名前を取得しようとすると、2 つ以上のクエリ (ページと元のクエリのカウント) が実行されています。

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

これにより、通話が非常に非効率になります。この場合は 1 人のユーザーですが、複数のユーザーの場合は加算されます。

余分なクエリの実行を追加せずに、1 つのクエリですべての権限の名前をユーザー エンティティに配置する方法はありますか?

これまでに試したこと:

  • @SecondaryTableUser エンティティの Right テーブルから列を直接定義するために使用します。最初に Role を User にリンクしてから、Role テーブルのフィールドを使用して Right テーブルをリンクすることができませんでした。したがって、最終的には、 User オブジェクトの上に @SecondaryTable アノテーションを付け、その下に Right オブジェクトの列を定義する必要があります。

  • User エンティティで使用@Formulaして、ネイティブ呼び出しをクエリに挿入します。注釈がすべてを権利のリストにマップする方法を理解していなかったため、これも機能しませんでした。

ここには他のオプションがあるかもしれません。または、上記のものを実装する際に何かひどく間違ったことをしました。しかし、今のところ、問題の解決策を見つけるためにどちらの方法をとるべきかわかりません。誰かが私に言うことができれば、それは素晴らしいことです.

前もってありがとう、
ロビン

4

1 に答える 1

3

Root.joinクエリの目的でテーブルの結合のみを行うを使用しています。読み込まれたエンティティの遅延関連付けはまだ初期化されません。

ご覧のとおり、あなたの意図は遅延コレクションも初期化することです。そのためには、Root.fetchを使用する必要があります ( から継承されたインターフェイス メソッドで定義されていますFetchParent)。

指定された結合タイプを使用して、指定されたコレクション値属性へのフェッチ結合を作成します。

ただし、あなたの意図は良い習慣ではありません。1 つのクエリで複数のコレクションを結合しないでください。そうしないと、クエリの結果セットが、結合されたコレクション間の完全なデカルト積で爆発します。結果セットに<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 に答える