我想问一下实体上有 equals 方法。 我正在运行一个设置,其中我仅根据 Id 比较实体,并认为这是最好的做事方式。然而我意识到,当使用级联持久保存新的 UserRoles 时,我遇到了问题,它们最初并不唯一,因为它们还没有 id。所以我无法将它们添加到集合中。
所以我的问题是,仅使用 id 字段是否是一个好方法?使用级联来持久化相关实体是一个好方法吗?就我而言,似乎我必须将用户和角色字段添加到 Equals(我不想这样做,因为我不想一直急切地加载它们),或者只是单独保留 userRoles。
背景:
@Entity
@Getter
@Setter
@ToString(exclude = {"user", "role"})
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
@NoArgsConstructor
@AllArgsConstructor
@Table(name="kasutaja_roll")
public class UserRole extends BaseEntity {
@Id
@SequenceGenerator(name = "kasutaja_roll_id_seq", sequenceName = "kasutaja_roll_id_seq", allocationSize = 1)
@GeneratedValue(generator = "kasutaja_roll_id_seq", strategy = GenerationType.SEQUENCE)
@EqualsAndHashCode.Include
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "kasutaja_id")
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "roll_id")
private Role role;
@Column(name = "kehtiv_alates")
private Instant validFrom;
@Column(name = "kehtiv_kuni")
private Instant validUntil;
public UserRole(User user, Role role) {
super();
this.validFrom = Instant.now();
this.user = user;
this.role = role;
}
}
@Entity
@Getter
@Setter
@ToString(exclude = {"roles"})
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "kasutaja")
public class User extends BaseEntity{
@Id
@SequenceGenerator(name = "kasutaja_id_seq", sequenceName = "kasutaja_id_seq", allocationSize = 1)
@GeneratedValue(generator = "kasutaja_id_seq", strategy = GenerationType.SEQUENCE)
@EqualsAndHashCode.Include
private Long id;
@OneToMany(mappedBy = "user", cascade={CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserRole> roles;
...
例如像这样添加它们:
for (Long roleId : requestDto.getRoles()) {
UserRole existingUserRole = existingUserRolesByRoleId.get(roleId);
if (existingUserRole == null) {
// No existing userRoll - create new one
Role role = roleRepository.findById(roleId)
.orElseThrow(() -> new IllegalArgumentException("Role not found with id: " + roleId));
UserRole newUserRole = new UserRole(user, role);
user.getRoles().add(newUserRole); //!!!here adding multiple ones fails because they are equal, since id = null!!!
} else if (existingUserRole.getValidUntil() != null) {
// Existing userRole is inactive - reactivate it
existingUserRole.setValidUntil(null);
existingUserRole.setValidFrom(now);
}
//existing userRole is active, do nothing
}
所以我的问题是,仅使用 id 字段是否是一个好方法?
简短的答案是否定的,不是,较长的答案是这取决于情况,并且您需要考虑新实例。
有很多文章(例如 this)解释了如何为 JPA 编写正确的
equals
/hashCode
方法。您需要考虑一些特殊性。
其次,JPA 和 Lombok 通常不是朋友,这是我的经验(请参阅我的这篇文章),应该避免。但这可能是个人恩怨。
话虽如此,您可能应该自己写
equals
/hashCode
。基本的 JPA 实现(带有新的 Java 构造)可能是这样的。
@Override
public boolean equals(Object that) {
if (that instanceof UserRole ur) {
return this.id != null && Objects.equals(this.id, that.id)
}
return false;
}
@Override
public int hashCode() {
return getClass().hashCode();
}
这样的东西应该有效。
instanceof
检查可以变得更加智能,您甚至可以在 BaseEntity
中提供类似的内容。