Controller
@RestController
@RequestMapping("/transaction")
public class TestTransactionController {
@Autowired
private TestService testService;
@RequestMapping(value = "/test", method = RequestMethod.POST)
public Result test(FrogTest frog) {
testService.updateAndInsert(frog);
return new Result();
}
}
Service
public interface TestService {
boolean updateAndInsert(FrogTest frog);
}
@Service
public class TestServiceImpl implements TestService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private IFrogTestService frogService;
@Override
public boolean updateAndInsert(FrogTest frog) {
try {
frogService.updateById(frog);
frogService.save(null);
return true;
} catch (Exception e) {
if(logger.isErrorEnabled()) {
logger.error("updateAndInsert error:", e);
}
throw new MyException(MyExceptionEnum.DATASOURCE_ERROR);
}
}
}
模拟先修改在添加,update正常执行,insert时会因error sql报错。如果updateAndInsert方法为一个事物,只需给该方法加上@Transactional注解即可。
@Override
@Transactional
public boolean updateAndInsert(FrogTest frog) {
try {
frogService.updateById(frog);
frogService.save(null);
return true;
} catch (Exception e) {
if(logger.isErrorEnabled()) {
logger.error("updateAndInsert error:", e);
}
throw new MyException(MyExceptionEnum.DATASOURCE_ERROR);
}
}
@Transactional注解应用于类和公开的方法,用于类时,该类中所有公开方法都加入事物管理。回滚RuntimeException和Error,但不回滚已检查异常。
注解中几个属性:
value:与transactionManager相同
transactionManager:指定事物管理器
propagation:事物传播
isolation:事物隔离
timeout:超时(只使用于REQUIRED和REQUIRES_NEW传播行为)
readOnly:是否只读
rollbackFor:回滚(以下四项的值为继承Throwable的类或类名)
rollbackForClassName:类名回滚
noRollbackFor:不回滚
noRollbackForClassName:设置类名不回滚
事物特性ACID
原子性(atomicity):一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability):持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
事物传播行为
REQUIRED:支持当前事物,如果不存在则创建一个事物。(默认值)
SUPPORTS:支持当前事务,如果不存在则以非事务方式执行。
MANDATORY:支持当前事务,如果不存在则抛出异常。
REQUIRES_NEW:创建一个新事务,并暂停当前事务(如果存在)。
NOT_SUPPORTED:以非事务方式执行,暂停当前事务(如果存在)。
NEVER:则以非事务方式执行,如果事务存在,抛出异常。
NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED。
【Spring学习34】Spring事务(4):事务属性之7种传播行为
事物隔离级别
DEFAULT:使用基础数据存储的默认隔离级别。(默认值)
READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。如果发生回滚,则第二个事务将检索到无效行。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
这里引用其他作者描述对脏读、不可重复读、幻读的理解:
作者:Somhu
原文:https://blog.csdn.net/Somhu/article/details/78775198?utm_source=copy
脏读:所谓的脏读,其实就是读到了别的事务回滚前的脏数据。比如事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读。
也就是说,当前事务读到的数据是别的事务想要修改成为的但是没有修改成功的数据。
不可重复读:事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了。
也就是说,当前事务先进行了一次数据读取,然后再次读取到的数据是别的事务修改成功的数据,导致两次读取到的数据不匹配,也就照应了不可重复读的语义。
幻读:事务A首先根据条件索引得到N条数据,然后事务B改变了这N条数据之外的M条或者增添了M条符合事务A搜索条件的数据,导致事务A再次搜索发现有N+M条数据了,就产生了幻读。
也就是说,当前事务读第一次取到的数据比后来读取到数据条目少。
Service
@Override
@Transactional
public boolean updateAndInsert(FrogTest frog) {
try {
frogService.updateById(frog);
frogService.save(null);
return true;
} catch (Exception e) {
if(logger.isErrorEnabled()) {
logger.error("updateAndInsert error:", e);
}
throw new MyException(MyExceptionEnum.DATASOURCE_ERROR);
}
}
@Override
public void update(FrogTest frog) {
updateAndInsert(frog);
}
如果将Service层修改为如上,Controller层先调用update方法,update在调用updateAndInsert方法,尽管后者加了@Transactional注解,方法中两个操作并不是一个事物。
因为@Transactional 的事务开启 ,或者是基于接口的或者是基于类的代理被创建。所以在同一个类中一个方法调用另一个方法有事务的方法,事务是不会起作用的。
参考:https://zhuanlan.zhihu.com/p/38208248
经过测试,update方法加上注解,updateAndInsert不加,事物起作用。