Unable to locate appropriate constructor

Hi guys,

Hibernate 5.6 with Spring 5.3 here. I’m just in the process of migrating Criteria API to JPA CriteriaBuilder. We use .hbm.xml mappings for the entities, no peristance.xml or something JPA specific

I’m doing something like this:

public List<ENTITY> findByCriteria(final Map<String, Object> criteriaMap, final List<String> fields, final Class<ENTITY> entityClass) {
    CriteriaBuilder cb = this.getSession().getCriteriaBuilder();
    CriteriaQuery cq = cb.createQuery(MlsEventConsumerVOImpl.class);
    Root root = cq.from(MlsEventConsumerVOImpl.class);

    List<Predicate> predicates = new ArrayList<>();

    // Construct predicates and projections...
    cq.where(cb.and(predicates.toArray(new Predicate[0])));
    Query query = getSession().createQuery(cq);
    List<ENTITY> results = query.getResultList();

    return results;
}

public class MlsEventConsumerVOImpl {

private Long rowguid;
private String path;
// More attributes

public MlsEventConsumerVOImpl () {

}

public void setRowguid(Long rowguid) { this.rowguid = roguid; }
// Further setters for every attribute

}

In the line

Query query = getSession().createQuery(cq);

I run into an exception:

Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate appropriate constructor on class [com.myapp.mls.business.object.MlsEventConsumerVOImpl]. Expected arguments are: long, java.lang.String, java.lang.String, java.lang.String, boolean, java.util.Date, java.lang.String, java.util.Date, java.lang.String, java.lang.String, java.lang.String, java.util.Date, int, java.lang.String, java.lang.String, boolean [select new com.myapp.mls.business.object.MlsEventConsumerVOImpl(generatedAlias0.rowguid, generatedAlias0.path, generatedAlias0.denotation, generatedAlias0.status, generatedAlias0.scheduled, generatedAlias0.creationDate, generatedAlias0.creationUser, generatedAlias0.lastAction, generatedAlias0.sourceFilter, generatedAlias0.payloadFilter, generatedAlias0.interval, generatedAlias0.time, generatedAlias0.dayOfMonth, generatedAlias0.weekdays, generatedAlias0.notificationTemplatePath, generatedAlias0.runWithoutEvent) from com.myapp.mls.business.object.MlsEventConsumerVOImpl as generatedAlias0 where ( generatedAlias0.scheduled=:param0 ) and ( generatedAlias0.status=:param1 )]
        at deployment.myapp-clienta.war//org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:74)
        at deployment.myapp-clienta.war//org.hibernate.hql.internal.ast.ErrorTracker.throwQueryException(ErrorTracker.java:93)
        at deployment.myapp-clienta.war//org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:282)
        at deployment.myapp-clienta.war//org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:192)
        at deployment.myapp-clienta.war//org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144)
        at deployment.myapp-clienta.war//org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113)
        at deployment.myapp-clienta.war//org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73)
        at deployment.myapp-clienta.war//org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:162)
        at deployment.myapp-clienta.war//org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:636)
        at deployment.myapp-clienta.war//org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:748)

I have made sure that for every attribute in my projection, a corresponding attribute + public setter is available in MlsEventConsumerVOImpl. What else could i check?

Edit: I have had a look into the class that is reponsible for selecting the appropriate constructur: org/hibernate/internal/util/ReflectHelper.java

The method getConstructor(…) works in a way where it accepts only constructors that have the same arguments that the select query itself provides as return values (which is never 0, so what’s the sense of the no-args constructor?). I don’t think this is correct, otherwise I would have to provide an endless number of constructors to my entity when the composition of the returned fields is variable.

Looks like you are calling cq.multiselect somewhere. If you want to fetch an entity, just remove that call.

That’s correct, I was using multiselect because I wanted to specify the projections / the attributes that should be fetched.

I think I misunderstand how projections work. I’m sure it works when I remove it. So when I’m using multiselect, I have to provide a corresponding constructor for the set of attributes in the multiselectß That would make sense.

cq.multiselect(projections).distinct(true);

Yeah, that’s not how this works. With multi-select you specify individual select items. If your result type (the class you pass to cb.createQuery) is other than Object[] or Tuple, then Hibernate will look for a constructor on the result type, which matches the types of the select items.

Not sure what you are trying to achieve, but I think it might be easier for you to achieve with Blaze-Persistence Entity-Views which works on top of JPA/Hibernate and supports exactly what you are trying to achieve here with the EntityViewSetting.fetch API.