Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing Spring Transaction Management via Annotation Configuration

Tech 1

Transaction management logic should be implemented at the service layer in Spring applications. Spring provides two transaction management modes:

  1. Programmatic transaction management (for rare custom transaction control scenarios)
  2. Declarative transaction management (recommended for most production use cases)
    • Annotation-based implementation (primary usage)
    • XML-based implementation (for legacy system compatibility)

The underlying implementation of Spring declarative transactions relies on AOP, whose core mechanism is dynamic proxy.

Spring Transaction Management Core API

The top-level transaction manager interface is PlatformTransactionManager, with different implementation classes provided for different data access frameworks to adapt to varied persistence layer technology stacks.

Annotation-based Transaction Control Implementation

First add transaction related configurations in applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       https://www.springframework.org/schema/tx/spring-tx.xsd">
    
    <!-- Enable Spring component scanning -->
    <context:component-scan base-package="com.example"/>
    <!-- Load external database configuration file -->
    <context:property-placeholder location="classpath:db.properties"/>
    <!-- Configure Druid connection pool -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${db.username}"/>
        <property name="password" value="${db.password}"/>
        <property name="url" value="${db.url}"/>
        <property name="driverClassName" value="${db.driver}"/>
    </bean>
    <!-- Configure JdbcTemplate and inject data source -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- Configure JDBC transaction manager -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- Enable transaction annotation support -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

Add the @Transactional annotation to the corresponding service layer method or class:

package com.example.service.impl;

import com.example.repository.AccountRepository;
import com.example.service.FundTransferService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
// @Transactional // Uncomment to apply transaction rules to all public methods of this class
public class FundTransferServiceImpl implements FundTransferService {

    @Autowired
    private AccountRepository accountRepository;

    @Override
    @Transactional // Transaction control only applies to this current method
    public int executeFundTransfer(int senderAccountId, int recipientAccountId, int transferAmount) {
        int affectedRows = 0;
        // Deduct transfer amount from sender account
        affectedRows += accountRepository.adjustAccountBalance(senderAccountId, -transferAmount);
        // Simulate unexpected runtime exception to validate rollback behavior
        int mockException = 1 / 0;
        // Add transfer amount to recipient account
        affectedRows += accountRepository.adjustAccountBalance(recipientAccountId, transferAmount);
        return affectedRows;
    }
}

When the service method runs, if an exception occurs the transaction automatically rolls back, otherwise all changes are committed to the database after the method executes successfully.

Common Parameters of @Transactional Annotation

The @Transactional annotation supports multiple configurable parameters to customize transaction behavior, as shown in its definition:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";
    @AliasFor("value")
    String transactionManager() default "";
    String[] label() default {};
    Propagation propagation() default Propagation.REQUIRED;
    Isolation isolation() default Isolation.DEFAULT;
    int timeout() default -1;
    String timeoutString() default "";
    boolean readOnly() default false;
    Class<? extends Throwable>[] rollbackFor() default {};
    String[] rollbackForClassName() default {};
    Class<? extends Throwable>[] noRollbackFor() default {};
    String[] noRollbackForClassName() default {};
}

propagation: Transaction Propagation Behavior

Defines how transactions are managed when multiple transaction-aware methods call each other. Common propagation strategies include:

  • REQUIRED: If the calling method already has a active transaction, the called methods join the existing transaction. If no active transaction exists, a new transaction is created for all participating methods.
  • REQUIRES_NEW: Always create a new independent transaction regardless of whether an existing transaction is present, suspending any active parent transaction for the duration of the new transaction execution.

isolation: Transaction Isolation Level

Controls the visibility of uncommitted changes between concurrent transactions, available levels are:

  1. DEFAULT: Uses the default isolation level of the underlying database. MySQL defaults to REPEATABLE_READ, Oracle defaults to READ_COMMITTED.
  2. READ_UNCOMMITTED: Lowest isolation level, allows uncommitted changes of one transaction to be visible to others, risks dirty reads, non-repaetable reads and phantom reads.
  3. READ_COMMITTED: Ensures only committed changes are visible to other transactions, prevents dirty reads but still allows non-repeatable and phantom reads.
  4. REPEATABLE_READ: Prevents dirty reads and non-repeatable reads, but phantom reads may still occur.
  5. SERIALIZABLE: Highest isolation level, executes all transactions sequentially, eliminates all read anomalies but has significant performance overhead.

timeout: Transaction Timeout

Defines the maximum allowed execution time for a transaction. If the transaction is not completed within the specified time, it is automatically rolled back. The default value is -1, meaning no time limit.

readOnly: Read-only Transaction Flag

When set to true, marks the transaction as read-only, disallows insert, update and delete operations, and enables performance optimizations for pure read scenarios.

rollbackFor: Rollback Trigger Exceptions

Explicitly specifies which Throwable subtypes will trigger transaction rollback. By default Spring rolls back transactions only for runtime exceptions and Error types.

noRollbackFor: Non-rollback Exceptionss

Specifies exception types that will not trigger transaction rollback even if they are thrown during transaction execution.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.