事务的介绍
事务具有4个特性:原子性、一致性、隔离性、持久性。通常称为ACID特性。
- 原子性(Atomicity):一个事务是一个不可分割的工作单位,事务中包括的诸多操作要么都做,要么都不做。
- 一致性(Consistency):事务必须使数据库从一个一致性状态变成另一个一致性状态
- 隔离性(Isolation):一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务时隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(Durability):一个事务一旦提交,他数据库中数据的改变就应该是永久性的,接下来的其他操作或故障不应该对其有任何影响
使用事务
实体类:
package com.example.chapter10_5.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false,unique = true,length = 10)
private String bookName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public Book(Long id, String bookName) {
super();
this.id = id;
this.bookName = bookName;
}
public Book() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Book [id=" + id + ", bookName=" + bookName + "]";
}
}
package com.example.chapter10_5.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.chapter10_5.entity.Book;
public interface BookRepository extends JpaRepository<Book, Long> {
}
package com.example.chapter10_5.controller;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.chapter10_5.entity.Book;
import com.example.chapter10_5.repository.BookRepository;
@RestController
public class BookController {
@Autowired
private BookRepository bookRepository;
@GetMapping("/test1")
public String test1() {
bookRepository.save(new Book(2l,"JAVA从入门到精通"));
bookRepository.save(new Book(3l,"SpringBoot2实战之旅"));
return "success";
}
@GetMapping("/test2")
@Transactional
public String test2() {
bookRepository.save(new Book(4l,"JAVA从入门到精通"));
bookRepository.save(new Book(5l,"SpringBoot2实战之旅"));
return "success";
}
}
package com.example.chapter10_5.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
@EntityScan(basePackages = {"com.example.chapter10_5.entity"})
@EnableJpaRepositories(basePackages = {"com.example.chapter10_5.repository"})
@ComponentScan(basePackages = {"com.example.chapter10_5.controller"})
public class Chapter115Application {
public static void main(String[] args) {
SpringApplication.run(Chapter115Application.class, args);
}
}
spring.datasource.url=jdbc:mysql://localhost:3306/spring_boot_szzl?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#create:没有表会创造表,原来表中的数据会清空
#create-drop:每次程序结束时会清空表
#update:每次运行程序,没有表格会新建表,表中的数据不会清空,只会更新
#validate:程序运行会校验数据与数据库字段类型是否相同,不同会报错
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
启动项目,分别请求两个方法,test1方法由于没有事务的原因,第一条数据会插入成功,第二条数据会插入失败,这个插入的第一条数据就是脏数据;test2方法由于加入事务的原因,两条数据都不会插入成功,显然test2方法的场景更符合事务的特性。
Spring事务扩展介绍
spring事务不仅可以通过使用事务注解@Transactional,同时支持编程式使用事务,但是这种模式不常用。
事务的隔离级别
使用事务其实重用到了一个注解@Transactional,这就是Spring的注解式事务。事务隔离级别是指若干个事务并发时的隔离程度,Spring声明事务看可以通过isolation属性来设置Spring的事务隔离级别。
- @Transactional(isolation = Isolation.DEFAULT):默认的隔离级别,及使用数据库的事务隔离级别
- @Transactional(isolation = Isolation.READ_UNCOMMITTED):读未提交,最低的事务隔离级别,允许其他事务读取未提交的数据,这种级别的事务隔离会产生脏读,不可重复读和幻读。
- @Transactional(isolation = Isolation.READ_COMMITTED):读已提交,能读取其他事务已提交的数据,不能读取未提交的数据,会产生不可重复读和幻读。
- @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读,可以防止不可重复读和脏读,但是会发生幻读。
- @Transactional(isolation = Isolation.SERIALIZABLE):串行化,最高级别的事务隔离,会避免脏读,不可重复读和幻读。在这种隔离级别下,事务会按顺序进行。
事务传播行为
- Propagation.REQUIRED:如果当前存在事务,就加入该事务;如果当前没有事务,就创建一个新的事务,这是spring默认的事务传播行为
- Propagation.REQUIRES_NEW:创建一个新事务,如果当前存在事务,就把当前事务挂起。新建事务和被挂起的事务没有任何关系,是两个独立的事务。外层事务回滚失败时,不能回滚内层事务执行结果,内外层事务不能相互干扰。
- Propagation.SUPPORTS:如果当前存在事务,就加入该事务;如果当前没有事务,就以非事务的方式继续运行。
- Propagation.NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,就把当前事务挂起。
- Propagation.NEVER:以非事务方式运行,如果当前存在事务,就抛出异常
- Propagation.MANDATORY:如果当前存在事务,就加入该事务;如果当前没有事务,就抛出异常
- Propagation.NESTED:如果当前存在事务,就创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,该取值就等价于Propagation.REQUIRED
声明式事务属性
Spring事务不只拥有事务隔离级别和事务传播行为,另外还包含很多属性。
- value:存放String类型的值,主要用来指定不同的事务管理器,满足在同一个系统中存在不同事务管理器。比如在Spring容器中声明了多种事务管理器,然后开发者可以根据设置指定需要使用的事务管理器,通常一个系统需要访问多个数据库的场景下,就会设置对个事务管理器,然后进行不同的选择
- transactionManager:与value类似,也是用来选择事务管理器。
- propagation:事务传播行为,默认值是Propagation.REQUIRED
- isolation:事务隔离级别,默认值是 Isolation.DEFAULT
- timeout:超时时间,默认值是-1,如果超过了设置的时间还没有执行完成,就会自动回滚当前事务
- readOnly:当前事务是不是只读事务,默认是false。通常可以设置读取数据的事务的属性值为true
- rollbackFor:可以设置触发事务的指定异常,允许指定多个类型的异常。
- noRollbackFor:与rollbackFor相反,可以设置不触发事务的指定异常,允许指定多个类型的异常
事务回滚规则
Spring的事务回滚通常是根据当前事务抛出异常的时候,Spring事务管理器捕捉到未经处理异常,然后根据规则来决定当前事务是否回滚。如果捕获的异常正好是设置notRollbackFor属性的异常,那么将不会被捕获。在默认配置下,Spring只有捕获运行时异常的子类时才会回滚。
@Transactional使用注意事项
- @Transactional需要在类的上方使用,而不是在接口的上方使用,如果在接口上方使用,事务就会失效
- @Transactional只能在public修饰的方法上,如果使用在private或protected修饰的方法上,事务就会无效
- @Transactional尽量不在类的上方使用,因为这样会对类内的全部方法使用事务,如果对查询方法使用事务,就可能会影响效率