import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Set;
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue
private Long id;
@Column(unique = true, nullable = false)
private String username;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_subscriptions",
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "subscription_id", referencedColumnName = "id"))
private Set<Subscription> subscriptions;
}
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Set;
@Entity
@Table(name = "subscriptions")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Subscription {
@Id
@GeneratedValue
private Long id;
@Column(unique = true, nullable = false)
private String name;
@OneToMany(mappedBy = "subscription")
private Set<Notification> notifications;
@ManyToMany(mappedBy = "subscriptions")
private Set<User> users;
}
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "notifications")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Notification {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String message;
@Column(name = "is_read", nullable = false)
private boolean isRead;
@ManyToOne
@JoinColumn(name = "subscription_id")
private Subscription subscription;
}
我创建了这 3 个实体。用户和订阅具有多对多关系。当我保存用户时,它工作正常,在数据库中我可以看到用户的订阅。
当我调用方法 findById 来获取用户时,我的订阅设置为空。我该如何修复它? 我正在使用 Spring Boot v3.1.3、Java v17 和 PostgreSQL v14.3
叹息。很多本来可以做但没有做的事情。
Eager
。 Hibernate 多对多 - 获取方法 eager 与lazySet
中使用 Entities
。思考它会加分,但它会带来问题。Notification
应该跟踪特定 User
的通知,您的数据库架构无法实现这一点。那么,让我们开始吧。如果我创建一个示例并执行
findById
我会得到以下 SQL。
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=?
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=?
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=?
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=?
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=?
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=?
这就是你的想法吗?我希望不是。让我们修复它吧。
@Entity
@Table(name = "subscriptions")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Subscription {
请注意,
@Data
已被删除并替换为 @Getter
和 @Setter
。现在我的sql只剩下原来的第一行了。
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=?
注意您已加入订阅表
left join (user_subscriptions s1_0 join subscriptions s1_1 on s1_1.id=s1_0.subscription_id)
但您尚未将其包含在
User
中使用。
select u1_0.id,s1_0.user_id,s1_1.id,s1_1.name,u1_0.username
让我们解决这个问题。
@Query("from User u left join fetch u.subscriptions where u.id = :id")
Optional<User> findByIdWithSubscriptions(@Param("id") Long id);
这给了我
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=?
User(id=1, username=un1, subscriptions=[Subscription(id=1, name=sub1), Subscription(id=2, name=sub2)])
这没有解决
Notifications
,但你的问题不包括这一点。最后,让我们摆脱 Eager
,因为这可以说是 JPA-ANTIPATTERN。
@ManyToMany
@JoinTable(name = "user_subscriptions",
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "subscription_id", referencedColumnName = "id"))
private Set<Subscription> subscriptions;
现在我们有了一个明智的
Repository
,其中findById
只为我们提供了所请求的实体
Hibernate: select u1_0.id,u1_0.username from users u1_0 where u1_0.id=?
方法 findByIdWithSubscriptions
为我们提供了加入的订阅并将它们包含在结果中。