Reputation: 1597
I need a standard approach to compare a JPA Entity to it's DTOs and determine if they represent the same business object. I can think of three approaches, a custom method on each DTO, an interface with a static method or a comparator.
Building on the answer from João Dias, approach 4 - Inheritance.
Pros/Cons
businessKeysMatch()
)equals()
but requires "boilerplate equals()/hashcode()
Any other pros/cons of the approaches or suggestions on other approaches?
In the end, I chose to use approach 2 (Interface). The Inheritance approach showed promise but one of the classes being a JPA Entity made the mapping more complex than I wanted.
Thanks for reading and thinking about my question!
Background
Entity
a database key
a business key that is unique enforced at ORM and database that determines equality (equals()
/hashcode()
)
public attributes (name, address, age, etc)
non-public / confidential attributes (password, refresh token, SSN, etc)
@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 (full)
business key that determines equality (equals()
/hashcode()
)
public attributes (name, address, age, etc)
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
class UserDto {
@EqualsAndHashCode.Include
@NotNull
Long businessKey1;
@EqualsAndHashCode.Include
@NotNull
Long businessKey2;
String name;
String address;
Integer age;
}
DTO (limited)
business key that determines equality (equals()
/hashcode()
)
selected public attributes (name)
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
class UserNameDto {
@EqualsAndHashCode.Include
@NotNull
Long businessKey1;
@EqualsAndHashCode.Include
@NotNull
Long businessKey2;
String name;
}
Approach 1 - Custom method added to each User*Dto
boolean businessKeysMatch(UserEntity entity) {
if((this.getBusinessKey1() == entity.getBusinessKey1()) && (this.getBusinessKey2() == entity.getBusinessKey2()))
return true;
return false;
}
Approach 2 - Add static method to common interface
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
}
Approach 3 - Comparator
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;
}
}
Approach 4 - Inheritance with equals/hashcode of base class only
@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();
}
}
Upvotes: 2
Views: 2166
Reputation: 17510
If you have multiple User*Dto
I would create an abstract AbstractUserDto
that would be then extended by all you concrete User DTOs. There you could place the method that you show in your Approach 1 (so that you don't duplicate the same code over and over again):
@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());
}
}
Upvotes: 1