Skip to content

Entities & Tuples

Konstantin Triger edited this page Sep 5, 2019 · 5 revisions
  1. JPA has a perfect concept of entities, but they come bundled with a restriction - they must be mapped to a real database entity (Table or View). Otherwise the validator complains. For cases when the query does not return an entity (e.g. DTO projection) the JPA spec provides Tuple. But, as you can see, the usage isn't simple: returned objects are not converted to first class Java objects like in entity case, and access to the properties is either by ordinal or string alias.

  2. FluentJPA solves this problem: when used with Hibernate as a JPA provider, it uses the standard JPA mapping annotation to map Java fields to SQL query results, more on that in Returning Results. (Under the hood it registers a Hibernate specific mapper that performs conversion).

  3. But this means that we need to define classes with the JPA mapping annotations, but without @Entity annotation, because of the 1-st restriction above.

  4. FluentJPA introduces @Tuple annotation, which should be used in place of JPA @Entity annotation in tuple (projections) cases. Other mapping annotations, like @Column should be used as usual. For example:

    @Tuple
    @Data //Generates getters, setters, hashCode, equals and toString. (lombok)
    public class AverageCost {
        @Column(name = "avg")
        private int average;
    }

    Implementation tip: when working with Sub Queries, we need a tuple per sub query. In other words many of them. In order to not pollute packages, they can be grouped inside an interface, like this:

    public interface ModeComTutorialTypes {
    
        @Tuple
        @Data
        class CrunchbaseCompany {
            private String permalink;
            private String foundedAtClean;
        }
    
        @Tuple
        @Data
        class CrunchbaseAcquisition {
            private String companyPermalink;
            private Timestamp acquiredAtCleaned;
        }
    
        ...
    }

    Then we implement this interface and enjoy simple name resolution, i.e.:

    public class ModeComTutorial implements ModeComTutorialTypes {
        public List<CrunchbaseCompany> method() {
            // ^^^^^^^^^^^^^^^^^^^^^^^----- returns a List of tuples
            FluentQuery query = FluentJPA.SQL(() -> {
    
                CrunchbaseCompany company = subQuery(...);
                CrunchbaseAcquisition acquisition = subQuery(...);
                ...
            });
    
            return query.createQuery(getEntityManager(),
                                     CrunchbaseCompany.class).getResultList();
        }
    }