Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring 3.0 ORM with JPA 2.0 TypedQuery ClassCastException [SPR-6733] #11399

Closed
spring-projects-issues opened this issue Jan 20, 2010 · 7 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jan 20, 2010

Ke CAI opened SPR-6733 and commented

see: http://stackoverflow.com/questions/2101500/spring-3-0-orm-with-jpa-2-0-classcastexception/2102328#2102328

I'm trying to use JPA 2.0 in Spring 3.0 ORM. The JPA vendor is Hibernate 3.5.0-Beta-3.

It works well with JPQL, but when I tried to use CriteriaQuery, an exception happens:

java.lang.ClassCastException: $Proxy50 cannot be cast to javax.persistence.TypedQuery at $Proxy38.createQuery(Unknown Source) at com.absorbx.retailx.dao.impl.ShopDaoImpl.findByCrieria(ShopDaoImpl.java:30) at com.absorbx.retailx.dao.SimpleDaoTest.testFindByCriteria(SimpleDaoTest.java:39) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

The DAO code:

@Repository
public class ShopDaoImpl implements
ShopDao {
@PersistenceContext
transient EntityManager entityManager;

@Override
public Shop findByCrieria() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Shop> c = cb.createQuery(Shop.class);
    Root<Shop> shop = c.from(Shop.class);
    c.select(shop).where(cb.equal(shop.get("name"), "petstore"));
    TypedQuery<Shop> q = entityManager.createQuery(c);
    return q.getSingleResult();
}

}

It seems to be a bug in Spring:

org/springframework/orm/jpa/SharedEntityManagerCreator.java:

if (result instanceof Query) {
Query query = (Query) result;
...
result = Proxy.newProxyInstance(Query.class.getClassLoader(),
new Class[] {Query.class}, new DeferredQueryInvocationHandler(query, target));
...
}

Good catch. Spring is checking to see if the query is an instance of Query, and generates the proxy of that type. Unfortunately, TypedQuery is a subtype of Query, and the generated proxy will still only implement Query. TypedQuery was introduced in JavaEE 6, so it's understandable why Spring doesn't handle it, although Spring 3 is supposed to handle JavaEE 6 properly. Definitely a bug.


Affects: 3.0 GA

Reference URL: http://stackoverflow.com/questions/2101500/spring-3-0-orm-with-jpa-2-0-classcastexception/2102328#2102328

Issue Links:

Referenced from: commits bcfef8a, 0aee6e9

2 votes, 3 watchers

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Good point! I've revised the query proxying code to determine all interfaces implemented by the target Query object which includes the JPA 2.0 TypedQuery interface and also query vendor interfaces.

This should be available in tonight's 3.0.1 snapshot already. Feel free to give it an early try...

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Daniel Kvasnička commented

I've encountered this issue as well, using GlassFish v3 and its built-in JPA implementation (EclipseLink I believe). I am using Spring 3.0.1.RELEASE-A.

TypedQuery<Test> query = em.createQuery("SELECT t FROM Test t;", Test.class);

produces

java.lang.ClassCastException: $Proxy111 cannot be cast to javax.persistence.TypedQuery
at $Proxy107.createQuery(Unknown Source)
...

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Daniel, I can't reproduce this: TypedQuery access against a shared EntityManager works for me. Could you please provide some details on where your "em" reference comes from? It would also be great if you could debug into the proxy's InvocationHandler a bit: Line 244 of SharedEntityManagerCreator is where the Query proxy is being built, using the interfaces of the target Query object for the proxy as well.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Mar 3, 2010

Daniel Kvasnička commented

Hi,

I managed to get rid of the error by changing my setup a bit. I am now using org.springframework.orm.jpa.support.JpaDaoSupport as a superclass for my DAO instead of my own generic DAO ancestor, which did the trick and greatly simplified my JPA-related code.

The em instance was from that base DAO of mine (an abstract Spring bean) and it was injected by Spring thanks to @PersistenceContext. My Spring JPA config was and is simple: <jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/pu"/>

My PU definition in persistence.xml:

<persistence-unit name="pu" transaction-type="JTA">
<jta-data-source>jdbc/testds</jta-data-source>

<properties>
     <property name="eclipselink.target-database" value="MySQL"/>
     <property name="eclipselink.logging.level" value="FINE"/>
</properties>

</persistence-unit>

Maybe the ultra-concise JNDI-only JPA config is the problem. When I had this:

   <bean id="entityManagerFactory"
	class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
	p:jpaVendorAdapter-ref="jpaAdapter" />

<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
	<property name="databasePlatform" value="org.eclipse.persistence.platform.database.MySQLPlatform" />
 	<property name="showSql" value="true" />
</bean>

<tx:jta-transaction-manager />
<tx:annotation-driven />

everything worked like a charm.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

I have a suspicion that the JNDI-obtained EntityManagerFactory from GlassFish has some special ClassLoader arrangement behind it. I've revised Spring's SharedEntityManagerCreator a bit so that it extracts interfaces using the target factory's actual ClassLoader, instead of the JPA API ClassLoader as it did before in such a case. This might solve your original issue.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Shashi Kale commented

Hi,

I am using 3.0.1.RELEASE-A of spring along with Spring DM and am facing a similar issue.
My code goes like

Query q = em.createQuery("SELECT e FROM " + entityClazz.getSimpleName()

  • " e");

I get

Caused by: java.lang.ClassCastException: $Proxy194 cannot be cast to javax.persistence.Query
at $Proxy56.createQuery(Unknown Source)
at com.arisglobal.aglite.dao.GenericCrudDAOImpl.findAll(GenericCrudDAOImpl.java:249)
at java.lang.Thread.run(Thread.java:619)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:196)

Daniel's solution wouldn't work for me since I can't discard my generic DAO implementation.

Hi Juergen,
is there a way I can resolve this?

Thanks and Regards,
Shashi

@spring-projects-issues
Copy link
Collaborator Author

Shashi Kale commented

Hi Again,

Missed to mention that if I make the method in my DAO transactional it works all fine. It doesn't work only if I make it non transactional.

Thanks,
Shashi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants