Hibernate - one table multiple entities

hibernate, sql

Solution

This is very simple to achieve with JPA and Hibernate.

Let's assume you are using the following `book` database table:

Mapping entities

Now, you can map two entities: `Book` and `BookSummary` to this table.

First, we will create a `BaseBook` abstract class which will be extended by all entities:

@MappedSuperclass
public abstract class BaseBook<T extends BaseBook> {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @NaturalId
    @Column(length = 15)
    private String isbn;
 
    @Column(length = 50)
    private String title;
 
    @Column(length = 50)
    private String author;
 
    public Long getId() {
        return id;
    }
 
    public T setId(Long id) {
        this.id = id;
        return (T) this;
    }
 
    public String getIsbn() {
        return isbn;
    }
 
    public T setIsbn(String isbn) {
        this.isbn = isbn;
        return (T) this;
    }
 
    public String getTitle() {
        return title;
    }
 
    public T setTitle(String title) {
        this.title = title;
        return (T) this;
    }
 
    public String getAuthor() {
        return author;
    }
 
    public T setAuthor(String author) {
        this.author = author;
        return (T) this;
    }
}

Now, the `BookSummary` entity simply extends the `BaseBook` superclass and adds no additional entity attribute.

@Entity(name = "BookSummary")
@Table(name = "book")
public class BookSummary extends BaseBook<BookSummary> {
 
}

On the other hand, the `Book` entity extends the `BaseBook` superclass and maps the `properties` attribute.

@Entity(name = "Book")
@Table(name = "book")
@TypeDef(
    name = "jsonb",
    typeClass = JsonBinaryType.class
)
@DynamicUpdate
public class Book extends BaseBook<Book> {
 
    @Type(type = "jsonb")
    @Column(columnDefinition = "jsonb")
    private String properties;
 
    public String getProperties() {
        return properties;
    }
 
    public Book setProperties(String properties) {
        this.properties = properties;
        return this;
    }
 
    public ObjectNode getJsonProperties() {
        return (ObjectNode) JacksonUtil
            .toJsonNode(properties);
    }
}

Persisting entities

This way, you can persist either a `Book` entity:

entityManager.persist(
    new Book()
        .setIsbn("978-9730228236")
        .setTitle("High-Performance Java Persistence")
        .setAuthor("Vlad Mihalcea")
        .setProperties(
            "{" +
                "   \"publisher\": \"Amazon\"," +
                "   \"price\": 44.99," +
                "   \"publication_date\": \"2016-20-12\"," +
                "   \"dimensions\": \"8.5 x 1.1 x 11 inches\"," +
                "   \"weight\": \"2.5 pounds\"," +
                "   \"average_review\": \"4.7 out of 5 stars\"," +
                "   \"url\": \"https://amzn.com/973022823X\"" +
            "}"
        )
);

or a `BookSummary`:

entityManager.persist(
    new BookSummary()
        .setIsbn("978-1934356555")
        .setTitle("SQL Antipatterns")
        .setAuthor("Bill Karwin")
);

Fetching entities

You can fetch the `BookSummary` entity:

BookSummary bookSummary = entityManager
    .unwrap(Session.class)
    .bySimpleNaturalId(BookSummary.class)
    .load("978-9730228236");
 
assertEquals(
    "High-Performance Java Persistence",
    bookSummary.getTitle()
);

or the `Book` entity if you want:

Book book = entityManager
    .unwrap(Session.class)
    .bySimpleNaturalId(Book.class)
    .load("978-9730228236");
 
assertEquals(
    "High-Performance Java Persistence, 2nd edition",
    book.getTitle()
);

Conclusion

So mapping multiple entities to the same database table, not only that it allows us to fetch data more efficiently, but it also speeds up the dirty checking process as Hibernate has to inspect fewer entity properties.

The only drawback of using this approach is that you have to make sure you don’t fetch more than one entity type for the same database table record, as otherwise, this can cause inconsistencies when flushing the Persistence Context.

Problem

Firstly, I have read Hibernate - One table with multiple entities?. However, I would like to map two entities to the same table, but I would like both of them to be entities, which I can select from. What I mean: - One table: Person (id, name, dateOfBirth, city, street, zipcode). - Two Entities: Person (id, name, dateOfBirth), Address (id, city, street, zipcode). So it's a 1:1 relationship between Entities, but still 1 table in DB. If I do it using the proposed solution (component keyword) in the above link, I can't query Address directly (I can access it via Person entity). And I want to be able to do ``` session.createCriteria(Adres.class) ``` How do I do that? UPDATE: I tried the one-to-one association between entities, in Address mapping: ``` <one-to-one name="Person " class="model_mapowanie_xml.Person "/> ``` and in Person mapping: ``` <one-to-one name="Address" class="model_mapowanie_xml.Address "/> ``` Both classes have fields referring to the other one. Selecting records works fine for that. However, how can I add in one transaction a record using both entities? (Id is db-generated) ``` Address ad = new Address(); ad.setProperty("Sydney"); Person p = new Person(); p.setProperty("John"); p.setAddress(ad); session.save(p); ``` and only Person part is saved, the address property remains empty.

Original source

Related problems