Spring/Hibernate data access concurrency problem - java

I've been given full responsibility for a bunch of large projects that I didn't write myself and I am hoping for some advice here because these projects have issues that are keeping me up at night and much of this is new to me.
I've noticed that database queries through the ORM will sometimes not find an entity that had requested to be saved already, causing invalid state and crashes. The problem becomes more apparent with more concurrent threads that access the database through the ORM.
All the assistance I can get with troubleshooting would be much appreciated.
Relevant dependencies
Web framework: Spring MVC
ORM: Hibernate
DBMS: MySQL 5.6
ehcache: 2.10.6
Hibernate: 5.1.17.Final
Hibernate validator: 5.4.3.Final
MySQL connector: 5.1.48
Spring: 4.3.29.RELEASE
Spring integration: 4.3.23.RELEASE
Spring security: 4.2.19.RELEASE
The web app runs under Tomcat 7 with Java 8 runtime.
We're behind on several dependencies and I want to tackle that another day.
Troubleshooting
I read that EntityManager by itself is not thread-safe and that #PersistenceContext turns it into a proxy that allows thread-safe access to the underlying EntityManager. I can also see that it's some sort of proxy in the debugger. Now I don't know exactly how that works and whether the way we use it is really safe.
Despite that, I notice the following:
An incoming HTTP request causes an entity to be saved to the database through the ORM.
Shortly afterwards, another incoming HTTP request wants to find the same entity through the ORM.
Most of the time the entity is as expected found, but sometimes it is not found despite the fact that it should have been saved already.
If I try to find the entity on the same thread right after saving it then it's always found, but if I launch a new thread right after saving it then the entity may not be found in that thread unless I Thread.sleep() before the lookup.
I can see in the debugger that the same instance of SomeService, SomeDaoImpl and the EntityManager proxy in the example code below is reused for multiple incoming HTTP requests and handled by different threads.
Even though our own classes are stateless, there is apparently a problem with data access through the ORM.
Greatly simplified pseudocode
interface Dao {
Entity save(entity);
};
abstract class AbstractDao implements Dao {
#PersistenceContext
private EntityManager em; // proxy
#Override
public Entity save(entity) {
return em.persist(entity); // or this.em.merge(entity)
}
protected EntityManager getEntityManager() {
return em;
}
}
abstract class AbstractUuidDao extends AbstractDao {
public Entity findByUuid(uuid) {
em = getEntityManager();
query = em.getCriteriaBuilder().createQuery(...);
// ...
return em.createQuery(query).setParameter(..., uuid).getSingleResult();
}
}
interface SomeDao extends Dao {};
#Repository
class SomeDaoImpl extends AbstractUuidDao implements SomeDao {}
#Service("someService")
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
class SomeService {
#Autowired
private SomeDao someDao;
// the first HTTP request calls this
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
void createSomething() {
uuid = "random uuid";
someDao.save(new SomeEntity(uuid));
someDao.findByUuid(uuid); // always returns non-null
}
// after createSomething() returns, the transaction is committed
// two HTTP requests can call this simultaneously but they come in after createSomething() has returned, and therefore after the transaction has already been committed
void getSomething(uuid) {
// uuid is the same "random uuid"
someDao.findByUuid(uuid); // can return either null or non-null
}
}
I believe this is the persistence configuration:
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="model" />
<property name="jpaDialect">
<bean class="custom class here" />
</property>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.connection.release_mode" value="after_transaction" />
<entry key="hibernate.current_session_context_class" value="org.springframework.orm.hibernate4.SpringSessionContext" /> <!-- also tried "org.springframework.orm.hibernate5.SpringSessionContext" -->
<entry key="hibernate.cache.use_query_cache" value="true" /> <!-- also tried "false" -->
<entry key="hibernate.cache.use_second_level_cache" value="true" /> <!-- also tried "false" -->
<entry key="hibernate.cache.use_minimal_puts" value="false" />
<entry key="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory" />
<entry key="hibernate.default_batch_fetch_size" value="8" />
<entry key="hibernate.jdbc.fetch_size" value="10" />
<entry key="hibernate.jdbc.batch_size" value="100" />
<entry key="hibernate.order_inserts" value="true" />
<entry key="hibernate.order_updates" value="true" />
<entry key="hibernate.format_sql" value="false" />
<entry key="hibernate.show_sql" value="false" />
</map>
</property>
</bean>
I'm sorry for the lack of real code and details but it is difficult for me to provide a working example without making the example more complex than needed.
Any ideas would be much appreciated.
Update 1
I revised the example code in my original post to more correctly reflect the real use case, but it does not change much.
I can see that everything happens in the expected sequence, i.e. the transaction is committed and subsequent queries only happen afterwards.
I replaced our custom JPA dialect with org.springframework.orm.jpa.vendor.HibernateJpaDialect since it seems like the custom class is no longer needed because it was meant to add support for custom isolation levels. That did not help.
I have upgraded Hibernate from version 5.1.17 to version 5.2.18 and reimplemented an incompatible third party Hibernate user type that prevented the upgrade.
I have not seen a single failure since that upgrade but I will keep testing.
Update 2
The issue does not seem to persist even with Hibernate version 5.2.0 either.
The following bugfix mentioned in the changelog looks very much related to my problem:
[HHH-10649] - When 2LC enabled, flush session and then refresh entity cause dirty read in another session / transaction
If "2LC" means "second-level cache" then I have as mentioned before had the problem even after turning this off.

Related

Delete by username spring boot [duplicate]

I get this error when trying to invoke "persist" method to save entity model to database in my Spring MVC web application. Can't really find any post or page in internet that can relate to this particular error. It seems like something's wrong with EntityManagerFactory bean but i'm fairly new to Spring programming so for me it seems like everything is initialized fine and according to various tutorial articles in web.
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/data/repository
http://www.springframework.org/schema/data/repository/spring-repository-1.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.2.xsd">
<context:component-scan base-package="wymysl.Controllers" />
<jpa:repositories base-package="wymysl.repositories"/>
<context:component-scan base-package="wymysl.beans" />
<context:component-scan base-package="wymysl.Validators" />
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean class="org.springframework.orm.hibernate4.HibernateExceptionTranslator"/>
<bean id="passwordValidator" class="wymysl.Validators.PasswordValidator"></bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#localhost:1521:xe" />
<property name="username" value="system" />
<property name="password" value="polskabieda1" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:./META-INF/persistence.xml" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.max_fetch_depth">3</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
</props>
</property>
</bean>
<mvc:annotation-driven />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
</bean>
<bean name="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:resources mapping="/resources/*" location="/resources/css/"
cache-period="31556926"/>
</beans>
RegisterController.java
#Controller
public class RegisterController {
#PersistenceContext
EntityManager entityManager;
#Autowired
PasswordValidator passwordValidator;
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(passwordValidator);
}
#RequestMapping(value = "/addUser", method = RequestMethod.GET)
public String register(Person person) {
return "register";
}
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public String register(#ModelAttribute("person") #Valid #Validated Person person, BindingResult result) {
if(result.hasErrors()) {
return "register";
} else {
entityManager.persist(person);
return "index";
}
}
I had the same problem and I annotated the method as #Transactional and it worked.
UPDATE: checking the spring documentation it looks like by default the PersistenceContext is of type Transaction, so that's why the method has to be transactional (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html):
The #PersistenceContext annotation has an optional attribute type,
which defaults to PersistenceContextType.TRANSACTION. This default is
what you need to receive a shared EntityManager proxy. The
alternative, PersistenceContextType.EXTENDED, is a completely
different affair: This results in a so-called extended EntityManager,
which is not thread-safe and hence must not be used in a concurrently
accessed component such as a Spring-managed singleton bean. Extended
EntityManagers are only supposed to be used in stateful components
that, for example, reside in a session, with the lifecycle of the
EntityManager not tied to a current transaction but rather being
completely up to the application.
I got this exception while attempting to use a deleteBy custom method in the spring data repository. The operation was attempted from a JUnit test class.
The exception does not occur upon using the #Transactional annotation at the JUnit class level.
This error had me foxed for three days, the situation I faced produced the same error. Following all the advice I could find, I played with the configuration but to no avail.
Eventually I found it, the difference, the Service I was executing was contained in a common jar, the issue turned out to be AspectJ not treating the Service instantiation the same. In effect the proxy was simply calling the underlying method without all the normal Spring magic being executed before the method call.
In the end the #Scope annotation placed on the service as per the example solved the issue:
#Service
#Scope(proxyMode = ScopedProxyMode.INTERFACES)
#Transactional
public class CoreServiceImpl implements CoreService {
#PersistenceContext
protected EntityManager entityManager;
#Override
public final <T extends AbstractEntity> int deleteAll(Class<T> clazz) {
CriteriaDelete<T> criteriaDelete = entityManager.getCriteriaBuilder().createCriteriaDelete(clazz);
criteriaDelete.from(clazz);
return entityManager.createQuery(criteriaDelete).executeUpdate();
}
}
The method I have posted is a delete method but the annotations affect all persistence methods in the same way.
I hope this post helps someone else who has struggled with the same issue when loading a service from a jar
boardRepo.deleteByBoardId(id);
Faced the same issue. GOT javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread
I resolved it by adding #Transactional annotation above the controller/service.
You need to add #Transactional to your methode
I had the same error because I switched from XML- to java-configuration.
The point was, I didn't migrate <tx:annotation-driven/> tag, as Stone Feng suggested.
So I just added #EnableTransactionManagement as suggested here
Setting Up Annotation Driven Transactions in Spring in #Configuration Class, and it works now
Adding the org.springframework.transaction.annotation.Transactional annotation at the class level for the test class fixed the issue for me.
I had the same problem and I added tx:annotation-driven in applicationContext.xml and it worked.
I had the same error when accessing an already transactional-annotated method from a non-transactional method within the same component:
Before:
#Component
public class MarketObserver {
#PersistenceContext(unitName = "maindb")
private EntityManager em;
#Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW)
public void executeQuery() {
em.persist(....);
}
#Async
public void startObserving() {
executeQuery(); //<-- Wrong
}
}
//In another bean:
marketObserver.startObserving();
I fixed the error by calling the executeQuery() on the self-referenced component:
Fixed version:
#Component
public class MarketObserver {
#PersistenceContext(unitName = "maindb")
private EntityManager em;
#Autowired
private GenericApplicationContext context;
#Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW)
public void executeQuery() {
em.persist(....);
}
#Async
public void startObserving() {
context.getBean(MarketObserver.class).executeQuery(); //<-- Works
}
}
Just a note for other users searching for answers for thie error. Another common issue is:
You generally cannot call an #transactional method from within the same class.
(There are ways and means using AspectJ but refactoring will be way easier)
So you'll need a calling class and class that holds the #transactional methods.
If you have
#Transactional // Spring Transactional
class MyDao extends Dao {
}
and super-class
class Dao {
public void save(Entity entity) { getEntityManager().merge(entity); }
}
and you call
#Autowired MyDao myDao;
myDao.save(entity);
you won't get a Spring TransactionInterceptor (that gives you a transaction).
This is what you need to do:
#Transactional
class MyDao extends Dao {
public void save(Entity entity) { super.save(entity); }
}
Unbelievable but true.
Without #Transactional annotation you can achieve the same goal with finding the entity from the DB and then removing that entity you got from the DB.
CrudRepositor -> void delete(T var1);
For us, the problem came down to same context settings in multiple configuration files. Check you've not duplicated the following in multiple config files.
<context:property-placeholder location="classpath*:/module.properties"/>
<context:component-scan base-package="...." />
I had the same error code when I used #Transaction on a wrong method/actionlevel.
methodWithANumberOfDatabaseActions() {
methodA( ...)
methodA( ...)
}
#Transactional
void methodA( ...) {
... ERROR message
}
I had to place the #Transactional just above the method methodWithANumberOfDatabaseActions(), of course.
That solved the error message in my case.
I removed the mode from
<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />
to make this work
I already had the #Transactional but still wasn't working. Turns out I had to get rid of parallelism to make it work.
If you are doing things in parallel, DON'T.
I had this issue for days and nothing I found anywhere online helped me, I'm posting my answer here in case it helps anyone else.
In my case, I was working on a microservice being called through remoting, and my #Transactional annotation at the service level was not being picked up by the remote proxy.
Adding a delegate class between the service and dao layers and marking the delegate method as transactional fixed this for me.
This helped us, maybe it can help others in the future. #Transaction was not working for us, but this did:
#ConditionalOnMissingClass("org.springframework.orm.jpa.JpaTransactionManager")
I got the same error when I executed the Spring JPA deleteAll() method from Junit test cases. I simply used the deleteInBatch() & deleteAllInBatch() and its perfectly works. We do not need to mark #Transactional at the test cases level.
For anyone with the same issue as I had, I was calling a public method method1 from within another class.
method1 then called another public method method2 within the same class.
method2 was annotated with #Transactional, but method1 was not.
All that method1 did was transform some arguments and directly call method2, so no DB operations here.
The issue got solved for me once I moved the #Transactional annotation to method1.
Not sure the reason for this, but this did it for me.
Calling the repository method was being called within a class with #Component, taking that method out of that class and placing it inside another with #Service worked.
It's like you are using the shared EntityManager when you are getting it Autowired so for persisting spring tells that this EntityManager bean is a shared bean and for persisting it needs a hold of this bean till the data persist doesn't get completed so for that we have to use #Transactional so that it gonna start and commit the persistence in a transaction so the data or operation gets completely saved or get rollback completely.
To fix this in a test, you can use #DataJpaTest or #AutoConfigureTestDatabase.

Using a spring bean with #Transactional is giving TransactionRequiredException: No EntityManager with actual transaction available for current thread [duplicate]

I get this error when trying to invoke "persist" method to save entity model to database in my Spring MVC web application. Can't really find any post or page in internet that can relate to this particular error. It seems like something's wrong with EntityManagerFactory bean but i'm fairly new to Spring programming so for me it seems like everything is initialized fine and according to various tutorial articles in web.
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/data/repository
http://www.springframework.org/schema/data/repository/spring-repository-1.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.2.xsd">
<context:component-scan base-package="wymysl.Controllers" />
<jpa:repositories base-package="wymysl.repositories"/>
<context:component-scan base-package="wymysl.beans" />
<context:component-scan base-package="wymysl.Validators" />
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean class="org.springframework.orm.hibernate4.HibernateExceptionTranslator"/>
<bean id="passwordValidator" class="wymysl.Validators.PasswordValidator"></bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#localhost:1521:xe" />
<property name="username" value="system" />
<property name="password" value="polskabieda1" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:./META-INF/persistence.xml" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.max_fetch_depth">3</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
</props>
</property>
</bean>
<mvc:annotation-driven />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
</bean>
<bean name="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:resources mapping="/resources/*" location="/resources/css/"
cache-period="31556926"/>
</beans>
RegisterController.java
#Controller
public class RegisterController {
#PersistenceContext
EntityManager entityManager;
#Autowired
PasswordValidator passwordValidator;
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(passwordValidator);
}
#RequestMapping(value = "/addUser", method = RequestMethod.GET)
public String register(Person person) {
return "register";
}
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public String register(#ModelAttribute("person") #Valid #Validated Person person, BindingResult result) {
if(result.hasErrors()) {
return "register";
} else {
entityManager.persist(person);
return "index";
}
}
I had the same problem and I annotated the method as #Transactional and it worked.
UPDATE: checking the spring documentation it looks like by default the PersistenceContext is of type Transaction, so that's why the method has to be transactional (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html):
The #PersistenceContext annotation has an optional attribute type,
which defaults to PersistenceContextType.TRANSACTION. This default is
what you need to receive a shared EntityManager proxy. The
alternative, PersistenceContextType.EXTENDED, is a completely
different affair: This results in a so-called extended EntityManager,
which is not thread-safe and hence must not be used in a concurrently
accessed component such as a Spring-managed singleton bean. Extended
EntityManagers are only supposed to be used in stateful components
that, for example, reside in a session, with the lifecycle of the
EntityManager not tied to a current transaction but rather being
completely up to the application.
I got this exception while attempting to use a deleteBy custom method in the spring data repository. The operation was attempted from a JUnit test class.
The exception does not occur upon using the #Transactional annotation at the JUnit class level.
This error had me foxed for three days, the situation I faced produced the same error. Following all the advice I could find, I played with the configuration but to no avail.
Eventually I found it, the difference, the Service I was executing was contained in a common jar, the issue turned out to be AspectJ not treating the Service instantiation the same. In effect the proxy was simply calling the underlying method without all the normal Spring magic being executed before the method call.
In the end the #Scope annotation placed on the service as per the example solved the issue:
#Service
#Scope(proxyMode = ScopedProxyMode.INTERFACES)
#Transactional
public class CoreServiceImpl implements CoreService {
#PersistenceContext
protected EntityManager entityManager;
#Override
public final <T extends AbstractEntity> int deleteAll(Class<T> clazz) {
CriteriaDelete<T> criteriaDelete = entityManager.getCriteriaBuilder().createCriteriaDelete(clazz);
criteriaDelete.from(clazz);
return entityManager.createQuery(criteriaDelete).executeUpdate();
}
}
The method I have posted is a delete method but the annotations affect all persistence methods in the same way.
I hope this post helps someone else who has struggled with the same issue when loading a service from a jar
boardRepo.deleteByBoardId(id);
Faced the same issue. GOT javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread
I resolved it by adding #Transactional annotation above the controller/service.
You need to add #Transactional to your methode
I had the same error because I switched from XML- to java-configuration.
The point was, I didn't migrate <tx:annotation-driven/> tag, as Stone Feng suggested.
So I just added #EnableTransactionManagement as suggested here
Setting Up Annotation Driven Transactions in Spring in #Configuration Class, and it works now
Adding the org.springframework.transaction.annotation.Transactional annotation at the class level for the test class fixed the issue for me.
I had the same problem and I added tx:annotation-driven in applicationContext.xml and it worked.
I had the same error when accessing an already transactional-annotated method from a non-transactional method within the same component:
Before:
#Component
public class MarketObserver {
#PersistenceContext(unitName = "maindb")
private EntityManager em;
#Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW)
public void executeQuery() {
em.persist(....);
}
#Async
public void startObserving() {
executeQuery(); //<-- Wrong
}
}
//In another bean:
marketObserver.startObserving();
I fixed the error by calling the executeQuery() on the self-referenced component:
Fixed version:
#Component
public class MarketObserver {
#PersistenceContext(unitName = "maindb")
private EntityManager em;
#Autowired
private GenericApplicationContext context;
#Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW)
public void executeQuery() {
em.persist(....);
}
#Async
public void startObserving() {
context.getBean(MarketObserver.class).executeQuery(); //<-- Works
}
}
Just a note for other users searching for answers for thie error. Another common issue is:
You generally cannot call an #transactional method from within the same class.
(There are ways and means using AspectJ but refactoring will be way easier)
So you'll need a calling class and class that holds the #transactional methods.
If you have
#Transactional // Spring Transactional
class MyDao extends Dao {
}
and super-class
class Dao {
public void save(Entity entity) { getEntityManager().merge(entity); }
}
and you call
#Autowired MyDao myDao;
myDao.save(entity);
you won't get a Spring TransactionInterceptor (that gives you a transaction).
This is what you need to do:
#Transactional
class MyDao extends Dao {
public void save(Entity entity) { super.save(entity); }
}
Unbelievable but true.
Without #Transactional annotation you can achieve the same goal with finding the entity from the DB and then removing that entity you got from the DB.
CrudRepositor -> void delete(T var1);
For us, the problem came down to same context settings in multiple configuration files. Check you've not duplicated the following in multiple config files.
<context:property-placeholder location="classpath*:/module.properties"/>
<context:component-scan base-package="...." />
I had the same error code when I used #Transaction on a wrong method/actionlevel.
methodWithANumberOfDatabaseActions() {
methodA( ...)
methodA( ...)
}
#Transactional
void methodA( ...) {
... ERROR message
}
I had to place the #Transactional just above the method methodWithANumberOfDatabaseActions(), of course.
That solved the error message in my case.
I removed the mode from
<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />
to make this work
I already had the #Transactional but still wasn't working. Turns out I had to get rid of parallelism to make it work.
If you are doing things in parallel, DON'T.
I had this issue for days and nothing I found anywhere online helped me, I'm posting my answer here in case it helps anyone else.
In my case, I was working on a microservice being called through remoting, and my #Transactional annotation at the service level was not being picked up by the remote proxy.
Adding a delegate class between the service and dao layers and marking the delegate method as transactional fixed this for me.
This helped us, maybe it can help others in the future. #Transaction was not working for us, but this did:
#ConditionalOnMissingClass("org.springframework.orm.jpa.JpaTransactionManager")
I got the same error when I executed the Spring JPA deleteAll() method from Junit test cases. I simply used the deleteInBatch() & deleteAllInBatch() and its perfectly works. We do not need to mark #Transactional at the test cases level.
For anyone with the same issue as I had, I was calling a public method method1 from within another class.
method1 then called another public method method2 within the same class.
method2 was annotated with #Transactional, but method1 was not.
All that method1 did was transform some arguments and directly call method2, so no DB operations here.
The issue got solved for me once I moved the #Transactional annotation to method1.
Not sure the reason for this, but this did it for me.
Calling the repository method was being called within a class with #Component, taking that method out of that class and placing it inside another with #Service worked.
It's like you are using the shared EntityManager when you are getting it Autowired so for persisting spring tells that this EntityManager bean is a shared bean and for persisting it needs a hold of this bean till the data persist doesn't get completed so for that we have to use #Transactional so that it gonna start and commit the persistence in a transaction so the data or operation gets completely saved or get rollback completely.
To fix this in a test, you can use #DataJpaTest or #AutoConfigureTestDatabase.

EntityManager.merge() is not being committed (Wildfly, JPA, JTA)

I can persist new data, but I cannot do updates. There are no errors, just no transactions committing the changes. I'm assuming this has something to do with the way that I've set up transactions. I'm trying a bunch of relatively new (to me) set of technologies. Below are the details.
I'm using the following tools/technologies:
Wildfly 8 and Java 7 (which is what my hosting service uses)
Annotations, with minimal XML being the goal
Struts 2.3 (using the convention plugin)
Spring 3.2
Hibernate 4.3
JTA (with container managed transactions (CMT))
JPA 2 (with a Container Managed Persistence Context)
EJBs (I have a remote client app that runs htmlunit tests)
Three WAR files and one EJB JAR file deployed
SpringBeanAutowiringInterceptor to autowire the EJBs (could there be an error in here where transactions don't commit?)
beanRefContext.xml (required by SpringBeanAutowiringInterceptor)
<beans>
<bean
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg value="classpath:campaignerContext.xml" />
</bean>
</beans>
campaignerContext.xml
<beans>
<context:component-scan base-package="..." />
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/CampaignerDS"/>
<tx:annotation-driven/>
<tx:jta-transaction-manager/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="campaigner" />
</bean>
<bean id="ehCacheManager" class="net.sf.ehcache.CacheManager" factory-method="create">
<constructor-arg type="java.net.URL" value="classpath:/campaigner_ehcache.xml"/>
</bean>
</beans>
persistence.xml
<persistence>
<persistence-unit name="campaigner" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:/jdbc/CampaignerDS</jta-data-source>
<class>....UserRegistration</class>
...
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
</properties>
</persistence-unit>
</persistence>
SecurityServiceBean.java
#EnableTransactionManagement
#TransactionManagement(value = TransactionManagementType.CONTAINER)
#TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW)
#Stateless
#Interceptors(SpringBeanAutowiringInterceptor.class)
#DeclareRoles("Security Admin")
public class SecurityServiceBean extends AbstractCampaignerServiceImpl implements
SecurityServiceLocal, SecurityServiceRemote
{
#Override
#PermitAll
#Transactional(propagation = Propagation.REQUIRES_NEW)
public UserRegistration confirmRegistration(
String confirmationCode) throws ApplicationException
{
UserRegistration userRegistration = this.userRegistrationDAO
.find(new UserRegistrationQuery(null, confirmationCode)).uniqueResult(); // Should be attached now
...
userRegistration.setConfirmationDate(new Date());
userRegistration.setState(State.CONFIRMED);
userRegistration = this.userRegistrationDAO.saveOrUpdate(userRegistration);
...
}
}
UserRegistrationDAO.java
#Override
public UserRegistration saveOrUpdate(
UserRegistration obj) throws DAOException
{
log.debug("[saveOrUpdate] isJoinedToTransaction? "
+ (this.em.isJoinedToTransaction() ? "Y " : "N"));
try
{
if (obj.getId() == null)
{
this.em.persist(obj);
log.debug("[saveOrUpdate] called persist()");
return obj;
}
else
{
UserRegistration attached = this.em.merge(obj);
log.debug("[saveOrUpdate] called merge()");
return attached;
}
}
catch (PersistenceException e)
{
throw new DAOException("[saveOrUpdate] obj=" + obj.toString() + ",msg=" + e.getMessage(), e);
}
}
Are there any settings in Wildfly's standalone.xml that you need to see or that I should be setting?
BTW, this is incredibly annoying and frustrating. This should be an easy one-time setup that I can do and then forget about as I move on to creating my website, which should be where most of my time is spent. The lack of comprehensive documentation anywhere is AMAZING. Right now, development has been halted until this is solved
/rant
UPDATES
I tried switching to an XA data source, because some sites claimed that was necessary, but that didn't work (didn't think so but had to try). Also tried configuring emf with dataSource instead of persistenceUnitName as some other sites have. No joy.
I tried replacing the transactionManager with JpaTransactionManager, but that just led to this exception: A JTA EntityManager cannot use getTransaction()
The answer, thanks to M. Deinum, is that I was using the wrong #Transactional. I should have been using javax.transaction.Transactional but was using the Spring one instead. Note that the correct one will look like "#Transactional(TxType.REQUIRES_NEW)" instead of "#Transactional(propagation = Propagation.REQUIRES_NEW)"

hibernate doesn't issue update after flush

I'm using hibernate 3.2.7 (same problem on 3.2.5) with spring 3.0.1, all deployed on weblogic 10.3 and with an Oracle 10g database. I'm using JTA transaction management and the transaction is distributed (it is actually started and ended in another application, this code is just in between).
The configuration used by hibernate is declared in my persistence.xml and is the following:
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.WeblogicTransactionManagerLookup"/>
<property name="hibernate.query.factory_class" value="org.hibernate.hql.classic.ClassicQueryTranslatorFactory"/>
<property name="hibernate.current_session_context_class" value="jta"/>
<property name="hibernate.connection.release_mode" value="auto"/>
The spring configuration regarding the transaction manager is the following:
<!-- Instructs Spring to perfrom declarative transaction managemenet on annotated classes -->
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
<!-- Data about transact manager and session factory -->
<bean id="txManager" class="org.springframework.transaction.jta.WebLogicJtaTransactionManager">
<property name="transactionManagerName" value="javax.transaction.TransactionManager"/>
<property name="defaultTimeout" value="${app.transaction.timeOut}"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- persistence unit is missing jta data source so that application server is not
creating EntitiyManagerFactory, spring will create its own LocalContainerEntityManagerFactoryBean overriding data source-->
<property name="dataSource" ref="myDataSource"/>
<!-- specific properties like jpa provider and jpa provider properties are in persistance unit -->
<property name="persistenceUnitName" value="my.persistence.unit"/>
</bean>
<!-- define data source in application server -->
<jee:jndi-lookup id="myDataSource" jndi-name="${db.jndiName}"/>
I'm using a generic CrudDao with an update method that looks like this:
public void update(Object entity) {
//entityManager injected by #PersistenceContext
entityManager.merge(entity);
entityManager.flush();
}
public Object getById(Object id, Class entityClass) throws PersistenceException{
return (Object)entityManager.find(entityClass, id);
}
UPDATED: added the getById method.
The code that does not work as expected looks like this:
MyObject myObj = getMyObjectThroughSomeOneToManyRelation(idOne, idOther);
// till now was null
myObj.setSomeDateAttr(someDate);
genericDao.update(myObj);
MyObject myObjFromDB = genericDao.getById(myObj.getId(), MyObject.class);
The result is that if I print myObj.getSomeDateAttr() it returns me the value of someDate, if I print myObjFromDB.getSomeDateAttr() it still has null.
I've tried changing the update method to:
org.hibernate.Session s = (org.hibernate.Session) entityManager.getDelegate();
s.evict(entity);
s.update(entity);
s.flush();
And it still doesn't work.
When turning on the show_sql flag of hibernate I don't see any update occurring when doing flush nor when I query the entity manager for the object with the same id. The selects are all visible.
UPDATE:
At the end of the transaction the update is actually called and everything is written to the db. So my problem is "just" during the transaction.
I'm afraid the problem may be linked with the configuration of the transaction manager on spring and on hibernate.
Hope that someone can help me as I have already lost a day and a half with no luck.
You need to look at the hibernate merge behaviour closely. As per documentation
if there is a persistent instance with the same identifier currently
associated with the session, copy the state of the given object onto
the persistent instance
if there is no persistent instance currently associated with the session, try to load it from the database, or create a new persistent instance
the persistent instance is returned
the given instance does not become associated with the session, it
remains detached
As per your statement on the sql queries in log, it look like
MyObject myObj = getMyObjectThroughSomeOneToManyRelation(idOne, idOther); returning the persistent object but when you modify it(becomes dirty) and call merge method, new state is copied to the current persistent object in session. If you see third point merge returns persistent object which is actually new manageable persistent object which you need to use in subsequent operations.
When you call find method hibernate returns the persistent object in session and not maneagable persistent object thats why you dont find the changes in object return by find.
To fix your problem change the reurn type of update method
public Object update(Object entity) {
//entityManager injected by #PersistenceContext
return entityManager.merge(entity);
}
and in service you need to use as below
MyObject myObj = getMyObjectThroughSomeOneToManyRelation(idOne, idOther);
// till now was null
myObj.setSomeDateAttr(someDate);
//You can use myObj as well instead myNewObj
MyObject myNewObj= genericDao.update(myObj);
//No need to call get
//MyObject myObjFromDB = genericDao.getById(myObj.getId(), MyObject.class);
System.out.println("Updated value:"+myNewObj.getSomeDateAttr());
Have a look at this artical as well.

Programmatically loading Entity classes with JPA 2.0?

With Hibernate you can load your Entity classes as:
sessionFactory = new AnnotationConfiguration()
.addPackage("test.animals")
.addAnnotatedClass(Flight.class)
.addAnnotatedClass(Sky.class)
.addAnnotatedClass(Person.class)
.addAnnotatedClass(Dog.class);
Is there a way to do the same thing - programmatically loading your Entity classes - in a JPA 2.0 compliant way?
The reason for this question is because I'd like to dynamically load my Entity classes, thus not necessarily programmatically.
With the help of Spring I did this in a JPA compliant way.
My "persistence.xml" looks empty, with no entities listed within the <persistence-unit> element.
I then wrote a class that implemented PersistenceUnitPostProcessor like so:
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import org.reflections.Reflections;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
public class ReflectionsPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor {
private String reflectionsRoot;
private Logger log = LoggerFactory.getLogger(ReflectionsPersistenceUnitPostProcessor.class);
#Override
public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
Reflections r = new Reflections(this.reflectionsRoot, new TypeAnnotationsScanner());
Set<String> entityClasses = r.getStore().getTypesAnnotatedWith(Entity.class.getName());
Set<String> mappedSuperClasses = r.getStore().getTypesAnnotatedWith(MappedSuperclass.class.getName());
for (String clzz : mappedSuperClasses)
{
pui.addManagedClassName(clzz);
}
for (String clzz : entityClasses)
{
pui.addManagedClassName(clzz);
}
}
public String getReflectionsRoot() {
return reflectionsRoot;
}
public void setReflectionsRoot(String reflectionsRoot) {
this.reflectionsRoot = reflectionsRoot;
}
}
Then I adjusted my spring context xml like this:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
</bean>
</property>
<property name="persistenceUnitName" value="GenericPersistenceUnit"/>
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml"/>
<property name="persistenceUnitPostProcessors">
<list>
<bean class="com.austinmichael.core.repository.ReflectionsPersistenceUnitPostProcessor">
<property name="reflectionsRoot" value="com.austinmichael"/>
</bean>
</list>
</property>
</bean>
Note the registration of the ReflectionsPersistenceUnitPostProcessor in the persistenceUnitPostProcessors setting.
And that's it. Every class with a JPA Entity or MappedSuperclass annotation on the classpath is added to the classpath. I had to give reflections the prefix of a package name to scan through which is why com.austinmichael is there at all. You could register a second ReflectionsPersistenceUnitPostProcessor with a different package name prefix if you want if your entities don't share a common package name prefix.
But, this is now JPAVendor agnostic.
Is there a way to do the same thing - programmatically loading your Entity classes - in a JPA 2.0 compliant way?
No, this is not supported by JPA so you'll have to do this in a provider specific way. James Sutherland described the process for EclipseLink in this thread like this:
You can access the EclipseLink ServerSession from the EntityManagerFactoryImpl (getServerSession()), and use its' addDescriptor(ClassDescriptor) or addDescriptors() API to add EclipseLink ClassDescriptor. You will need to build the ClassDescriptor meta-data objects directly yourself (or use the Mapping Workbench to create them), as loading from JPA annotations or orm.xml would be more difficult.
Also have a look at this more recent thread for more code sample (the API looks like a bit verbose).
References
Re: [eclipselink-users] Close to get it. Just a little help
Re: JPA: adding entities to EntityManagerFactory programmatically
I don't think there is such option - JPA supports scanning the classpath for entities or explicitly listing the entity classes in the persistence.xml. Since you're using hibernate as the persistence provider however you can always resort to hibernate code. Have a look at the HibernateEntityManager and HibernateEntityManagerFactory classes. You can cast entity manager factories and entity managers to them and do the usual hibernate stuff.

Categories