Force Hibernate to always use default no args constructor even when select parameters are specified

by Jacek Ĺšlimok   Last Updated October 15, 2019 08:26 AM - source

A bit of context first - I'm implementing an API where the requests contain fields that the client is interested in, basically parameters to select directly. Because the overall size of data is quite large when not limited this way, it's an obvious performance benefit to do so both when it comes to serializing and transmitting the data, as well as limiting the amount of data retrieved from the database. Therefore I'm attempting to pass those to select when doing a database query.

The issue I'm facing is that when doing a query with select, Hibernate expects specific constructor to exist that corresponds to the select parameters. For example, if I specify that I'm only interested in id (long) and model (String) columns in my BasicInfo class, then Hibternate attempts to find and call BasicInfo(int, String) constructor. This is indicated by the error message I'm getting:

org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate appropriate constructor on class [pl.elzab.tech.device.common.persistent.BasicInfo]. Expected arguments are: long, java.lang.String [select new pl.elzab.tech.device.common.persistent.BasicInfo(generatedAlias0.id, generatedAlias0.model) from pl.elzab.tech.device.common.persistent.BasicInfo as generatedAlias0]

This is not an option for me. The client may wish to ask for let's say model (String) and serial (also a String) in one request in this order, while another client may wish to ask for the same fields in reverse order. Because I'd only be able to create one combination of BasicInfo(String, String) constructor, one of the requests is going to retrieve data in reversed oder. Even omitting this fact, I'd have to create all possible constructor permutations of all possible fields that may be requested which - with the number of fields that exist in this class - makes it very inefficient, if not impossible thing to do.

What I'm looking for is a way to force Hibernate to use a default class constructor even when select query parameter is specified and use setters to set the requested fields. If not that, then maybe another way that would allow me to not retrieve full row data from the database when each request is made (and then limiting it in the application code before sending back to the client).

I've also tried to register a custom hibernate interceptor and overriding its `instantiate method like so:

public class CustomInterceptor extends EmptyInterceptor {

    @Override
    public Object instantiate(String entityName, EntityMode entityMode, Serializable id) {
        if(entityName.equals(BasicInfo.class.getName()))
            return new BasicInfo();
        return null;
    }

}

hoping that this would change the object creation strategy. In this case when I don't specify select parameters, this method is correctly invoked. However when select parameters are given, it is not called at all and Hibternate attempts to directly call constructor instead.

Below are relevant portions of the project:

Code setting select parameters (irrelevant parts of code removed for clarity):

CriteriaQuery<T> query = cb.createQuery(responseClass);
Root<T> from = query.from(responseClass);
// Handle select
CriteriaQuery<T> select = null;
if(request.getSelect() != null) {
    List<Selection<?>> selectionList = new ArrayList<Selection<?>>();
    for(String s : request.getSelect()) {
        selectionList.add(from.get(s));
    }
    select = query.multiselect(selectionList);
} else {
    // select all fields
    select = query.select(from);
}
TypedQuery<T> typedQuery = em.createQuery(select);      
List<T> itemsList = typedQuery.getResultList();

And applicaion.yml:

spring:
  datasource:
    platform: h2
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true


Related Questions




Issue with One to One Mapping in Hibernate?

Updated November 20, 2017 07:26 AM

@ManyToMany org.hibernate.MappingException

Updated August 19, 2018 13:26 PM