之前一篇有讲过如何在spring boot中集成JPA和Mybatis,本篇就在此基础上以JPA为例讲一下如何对事务进行操作
- 创建一个spring boot项目,pom文件如下:
<?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.quick</groupId>
<artifactId>quick-transactional</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>quick-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-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</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:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/restful?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
initialize: true
init-db: true
jpa:
database: mysql
show-sql: true
hibernate:
ddl-auto: update
naming:
strategy: org.hibernate.cfg.ImprovedNamingStrategy
@Data
@Entity
@Table(name="book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false)
private Integer id;
@Column(nullable = false,name = "name")
private String name;
@Column(nullable = false,name = "isbn")
private String isbn;
@Column(nullable = false,name = "author")
private String author;
}
@Data
@Entity
@Table(name="user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false)
private Integer id;
@Column(nullable = false,name = "name")
private String name;
@Column(nullable = false,name = "hobby")
private String hobby;
}
@Repository("bookRepository")
public interface BookRepository extends JpaRepository<Book,Integer>{
}
@Repository("userRepository")
public interface UserRepository extends JpaRepository<User,Integer> {
}
public interface BookService {
void save();
}
@Service("bookService")
public class BookServiceImpl implements BookService{
@Resource
private BookRepository bookRepository;
@Transactional
@Override
public void save(){
try {
Book book = new Book();
book.setName("book");
book.setAuthor("wang");
book.setIsbn("666");
bookRepository.save(book);
int a = 1/0;
}catch (Exception e){
throw new RuntimeException("error");
}
}
}
public interface UserService {
void save();
}
@Service
public class UserServiceImpl implements UserService{
@Resource
private UserRepository userRepository;
@Resource
private BookService bookService;
@Transactional
@Override
public void save() {
User user = new User();
user.setName("wang");
user.setHobby("pingpang");
userRepository.save(user);
bookService.save();
}
}
针对于需要进行事务管理的方法,大家只要在方法上加入 @Transactional注释即可启用事务
@RunWith(SpringRunner.class)
@SpringBootTest
public class QuickTransactionalApplicationTests {
@Resource
private UserService userService;
@Test
public void contextLoads(){
userService.save();
}
}
由于我们在BookServiceImpl的save方法中手动制造了一个异常,大家执行以下测试类,就会发现,数据没有插入,事务回滚成功。
下面介绍一下@Transactional注解的参数以及使用:
事物传播行为介绍:
- @Transactional(propagation=Propagation.REQUIRED) :如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
- @Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不为这个方法开启事务
- @Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
- @Transactional(propagation=Propagation.MANDATORY) :必须在一个已有的事务中执行,否则抛出异常
- @Transactional(propagation=Propagation.NEVER) :必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
- @Transactional(propagation=Propagation.SUPPORTS) :如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
事物超时设置:
- @Transactional(timeout=30) //默认是30秒
事务隔离级别:
- @Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用
- @Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)
- @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)
- @Transactional(isolation = Isolation.SERIALIZABLE):串行化
MYSQL: 默认为REPEATABLE_READ级别
SQLSERVER: 默认为READ_COMMITTED
@Transactional注解中常用参数说明

注意的几点:
- @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
- 用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
- @Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
- @Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 元素的出现 开启 了事务行为。
- Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。
源码地址:gitee.com/wangGet/spring-boot-quick.git