Configuring Database Transactions in Spring: XML and Annotation Approaches

Transaction Fundamentals

A database transaction encapsulates a sequence of operations in to a single logical execution unit. When a transaction commits, all modifications within its scope become permanent; if any step fails, the entire sequence is reverted, leaving the database in its prior consistent state.

Effective transaction processing guarantees four fundamental properties:

  • Atomicity: Every operation within the transaction completes successfully, or the entire transaction is aborted.
  • Consistency: Execution of a transaction transitions the database from one valid state to another, preserving all defined rules and constraints.
  • Isolation: Concurrent transactions operate independently, preventing uncommitted data from affecting one another.
  • Durability: Once a transaction is committed, its effects remain intact even in the face of subsequent system crashes.

Programmatic and Declarative Models

Spring Framework supports two distinct strategies for demarcating transaction boundaries.

The programmatic model embeds transaction control logic directly with in application code. Developers manually invoke begin, commit, and rollback operations via Spring’s PlatformTransactionManager or lower-level APIs such as JDBC or Hibernate. Although this technique permits precise, line-level control over transactions, it entangles cross-cutting infrastructure concerns with business logic. For this reason, Spring documentation generally recommends against programmatic management for typical enterprise scenarios.

The declarative model, conversely, delegates transaction demarcation to the Spring container. Rules are expressed externally—through XML metadata or Java annotations—so that transactional behavior can be altered without touching compiled code. Because this strategy is implemented via Spring’s aspect-oriented programming (AOP) proxy mechanism, it represents the least invasive option and is well suited for coarse-grained, method-level boundaries.

XML-Based Transaction Configuration

Under the hood, declarative transaction support relies on Spring AOP. Advice is woven around proxied beans, meaning transactional semantics apply to whole methods rather than arbitrary blocks of code inside them.

The XML fragment below illustrates a complete configuration: a data source, a JdbcTemplate, DAO and service beans, a DataSourceTransactionManager, and an AOP pointcut that matches service-layer methods.

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/inventory"/>
        <property name="username" value="root"/>
        <property name="password" value="secret"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="orderDao" class="com.example.tx.OrderDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

    <bean id="orderService" class="com.example.tx.OrderService">
        <property name="orderDao" ref="orderDao"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" read-only="false" rollback-for="Exception"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="serviceOperation" expression="execution(* com.example.tx.OrderService.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation"/>
    </aop:config>
</beans>

With infrastructure in place, the service class below attempts to persist an entity and then deliberately raises an unchecked exception to validate that the platform automatically rolls back the preceding insert.

package com.example.tx;

public class OrderService {

    private OrderDao orderDao;

    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    public void createOrder(Order order) {
        orderDao.insert(order);
        throw new IllegalStateException("Deliberate failure to test rollback");
    }
}

The DAO executes parameterized SQL through the injected JdbcTemplate:

package com.example.tx;

import org.springframework.jdbc.core.JdbcTemplate;

public class OrderDao {

    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void insert(Order order) {
        String sql = "INSERT INTO orders(id, product_name, quantity) VALUES (?, ?, ?)";
        jdbcTemplate.update(sql, order.getId(), order.getProductName(), order.getQuantity());
    }
}

Annotation-Driven Transaction Demarcation

Spring also supports concise, annotation-centric configuration through @Transactional. When this mode is active, the container scans for the annotation on classes or individual methods and generates transactional proxies automatically.

To bootstrap the feature, declare a transaction manager and enable annotation processing:

<?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
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="com.example.annotation.tx"/>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/inventory"/>
        <property name="username" value="root"/>
        <property name="password" value="secret"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

Transactional semantics are then declared inline. The annotation may target a method, in which case only that method is wrapped; when placed at the class level, all public methods inherit the policy. Attributes such as propagation, isolation, timeout, and read-only status fine-tune runtime behavior.

package com.example.annotation.tx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderService {

    @Autowired
    private OrderDao orderDao;

    @Transactional(
        readOnly = false,
        timeout = -1,
        isolation = Isolation.DEFAULT,
        propagation = Propagation.REQUIRED
    )
    public void createOrder(Order order) {
        orderDao.insert(order);
        throw new IllegalStateException("Deliberate failure to test rollback");
    }
}

The repository implementation remains largely unchanged, relying on autowired infrastructure to execute SQL:

package com.example.annotation.tx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class OrderDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insert(Order order) {
        String sql = "INSERT INTO orders(id, product_name, quantity) VALUES (?, ?, ?)";
        jdbcTemplate.update(sql, order.getId(), order.getProductName(), order.getQuantity());
    }
}

Tags: Spring Framework Transaction Management java JDBC aop

Posted on Fri, 21 Aug 2026 16:45:42 +0000 by receiver