第10章—开启事务
spring boot 系列学习记录:http://www.cnblogs.com/jinxiaohang/p/8111057.html
码云源码地址:https://gitee.com/jinxiaohang/springboot
此前,我们主要通过XML配置Spring来托管事务。在SpringBoot则非常简单,只需在业务层添加事务注解(@Transactional )即可快速开启事务。虽然事务很简单,但对于数据方面是需要谨慎对待的,识别常见坑点对我们开发有帮助。
推荐做法:在业务层统一抛出异常,然后在控制层统一处理。
1. 引入依赖
如果是新建项目的,可以在这页添加依赖,如是原有项目可直接编写。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.xiaohang</groupId> <artifactId>springboot-transactional</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springboot-transactional</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
二、添加数据源
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
三、编写各层代码
- 实体层
import lombok.Data; import javax.persistence.Entity; import javax.persistence.Id; import java.math.BigDecimal; @Data @Entity public class UserAccount { @Id private String userId; private String userName; private BigDecimal balance; }
- dao层
import com.xiaohang.springboottransactional.entity.UserAccount; import org.springframework.data.jpa.repository.JpaRepository; public interface UserAccountRepository extends JpaRepository<UserAccount,String>{ }
- service层
public interface UserAccountService { /** * 转账 */ public UserAccount change(String userId,BigDecimal bigDecimal,String userId2) throws Exception; }
- service实现层
import com.xiaohang.springboottransactional.entity.UserAccount; import com.xiaohang.springboottransactional.repository.UserAccountRepository; import com.xiaohang.springboottransactional.service.UserAccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; @Service public class UserAccountServiceImpl implements UserAccountService { @Autowired private UserAccountRepository userAccountRepository; @Transactional @Override public UserAccount change(String userId, BigDecimal bigDecimal, String userId2) throws Exception{ UserAccount userAccount = userAccountRepository.findOne(userId); UserAccount userAccount2 = userAccountRepository.findOne(userId2); userAccount.setBalance(userAccount.getBalance().add(bigDecimal)); userAccount2.setBalance(userAccount2.getBalance().subtract(bigDecimal)); userAccountRepository.save(userAccount); int n=1/0;//throw new Exception();//假设发生故障 userAccountRepository.save(userAccount2); return userAccountRepository.findOne(userId); } }
- 测试层
import com.xiaohang.springboottransactional.service.UserAccountService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.math.BigDecimal; @RunWith(SpringRunner.class) @SpringBootTest public class UserAccountServiceImplTest { @Autowired private UserAccountService userAccountService; @Test public void change() throws Exception { try { System.out.println(userAccountService.change("1",new BigDecimal("100"),"2")); }catch (Exception e){ System.out.println("抛出异常"); } } }
三、测试
原始数据:
不开启事务,不抛出异常时:
不开启事务,抛出异常时(转账时发生错误,钱扣完,但是对方钱未到账;或者钱到帐了,但是钱未扣。)
先恢复数据:
开启事务,抛出异常时。