Strange exception on inital request to Repository when using Spring Data JPA with EJB/CDI
Asked Answered
F

0

3

I've created a small project which combines Spring Data, a JPA Repository, EJB/CDI and either Wildfly Swarm or plain Wildfly.

The REST resource (an EJB) calls a CDI bean which has a Spring Data JPA Repository injected.

The initial request to the repository ends in an exception, but subsequent calls work just fine.

2016-12-15 23:03:09,690 WARN  [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (default task-1) SQL Error: 0, SQLState: null
2016-12-15 23:03:09,690 ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (default task-1) javax.resource.ResourceException: IJ000460: Error checking for a transaction
2016-12-15 23:03:10,268 ERROR [io.undertow.request] (default task-1) UT005023: Exception handling request to /Penny: org.jboss.resteasy.spi.UnhandledException: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
...
Caused by: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
...
Caused by: java.lang.Throwable: setRollbackOnly called from:
    at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.setRollbackOnly(TransactionImple.java:339)
    at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.setRollbackOnly(BaseTransaction.java:159)
    at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.setRollbackOnly(BaseTransactionManagerDelegate.java:143)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.markForRollbackOnly(AbstractEntityManagerImpl.java:1509)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1611)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.buildQueryFromName(AbstractEntityManagerImpl.java:753)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.createNamedQuery(AbstractEntityManagerImpl.java:730)
    at org.springframework.data.jpa.repository.query.NamedQuery.hasNamedQuery(NamedQuery.java:99)
    at org.springframework.data.jpa.repository.query.NamedQuery.lookupFrom(NamedQuery.java:121)
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$DeclaredQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:162)
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:212)
    at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:77)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:435)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:220)
    at org.springframework.data.jpa.repository.cdi.JpaRepositoryBean.create(JpaRepositoryBean.java:73)
    at org.springframework.data.repository.cdi.CdiRepositoryBean.create(CdiRepositoryBean.java:372)
    at org.springframework.data.repository.cdi.CdiRepositoryBean.create(CdiRepositoryBean.java:170)
    at org.jboss.weld.context.AbstractContext.get(AbstractContext.java:96)
    at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.get(ContextualInstanceStrategy.java:101)
    at org.jboss.weld.bean.ContextualInstance.get(ContextualInstance.java:50)
    at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:99)
    at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:99)
    at ch.maxant.demo.swarmproblems.EmployeeService.findByName(EmployeeService.java)
    at ch.maxant.demo.swarmproblems.EmployeeService$Proxy$_$$_WeldClientProxy.findByName(Unknown Source)
    at ch.maxant.demo.swarmproblems.EmployeeResource.get(EmployeeResource.java)

Debugging, I found out that org.springframework.data.jpa.respository.query.NamedQuery#lookupFrom ends up calling through to org.hibernate.jpa.spi.AbstractentityManagerImpl#buildQueryfromName which seems to throw an IllegalArgumentException: No query defined for that name [Employee.findByName]. That kind of makes sense, in that Spring appears to be loading the named query lazily.

Is this a bug in Spring or is the application not setup properly?

A different project which uses JBoss Wildfly (8) has the same problem: https://github.com/maxant/jee7webappwithspringdata.

Fillet answered 15/12, 2016 at 22:8 Comment(7)
See #41622179. Sorry, I found your post after answering the one I linked to.Nelle
Thanks for replying. Unfortunately it doesn't work with Wildfly Swarm (I'll test it with just Wildfly later). See github.com/maxant/swarm-demo. If I add @Eager, I get this error: "WFLYCTL0412: Required services that are not installed:" => ["jboss.deployment.unit.\"swarm-demo.war\".WeldStartService"], "WFLYCTL0180: Services with missing/unavailable dependencies" => undefinedFillet
as this might be just a swarm problem, I've posted it to here: groups.google.com/forum/#!topic/wildfly-swarm/2jUHkF84uqcFillet
The problem was that the Entity Manager producer which I have to have so that Spring can get a "default" Entity manager was RequestScope. It works once I changed that to Dependent. But shouldn't entity managers be request scoped because they aren't thread safe?Fillet
@Nelle I updated my project which uses a stateless Session EJB to call a CDI dependent scoped bean. There, I log the instance hashcodes of the CDI bean, the injected Spring Data Repository, and an injected Entity Manager (using the PersistenceContext annotation). I then apply a LOT of load. Because of the stateless session EJB, the logs include multiple CDI bean instances, multiple entity manager instances, but ALWAYS the exact same Spring Data Repository. Does the Repository contain some "magic" code to ensure calls to the entity manager which it uses are called in a thread safe manner?Fillet
Spring Data performs no synchronization on the EntityManager. In the case of WildFly, it uses TransactionScopedEntityManager that routes calls to the EntityManager associated with the transaction. Transactions are bound to threads and so you get the required isolation.Nelle
cool, I understand it now. If you add an answer, I'll gladly accept and upvote it.Fillet

© 2022 - 2024 — McMap. All rights reserved.