下面是详细讲解SpringBoot注解事务声明式事务的方式的完整攻略。
什么是事务
在数据库的操作中,当多条SQL语句同时执行时,为了保证数据的一致性和完整性,我们需要让这些SQL语句在一个整体中完成,有且只有所有语句都执行成功时才提交到数据库里,而任一条语句执行失败时则所有语句都不会被提交。这个整体操作就是“事务”。
在Java中,事务可以通过编程式、声明式两种方式来实现。其中,声明式事务更加方便、简洁,不需要像编程式事务那样手动管理事务,而是通过注解自动实现。
SpringBoot注解事务的声明方式
在SpringBoot中,声明式事务的方式有两种,一种是基于AOP的方式,即通过在方法上添加@Transactional注解来开启事务;另一种是基于AspectJ的方式,即通过XML配置或@AspectJ注解来开启事务。这里我们主要讲解基于AOP的方式。
基于AOP的声明式事务
1.配置数据源
首先通过application.properties将数据源配置好:
spring.datasource.url=jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
2.添加依赖
在pom.xml中添加SpringBoot的jdbc、MySQL连接驱动、事务等依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
3.添加注解
在需要使用事务的方法上添加@Transactional注解。这里我们用一个简单的例子来说明:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
@Transactional
public void add(User user) {
jdbcTemplate.update("insert into user(name, age) values(?, ?)", user.getName(), user.getAge());
}
@Override
public User find(int id) {
return jdbcTemplate.queryForObject("select * from user where id=?", new BeanPropertyRowMapper<>(User.class), id);
}
}
在add方法上添加了@Transactional注解,表示此方法需要开启事务。如果在add方法执行过程中抛出异常,事务将会自动回滚。
4.测试
可以通过JUnit测试来验证事务是否正常工作。
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void test_add() {
User user = new User("test", 18);
userService.add(user);
User result = userService.find(user.getId());
assertNotNull(result);
assertEquals(user.getId(), result.getId());
}
@Test
public void test_exception() {
User user = new User("test", 18);
userService.add(user);
assertThrows(DataIntegrityViolationException.class, () -> {
userService.add(user);
});
}
}
test_add方法测试了正常情况下的插入操作,test_exception方法测试了在插入数据时出现异常的情况,如果事务正常工作则会回滚整个事务。
通过以上步骤,我们就可以轻松地使用基于AOP的声明式事务了。
基于AspectJ的声明式事务
基于AOP的方式声明事务需要使用@Transactional注解,而基于AspectJ的方式需要通过XML配置或@AspectJ注解来实现。
这里我们以@AspectJ注解的方式为例,步骤如下:
1.添加依赖
在pom.xml中添加SpringBoot和AspectJ的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
2.添加注解
在SpringBoot的启动类上添加@EnableAspectJAutoProxy注解,表示启用AspectJ自动代理。
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
然后在需要使用事务的方法上添加@Transational注解,并在配置类上添加@Aspect注解。
@Service
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public User find(int id) {
return jdbcTemplate.queryForObject("select * from user where id=?", new BeanPropertyRowMapper<>(User.class), id);
}
@Override
@Transactional
public void add(User user) {
jdbcTemplate.update("insert into user(name, age) values(?, ?)", user.getName(), user.getAge());
}
}
@Aspect
@Configuration
public class TransactionAspect {
@Autowired
private DataSource dataSource;
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource);
}
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionPointcut() {}
@Around("transactionPointcut()")
public void transactional(ProceedingJoinPoint joinPoint) {
TransactionStatus transactionStatus = null;
try {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
transactionStatus = transactionManager().getTransaction(def);
joinPoint.proceed();
transactionManager().commit(transactionStatus);
} catch (Throwable e) {
if (transactionStatus != null) {
transactionManager().rollback(transactionStatus);
}
throw new RuntimeException(e);
}
}
}
其中,@Pointcut注解表示切入点,即需要被拦截的方法,这里使用了@Transactional注解来表示切入点;@Around注解表示环绕通知,即在方法执行前后执行的逻辑,这里使用该注解来实现事务的开启、提交和回滚操作。
示例
为了更好地理解使用注解声明式事务的方式,这里我们用一个简单的示例来演示。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
private Integer age;
}
public interface UserService {
void add(User user);
User find(int id);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@Transactional
public void add(User user) {
userDao.add(user);
}
@Override
public User find(int id) {
return userDao.find(id);
}
}
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public User find(int id) {
return jdbcTemplate.queryForObject("select * from user where id=?", new BeanPropertyRowMapper<>(User.class), id);
}
public void add(User user) {
jdbcTemplate.update("insert into user(name, age) values(?, ?)", user.getName(), user.getAge());
}
}
在UserService的实现类UserServiceImpl中,我们在add方法上添加了@Transactional注解,表示需要开启事务;在UserDao中直接使用了JdbcTemplate来执行SQL语句。
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void test_add() {
User user = new User(null, "test", 18);
userService.add(user);
User result = userService.find(user.getId());
assertNotNull(result);
assertEquals(user.getName(), result.getName());
}
@Test
public void test_exception() {
User user = new User(null, "test", 18);
userService.add(user);
assertThrows(DataIntegrityViolationException.class, () -> {
userService.add(user);
});
}
}
在测试用例中,我们测试了正常插入数据和插入数据出现异常时事务回滚的情况。
总结
通过上述内容,我们可以总结出使用SpringBoot注解事务声明式事务的方式的步骤:
- 配置数据源。
- 添加依赖。
- 在需要使用事务的方法上添加@Transactional注解,或在切点方法上添加该注解。
- 测试所有情况以确定事务是否正确工作。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot 注解事务声明式事务的方式 - Python技术站