Taekwondavide
Taekwondavide

Reputation: 268

Different entities representing the same table, with and without joins, with JPA

I'm working with a database with many joins. I'd like to have some tables represented by two or more different entities, with and without joins. This is a simplified example:

VERSION WITH JOINS

@Entity
@Table(name = "books")
public class Book{

    @Id
    private Integer id;

    @Column(name = "title")
    private String title;

    @ManyToOne
    @JoinColumn(name = "author_id", foreignKey = @ForeignKey(name = "id"))
    private @NotNull Author author; // the class Author is an entity for the table "authors"

}

VERSION WITHOUT JOINS

@Entity
@Table(name = "books")
public class BookWithDetails{

    @Id
    private Integer id;

    @Column(name = "title")
    private String title;

    @Column(name = "author_id")
    private Integer authorId;

}

I want to build an API that allows me to retrieve the details only when I need them. For example:

/api/books/123

{
  "id": 123,
  "title": "The Lord of the Rings",
  "authorId": 321
}

/api/books/123/details

{
  "id": 123,
  "title": "The Lord of the Rings",
  "author": {
    "name": "John Ronald Reuel",
    "surname": "Tolkien"
  }
}

The ideal solution would be having BookWithDetails extend Book and, in the book/author example, having the Author author replacing Integer authorId (but having both would be fine too).

I can't get this to work as both entities refer to the same table and Hibernate automatically search for a dtype discriminant column which does not exist.

I searched for similar questions and tried many solutions but none of them fit my needs, mostly because they require the use of a discriminator column or they are about different tables sharing common fields.

Any ideas?

Upvotes: 1

Views: 10947

Answers (2)

Christian Beikov
Christian Beikov

Reputation: 16400

It's not a good idea to have multiple entities referring to the same table. You can make use of the immutable entity concept Hibernate offers, but that will only get you that far.

I think this is a perfect use case for Blaze-Persistence Entity Views.

I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.

A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:

@EntityView(Books.class)
public interface BookWithoutDetails {
    @IdMapping
    Integer getId();
    String getTitle();
    @Mapping("author.id")
    Integer getAuthorId();
}

@EntityView(Books.class)
public interface BookWithDetails {
    @IdMapping
    Integer getId();
    String getTitle();
    AuthorDto getAuthor();
    @EntityView(Author.class)
    interface AuthorDto {
        @IdMapping
        Long getId();
        String getName();
    }
}

Querying is a matter of applying the entity view to a query, the simplest being just a query by id.

BookWithDetails a = entityViewManager.find(entityManager, BookWithDetails.class, id);

The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

List<BookWithDetails> findAll();

Upvotes: 3

JLazar0
JLazar0

Reputation: 1292

It does not make sense to have two entities for the same table, it is also a vulnerability to directly expose the entities in the API, use a framework like Orika and create plain objects to send.

You could create two different mappers to have with and without details.

Upvotes: 1

Related Questions