Timothy Vogel
Timothy Vogel

Reputation: 1597

Compare JPA Entity and DTO

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

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

DTO (full)

DTO (limited)

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

Answers (1)

Jo&#227;o Dias
Jo&#227;o Dias

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

Related Questions