Spring Transaction Management with Pure Annotations: Step-by-Step Implementation

This tutorial demonstrates how to implement Spring declarative transaction management using only annotations, covering project setup, configuration classes, MyBatis integration, service layer @Transactional usage, and JUnit tests verifying commit and rollback behavior.

Java Captain
Java Captain
Java Captain
Spring Transaction Management with Pure Annotations: Step-by-Step Implementation

Introduction to Spring Declarative Transactions

The article explains how to use Spring's @Transactional annotation to manage transactions at the business layer, ensuring ACID properties (Atomicity, Consistency, Isolation, Durability) for database operations. The example simulates a bank transfer between two accounts, where both debit and credit must succeed or both fail.

Project Setup and Dependencies

A Maven project is created with the following key dependencies (versions as used in the demo):

Spring Context, Spring JDBC, Spring Test: 5.3.17

MySQL Connector/J: 8.0.28

Druid connection pool: 1.2.8

MyBatis: 3.5.9

MyBatis-Spring: 2.0.7

JUnit: 4.13.2 (test scope)

Dependencies are declared in pom.xml and downloaded via Maven refresh.

Database Preparation

A MySQL database testdb is created with a bankuser table (id, name, money) using InnoDB engine. Three initial rows are inserted:

INSERT INTO `bankuser` (`id`, `name`, `money`) VALUES
	(1, '任肥肥', 3000),
	(2, '侯胖胖', 3000),
	(3, '乔豆豆', 3000);

Connection properties are stored in jdbc.properties (driver, URL, username, password) and Druid pool settings (initialSize=3, maxActive=20, maxWait=3000).

Configuration Classes

JdbcConfig

Annotated with @PropertySource("classpath:jdbc.properties") to load properties. Uses @Value to inject values into fields. Provides two @Bean methods: getDataSource(): Creates and configures a DruidDataSource with the injected properties. getTransactionManager(DataSource): Returns a DataSourceTransactionManager for JDBC transaction management.

MybatisConfig

Defines two beans: getSqlSessionFactoryBean(DataSource): Configures SqlSessionFactoryBean with the data source and type aliases package com.jobs.domain. (Optional MyBatis logging configuration is commented out.) getMapperScannerConfigurer(): Sets base package com.jobs.dao for automatic mapper interface scanning.

SpringConfig

Main configuration class annotated with:

@Configuration
@ComponentScan("com.jobs")
@Import({JdbcConfig.class, MybatisConfig.class})
@EnableTransactionManagement

— crucial for enabling Spring's annotation-driven transaction support.

Domain Entity

BankUser

class with fields id, name, money, constructors, getters/setters (omitted), and toString().

DAO Layer (MyBatis Annotations)

BankUserDao

interface uses MyBatis annotations:

@Update("update bankuser set money = money + #{money} where name = #{name}")

for addMoney.

@Update("update bankuser set money = money - #{money} where name = #{name}")

for minusMoney. @Select with @Results mapping columns to properties for getBankUserList() returning List<BankUser>.

Service Layer with @Transactional

Interface BankUserService

Declares three methods:

@Transactional void transferMoney(String from, String to, Integer money)

— normal transfer.

@Transactional void transferError(String from, String to, Integer money)

— intentionally fails to test rollback. List<BankUser> getBankUserList() — read-only, no transaction needed.

The article recommends placing @Transactional on the interface (or interface methods) rather than the implementation class, so that any future implementation automatically inherits the transaction behavior.

Implementation BankUserServiceImpl

Injects BankUserDao via @Resource (to avoid IDE false warnings). Implements: transferMoney: calls minusMoney then addMoney. transferError: calls minusMoney, then triggers int result = 1 / 0; (ArithmeticException), then addMoney (never reached). The comment emphasizes not catching exceptions inside the service method; otherwise Spring would not see the exception and would commit the transaction. Exception handling should be done by the caller. getBankUserList: delegates to DAO.

JUnit Test Verification

Test class BankUserServiceTest uses @RunWith(SpringJUnit4ClassRunner.class) and @ContextConfiguration(classes = SpringConfig.class) to load the Spring context. Autowires BankUserService.

Test 1: Successful Transfer ( transferMoney )

Prints account balances before and after transferring 1000 from "任肥肥" to "乔豆豆". Output shows balances updated correctly (2000, 3000, 4000).

Test 2: Failed Transfer with Rollback ( transferError )

Attempts to transfer 1000 from "侯胖胖" to "乔豆豆". The test method wraps the call in try-catch to capture the exception. After the exception, balances are printed again and remain unchanged (3000, 3000, 3000), proving the transaction rolled back.

Key takeaway: The caller (test method) handles the exception; the service method lets it propagate.

Conclusion

Spring's annotation-driven transaction management simplifies complex business logic by allowing developers to write fine-grained DAO operations and compose them in service methods annotated with @Transactional. The framework ensures atomicity automatically, reducing boilerplate code and potential errors.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

SpringMyBatisAnnotationsJUnitTransaction ManagementDeclarative TransactionsDataSourceTransactionManager@EnableTransactionManagement
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.