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

Robin Hermans

Short version

I have a basic setup where a user table is linked to a role table and a role table is linked to a right. These are both Many-to-Many relations. The roles are a dynamic entity and not of interest for the application (only for visual aspects). When I fetch a user I want to return the data in the user table including a list of the names of all rights.

To clarify, this is what I want the solution to do:

solution overview

I managed to get the rights in my user object and return them, but it's inefficient due to the extra query calls hibernate makes after the original query was called.

Detailed version

Let me first give you some information on how to entities are linked and what the code looks like. My (simplified) database table structure looks like this:

Database diagram

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
}

It's important to know that I use the Java Specifications API to join the 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();
};

This creates the correct query:

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

Everything looked to good to me till now. But when I tried to fetch the names of all roles, there where more than two queries (count for page and the original one) being executed

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

The extra query looks like:

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

This makes the call very inefficient to me. In this case it's a single user, but with multiple users it adds up.

Is there a way to have a single query put the names of all rights in the user entity, without adding extra query executions?

Things I tried so far:

  • Using @SecondaryTable to directly define column from the Right table in my User entity. I could not get to first link the Role to the User and then use fields from the Role table to link the Right table. So in the end I would have to @SecondaryTable annotation on top of my User object and define columns of the Right object below.

  • Using @Formula in the User entity to insert a native call into the query. This did also not work as the annotation did not understand how to map everything into a list of rights.

There might be other options here, or I did something horribly wrong with implementing the ones above. But for now I don't which way to go in finding a solution for my problem. If someone could tell me, that would be great.

Thanks in advance,
Robin

Dragan Bozanovic

You are using Root.join which does just the joining of tables for the purposes of the query; lazy associations in the loaded entities will still not be initialized.

As I see, your intention is to initialize the lazy collections as well. For that you have to use Root.fetch (defined in the interface method inherited from the FetchParent):

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

However, your intention is not a good practice; do not join multiple collections in one query, otherwise the query result set will explode with full Cartesian product between the joined collections. Your result set contains <num of users> * <num of roles per user> * <num of rights per role> rows. So, each user data is repeated <num of roles per user> * <num of rights per role> times in the generated result set.

The approach I find to be the best and most straightforward is to specify batch size on lazy associations.

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类常见问题

How do I "undo" a --single-branch clone?

来自分类Dev

How do I create a link to a pdf file with rails?

来自分类Dev

Web API OData: How do you $expand on a single entity?

来自分类Dev

how to link a page in spring mvc

来自分类Dev

How do I include jquery in javafx webview?

来自分类Dev

How to chain these two Postman requests

来自分类Dev

Spring Data Hibernate JPQL聚合查询

来自分类Dev

How can I dynamically include external files / user control?

来自分类Dev

How to set the count property of a related entity without having to perform an extra query or include unnecessary data

来自分类Dev

How do I link boost to my program?

来自分类Dev

Spring Data DSL查询中的Hibernate SQLGrammarException

来自分类Dev

How can I efficiently find the accuracy of a classifier

来自分类Dev

knockoutjs - How can I populate an empty observable array in response to user action (anchor link click)

来自分类Dev

how do I mix two types in scrapy

来自分类Dev

how do you easily create tables in spring mvc

来自分类Dev

How do I reprompt to allow user to continue converting values?

来自分类Dev

How do I efficiently crop an *indexed* Bitmap and get an *indexed* result?

来自分类Dev

Can't link two tables?

来自分类Dev

Spring Data Hibernate + Pageable:返回空结果

来自分类Dev

How do I link to a pdf in AngularJS?

来自分类Dev

How do we solve the given scenario efficiently?

来自分类Dev

efficiently locf by groups in a single R data.table

来自分类Dev

Spring Boot + Hibernate + Postgres - not creating tables

来自分类Dev

Postgresql: How to relate data between two tables?

来自分类Dev

How do i right align an icon set arrow under conditional formatting so that both number and arrow are on right side of cell neatly together?

来自分类Dev

How do I make Auto Hotkey do Alt+F4 when I press right mouse button and scroll wheel?

来自分类Dev

Do I need a keyring password on a luks encrypted single user machine?

来自分类Dev

elasticsearch,spring-data和hibernate

来自分类Dev

SpringBoot/Spring Data/Hibernate 入门

Related 相关文章

  1. 1

    How do I "undo" a --single-branch clone?

  2. 2

    How do I create a link to a pdf file with rails?

  3. 3

    Web API OData: How do you $expand on a single entity?

  4. 4

    how to link a page in spring mvc

  5. 5

    How do I include jquery in javafx webview?

  6. 6

    How to chain these two Postman requests

  7. 7

    Spring Data Hibernate JPQL聚合查询

  8. 8

    How can I dynamically include external files / user control?

  9. 9

    How to set the count property of a related entity without having to perform an extra query or include unnecessary data

  10. 10

    How do I link boost to my program?

  11. 11

    Spring Data DSL查询中的Hibernate SQLGrammarException

  12. 12

    How can I efficiently find the accuracy of a classifier

  13. 13

    knockoutjs - How can I populate an empty observable array in response to user action (anchor link click)

  14. 14

    how do I mix two types in scrapy

  15. 15

    how do you easily create tables in spring mvc

  16. 16

    How do I reprompt to allow user to continue converting values?

  17. 17

    How do I efficiently crop an *indexed* Bitmap and get an *indexed* result?

  18. 18

    Can't link two tables?

  19. 19

    Spring Data Hibernate + Pageable:返回空结果

  20. 20

    How do I link to a pdf in AngularJS?

  21. 21

    How do we solve the given scenario efficiently?

  22. 22

    efficiently locf by groups in a single R data.table

  23. 23

    Spring Boot + Hibernate + Postgres - not creating tables

  24. 24

    Postgresql: How to relate data between two tables?

  25. 25

    How do i right align an icon set arrow under conditional formatting so that both number and arrow are on right side of cell neatly together?

  26. 26

    How do I make Auto Hotkey do Alt+F4 when I press right mouse button and scroll wheel?

  27. 27

    Do I need a keyring password on a luks encrypted single user machine?

  28. 28

    elasticsearch,spring-data和hibernate

  29. 29

    SpringBoot/Spring Data/Hibernate 入门

热门标签

归档