Wunsz
Wunsz

Reputation: 175

Type specified for TypedQuery is incompatible with itself

I have a problem with Spring & Hibernate. I have a method in following fashion:

@Override
@Transactional
public List<SomeEntityClass> getSomeData(Integer someParam1, Long someParam2) {

    TypedQuery<SomeEntityClass> query = em.createQuery(
            "SELECT sec FROM SomeEntityClass sec " +
                    "WHERE sec.someField1 = :param1 " +
                    "AND sec.someField2 = :param2 ", SomeEntityClass.class);
    query.setParameter("param1", someParam1);
    query.setParameter("param2", someParam2);

    try {
        return query.getResultList();
    } catch (Exception e) {
        return new LinkedList<>();
    }
}

This method is part of web socket handler and is called when a message is received. However, when it does, I get following error:

org.springframework.dao.InvalidDataAccessApiUsageException: 
   Type specified for TypedQuery [some.package.subpackage.SomeEntityClass] is incompatible 
    with query return type [class some.package.subpackage.SomeEntityClass]

I have found similar question and the provided answer indicated class being loaded from different class loaders. However:

ClassLoader a = UserMicroscopeLensLight.class.getClassLoader();
ClassLoader b = em.getClass().getClassLoader();

a.equals(b)

Yields

true


EDIT: 2018-02-08 14:05 CET


So I debugged through the code and got into AbstractEntityManagerImpl.class decompiled from byte code and got following lines:

if (!resultClass.isAssignableFrom(hqlQuery.getReturnTypes()[0].getReturnedClass())) {
    throw new IllegalArgumentException("Type specified for TypedQuery [" + resultClass.getName() + "] is incompatible with query return type [" + hqlQuery.getReturnTypes()[0].getReturnedClass() + "]");
}

The resultClass.getClassLoader() gave:

RestartClassLoader@9995

whereas hqlQuery.getReturnTypes()[0].getReturnedClass().getClassLoader() (and also this.getClass().getClassLoader() resulted in:

Launcher$AppClassLoader@10034

Funny thing is that, calling em.getClass().getClassLoader() in the query method results in:

RestartClassLoader@9995

So I'm asking... WTF?! Looking at the trace:

at org.hibernate.jpa.spi.AbstractEntityManagerImpl.resultClassChecking(AbstractEntityManagerImpl.java:387)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:344)
at sun.reflect.GeneratedMethodAccessor109.invoke(Unknown Source:-1)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
at com.sun.proxy.$Proxy109.createQuery(Unknown Source:-1)
at some.package.name.SomeDaoImpl.getSomeData(SomeDaoImpl.java:21)

Iit seems that, the entity manager for called in getSomeData is just a proxy to some SharedEntityManager and they both have different class loaders...

Upvotes: 2

Views: 3826

Answers (2)

Wunsz
Wunsz

Reputation: 175

Ok. I found the issue. Removing spring dev tools resolved everything:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>

Idea came from Spring's github issue.

Upvotes: 1

coladict
coladict

Reputation: 5095

This definitely looks like a classloader issue. To confirm it, try this code fragment. I'm only posting it as an answer, because I can't fit it in a comment.

for (EntityType e : em.getMetamodel().getEntities()) {
    if (e.getJavaType().getName().equals(SomeEntityClass.class.getName())) {
        assert(e.getJavaType().equals(SomeEntityClass.class));
    }
}

If the assertion fails, then the class is indeed loaded twice.

Upvotes: 2

Related Questions