java Hibernate JPA多对多Eager Loading不工作

jei2mxaa  于 2024-01-05  发布在  Java
关注(0)|答案(1)|浏览(133)
  1. import jakarta.persistence.*;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import java.util.Set;
  6. @Entity
  7. @Table(name = "users")
  8. @Data
  9. @NoArgsConstructor
  10. @AllArgsConstructor
  11. public class User {
  12. @Id
  13. @GeneratedValue
  14. private Long id;
  15. @Column(unique = true, nullable = false)
  16. private String username;
  17. @ManyToMany(fetch = FetchType.EAGER)
  18. @JoinTable(name = "user_subscriptions",
  19. joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
  20. inverseJoinColumns = @JoinColumn(name = "subscription_id", referencedColumnName = "id"))
  21. private Set<Subscription> subscriptions;
  22. }
  23. import jakarta.persistence.*;
  24. import lombok.AllArgsConstructor;
  25. import lombok.Data;
  26. import lombok.NoArgsConstructor;
  27. import java.util.Set;
  28. @Entity
  29. @Table(name = "subscriptions")
  30. @Data
  31. @NoArgsConstructor
  32. @AllArgsConstructor
  33. public class Subscription {
  34. @Id
  35. @GeneratedValue
  36. private Long id;
  37. @Column(unique = true, nullable = false)
  38. private String name;
  39. @OneToMany(mappedBy = "subscription")
  40. private Set<Notification> notifications;
  41. @ManyToMany(mappedBy = "subscriptions")
  42. private Set<User> users;
  43. }
  44. import jakarta.persistence.*;
  45. import lombok.AllArgsConstructor;
  46. import lombok.Data;
  47. import lombok.NoArgsConstructor;
  48. @Entity
  49. @Table(name = "notifications")
  50. @Data
  51. @NoArgsConstructor
  52. @AllArgsConstructor
  53. public class Notification {
  54. @Id
  55. @GeneratedValue
  56. private Long id;
  57. @Column(nullable = false)
  58. private String title;
  59. @Column(nullable = false)
  60. private String message;
  61. @Column(name = "is_read", nullable = false)
  62. private boolean isRead;
  63. @ManyToOne
  64. @JoinColumn(name = "subscription_id")
  65. private Subscription subscription;
  66. }

字符串
我已经创建了这3个实体。用户和订阅有ManyToMany关系。当我保存用户时,它工作正常,在数据库中我可以看到用户的订阅。


的数据
当我调用方法findById来获取用户时,我得到订阅设置为空。我如何修复它?我使用Sping Boot v3.1.3,Java v17和PostgreSQL v14.3

2eafrhcq

2eafrhcq1#

有几个问题本可以在这里解决,但没有。所有的问题我都注意到了。

那么,让我们开始吧。如果我创建一个示例并执行findById,我会得到以下SQL。

  1. Hibernate: select u1_0.id,s1_0.user_id,s1_1.id,s1_1.name,u1_0.username from users u1_0 left join (user_subscriptions s1_0 join subscriptions s1_1 on s1_1.id=s1_0.subscription_id) on u1_0.id=s1_0.user_id where u1_0.id=?
  2. Hibernate: select n1_0.subscription_id,n1_0.id,n1_0.is_read,n1_0.message,n1_0.title from notifications n1_0 where n1_0.subscription_id=?
  3. Hibernate: select u1_0.subscription_id,u1_1.id,u1_1.username from user_subscriptions u1_0 join users u1_1 on u1_1.id=u1_0.user_id where u1_0.subscription_id=?
  4. Hibernate: select s1_0.user_id,s1_1.id,s1_1.name from user_subscriptions s1_0 join subscriptions s1_1 on s1_1.id=s1_0.subscription_id where s1_0.user_id=?
  5. Hibernate: select n1_0.subscription_id,n1_0.id,n1_0.is_read,n1_0.message,n1_0.title from notifications n1_0 where n1_0.subscription_id=?
  6. Hibernate: select u1_0.subscription_id,u1_1.id,u1_1.username from user_subscriptions u1_0 join users u1_1 on u1_1.id=u1_0.user_id where u1_0.subscription_id=?

字符串
这就是你想要的吗,希望不是,我们来解决

  1. @Entity
  2. @Table(name = "subscriptions")
  3. @Getter
  4. @Setter
  5. @NoArgsConstructor
  6. @AllArgsConstructor
  7. @Builder
  8. public class Subscription {


请注意,@Data已经被删除,并被@Getter@Setter替换。您也可以通过使用List而不是Set来修复它,但我将把它作为读者找出原因的练习。现在我的SQL只是原始的第一行。

  1. Hibernate: select u1_0.id,s1_0.user_id,s1_1.id,s1_1.name,u1_0.username from users u1_0 left join (user_subscriptions s1_0 join subscriptions s1_1 on s1_1.id=s1_0.subscription_id) on u1_0.id=s1_0.user_id where u1_0.id=?


请注意,您已经联接了订阅表

  1. left join (user_subscriptions s1_0 join subscriptions s1_1 on s1_1.id=s1_0.subscription_id)


但是你没有将它包含在User中使用。

  1. select u1_0.id,s1_0.user_id,s1_1.id,s1_1.name,u1_0.username


我们来解决这个问题。

  1. @Query("from User u left join fetch u.subscriptions where u.id = :id")
  2. Optional<User> findByIdWithSubscriptions(@Param("id") Long id);


这给了我

  1. Hibernate: select u1_0.id,s1_0.user_id,s1_1.id,s1_1.name,u1_0.username from users u1_0 left join (user_subscriptions s1_0 join subscriptions s1_1 on s1_1.id=s1_0.subscription_id) on u1_0.id=s1_0.user_id where u1_0.id=?
  2. User(id=1, username=un1, subscriptions=[Subscription(id=1, name=sub1), Subscription(id=2, name=sub2)])


最后,让我们摆脱Eager,因为它可以说是一个JPA-ANTIPATTERN. JPA and Hibernate FetchType EAGER is a code smell

  1. @ManyToMany
  2. @JoinTable(name = "user_subscriptions",
  3. joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
  4. inverseJoinColumns = @JoinColumn(name = "subscription_id", referencedColumnName = "id"))
  5. private Set<Subscription> subscriptions;


现在我们有了一个合理的Repository,其中findById只给我们 * 请求的实体

  1. Hibernate: select u1_0.id,u1_0.username from users u1_0 where u1_0.id=?


方法findByIdWithSubscriptions给出了联合订阅并将它们包含在结果中。

展开查看全部

相关问题