How to inherit properties from a base class entity using @MappedSuperclass with JPA and Hibernate

Imagine having a tool that can automatically detect if you are using JPA and Hibernate properly. Hypersistence Optimizer is that tool!


In this article, we are going to see how @MappedSuperclass can help us reuse the @Id mapping of a JPA and Hibernate entity so that it won’t have to be declared on each and every entity.

Domain Model

Assuming we are have the following tables:

MappedSuperclass MySQL tables

We don’t want to declare the @Id on every entity (e.g. Post, PostDetails, PostComment, Tag), so let’s see how we can address this issue.


The JPA standard specification defines the @MappedSuperclass annotation to allow an entity to inherit properties from a base class.

Unlike the @Inheritance annotation which maps the Java Object inheritance to a relational database model which emulates inheritance, @MappedSuperclass only models inheritance in the OOP world.

From a database perspective, the @MappedSuperclass inheritance model is invisible since all the base class properties are simply copied to the database table mapped by the actual entity class.

Therefore, we can define the following BaseEntity base class:

public class BaseEntity {

    private Long id;

    private Integer version;

    //Getters and setters omitted for brevity

Now, our entities can extend the BasedEntity class and skip declaring the @Id or @Version properties since they are inherited from the base class. If the BaseEntity were not annotated with @MappedSuperclass, the @Id or @Version properties would not be inherited by the classes extending BasedEntity.

Post entity

@Entity(name = "Post")
@Table(name = "post")
public class Post extends BaseEntity {

    private String title;

        mappedBy = "post",
        cascade = CascadeType.ALL,
        orphanRemoval = true
    private List comments = new ArrayList();

        mappedBy = "post",
        cascade = CascadeType.ALL,
        orphanRemoval = true,
        fetch = FetchType.LAZY
    private PostDetails details;

        name = "post_tag",
        joinColumns = @JoinColumn(name = "post_id"),
        inverseJoinColumns = @JoinColumn(name = "tag_id")
    private Set tags = new HashSet();

    //Getters and setters omitted for brevity

    public void addComment(PostComment comment) {

    public void addDetails(PostDetails details) {
        this.details = details;

    public void removeDetails() {
        this.details = null;

Note that we are using mappedBy @OneToMany associations because this is the best way to map this type of relationship.

Also, the @ManyToMany association uses Set instead of List because that’s going to yield more efficient queries for this type of relationship.

PostComment entity

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment extends BaseEntity {

    @ManyToOne(fetch = FetchType.LAZY)
    private Post post;

    private String review;

    //Getters and setters omitted for brevity

Note that we are using FetchType.LAZY because, by default, @ManyToOne associations are fetched eagerly, and that’s bad for performance.

PostDetails entity

@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails extends BaseEntity {

    @Column(name = "created_on")
    private Date createdOn;

    @Column(name = "created_by")
    private String createdBy;

    @OneToOne(fetch = FetchType.LAZY)
    private Post post;

    //Getters and setters omitted for brevity

Note that we are using @MapsId which is the best way to map a @OneToOne association.

Also, we are using FetchType.LAZY because, by default, @OneToOne associations are fetched eagerly, and that’s bad for performance.

Tag entity

@Entity(name = "Tag")
@Table(name = "tag")
public class Tag extends BaseEntity {

    private String name;

    //Getters and setters omitted for brevity

Note the use of the @NaturalId annotation which allows you to map a business key and fetch the Tag entity by its natural identifier.

Testing time

Now, when creating two Tag entities:

Tag jdbc = new Tag();


Tag hibernate = new Tag();


Hibernate generates the following queries:

INSERT INTO tag (version, name) 

INSERT INTO tag (version, name) 
VALUES (0, 'Hibernate')

Note that the version property is set because it’s inherited from the BaseEntity class.

We don’t need to provide the @Id because the IDENTITY strategy generates the entity identifier upon persisting the entity.

When saving a Post and its associated PostDetails entity:

Post post = new Post();
post.setTitle("High-Performance Java Persistence");

PostDetails postDetails = new PostDetails();
postDetails.setCreatedBy("Vlad Mihalcea");
postDetails.setCreatedOn(new Date());

Session session = entityManager.unwrap(Session.class);



Hibernate generates the following queries:

INSERT INTO post (version, title) 
VALUES (0, 'High-Performance Java Persistence')

INSERT INTO post_details (version, created_by, created_on, id) 
VALUES (0, 'Vlad Mihalcea', '2017-11-08 12:29:23.498', 1)

INSERT INTO post_tag (post_id, tag_id) 
values (1, 2)

INSERT INTO post_tag (post_id, tag_id) 
values (1, 1)

When saving a PostComment entity:

Post post = entityManager.createQuery(
    "select p " +
    "from Post p " +
    "where p.title = :title", Post.class)
.setParameter("title", "High-Performance Java Persistence")

PostComment postComment = new PostComment();
postComment.setReview("THE book on Hibernate");


Hibernate generates the following queries:

INSERT INTO post_comment (version, post_id, review) 
VALUES (0, 1, 'THE book on Hibernate')

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.


So, every time you need to inherit properties from a base class, you need the @MappedSuperclass annotation. Otherwise, JPA entities will ignore the base class properties even if your entity extends a given base class.

Transactions and Concurrency Control eBook

9 Comments on “How to inherit properties from a base class entity using @MappedSuperclass with JPA and Hibernate

  1. The hibernate-jpamodelgen will not work well with MappedSuperClasses coming from an external jar. They have to be in the same package.

    • Sounds like a bug. You should create a Jira issue for it.

  2. Hi Vlad,

    What is the bast way to inherit OneToMany relationship from a base class?

    I need to add a scalable solution where some of my entities declare the same collection. I don’t want to create one collection table for each main entity that has the same type of collection.

    My scenario is a bit complex, but to put in simple terms, imagine I have Person and Company, both can have multiple contact phone information so I want each to have a collection of Phone. Ideally, I want only one table to hold phones. In this case, shouldn’t the phone table contain a owner_type column to identity what actual child class is the owner of this phone? How to I declare this using JPA annotations? Is there a way to to this using @ElementCollection/@Embeddable in a @MapperSuperclass?

      • So, I just read chapter section 11.4 Mapped superclass, and my conclusion is that what I’m looking for is not possible. The last sentence, just before chapter 12 (flushing) says @MappedSuperclass cannot have @OneToMany. Did I understand correctly? Any chance of achieving that with @ElementCollection/@Embeddable? I wanted to avoid multiple phone tables (one for each entity), similar to the topic_statistics in the books scenario.

      • Why would you assume that? Show me the paragraph that says that.

      • “Polymorphic queries against the Topic class are not permitted and the Board entity cannot
        define a @OneToMany Topic collection either.”

      • Yes, that’s correct. However, you asked about adding a collection association to the @MappedSuperclass, which is supported since the association is pushed to the owning entity.

      • Thanks, I’ll try to create a sample project on github to better illustrate the problem.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.