比较 JPA 实体和 DTO
Compare JPA Entity and DTO
我需要一种标准方法来将 JPA 实体与其 DTO 进行比较,并确定它们是否代表相同的业务对象。我可以想到三种方法,每个 DTO 上的自定义方法,带有静态方法或比较器的接口。
基于 João Dias 的答案,方法 4 - 继承。
Pros/Cons
- 方法 1 - 一直都不好
- 方法 2 - 使用接口支持组合而不是继承,但需要使用自定义方法名称的语义 (
businessKeysMatch()
)
- 方法 3 - 不需要修改源代码
- 方法 4 - 简化语义,因为它使用标准
equals()
但需要“样板 equals()/hashcode()
任何其他 pros/cons 方法或对其他方法的建议?
最终,我选择了方法二(Interface)。继承方法显示出希望,但作为 JPA 实体的 classes 之一使映射比我想要的更复杂。
感谢阅读和思考我的问题!
背景
实体
一个数据库键
在确定相等性的 ORM 和数据库中强制执行的唯一业务密钥 (equals()
/hashcode()
)
public 属性(姓名、地址、年龄等)
非public/机密属性(密码、刷新令牌、SSN 等)
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Entity
@Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "businessKey1", "businessKey2" }) })
class UserEntity {
@Id
Long id;
@NotNull
@EqualsAndHashCode.Include
Long businessKey1;
@NotNull
@EqualsAndHashCode.Include
Long businessKey2;
String name;
Integer age;
String password;
String refreshToken;
String SSN;
}
DTO(完整)
确定相等性的业务键(equals()
/hashcode()
)
public 属性(姓名、地址、年龄等)
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
class UserDto {
@EqualsAndHashCode.Include
@NotNull
Long businessKey1;
@EqualsAndHashCode.Include
@NotNull
Long businessKey2;
String name;
String address;
Integer age;
}
DTO(有限)
确定相等性的业务键(equals()
/hashcode()
)
已选择public个属性(名称)
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
class UserNameDto {
@EqualsAndHashCode.Include
@NotNull
Long businessKey1;
@EqualsAndHashCode.Include
@NotNull
Long businessKey2;
String name;
}
方法 1 - 添加到每个 User*Dto
的自定义方法
boolean businessKeysMatch(UserEntity entity) {
if((this.getBusinessKey1() == entity.getBusinessKey1()) && (this.getBusinessKey2() == entity.getBusinessKey2()))
return true;
return false;
}
方法 2 - 将静态方法添加到公共接口
interface UserKeys {
Long getBusinessKey1();
Long getBusinessKey2();
static boolean businessKeysMatch(UserKeys o1, UserKeys o2) {
if((o1.getBusinessKey1() == o2.getBusinessKey1()) && (o1.getBusinessKey2() == o2.getBusinessKey2()))
return true;
return false;
}
}
class UserEntity implements UserKeys {
// no other changes
}
class UserDto implements UserKeys {
// no other changes
}
class UserEntity implements UserKeys {
// no other changes
}
方法 3 - 比较器
interface UserBusinessKey {
Long getBusinessKey1();
Long getBusinessKey2();
}
class UserDto implements UserCompare {
// no other changes
}
class UserEntity implements UserCompare {
// no other changes
}
class UserCompare implements Comparator<UserBusinessKey> {
public int compare(UserBusinessKey o1, UserBusinessKey o2) {
int key1Compare = o1.getBusinessKey1().compareTo(o2.getBusinessKey1());
if (key1Compare == 0)
return o1.getBusinessKey2().compareTo(o2.getBusinessKey2());
return key1Compare;
}
}
方法 4 - 仅继承基数 class 的 equals/hashcode
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Data
abstract class UserBase {
@NotNull
Long businessKey1;
@NotNull
Long businessKey2;
// lombok generates a standard equals() / hashcode() pair
}
@SuperBuilder
@Getter
@Setter
@ToString
@Entity
@Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "businessKey1", "businessKey2" }) })
class UserEntity extends UserBase {
@Id
Long id;
String name;
Integer age;
String password;
String refreshToken;
String SSN;
// handcoded equals/hashcode that only call super
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
@Override
public int hashCode() {
return super.hashCode();
}
}
@SuperBuilder
@Getter
@Setter
@ToString
class UserDto extends UserBase {
String name;
String address;
Integer age;
// handcoded equals/hashcode that only call super
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
@Override
public int hashCode() {
return super.hashCode();
}
}
如果您有多个 User*Dto
,我会创建一个抽象 AbstractUserDto
,然后由所有具体的用户 DTO 对其进行扩展。您可以在那里放置您在方法 1 中显示的方法(这样您就不会一遍又一遍地复制相同的代码):
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public abstract class AbstractUserDto {
@EqualsAndHashCode.Include
@NotNull
Long businessKey1;
@EqualsAndHashCode.Include
@NotNull
Long businessKey2;
String name;
public final boolean businessKeysMatch() {
return (this.getBusinessKey1() == entity.getBusinessKey1()) && (this.getBusinessKey2() == entity.getBusinessKey2());
}
}
我需要一种标准方法来将 JPA 实体与其 DTO 进行比较,并确定它们是否代表相同的业务对象。我可以想到三种方法,每个 DTO 上的自定义方法,带有静态方法或比较器的接口。
基于 João Dias 的答案,方法 4 - 继承。
Pros/Cons
- 方法 1 - 一直都不好
- 方法 2 - 使用接口支持组合而不是继承,但需要使用自定义方法名称的语义 (
businessKeysMatch()
) - 方法 3 - 不需要修改源代码
- 方法 4 - 简化语义,因为它使用标准
equals()
但需要“样板equals()/hashcode()
任何其他 pros/cons 方法或对其他方法的建议?
最终,我选择了方法二(Interface)。继承方法显示出希望,但作为 JPA 实体的 classes 之一使映射比我想要的更复杂。
感谢阅读和思考我的问题!
背景
实体
一个数据库键
在确定相等性的 ORM 和数据库中强制执行的唯一业务密钥 (
equals()
/hashcode()
)public 属性(姓名、地址、年龄等)
非public/机密属性(密码、刷新令牌、SSN 等)
@Data @EqualsAndHashCode(onlyExplicitlyIncluded = true) @Entity @Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "businessKey1", "businessKey2" }) }) class UserEntity { @Id Long id; @NotNull @EqualsAndHashCode.Include Long businessKey1; @NotNull @EqualsAndHashCode.Include Long businessKey2; String name; Integer age; String password; String refreshToken; String SSN; }
DTO(完整)
确定相等性的业务键(
equals()
/hashcode()
)public 属性(姓名、地址、年龄等)
@Data @EqualsAndHashCode(onlyExplicitlyIncluded = true) class UserDto { @EqualsAndHashCode.Include @NotNull Long businessKey1; @EqualsAndHashCode.Include @NotNull Long businessKey2; String name; String address; Integer age; }
DTO(有限)
确定相等性的业务键(
equals()
/hashcode()
)已选择public个属性(名称)
@Data @EqualsAndHashCode(onlyExplicitlyIncluded = true) class UserNameDto { @EqualsAndHashCode.Include @NotNull Long businessKey1; @EqualsAndHashCode.Include @NotNull Long businessKey2; String name; }
方法 1 - 添加到每个 User*Dto
的自定义方法 boolean businessKeysMatch(UserEntity entity) {
if((this.getBusinessKey1() == entity.getBusinessKey1()) && (this.getBusinessKey2() == entity.getBusinessKey2()))
return true;
return false;
}
方法 2 - 将静态方法添加到公共接口
interface UserKeys {
Long getBusinessKey1();
Long getBusinessKey2();
static boolean businessKeysMatch(UserKeys o1, UserKeys o2) {
if((o1.getBusinessKey1() == o2.getBusinessKey1()) && (o1.getBusinessKey2() == o2.getBusinessKey2()))
return true;
return false;
}
}
class UserEntity implements UserKeys {
// no other changes
}
class UserDto implements UserKeys {
// no other changes
}
class UserEntity implements UserKeys {
// no other changes
}
方法 3 - 比较器
interface UserBusinessKey {
Long getBusinessKey1();
Long getBusinessKey2();
}
class UserDto implements UserCompare {
// no other changes
}
class UserEntity implements UserCompare {
// no other changes
}
class UserCompare implements Comparator<UserBusinessKey> {
public int compare(UserBusinessKey o1, UserBusinessKey o2) {
int key1Compare = o1.getBusinessKey1().compareTo(o2.getBusinessKey1());
if (key1Compare == 0)
return o1.getBusinessKey2().compareTo(o2.getBusinessKey2());
return key1Compare;
}
}
方法 4 - 仅继承基数 class 的 equals/hashcode
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Data
abstract class UserBase {
@NotNull
Long businessKey1;
@NotNull
Long businessKey2;
// lombok generates a standard equals() / hashcode() pair
}
@SuperBuilder
@Getter
@Setter
@ToString
@Entity
@Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "businessKey1", "businessKey2" }) })
class UserEntity extends UserBase {
@Id
Long id;
String name;
Integer age;
String password;
String refreshToken;
String SSN;
// handcoded equals/hashcode that only call super
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
@Override
public int hashCode() {
return super.hashCode();
}
}
@SuperBuilder
@Getter
@Setter
@ToString
class UserDto extends UserBase {
String name;
String address;
Integer age;
// handcoded equals/hashcode that only call super
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
@Override
public int hashCode() {
return super.hashCode();
}
}
如果您有多个 User*Dto
,我会创建一个抽象 AbstractUserDto
,然后由所有具体的用户 DTO 对其进行扩展。您可以在那里放置您在方法 1 中显示的方法(这样您就不会一遍又一遍地复制相同的代码):
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public abstract class AbstractUserDto {
@EqualsAndHashCode.Include
@NotNull
Long businessKey1;
@EqualsAndHashCode.Include
@NotNull
Long businessKey2;
String name;
public final boolean businessKeysMatch() {
return (this.getBusinessKey1() == entity.getBusinessKey1()) && (this.getBusinessKey2() == entity.getBusinessKey2());
}
}