示例:Spring JDBC编程式事务

以下是一个完整的 Spring JDBC 编程式事务示例,包含批量插入、事务管理、XML 配置和单元测试:


1. 项目依赖(pom.xml)

xml 复制代码
<dependencies>
    <!-- Spring JDBC -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.20</version>
    </dependency>
    <!-- Spring 事务管理 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.3.20</version>
    </dependency>
    <!-- 数据库驱动(MySQL) -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
    <!-- 单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

2. applicationContext.xml 配置

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

    <!-- 数据源配置 -->
    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test_db?useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
        <property name="maximumPoolSize" value="5"/>
    </bean>

    <!-- 事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

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

    <!-- DAO -->
    <bean id="employeeDao" class="com.example.dao.EmployeeDao">
        <constructor-arg ref="jdbcTemplate"/>
    </bean>

    <!-- Service -->
    <bean id="employeeService" class="com.example.service.EmployeeService">
        <constructor-arg ref="employeeDao"/>
        <constructor-arg ref="transactionManager"/>
    </bean>
</beans>

3. DAO 层代码

java 复制代码
package com.example.dao;

import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;

public class EmployeeDao {

    private final JdbcTemplate jdbcTemplate;

    public EmployeeDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    // 批量插入员工数据
    public void batchInsertEmployees(List<Employee> employees) {
        String sql = "INSERT INTO employee(name, age, department) VALUES(?, ?, ?)";

        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                Employee emp = employees.get(i);
                ps.setString(1, emp.getName());
                ps.setInt(2, emp.getAge());
                ps.setString(3, emp.getDepartment());
            }

            @Override
            public int getBatchSize() {
                return employees.size();
            }
        });
    }
}

4. Service 层代码(编程式事务)

java 复制代码
package com.example.service;

import com.example.dao.EmployeeDao;
import com.example.model.Employee;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.List;

public class EmployeeService {

    private final EmployeeDao employeeDao;
    private final PlatformTransactionManager transactionManager;

    public EmployeeService(EmployeeDao employeeDao, PlatformTransactionManager transactionManager) {
        this.employeeDao = employeeDao;
        this.transactionManager = transactionManager;
    }

    // 批量插入数据(事务控制)
    public void batchInsertWithTransaction(List<Employee> employees) {
        // 定义事务属性
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);

        // 开启事务
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            // 执行批量插入
            employeeDao.batchInsertEmployees(employees);
            
            // 模拟业务校验(例如:插入第10条数据时抛出异常)
            if (employees.size() >= 10) {
                throw new RuntimeException("Simulated failure after 10 records");
            }

            // 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(status);
            throw new RuntimeException("Batch insert failed, rolled back", e);
        }
    }
}

5. Model 类

java 复制代码
package com.example.model;

public class Employee {
    private String name;
    private int age;
    private String department;

    // 构造方法、Getter/Setter 略
}

6. 单元测试

java 复制代码
import com.example.model.Employee;
import com.example.service.EmployeeService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertEquals;

public class EmployeeServiceTest {

    @Test
    public void testBatchInsertSuccess() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        EmployeeService service = context.getBean(EmployeeService.class);

        // 准备测试数据(少于10条)
        List<Employee> employees = generateEmployees(5);

        // 执行插入
        service.batchInsertWithTransaction(employees);

        // 验证插入结果(此处需要根据实际数据库查询验证)
        // 例如:通过 JdbcTemplate 查询数据库记录数
        assertEquals(5, queryEmployeeCount());

        context.close();
    }

    @Test(expected = RuntimeException.class)
    public void testBatchInsertFailure() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        EmployeeService service = context.getBean(EmployeeService.class);

        // 准备测试数据(触发异常)
        List<Employee> employees = generateEmployees(15);

        try {
            service.batchInsertWithTransaction(employees);
        } finally {
            // 验证数据未插入(回滚)
            assertEquals(0, queryEmployeeCount());
        }
        context.close();
    }

    private List<Employee> generateEmployees(int count) {
        List<Employee> list = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            Employee emp = new Employee();
            emp.setName("Employee" + i);
            emp.setAge(20 + i);
            emp.setDepartment("IT");
            list.add(emp);
        }
        return list;
    }

    private int queryEmployeeCount() {
        // 实际项目中通过 JdbcTemplate 查询数据库
        // 示例代码:
        // return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM employee", Integer.class);
        return 0; // 需根据实际情况实现
    }
}

关键点说明

  1. 事务控制

    • 使用 PlatformTransactionManager 手动控制事务边界。
    • 在异常时调用 rollback(),正常时调用 commit()
  2. 批量插入

    • 使用 JdbcTemplate.batchUpdate() 执行批量操作。
    • 通过 BatchPreparedStatementSetter 设置参数。
  3. 测试策略

    • 成功场景:插入少于 10 条数据,验证事务提交。
    • 失败场景:插入超过 10 条数据触发异常,验证事务回滚。
  4. 数据库清理

    • testBatchInsertFailurefinally 块中验证数据回滚。
    • 实际项目中可通过 @Before@After 方法初始化和清理数据。

运行测试

  1. 确保 MySQL 数据库已启动,创建 test_db 数据库和 employee 表:

    sql 复制代码
    CREATE DATABASE test_db;
    USE test_db;
    CREATE TABLE employee (
        id INT PRIMARY KEY AUTO_INCREMENT,
        name VARCHAR(50),
        age INT,
        department VARCHAR(50)
    );
  2. 运行测试类 EmployeeServiceTest,观察事务是否按预期工作。


通过编程式事务管理能够确保批量操作的原子性,同时掌握 XML 配置和手动事务控制的完整流程。

相关推荐
敖云岚4 分钟前
【AI】SpringAI 第五弹:接入千帆大模型
java·大数据·人工智能·spring boot·后端
桦说编程11 分钟前
CompletableFuture典型错误 -- 代码出自某大厂
java·后端·响应式编程
root666/20 分钟前
【大数据技术-联邦集群RBF】DFSRouter日志一直打印修改Membership为EXPIRED状态的日志分析
java·大数据·hadoop
佩奇的技术笔记23 分钟前
Java学习手册:Filter 和 Listener
java
sugar__salt32 分钟前
反射,枚举,lambda表达式
java
Spring小子39 分钟前
黑马点评商户查询缓存--缓存更新策略
java·数据库·redis·后端
foo1st1 小时前
JDK(Ubuntu 18.04.6 LTS)安装笔记
java·笔记·ubuntu
DKPT1 小时前
常见正则表达式整理与Java使用正则表达式的例子
java·笔记·学习·面试·正则表达式