Reputation: 175
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
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
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