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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Minimizing Latency in Kafka Streaming Applications That Use External API or Database Calls
  • Spring Microservice Tip: Abstracting the Database Hostname With Environment Variable
  • A Guide to Enhanced Debugging and Record-Keeping
  • Design to Support New Query Parameters in GET Call Through Configurations Without Making Code Changes

Trending

  • Unlocking Data with Language: Real-World Applications of Text-to-SQL Interfaces
  • The Human Side of Logs: What Unstructured Data Is Trying to Tell You
  • Build Your First AI Model in Python: A Beginner's Guide (1 of 3)
  • Analyzing Techniques to Provision Access via IDAM Models During Emergency and Disaster Response
  1. DZone
  2. Data Engineering
  3. Databases
  4. Spring Transaction Management Over Multiple Threads

Spring Transaction Management Over Multiple Threads

Learn why Spring transactions over multiple threads fail, and how to use them successfully, plus practical applications of multi-threaded database transactions.

By 
Dulaj Atapattu user avatar
Dulaj Atapattu
·
Apr. 25, 17 · Opinion
Likes (23)
Comment
Save
Tweet
Share
160.7K Views

Join the DZone community and get the full member experience.

Join For Free

Image title

The Spring framework provides a comprehensive API for database transaction management. Spring takes care of all underlying transaction management considerations and provides a consistent programming model for different transaction APIs such as Java Transaction API (JTA), JDBC, Hibernate, Java Persistence API (JPA), and Java Data Objects (JDO). There are two main types of transaction management in Spring: declarative transaction management, which is a high level one, and programmatic transaction management, which is more advanced but flexible.

The Spring API works very well with almost all of the transaction management requirements as long as the transaction is on a single thread. The problem arises when we want to manage a transaction across multiple threads. Spring doesn't support transactions over multiple threads out of the box. Spring doesn't explicitly mention that in the documentation, but you will end up with runtime errors or unexpected results if you try to do so.

Why Do Spring Transactions Over Multiple Threads Fail?

Spring stores a set of thread locals inside the  org.springframework.transaction.support.TransactionSynchronizationManager class. These thread locals are specific for an ongoing transaction on a single thread (Thread locals values are specific for a single thread. Thread local value set by one thread cannot be accessed by another thread).

public abstract class TransactionSynchronizationManager {

private stati final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal("Current transaction name");
private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal("Current transaction read-only status");
private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal("Current transaction isolation level");
private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal("Actual transaction active");

}

If we start the transaction from one thread and try to commit or rollback the transaction from another thread, a runtime error will be generated complaining that the Spring transaction is not active on the current thread. Though we start and end the transaction from the same thread, we cannot perform database operations belong to transaction from another thread too.

When we initialize the transaction the  actualTransactionActive thread local is set to true.  synchronizations thread local is also initialized. Other thread locals are also accessed and updated during the lifecycle of the transaction. When we try to commit or rollback the transaction at the end of the transaction scope, values of these thread locals are again checked. What happens if we use multiple threads for the transaction is that these thread local values are not visible across multiple threads. Therefore, Spring cannot maintain the transaction state throughout the transaction. 

How to Use Spring Transactions With Multiple Threads

Now you understand the problem with spring transactions over multiple threads. The thread local values are not propagating to new threads from old threads. The only solution here is to manually copy these thread local values to newly spawned threads to keep the transaction unbroken.

As mentioned above,  actualTransactionActive thread local is used to check whether the transaction is active on the current thread. This thread local is set true on the thread which initializes the transaction. But, since this thread local value is not visible to other threads, we have to maintain another boolean flag to inform the activeness of the transaction to other threads. Then we can check that flag from the new thread and set  actualTransactionActive thread local to true manually. Thread local value can be set by calling following code line:

TransactionSynchronizationManager.setActualTransactionActive(true);

Practical Applications of Multi-Threaded Database Transactions

Database processing element of Adroitlogic Project-X is a good industrial level practical application of multi-threaded database transactions. 

What Is Project-X?

Project X is the base framework for a new generation of redesigned integration products by Adroitlogic. Project-X consists of all the API definitions, core implementations of those APIs, messaging engine, message format definitions and implementations, metrics engine, etc. to be used by those integration products.

What Is a Database Processing Element?

The database connector and the database processing element provide the data persistent capabilities to Project-x. There are three main components to perform database operations: db egress connector, db ingress connector, and the db processing element. Database processing element supports for all four CRUD operations. Database egress connector provides create, update, delete operations and database ingress connector provides read operation in a timed manner.

How Transactions Are Used in The Database Processor

Let’s see how to configure an integration flow to perform a database transactional operation. Here we enter the data to a table, obtain the id of the last inserted row, and then again insert data to another table with the id obtained from the previous database operation.

Suppose there is a database table named as people having columns named as id, name, and age. The second table is named as students and the columns are id, school, and grade. Here, the id of the students table is a foreign key referenced from the people table.
Image title

The database transaction scope start element[1] starts the transaction scope. All the database operations happening within transaction scope are guaranteed to be committed if and only if all the operations within the transaction scope were a success. Then the clone message processing element[2] just takes a clone of the message and sends one copy as the response to the nio http ingress connector and the other copy to the database processor. Two new threads are created here and thread locals of the TransactionSynchronizationManager class are copied to the newly created threads. Then, database processing element[3] does the first insert to the people table in the database. Next, database processing element[5] reads the last inserted row from the people table and returns the result in DBResultsSetFormat. Then we use a custom processing element[6] to extract the id from the DBResultsSetFormat and set it to the header of the message. The last database processing element, element[7], does the second insert to the students table using the data read from the payload the id read from the message header. Finally, the database transaction scope end element closes the transaction scope and commit the changes to the database if all the operations are complete. The database transaction will be rolled back in any kind of failure within the transaction scope of the flow.

Spring Framework Database

Published at DZone with permission of Dulaj Atapattu. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Minimizing Latency in Kafka Streaming Applications That Use External API or Database Calls
  • Spring Microservice Tip: Abstracting the Database Hostname With Environment Variable
  • A Guide to Enhanced Debugging and Record-Keeping
  • Design to Support New Query Parameters in GET Call Through Configurations Without Making Code Changes

Partner Resources

×

Comments
Oops! Something Went Wrong

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

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

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 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!