DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Testcontainers With Kotlin and Spring Data R2DBC
  • How to Train a Joint Entities and Relation Extraction Classifier Using BERT Transformer With spaCy 3
  • Master-Class: Understanding Database Replication (Single, Multi, and Leaderless)
  • Liquibase: Database Change Management and Automated Deployments

Trending

  • Why Your DLP Policies Fall Short the Moment AI Agents Enter the Picture
  • Java Backend Development in the Era of Kubernetes and Docker
  • Navigating the Complexities of AI-Driven Integration in Multi-Cloud Environments: A Veteran’s Insights
  • Kafka and Spark Structured Streaming in Enterprise: The Patterns That Hold Up Under Pressure
  1. DZone
  2. Data Engineering
  3. Databases
  4. A Case Study: Dancing With the @Transactional Annotation in Exceptional Cases

A Case Study: Dancing With the @Transactional Annotation in Exceptional Cases

A case study on using the @Transactional annotation in your Java code and how it can both help, and potentially hinder, your development process.

By 
Arzu Behiye TARIMCI user avatar
Arzu Behiye TARIMCI
·
Suna Aytar user avatar
Suna Aytar
·
Mar. 23, 21 · Tutorial
Likes (6)
Comment
Save
Tweet
Share
4.8K Views

Join the DZone community and get the full member experience.

Join For Free

Spring's @Transactional annotation is easy to use, but it may cause different types of errors if you don't know about the common pitfalls to look out for. In this article, I will show you the possible errors you can run into. 

Suppose we write pseudocode like this: We have a service called DanceService.java and this service has a transactional method called letsDance. This code runs perfectly. What happens when we have an exception?

Suppose the letsDance method is called by a Controller or by a web service and proxy mode is used. So the proxy of the DanceService class handles all the details of the transaction such as opening the connection, committing the transaction, and closing the connection.

According to the pseudocode above, what happens when we get an error in the adjustMusic method? We want the business logic to continue running after receiving the exception. Inside this method, we call another service’s method: the adjustVolume method and, in some cases, we throw an exception. How does the remaining code act when the exception is thrown by the adjustVolume method? Remember the adjustMusic method of DanceService class and that we want to ignore all errors in the adjustVolume method. Ok, let me show you the errors you will have. After the exception, the oneStepLeft method runs and makes some updates in the database.

Java
 




xxxxxxxxxx
1


 
1
public void oneStepLeft (){
2
    querySomeDataFromDb();
3
    xxDao.save();
4
   }


But in the first line of code in the oneStepLeft method, which queries the database or updates the database, the line gets an error: “org.springframework.orm.jpa.JpaSystemException: could not inspect JDBC autocommit mode; nested exception is org.hibernate.exception.GenericJDBCException: could not inspect JDBC autocommit mode”. The pseudocode above has this error when calling queryDataFromDb.

So why do we have an error? Didn’t they tell us that everything would be fine when we use the @Transactional annotation?

When we carefully inspect the application server logs, we the following line of code before “could not inspect JDBC autocommit mode” error:

Plain Text
 




x


 
1
Caused by: java.sql.SQLException: Unexpected exception while enlisting XAConnection java.sql.SQLException: Transaction rolled back: setRollbackOnly called on transaction
2
 
          
3
                at weblogic.jdbc.jta.DataSource.enlist(DataSource.java:1795)
4
 
          
5
                at weblogic.jdbc.jta.DataSource.refreshXAConnAndEnlist(DataSource.java:1680)
6
 
          
7
                at weblogic.jdbc.wrapper.JTAConnection.getXAConn(JTAConnection.java:229)
8
 
          
9
                at weblogic.jdbc.wrapper.JTAConnection.checkConnection(JTAConnection.java:91)
10
 
          
11
                at weblogic.jdbc.wrapper.JTAConnection.checkConnection(JTAConnection.java:74)
12
 
          
13
.....


The transaction rolls back in the catch block of the adjustMusic method when we get an exception, but it lets the code continue to run. When the first time code wants to access the database for doing some work, the code explodes.

And if we go further into the letsDance method, we are now in the catch block. Then we jump to the finally block and call the createDanceLog method, which is transactional as well.

Java
 




xxxxxxxxxx
1


 
1
public void createDeviceInfoLog(DanceData danceData){
2
   //…
3
   xxDao.save();
4
}


What happens when the line of code with save() runs? Do we succeed or do we have an error?

Unfortunately, we get another error: “org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress.”

(I love this type of error very much. When I was new to JPA and got this error, I could not solve the problem and fix the error. There is not enough information and solution recommendations on the web.)

Here is the stacktrace:

Plain Text
 




xxxxxxxxxx
1
29


 
1
<   com.sun.xml.ws.server.sei.TieHandler> <BEA-000000> <no transaction is in progress; nested exception is                     javax.persistence.TransactionRequiredException: no transaction is in progress
2
 
          
3
org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
4
 
          
5
                at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:413)
6
 
          
7
                at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:227)
8
 
          
9
                at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:436)
10
 
          
11
                at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
12
 
          
13
                at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
14
 
          
15
                Truncated. see log file for complete stacktrace
16
 
          
17
Caused By: javax.persistence.TransactionRequiredException: no transaction is in progress
18
 
          
19
                at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:993)
20
 
          
21
                at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
22
 
          
23
                at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
24
 
          
25
                at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
26
 
          
27
                at java.lang.reflect.Method.invoke(Method.java:498)
28
 
          
29
                Truncated. see log file for complete stacktrace


Now, let me tell you the details about the source of this error: the transaction rolls back in the adjustMusic method and then we continue. Because the transaction is broken down and ends before running the createDeviceInfoLog method, the transaction is already marked for death. So when we try to get a connection for the database in the method and there is no transaction left, we get a “no transaction is in progress” error.

In this case, what should we do to fix the problem? After receiving an exception from MusicService in adjustVolume, the remaining the lines of code should run without getting another exception. When we get an exception from MusicService, the transaction is rolled back.

Spring will automatically rollback the transaction if a runtime exception is thrown from a transactional method call.   

To prevent automatic rollbacks, we should use the noRollbackFor attribute of the @Transactional annotation:

@Transactional (noRollbackFor=Exception.class)

This annotation indicates that a rollback should not be issued if the target method raises this exception.

Java
 




xxxxxxxxxx
1


 
1
@Transactional(noRollbackFor=Exception.class)
2
 
          
3
public void adjustVolume(DanceData danceData)


When we update the code above like this and then run the code again,  there are no more “setRollbackOnly called on transaction” or “no transaction is in progress” exceptions, because the transaction is not rolled back and it commits perfectly in the end. The code as runs smoothly as we want.

This is a really weird case. When you get “no transaction is in progress error,” you probably say “oh my god, what did I do wrong?” We can solve our problem with the noRollbackFor attribute, but before using this, we have tried many different code tricks and we have wasted our time and efforts. The solution is inside the details of Spring Declarative Transaction Management.

The @Transaction annotation is a utility of Spring Declarative Transaction Management and it hides the complexity of dealing with transaction handling codes. However, it is not as easy as just putting the @Transactional annotation in your code. You should understand the transaction management concept well before starting to code. Besides, you should carefully test your code for exceptional cases. 

To sum up, you should be aware of the unexpected steps of your dance partner and you should adjust yourself quickly.

Annotation Database

Opinions expressed by DZone contributors are their own.

Related

  • Testcontainers With Kotlin and Spring Data R2DBC
  • How to Train a Joint Entities and Relation Extraction Classifier Using BERT Transformer With spaCy 3
  • Master-Class: Understanding Database Replication (Single, Multi, and Leaderless)
  • Liquibase: Database Change Management and Automated Deployments

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook