Auditing db-rollbacks - java

I'm wondering if there is a build-in/standard way to audit db-rollbacks in an Spring (3.1), Hibernate/JPA environment.
Greatful for any hints.
Thanks
Jonny

I thing enabling "hibernate.show_sql" and using the logging configuration
log4j.logger.org.hibernate.SQL=DEBUG, SQL_APPENDER
log4j.additivity.org.hibernate.SQL=false
should help you log SQL statements.

If you want to monitor rollbacks you can setup logging for org.hibernate.transaction package
13:50:28,597 DEBUG http-8080-1 org.hibernate.transaction.JDBCTransaction - begin
13:50:28,657 DEBUG http-8080-1 org.hibernate.transaction.JDBCTransaction - current autocommit status: true
13:50:28,657 DEBUG http-8080-1 org.hibernate.transaction.JDBCTransaction - disabling autocommit
13:50:28,714 DEBUG http-8080-1 org.hibernate.transaction.JDBCTransaction - commit
13:50:28,773 DEBUG http-8080-1 org.hibernate.transaction.JDBCTransaction - re-enabling autocommit
13:50:28,828 DEBUG http-8080-1 org.hibernate.transaction.JDBCTransaction - committed JDBC Connection
If you want to take some action upon transaction rollback, you can use AOP to create an aspect and map it to org.hibernate.Transaction.rollback() method execution.
#Aspect
public class TransactionMonitoringAspect {
#Before("execution(* org.hibernate.Transaction.rollback())")
public void deviceLoad(String deviceSerial) {
//do something here
}
}
you can use various annotations from org.aspectj.lang.annotation package to execute various methods around rollback() method.

Related

Spring opens a new transaction for each JpaRepository method that is called within an #Transactional annotated method

I have a method that is annotated with #Transactional. That should mean that any database queries that are fired within this method should all use the same transaction. But in reality that doesn't happen. What does happen is that a transaction is opened for the method itself but then when the first JpaRepository method is called a new transaction is opened for that particular method call.
To make matters more complex, For custom repository methods this new transaction is only opened when the JpaRepository or the JpaRepository custom method is annotated with #Transactional as well.
If not i get the following trace log statement about it:
No need to create transaction for
[org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByIdNotNull]:
This method is not transactional.
So it doesn't create a new transaction but it also doesn't seem to use the transaction created by the calling method either.
Heres the repository class:
#Repository
public interface LanguageDao extends JpaRepository<Language, Long> {
#Transactional
public Language findByLanguageCode(String languageCode);
public Language findByIdNotNull();
}
Heres the method that uses different repository methods.
#Transactional
public void afterSingletonsInstantiated() {
languageDao.findByLanguageCode(); //This custom method opens a new transaction, but only because i've annotated this method with #Transactional as well.
languageDao.findAll(); //This one as well because its a standard JpaRepository method.
languageDao.findByIdNotNull();//This custom method doesn't because it lacks its own #Transactional annotation.
}
Heres the #Configuration file, with transaction management and jpa repositories enabled
#EnableJpaRepositories(basePackages={"DAOs"}, transactionManagerRef = "customTransactionManager", enableDefaultTransactions = true)
#EnableTransactionManagement
#Configuration
public class RootConfig implements InitializingBean {
#Bean(name = "customTransactionManager")
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
if (shouldCreateInitialLuceneIndex) {
EntityManager entityManager = entityManagerFactory.createEntityManager();
createInitialLuceneIndex(entityManager);
entityManager.close();
}
return transactionManager;
}
}
Relevant application.properties settings
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.database-platform = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.open-in-view = false
A bit of actual logs. The first line shows that a transaction for the method afterSingletonsInstantiated is created.
[TRACE] 2021-11-08 15:32:40.811 [main] TransactionInterceptor - Getting transaction for [config.StartupChecks$$EnhancerBySpringCGLIB$$134b7631.afterSingletonsInstantiated]
[INFO ] 2021-11-08 15:32:40.815 [main] StartupChecks - Calling sequence table reset procedure
[DEBUG] 2021-11-08 15:32:40.833 [main] SQL - {call RESET_SEQUENCE_TABLE_VALUES_TO_LATEST_ID_VALUES()}
[INFO ] 2021-11-08 15:32:41.087 [main] StartupChecks - Sequence tables reset call finished!
[INFO ] 2021-11-08 15:32:41.087 [main] StartupChecks - doing stuff
[INFO ] 2021-11-08 15:32:41.087 [main] StartupChecks - testing!
[TRACE] 2021-11-08 15:32:41.087 [main] TransactionInterceptor - Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
[DEBUG] 2021-11-08 15:32:41.088 [main] SQL - select language0_.id as id1_77_, language0_.dateCreated as datecrea2_77_, language0_.englishLanguageName as englishl3_77_, language0_.languageCode as language4_77_, language0_.rightToLeft as righttol5_77_, language0_.translatedLanguageName as translat6_77_ from languages language0_
[TRACE] 2021-11-08 15:32:41.091 [main] TransactionInterceptor - Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
[INFO ] 2021-11-08 15:32:41.091 [main] StartupChecks - end test!
[TRACE] 2021-11-08 15:32:41.091 [main] TransactionInterceptor - Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByLanguageCode]
[DEBUG] 2021-11-08 15:32:41.112 [main] SQL - select language0_.id as id1_77_, language0_.dateCreated as datecrea2_77_, language0_.englishLanguageName as englishl3_77_, language0_.languageCode as language4_77_, language0_.rightToLeft as righttol5_77_, language0_.translatedLanguageName as translat6_77_ from languages language0_ where language0_.languageCode=?
[TRACE] 2021-11-08 15:32:41.113 [main] TransactionInterceptor - Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByLanguageCode]
[TRACE] 2021-11-08 15:32:41.113 [main] TransactionInterceptor - No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByIdNotNull]: This method is not transactional.
[DEBUG] 2021-11-08 15:32:41.115 [main] SQL - select authority0_.ID as id1_7_, authority0_.dateCreated as datecrea2_7_, authority0_.NAME as name3_7_ from AUTHORITY authority0_ where authority0_.ID is not null limit ?
[TRACE] 2021-11-08 15:32:41.120 [main] TransactionInterceptor - No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findFirstByIdNotNull]: This method is not transactional.
Here is a list of the things that i've already tried.
Annotate languageDao with #Transactional(propagation = Propagation.SUPPORTS) or
#Transactional(propagation = Propagation.NESTED). NESTED isn't supported by hibernate and thus this causes an error, This error remains even when i set nestedTransactionAllowed to true on the transactionmanager. The setting SUPPORTS is ignored. The repository still starts a new transaction for each method that is called. (Update: Propagation.MANDATORY has no effect either)
I've named my transactionmanager customTransactionManager and added this as a parameter to #EnableJpaRepositories like so: #EnableJpaRepositories(basePackages={"DAOs"}, transactionManagerRef = "customTransactionManager")
I've set enableDefaultTransactions of #EnableJpaRepositories to false. This causes default methods like findAll() and save() to no longer be executed in a transaction by default. However it doesn't force them to use the transaction of the calling method that was annotated with #Transactional.
So my question is: How do i make the (custom) jpa repositories use the transaction that was started by the calling method?
EDIT: Here JPA - Spanning a transaction over multiple JpaRepository method calls a similar problem is described. According to the user spring only uses the existing transaction when the repository implements Repository instead of CrudRepository or JpaRepository. But this is a workaround.
EDIT 2: My #Transactional annotations keep working when i remove #EnableTransactionManagement. According to this post that can occur when i use spring-boot-starter-jdbc or spring-boot-starter-data-jpa as a dependency, which i do. Could these dependencies somehow interfere with the normal working of the transaction manager?
Here is my attempt at understanding your problem. I would recommend enabling extra debug
logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG
My test Service class - note that this is marked as transactional - for now that's the only place it is put as that's what we intend - to create a transactional boundary.
#Service
public class LanguageService {
#Autowired
private LanguageRepository languageRepository;
#Transactional
public void runAllMethods() {
languageRepository.findByLanguageCode("en");
languageRepository.findAll();
languageRepository.findByIdNotNull();
}
}
Next is the repository - there are no transactional annotations.
public interface LanguageRepository extends JpaRepository<Language, Long> {
public Language findByLanguageCode(String languageCode);
public Language findByIdNotNull();
}
Now on hitting the service via a controller - I get below logs. Notice the line where it says "Creating new transaction with name [com.shailendra.transaction_demo.service.LanguageService.runAllMethods]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT" - meaning that the transaction was created at the beginning of method invocation.
Also note the statement "Participating in existing transaction" which indicates that method is participating in transaction.
2021-11-09 11:43:06.061 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(2084817241<open>)] for JPA transaction
2021-11-09 11:43:06.061 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [com.shailendra.transaction_demo.service.LanguageService.runAllMethods]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2021-11-09 11:43:06.069 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle#3107a702]
2021-11-09 11:43:06.069 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor : Getting transaction for [com.shailendra.transaction_demo.service.LanguageService.runAllMethods]
2021-11-09 11:43:06.099 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByLanguageCode]: This method is not transactional.
Hibernate: select language0_.id as id1_0_, language0_.date_created as date_cre2_0_, language0_.english_language_name as english_3_0_, language0_.language_code as language4_0_, language0_.right_to_left as right_to5_0_, language0_.translated_language_name as translat6_0_ from language language0_ where language0_.language_code=?
2021-11-09 11:43:06.333 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(2084817241<open>)] for JPA transaction
2021-11-09 11:43:06.333 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Participating in existing transaction
2021-11-09 11:43:06.333 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
Hibernate: select language0_.id as id1_0_, language0_.date_created as date_cre2_0_, language0_.english_language_name as english_3_0_, language0_.language_code as language4_0_, language0_.right_to_left as right_to5_0_, language0_.translated_language_name as translat6_0_ from language language0_
2021-11-09 11:43:06.348 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]
2021-11-09 11:43:06.348 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findByIdNotNull]: This method is not transactional.
Hibernate: select language0_.id as id1_0_, language0_.date_created as date_cre2_0_, language0_.english_language_name as english_3_0_, language0_.language_code as language4_0_, language0_.right_to_left as right_to5_0_, language0_.translated_language_name as translat6_0_ from language language0_ where language0_.id is not null
2021-11-09 11:43:06.348 TRACE 24956 --- [nio-8181-exec-1] o.s.t.i.TransactionInterceptor : Completing transaction for [com.shailendra.transaction_demo.service.LanguageService.runAllMethods]
2021-11-09 11:43:06.348 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Initiating transaction commit
2021-11-09 11:43:06.348 DEBUG 24956 --- [nio-8181-exec-1] o.s.orm.jpa.JpaTransactionManager : Committing JPA transaction on EntityManager [SessionImpl(2084817241<open>)]
For readonly methods - like findAll - you would see "No need to create transaction" - that's because although the default Repository implementation "SimpleJpaRepository" is marked as transactional - the readonly methods are not marked transactional.
#Repository
#Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
After having tried different things, including using a TransactionTemplate i've settled for the following solution:
First i've turned off the default transaction policy for the jparepository methods by annotating a configuration class with the following:
#EnableJpaRepositories(enableDefaultTransactions = false)
enableDefaultTransactions = false causes any inherited method of JpaRepository to stop creating a transaction whenever they're called. Only jpa methods that are explicitly annotated with #Transactional will continue to create a new transaction when called.
All the other ones will now use any transaction that is started by the calling method, for instance a service method that is annotated with #Transactional.
This isn't obvious though because the This method is not transactional log trace message will still be generated for any jpa method that isn't explicitly annotated with #Transactional. This can be a bit confusing.
However i've proven that these methods really do use the transaction of the calling method by testing it with the following custom update method.
#Modifying
#Query("UPDATE User u SET u.userStatus = 1 WHERE u.userStatus = 0")
public void resetActiveUserAccountsToStatusOffline();
Such a method needs to have a transaction or else the exception javax.persistence.TransactionRequiredException: Executing an update/delete query is thrown. But as you can see this jpa method wasn't annotated with #Transactional so it really did use the transaction that was started by the calling service method.
There is one small disadvantage to setting enableDefaultTransactions = false and that is that the transaction type of inherited methods like findAll will not always use a transaction that is read only. This really depends on whether the service level transaction is readonly or not. However you could still override the findAll method and explictly annotate it with Transactional(readOnly = false). Another thing to beware of is that any calling method must always be annotated with #Transactional or the jpa method will run outside a transaction.
I think the advantage far outweighs these small disadvantages though. Because it is very costly performance wise when a new transaction is created for every jpa method call. So this is the solution i'll settle for right now.
To test your own transactions you'll need to add this to your application.properties
logging.level.org.springframework.transaction.interceptor=TRACE
If the setting doesn't work please add Log4j2 to your project.
EDIT:
These additional transactions that are opened by the JpaMethods are only logical transactions when a physical transaction has already been created by the calling method. More about this here: https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction
These jpa methods still use the transaction created by the calling method.
The last answer in this SO thread also explains the difference between logical and physical transactions well: Difference between physical and logical transactions in spring
Experienced same issue while having multiple datasources, hence multiple transaction managers. Apparently the problem was that service methods marked #Transactional used the primary transaction manager, while the repositories were configured to use custom transaction manager:
#EnableJpaRepositories(
basePackageClasses = {
MyRepository.class
},
entityManagerFactoryRef = "customEntityManager",
transactionManagerRef = "customTransactionManager"
)
Solved the issue using spring's annotation on service methods with transactionManager param specified #Transactional(transactionManager = "customTransactionManager")

Play 2.5 - #Transactional readOnly ignored?

I have a Java Play 2.5 application, and I have a transaction marked as readonly (at the Controller level):
#Transactional(readOnly = true)
public Result list() { ...
(this is the play.db.jpa.Transactional annotation).
So, if I turn on logging for hibernate transaction:
<logger name="org.hibernate.engine.transaction" level="TRACE"/>
Then I see the transaction being made and being committed.
019-12-03 23:42:23,754 [debug] from org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator in main - No JtaPlatform was specified, checking resolver
2019-12-03 23:42:23,755 [debug] from org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformResolverInitiator in main - No JtaPlatformResolver was specified, using default [org.hibernate.engine.transaction.jta.platform.internal.StandardJtaPlatformResolver]
2019-12-03 23:42:23,766 [debug] from org.hibernate.engine.transaction.jta.platform.internal.StandardJtaPlatformResolver in main - Could not resolve JtaPlatform, using default [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2019-12-03 23:58:46,769 [debug] from org.hibernate.engine.transaction.internal.TransactionImpl in application-akka.actor.default-dispatcher-5 - begin
2019-12-03 23:58:46,878 [error] from application in application-akka.actor.default-dispatcher-5 - Transaction: org.hibernate.engine.transaction.internal.TransactionImpl#3d505ddd[transactionCoordinator=org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl#89a1241,exceptionConverter=org.hibernate.internal.ExceptionConverterImpl#76f0443c,transactionDriverControl=org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl#49f1518a]
2019-12-03 23:59:27,638 [debug] from org.hibernate.engine.transaction.internal.TransactionImpl in application-akka.actor.default-dispatcher-5 - committing
However, I have one problem - it's not read-only! I've added some code that modifies an object (obj.setField("changed value")), and it's saved into the database at the end of the transaction!
I was expecting an exception telling me that the transaction is read-only and that it cannot be committed!
Why it the transaction not read-only?
Can I somehow see in logs if Hibernate creates the transaction readonly or not?
Other info: Java Play 2.5.10, Hibernate 5 (5.2.6), and PostgreSQL as the DB.
So, it seems that it was because the controller class itself was annotated with #Transactional, and the method with #Transactional(readOnly=true).
Somehow, the class annotation overrides the method annotation. This doesn't seem logical to me, but in practice, that seems to be the case: as soon as I've removed the #Transactional from the class level, it worked just fine.

Transaction required exception JPA / Spring

I have a method in the repository class marked as #Transactional, the aspect is being executed as seen in the stacktrace, but the exception being thrown is "Transaction required exception"
I changed the #Repository annotation to #Component (and it seemd like it fixed this problem in some situations), but it is still happening on the web role.
Here is the stacktrace:
2015-04-13 08:00:56,497 [http-nio-8080-exec-9] WARN es.mycompany.util.filters.MyFilter - Error storing : /admin/online/update
org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:410)
at org.springframework.orm.jpa.aspectj.JpaExceptionTranslatorAspect.ajc$afterThrowing$org_springframework_orm_jpa_aspectj_JpaExceptionTranslatorAspect$1$18a1ac9(JpaExceptionTranslatorAspect.aj:37)
at es.mycopmany.dao.MyDAO.updateLastUpdatedTs_aroundBody2(MyDAO.java:36)
at es.mycopmany.dao.MyDAO$AjcClosure3.run(MyDAO.java:1)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96cproceed(AbstractTransactionAspect.aj:66)
at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect$1.proceedWithInvocation(AbstractTransactionAspect.aj:72)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(AbstractTransactionAspect.aj:70)
at es.mycompany.dao.MyDAO.updateLastUpdatedTs(MyDAO.java:31)
And here is the code throwing the exception:
#Transactional
public void updateLastUpdatedTs(String id, Calendar date) {
Query query = entityManager.createQuery("update MyEntity set lastUpdatedTs = :ts "
+ " where id= :id");
query.setParameter("ts", date);
query.setParameter("id", id);
query.executeUpdate();
}
Transactional annotation comes from org.springframework.transaction.annotation.Transactional
Versions:
Spring: 4.1.5.RELEASE
Hibernate: 4.3.8.Final
Aspectj: 1.8.5
Tomcat 8.0.20
Configurations:
EMF:
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
id="entityManagerFactory">
<property name="persistenceUnitName" value="athena" />
<property name="dataSource" ref="dataSource" />
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
</property>
</bean>
Transactions:
<bean class="org.springframework.orm.jpa.JpaTransactionManager"
id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager" />
I am going truly nuts with this, any help would be great.
As a note, this all works perfectly fine on my development environment (Windows, Idea Tomcat 8, JDK 8.0.31 (Oracle's), but it raises this error on Amazon EC2 Elasticbeanstalk (Tomcat 8, 64bit Amazon Linux 2015.03, Open JDK 8.0.31 (Tried to use 8.0.40 from Oracle as well)
Edit: A bit more info: The exception is thrown on a Filter, at the end of the whole filter chain.
Here is some debug info before the exception:
2015-04-13 14:57:48,578 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Creating new transaction with name [MyService.myMethod]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2015-04-13 14:57:48,578 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Opened new EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#33f67ee5] for JPA transaction
2015-04-13 14:57:48,580 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle#3112368a]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.jdbc.datasource.ConnectionHolder#3193771b] for key [HikariDataSource (HikariPool-1)] to thread [http-bio-8080-exec-7]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.orm.jpa.EntityManagerHolder#497d4e44] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#5019da97] to thread [http-bio-8080-exec-7]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.aspectj.AnnotationTransactionAspect - Getting transaction for [MyService.myMethod]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder#497d4e44] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#5019da97] bound to thread [http-bio-8080-exec-7]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#33f67ee5] for JPA transaction
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder#3193771b] for key [HikariDataSource (HikariPool-1)] bound to thread [http-bio-8080-exec-7]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Participating in existing transaction
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] TRACE org.springframework.transaction.aspectj.AnnotationTransactionAspect - Getting transaction for [MyDao.updateLastUpdatedTs]
2015-04-13 14:57:48,581 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Opening JPA EntityManager
2015-04-13 14:57:48,582 [http-bio-8080-exec-7] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Registering transaction synchronization for JPA EntityManager
2015-04-13 14:57:48,582 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.orm.jpa.EntityManagerHolder#4f83552c] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#7cc8111c] to thread [http-bio-8080-exec-7]
2015-04-13 14:57:48,608 [http-bio-8080-exec-7] TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder#4f83552c] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#7cc8111c] bound to thread [http-bio-8080-exec-7]
2015-04-13 14:57:48,608 [http-bio-8080-exec-7] TRACE org.springframework.transaction.aspectj.AnnotationTransactionAspect - Completing transaction for [MyDao.updateLastUpdatedTs] after exception: org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
Which actually says, it has created the Transaction, it then joined the transaction (There are two #Transactionals now, one at the service layer, and the other at the DAO layer), and then it rollsback the transaction, due to an exception "Transaction required".
This is nuts.
EDIT
Well, I found this line of debug:
2015-04-13 15:27:44,074 [http-bio-8080-exec-2] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Participating transaction failed - marking existing transaction as rollback-only
The values here are: propagation=REQUIRED, isolation=DEFAULT
Seems that, there is a transaction, which was checked as completed, and joining the transaction failed, so it marks it as rollback only, because it could not join it.
I changed the annotation-driven config, just by adding proxy-target-class="true" seems to have fixed the issue in one of our environments (ap-southeast) which is Amazon Shanghai, but as for Europe (eu-west), the problem is still happening. This is a nightmare, all the configurations are exactly the same (it just points to different db & s3)
<tx:annotation-driven mode="aspectj" proxy-target-class="true" transaction-manager="transactionManager" />
SOLUTION:
I finally got something, after all. This fixes it (at least apparently).
Reason:
Apparently it as something to do with spring initialization, and scheduling some tasks before the initialization has finished, and something got messed up.
I set the transactional annotation at the service layer with a REQUIRES_NEW propagation, to force a new transaction to be created.
#Transactional(propagation = Propagation.REQUIRES_NEW)
Removed the #Transactional from the DAO layer.
I also had to make some changes to the connector, incrementing maxThreads and max/min spare threads.
I also changed all my #Scheduled initialization tasks to start 10 minutes after tomcat start
After all this changes, the error went away.
Notes
I also removed the previous change: "proxy-target-class="true"", and it is still working fine, so that weren't really a good fix here, but it might work for you as it did for me on some cases (background tasks).
As as side note, the other change that I had to do to make this work, was to change #Repository to #Component, as some transactions weren't doing writes to the DB on scheduled tasks.
Not an expert in spring, but hope this helps.
Some days ago, reading Spring documentation for a similar issue, I've found:
In particular, you do not need an application server simply for declarative transactions through EJBs. In fact, even if your application server has powerful JTA capabilities, you may decide that the Spring Framework’s declarative transactions offer more power and a more productive programming model than EJB CMT.
AFAIK, in our services, we declare transactions more specific to avoid some of this problems like:
#Transactional
(
propagation = Propagation.REQUIRED,
readOnly = false,
rollbackFor = Throwable.class
)
If your annotations works ONLY in some servers, try to be specific when declaring it in order to cover your transaction scenario. In this way I guess you will achieve same behaviour in all servers.
I just had the very same problem. Turned out, that I tried to use the class-wide defined EntityManager in a background thread created on the fly, and that caused the exception. There were no other error messages regarding this issue, and the stacktrace pointed to query.executeUpdate(), so it was a bit difficult to sort this out. Going back to serial processing made the error vanish.
I think the issue has been solved in latest versions of spring. Just adding the #Transactional annotation on top of my service class worked for me.

Prevent closing ResultSet on transaction commit

I must preserve a ResultSet which was opened within a #Transactional Controller, to be consumed within a MessageConverter. To that end I have configured the following:
MVC Interceptor:
<mvc:interceptors>
<bean class="org.springframework.orm.hibernate4.support.OpenSessionInViewInterceptor"
p:sessionFactory-ref="sessionFactory"/>
</mvc:interceptors>
on the SessionFactory bean:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
<property name="hibernateProperties">
<props>
<prop key="hibernate.connection.release_mode">on_close</prop>
....
</props>
</property>
</bean>
within the Controller method:
session.doWork((con) -> { con.setHoldability(HOLD_CURSORS_OVER_COMMIT); });
Yet the PSQLException: This ResultSet is closed. persists. This is a relevant snippet in the log when the transaction commits upon controller method return:
TRACE o.h.e.j.i.JdbcCoordinatorImpl - Registering result set [org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet#5a63c2aa]
DEBUG o.h.e.t.s.AbstractTransactionImpl - committing
TRACE o.h.internal.SessionImpl - Automatically flushing session
TRACE o.h.internal.SessionImpl - before transaction completion
DEBUG o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection
DEBUG o.h.e.t.i.j.JdbcTransaction - re-enabling autocommit
TRACE o.h.e.t.i.TransactionCoordinatorImpl - after transaction completion
TRACE o.h.internal.SessionImpl - after transaction completion
TRACE o.h.internal.SessionImpl - Setting flush mode to: MANUAL
DEBUG o.h.internal.SessionImpl - Disconnecting session
TRACE o.h.e.j.i.JdbcCoordinatorImpl - Releasing JDBC container resources [org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl#97bfa5f]
TRACE o.h.e.j.i.JdbcCoordinatorImpl - Closing result set [org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet#5a63c2aa]
TRACE o.h.e.j.i.JdbcCoordinatorImpl - Closing prepared statement [select...]
DEBUG o.h.e.j.i.LogicalConnectionImpl - Releasing JDBC connection
DEBUG o.h.e.j.i.LogicalConnectionImpl - Released JDBC connection
DEBUG o.h.e.j.s.SqlExceptionHelper - could not advance using next() [n/a] org.postgresql.util.PSQLException: This ResultSet is closed.
Is there something more I can do to stop this from happening?
By default Spring will manage the Hibernate session on its own. One of the consequences, as documented in the HibernateTransactionManager Javadoc, is that Spring will explicitly call session.disconnect(), which will invalidate your connection.release_mode=on_close setting. To change this behavior to Hibernate-managed sessions, be sure to set the hibernateManagedSession property on HibernateTransactionManager to true:
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:hibernateManagedSession="true" />
Doing it this way is bound to disturb certain mechanisms which assume the default behavior. One of them is transaction management via the TransactionTemplate: no Hibernate session will be automatically created. This can be fixed by explicitly binding a session to the current thread, and is best captured in a subclass of TransactionTemplate:
#Component
public class HibernateTransactionTemplate extends TransactionTemplate
{
#Autowired private SessionFactory sf;
#Autowired #Override public void setTransactionManager(PlatformTransactionManager txm) {
super.setTransactionManager(txm);
}
#Override public <T> T execute(TransactionCallback<T> action) throws TransactionException {
final Session ses = sf.openSession();
TransactionSynchronizationManager.bindResource(sf, new SessionHolder(ses));
try { return super.execute(action); }
finally {
ses.close();
TransactionSynchronizationManager.unbindResource(sf);
}
}
}

Hibernate Hangs on saveOrUpdate

I am doing a standart getHibernateTemplate().saveOrUpdate() in Spring Hibernate application, but function never returns and does not print any errors.
Debug log is like below.
19:06:07.014 [qtp8540084-26] DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'save' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
19:06:07.014 [qtp8540084-26] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'transactionManager'
19:06:07.014 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Found thread-bound Session [org.hibernate.impl.SessionImpl#13ca565] for Hibernate transaction
19:06:07.014 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Creating new transaction with name [com.mydao.InventoryDAOImpl.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
19:06:07.014 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl#13ca565]
19:06:07.014 [qtp8540084-26] DEBUG o.h.transaction.JDBCTransaction - begin
19:06:07.014 [qtp8540084-26] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection
19:06:07.014 [qtp8540084-26] DEBUG o.s.j.d.DriverManagerDataSource - Creating new JDBCDriverManager Connection to [jdbc:hsqldb:file:hsqldb/MyDB]
19:06:07.014 [qtp8540084-26] DEBUG o.h.transaction.JDBCTransaction - current autocommit status: true
19:06:07.014 [qtp8540084-26] DEBUG o.h.transaction.JDBCTransaction - disabling autocommit
19:06:07.014 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [org.hsqldb.jdbc.jdbcConnection#65335b]
19:06:07.030 [qtp8540084-26] DEBUG o.s.orm.hibernate3.HibernateTemplate - Found thread-bound Session for HibernateTemplate
19:06:07.030 [qtp8540084-26] DEBUG o.s.orm.hibernate3.HibernateTemplate - Not closing pre-bound Hibernate Session after HibernateTemplate
19:06:07.030 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Initiating transaction rollback
19:06:07.030 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Rolling back Hibernate transaction on Session [org.hibernate.impl.SessionImpl#13ca565]
19:06:07.030 [qtp8540084-26] DEBUG o.h.transaction.JDBCTransaction - rollback
19:06:07.030 [qtp8540084-26] DEBUG o.h.transaction.JDBCTransaction - re-enabling autocommit
19:06:07.030 [qtp8540084-26] DEBUG o.h.transaction.JDBCTransaction - rolled back JDBC Connection
19:06:07.030 [qtp8540084-26] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
19:06:07.030 [qtp8540084-26] DEBUG o.s.o.h.HibernateTransactionManager - Not closing pre-bound Hibernate Session [org.hibernate.impl.SessionImpl#13ca565] aftertransaction
19:06:07.030 [qtp8540084-26] DEBUG org.hibernate.impl.SessionImpl - disconnecting session
19:06:07.045 [qtp8540084-26] DEBUG org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
19:06:07.045 [qtp8540084-26] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
19:06:07.045 [qtp8540084-26] DEBUG o.s.a.f.a.ThrowsAdviceInterceptor - Found handler for exception of type [java.lang.Throwable]: public void org.springframework.flex.core.ExceptionTranslationAdvice.afterThrowing(java.lang.Throwable)throws java.lang.Throwable
19:06:07.077 [qtp8540084-26] DEBUG o.s.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'Spring MVC Dispatcher Servlet': assuming HandlerAdapter completed request handling
19:06:07.077 [qtp8540084-26] DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request
19:06:07.092 [qtp8540084-26] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Chainprocessed normally
19:06:07.092 [qtp8540084-26] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter -SecurityContextHolder now cleared, as request processing completed
19:06:07.092 [qtp8540084-26] DEBUG o.s.o.h.s.OpenSessionInViewFilter - Closing single Hibernate Session in OpenSessionInViewFilter
19:06:07.092 [qtp8540084-26] DEBUG o.s.o.hibernate3.SessionFactoryUtils - Closing Hibernate Session
Regards.
Edit1: I can use this same method of DAO for other entities successfully. Also, I can update a persistent object of this entity, but i can not "save" or "create" a new one with saveOrUpdate.
This hang can be caused by a deadlock between transactions.
Such deadlocks are often caused by incorrect use of several transactions in a single thread (if transactions A and B are created by a single thread, transaction A waits for transaction B to release a lock, but control flow never reaches the point where B is to be committed).
So, make sure that Spring transaction management is configured properly and that you don't mix Spring-managed transactions with manually managed ones.
Another possible reason is a long-running transaction created by external system. If you have some external systems connected to the database in question (for example, administration tools), make sure that transactions created by these systems are committed.
It can also happen if any of the child entities are lazy fetched.changing to eager fetch will resolve the issue

Categories

Resources