为了详细展示 Spring 事务的使用,我将提供一个完整的示例,包括配置、代码和说明。这将涵盖以下几个方面:
- 数据库配置:包括数据源和事务管理器的配置。
- 实体类:用于数据库操作的数据模型。
- DAO 层:数据访问对象,用于执行数据库操作。
- 服务层:业务逻辑层,包含事务管理的业务方法。
- 配置:包括 Java 配置和 XML 配置示例。
- 测试:如何测试事务管理的功能。
示例项目结构
假设我们有一个简单的 Spring 项目,用于处理用户的 CRUD 操作,以下是示例项目的主要结构:
src/main/java
└── com
└── example
├── config
│ ├── AppConfig.java
│ └── TransactionConfig.xml
├── dao
│ └── UserDao.java
├── entity
│ └── User.java
├── service
│ └── UserService.java
└── test
└── UserServiceTest.java
一、数据库配置
1. 数据源配置
Java 配置 (AppConfig.java
)
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = "com.example") // 扫描指定包下的组件
public class AppConfig {
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/testdb");
dataSource.setUsername("root");
dataSource.setPassword("password");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
XML 配置 (TransactionConfig.xml
)
<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="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/testdb"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<!-- JdbcTemplate 配置 -->
<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"/>
<!-- 扫描包路径 -->
<context:component-scan base-package="com.example"/>
</beans>
二、实体类
User.java
package com.example.entity;
public class User {
private int id;
private String username;
private String password;
// Getters and Setters
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
三、DAO 层
UserDao.java
package com.example.dao;
import com.example.entity.User;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void createUser(User user) {
String sql = "INSERT INTO users (username, password) VALUES (?, ?)";
jdbcTemplate.update(sql, user.getUsername(), user.getPassword());
}
public User getUserById(int id) {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new Object[]{id}, (rs, rowNum) -> {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
return user;
});
}
public void updateUser(User user) {
String sql = "UPDATE users SET username = ?, password = ? WHERE id = ?";
jdbcTemplate.update(sql, user.getUsername(), user.getPassword(), user.getId());
}
public void deleteUser(int id) {
String sql = "DELETE FROM users WHERE id = ?";
jdbcTemplate.update(sql, id);
}
}
四、服务层
UserService.java
package com.example.service;
import com.example.dao.UserDao;
import com.example.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional
public void createUser(User user) {
userDao.createUser(user);
// Simulate an exception to test rollback
if ("error".equals(user.getUsername())) {
throw new RuntimeException("Simulated exception");
}
}
@Transactional
public void updateUser(User user) {
userDao.updateUser(user);
// Simulate an exception to test rollback
if ("error".equals(user.getUsername())) {
throw new RuntimeException("Simulated exception");
}
}
}
五、测试
UserServiceTest.java
package com.example.test;
import com.example.config.AppConfig;
import com.example.entity.User;
import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class UserServiceTest {
@Test
public void testCreateUserWithTransaction() {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
User user = new User();
user.setUsername("normaluser");
user.setPassword("password");
// Test normal behavior
userService.createUser(user);
// Test exception and rollback
User errorUser = new User();
errorUser.setUsername("error");
errorUser.setPassword("password");
assertThrows(RuntimeException.class, () -> {
userService.createUser(errorUser);
});
}
@Test
public void testUpdateUserWithTransaction() {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
User user = new User();
user.setId(1); // assuming user with ID 1 exists
user.setUsername("updateduser");
user.setPassword("newpassword");
// Test normal behavior
userService.updateUser(user);
// Test exception and rollback
User errorUser = new User();
errorUser.setId(1); // assuming user with ID 1 exists
errorUser.setUsername("error");
errorUser.setPassword("newpassword");
assertThrows(RuntimeException.class, () -> {
userService.updateUser(errorUser);
});
}
}
六、总结
- 配置:数据库连接和事务管理器的配置可以使用 Java 配置或 XML 配置,根据项目需求选择合适的方式。
- 实体类:定义了与数据库表映射的 Java 类。
- DAO 层:负责具体的数据库操作,如创建、读取、更新和删除(CRUD)操作。
- 服务层:封装业务逻辑,并通过
@Transactional
注解来管理事务。 - 测试:验证事务管理是否按预期工作,测试正常情况下和异常情况下的事务回滚行为。
通过上述示例,您可以清楚地了解如何在 Spring 框架中配置和使用事务管理。这个示例展示了如何通过 Java 配置和 XML 配置来设置事务管理器,如何在服务层使用事务管理,以及如何编写测试用例来验证事务的行为。