Search code examples
javahibernatejpaormhibernate-mapping

Hibernate entity polymorphism with using @MappedSuperclass annotation


Consider following model:

Abstract class AbstractLog

@MappedSuperclass
public abstract class AbstractLog{

    private Long id;
    private Set<Comment> comments;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "quasar_log_id_seq")
    public Long getId() {
        return id;
    }

    @OrderBy(clause = "created")
    @OneToMany(mappedBy = "abstractLog", fetch = FetchType.LAZY, cascade = { CascadeType.ALL }, orphanRemoval = true)
    public Set<Comment> getComments() {
        return comments;
    }

    // setters
}

AuditLog

@Entity
@SequenceGenerator(name = "quasar_log_id_seq", sequenceName = "quasar_log_id_seq")
public class AuditLog extends AbstractLog {

    private Set<AuditLogItem> items;

    @OrderBy(clause = "date")
    @OneToMany(mappedBy = "auditLog")
    public Set<AuditLogItem> getItems() {
        return items;
    }
}

DocumentationLog

@Entity
@SequenceGenerator(name = "quasar_log_id_seq", sequenceName = "quasar_log_id_seq")
public class DocumentationLog extends AbstractLog {

    private Set<DocumentationLogItem> items;

    @OrderBy(clause = "date")
    @OneToMany(mappedBy = "documentationLog")
    public Set<DocumentationLogItem> getItems() {
        return items;
    }

}

Comment

@Entity
@SequenceGenerator(name = "quasar_comment_id_seq", sequenceName = "quasar_comment_id_seq", initialValue = 1, allocationSize =1)
@Table(name = "quasar_comment")
public class Comment extends IdentifiableEntity {

    private User user;
    private String comment;
    private AbstractLog abstractLog;

    @Id
    @Override
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "quasar_comment_id_seq")
    public Long getId() {
        return super.getId();
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "id_user")
    public User getUser() {
        return user;
    }

    @Column(name = "comment", length = 300)
    public String getComment() {
        return comment;
    }


    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "audit_log_id", nullable = false)
    public AbstractLog getAbstractLog() {
        return abstractLog;
    }

}

You will notice that, ID sequence generator is same for all extended entities of AbstractLog.

The aim is that, the Entity Comment was shared between all extended entities of AbstractLog. Is it possible using annotation @MappedSuperclass? If I used @Inheritance annotation, the class AbstractLog will bee created too, but that I don't want that. Currently is thrown references an unknown entity error.


Solution

  • @MappedSuperclass is used to inherit properties but because it doesn't represent an Entity you can use entity relationships, so you are left with only basic types, id generation and optimistic locking version.

    If you don't want the base class to be a table of it's own (e.g. table-per-subclass), you can use a table-per-concrete-class strategy. Each entity will have it's own table, and the inherited properties will only be a Java convenience since they will be duplicated in each SQL table.

    So you can replace @MappedSuperclass with:

    @Inheritance(InheritanceType.TABLE_PER_CLASS)
    

    But according to Java Persistence wiki:

    Single table inheritance is the default, and table per class is an optional feature of the JPA spec, so not all providers may support it.

    Hibernate supports it, but if you plan on using other JPA provider you might not have support for it.