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.
Related
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")
I'm working on a Java web application that uses Spring and Informix database. Server with the web application and with the database are two different machines.
I noticed that one of the SQL queries takes more time to execute on the production server than on the test server. I took a look in the logs and saw that the most time consuming part of the whole transaction takes place between two messages of the Transaction Synchronization Manager.
This is the log entries of the transaction:
2021-02-23 13:04:07,968 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Using transaction object [org.springframework.jdbc.datasource.DataSourceTransactionManager$DataSourceTransactionObject#3f847659]
2021-02-23 13:04:07,969 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] Initializing transaction synchronization
2021-02-23 13:04:07,969 DEBUG [org.springframework.transaction.interceptor.TransactionInterceptor] Getting transaction for [com.prod.service.material.MaterialService.getMaterialIds]
2021-02-23 13:04:07,969 DEBUG [com.prod.service.material.MaterialServiceImpl] getMaterialIds
2021-02-23 13:04:07,969 DEBUG [com.prod.dao.material.MaterialDaoJdbc] getMaterialIds
2021-02-23 13:04:07,969 DEBUG [com.prod.dao.material.MaterialDaoJdbc$MaterialListQuery] RdbmsOperation with SQL […] compiled
2021-02-23 13:04:07,969 DEBUG [org.springframework.jdbc.core.JdbcTemplate] Executing prepared SQL query
2021-02-23 13:04:07,969 DEBUG [org.springframework.jdbc.core.JdbcTemplate] Executing prepared SQL statement […]
2021-02-23 13:04:07,969 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Fetching JDBC Connection from DataSource
2021-02-23 13:04:07,969 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Registering transaction synchronization for JDBC Connection
2021-02-23 13:04:07,969 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] Bound value [org.springframework.jdbc.datasource.ConnectionHolder#8bd3c80] for key [org.apache.tomcat.dbcp.dbcp2.BasicDataSource#16aac72f] to thread [http-nio-127.0.0.1-8080-exec-21]
2021-02-23 13:04:07,972 DEBUG [org.springframework.jdbc.core.StatementCreatorUtils] Setting SQL statement parameter value: column index 1, parameter value [2010], value class [java.lang.String], SQL type 12
*2021-02-23 13:04:07,972 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder#8bd3c80] for key [org.apache.tomcat.dbcp.dbcp2.BasicDataSource#16aac72f] bound to thread [http-nio-127.0.0.1-8080-exec-21]
*2021-02-23 13:04:13,881 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder#8bd3c80] for key [org.apache.tomcat.dbcp.dbcp2.BasicDataSource#16aac72f] bound to thread [http-nio-127.0.0.1-8080-exec-21]
2021-02-23 13:04:13,881 DEBUG [org.springframework.transaction.interceptor.TransactionInterceptor] Completing transaction for [com.prod.service.material.MaterialService.getMaterialIds]
2021-02-23 13:04:13,881 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Triggering beforeCommit synchronization
2021-02-23 13:04:13,881 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Triggering beforeCompletion synchronization
2021-02-23 13:04:13,881 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] Removed value [org.springframework.jdbc.datasource.ConnectionHolder#8bd3c80] for key [org.apache.tomcat.dbcp.dbcp2.BasicDataSource#16aac72f] from thread [http-nio-127.0.0.1-8080-exec-21]
2021-02-23 13:04:13,881 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] Returning JDBC Connection to DataSource
2021-02-23 13:04:13,881 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Triggering afterCommit synchronization
2021-02-23 13:04:13,881 DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] Triggering afterCompletion synchronization
2021-02-23 13:04:13,881 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] Clearing transaction synchronization
The SQL query itself is pretty complex and returns around 2000 rows of data.
The Transaction Manager and Data Source are specified as:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>com.informix.jdbc.IfxDriver</value></property>
<property name="url"><value>jdbc:informix-sqli://app.prod.com:11111/prod:informixserver=prod</value></property>
<property name="username"><value>prod</value></property>
<property name="password"><value>1234</value></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
System is:
OS: RHEL Server 7.9
Webserver: Tomcat 9.0.34
Why do we have those two messages from the Transaction Manager? Isn't it a single connection to the data source?
Also, what process can happen between those two messages that takes 6 secs to execute? Is the data being placed somewhere?
I ask because I don't know why it takes longer on the production server whereas on the test server with identical data it takes only 2 secs (between two messages I mentioned above).
Thank you!
EDIT
The correct data source definition is as follows (I copied from the wrong file previously):
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>java:/comp/env/jdbc/MyDS</value></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
And the context.xml file contains:
<Resource name="jdbc/MyDS"
auth="Container" type="javax.sql.DataSource"
driverClassName="com.informix.jdbc.IfxDriver"
url="jdbc:informix-sqli://app.prod.com:11111/prod:informixserver=prod;ifxIFX_LOCK_MODE_WAIT=20;ifxIFX_XASPEC=Y;"
username="prod" password="prod"
maxActive="100" maxIdle="25" maxWait="10000"
removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true"/>
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.
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);
}
}
}
I am new to Spring and hibernate please help,
I am using (Jboss 6.0 Final as Server)
org.springframework.transaction.jta.JtaTransactionManager
as bean with properties set
transactionManagerName as java:/TransactionManager and
userTransactionName as java:comp/UserTransaction.
In code I have set jtaTxManager property thr setters.
Then
javax.transaction.TransactionManager tx = jtaTxManager.getTransactionManager();
and then transaction is started using tx.begin() statement.
I have used sessionFactory.getCurrentSession() to get session of hibernate
at last I have used
tx.commit()
I am using hibernate to save multiple records within jta transaction
but If in between any database error occurs between some record like constraint violation exception getting thrown on tx.commit() which is javax.transaction.RollbackException which when catch I used to call tx.rollback() but my transaction is not getting rollback and getting following exception on tx.rollback().I have not set any type auto commit property in hibernate properties.
But my first of records in gets saved in database ideally they should not saved but while rollback this exception occurs so i think that's why they are getting saved.
java.lang.IllegalStateException: BaseTransaction.rollback - [com.arjuna.ats.internal.jta.transaction.arjunacore.notx] [com.arjuna.ats.internal.jta.transaction.arjunacore.notx] no transaction!
at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.rollback(BaseTransaction.java:158)
at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.rollback(BaseTransactionManagerDelegate.java:114)
This issue was due to datasource configuration, forgot to mentioned earlier,
Removed from application-context.xml
org.springframework.jdbc.datasource.DriverManagerDataSource
and rather than this, used jndi datasorce of jboss configured in oracle-xa-ds.xml
The Javadoc states that the RollbackException gets thrown when the transaction has been rolledback instead of commited - you don't have to rollback it manually in such a case, I think.
http://download.oracle.com/javaee/6/api/javax/transaction/Transaction.html#commit%28%29