#HOWTO: Avoid repeating attributes in JPA entities

Today I want to show you a way to avoid repeating the same attributes in your JPA entities over and over again. Let’s say some of your columns (e.g. timestamps for the creation date, the date of the last update, an intern identifier or something else) are present on most of your entities. In most of the timestamp column cases, you’ll use the JPA lifecycle listeners for @PreUpdate or @PrePersist. Repeating these columns over and over again could lead to developer errors and you need to test them on every entity.

To solve this problem, JPA offers an annotation called @MappedSuperclass and make use of Java’s inheritance and extract all repeating attributes to this superclass. This superclass won’t be mapped to a table in the database. In the following example, I’ll model a Product and a Customer domain entity which have some columns in common. In addition, I make use of some Hibernate specific annotations like @CreationTimestamp/@UpdateTimestamp which are not in the JPA standard right now, but the result of these annotations could also be achieved with @PreUpdate/@PrePersist. Due to the fact that all the JPA entities need a primary ID, I also extracted the ID column to my superclass:

@Data // annotation from Lombok to avoid writing getter/setter etc.
@MappedSuperclass
public class BaseEnterpriseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String internId;

    @CreationTimestamp
    private LocalDateTime createdAt;

    @UpdateTimestamp
    private LocalDateTime updatedAt;

    @PrePersist
    public void prePersist() {
        this.internId = String.valueOf(Math.abs(ThreadLocalRandom.current().nextInt()));
    }

}

The domain JPA entities look like the following:

@Data
@Entity
public class Product extends BaseEnterpriseEntity {

    @Column(nullable = false, length = 100)
    private String name;

    private int amount;

}

@Data
@Entity
public class Customer extends BaseEnterpriseEntity {

    @Column(nullable = false, length = 100)
    private String name;

    @Column(nullable = false, length = 40)
    private String customerId;

}

With this solution, you can control your common columns at one place and improve the testability as you just need to verify these columns on one entity. One important downside of this solution is that you can only use the JPA lifecycle listener annotations @PrePersist/@PreUpdate ... once and not multiple times.

I created an example project for this blog post with an embedded H2 in-memory. You can find the sources on GitHub and try them on your machine.

See you!

Leave a comment

Your email address will not be published. Required fields are marked *