1. 事务的四个基本性质
原子性:
指事物的操作要么全部成功,要么全部失败,也就是说如果成功则全部更新到数据库,如果失败就不对数据库产生影响。
一致性:
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
隔离性:
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
持久性:
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
2. 事务的并发带来的问题
幻读:
事务A对一定范围的数据进行批量修改,事务B在这个范围增加一条数据,这时候第一个事务就会丢失对新增数据的修改。
脏读:
事务A对一个数据进行增删改,,但是还未提交的情况下,事务B读取了事务A修改后的数据,如果事务A回滚了,那么就出现了脏读。
不可重复读:
事务A中发生了两次读操作,第一次读操作和第二次操作之间,事务B对数据进行了修改,这时候两次读取的数据是不一致的。
3. 事务的隔离级别
读未提交:
Read-Uncommitted 无法保证任何情况的发生
读已提交:
Read-Committed 避免脏读,允许不可重复读和幻读
可重复读:
Repeatable-Read 避免脏读,不可重复读,允许幻读
串行化:
Serializable 串行化读,事务只能一个一个执行,避免了脏读、不可重复读、幻读。执行效率慢,使用时慎重
4.Spring中的隔离级别
ISOLATION_DEFAULT这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与 JDBC 的隔离级别相对应。
ISOLATION_READ_UNCOMMITTED这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
ISOLATION_READ_COMMITTED保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。
ISOLATION_REPEATABLE_READ这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
ISOLATION_SERIALIZABLE这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
5.事务的传播属性
类ServiceATransactional 以下简称A类
类ServiceBTransactional 以下简称B类
1.PROPAGATION_REQUIRED(spring 默认)
如果上下文中已经存在事务,则加入当前事务执行,否则则新建事务执行
场景一:
B类中
@Transactional(propagation=Propagation.REQUIRED ,rollbackFor = Exception.class)
public void methodRequired() {
List<WareHouse> wal=wareHouseDao.queryWareHouseForPage(null);
WareHouse wa=wal.get(0);
wa.setWareHouseName("test02");
wareHouseDao.updateWareHouse(wa);
}
A类中嵌套调用B类以上方法
/**
* @Description: 在上下文中存在事务时测试REQUIRED传播属性
* @return: void
*/
@Transactional(propagation=Propagation.REQUIRED ,rollbackFor = Exception.class)
public void testREQUIRED01() {
List<WareHouse> wal=wareHouseDao.queryWareHouseForPage(null);
WareHouse wa=wal.get(0);
wa.setWareHouseName("test02");
wareHouseDao.updateWareHouse(wa);
serviceBTransactional.methodRequired();
}
在以上场景中,A类中的方法为required,在执行时A类方法上下文中没有事务,所以A类方法会创建事务来处理A类中方法,A类方法中调用了B类的方法也是required属性所以此时B类方法将不再创建事务,而是加入到A类中的事务中来执行。
输出日志:
Creating a new SqlSession
//A创建事务
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@11d4dbd6]
//jdbc交给spring管理
JDBC Connection [com.mysql.jdbc.JDBC4Connection@188b6035] will be managed by Spring
==> Preparing: SELECT warehouses.wareHouseId,warehouses.wareHouseName,warehouses.wareHouseArea,warehouses.wareHouseAddr, warehouses.price,warehouses.wareHouseDetail,warehouses.createTime,warehouses.updateTime ,company.userId,company.userName,company.contactPersonName,company.companyName, company.companyAddr,company.companyContact,company.companyDetail,company.internetAddr FROM warehouses,company WHERE company.userId=warehouses.userId
==> Parameters:
<== Columns: wareHouseId, wareHouseName, wareHouseArea, wareHouseAddr, price, wareHouseDetail, createTime, updateTime, userId, userName, contactPersonName, companyName, companyAddr, companyContact, companyDetail, internetAddr
<== Row: 1, test04, 1000m3, 乌鲁木齐市人民路, 2000/m3, <<BLOB>>, 2017-03-19 16:06, 2017-03-19 16:06, 4beb1e8975e9490c970f2b39d5537e04, ppb, ppb, 菜鸟物流, 北京朝阳区大羊坊路, 13681440912, <<BLOB>>, www.ppb.com.cn
<== Total: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@11d4dbd6]
//从当前事务获取sqlSession
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@11d4dbd6] from current transaction
==> Preparing: UPDATE warehouses SET wareHouseName=?, wareHouseArea=?, wareHouseAddr=?, price=?, wareHouseDetail=?, createTime=?, updateTime=? where wareHouseId=?
==> Parameters: test02(String), 1000m3(String), 乌鲁木齐市人民路(String), 2000/m3(String), 主要用来存储物料(String), 2017-03-19 16:06(String), 2017-03-19 16:06(String), 1(String)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@11d4dbd6]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@11d4dbd6] from current transaction
==> Preparing: SELECT warehouses.wareHouseId,warehouses.wareHouseName,warehouses.wareHouseArea,warehouses.wareHouseAddr, warehouses.price,warehouses.wareHouseDetail,warehouses.createTime,warehouses.updateTime ,company.userId,company.userName,company.contactPersonName,company.companyName, company.companyAddr,company.companyContact,company.companyDetail,company.internetAddr FROM warehouses,company WHERE company.userId=warehouses.userId
==> Parameters:
<== Columns: wareHouseId, wareHouseName, wareHouseArea, wareHouseAddr, price, wareHouseDetail, createTime, updateTime, userId, userName, contactPersonName, companyName, companyAddr, companyContact, companyDetail, internetAddr
<== Row: 1, test02, 1000m3, 乌鲁木齐市人民路, 2000/m3, <<BLOB>>, 2017-03-19 16:06, 2017-03-19 16:06, 4beb1e8975e9490c970f2b39d5537e04, ppb, ppb, 菜鸟物流, 北京朝阳区大羊坊路, 13681440912, <<BLOB>>, www.ppb.com.cn
<== Total: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@11d4dbd6]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@11d4dbd6] from current transaction
==> Preparing: UPDATE warehouses SET wareHouseName=?, wareHouseArea=?, wareHouseAddr=?, price=?, wareHouseDetail=?, createTime=?, updateTime=? where wareHouseId=?
==> Parameters: test02(String), 1000m3(String), 乌鲁木齐市人民路(String), 2000/m3(String), 主要用来存储物料(String), 2017-03-19 16:06(String), 2017-03-19 16:06(String), 1(String)
<== Updates: 1
Releasing transactional SqlSession
//A提交事务
[org.apache.ibatis.session.defaults.DefaultSqlSession@11d4dbd6]
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@11d4dbd6]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@11d4dbd6]
通过测试打印日志就能发现是否跟结论一致就不一一展开
2.PROPAGATION_REQUIRES_NEW:
如果当前上下文中已经存在事务,则挂起当前事务,新建事务执行,执行完成后恢复上下文事务。
3.PROPAGATION_SUPPORTS:
如果上下文中存在事务,则加入当前事务执行,否则以非事务的方式执行。
4.PROPAGATION_NOT_SUPPORTED:
如果上下文中存在事务,则挂起当前事务,然后以非事务的方式执行完成后,恢复上下文事务
5.PROPAGATION_MANDATORY:
如果上下文存在事务,则加入当前事务执行,如果没有则抛出异常
6.PROPAGATION_NEVER:
如果当前上下文中存在事务,则抛出异常
7.PROPAGATION_NESTED:
嵌套事务
f09f85a4-1577-4bc3-a28d-8ab1cd6b3509.jpg1.如果事务AD与BC一起commit,即:作为事务AD的子事务,事务BC只有在事务AD成功commit时(阶段3成功)才commit。这个需求简单称之为“联合成功”。这一点PROPAGATION_REQUIRED可以做到
2.如果需要事务BC异常rollback时不影响AD事务的执行,此时PROPAGATION_REQUIRES_NEW可以做到。
但是要同时满足以上两点就需要使用PROPAGATION_NESTED
在使用NESTED时当AD事务执行到B点时设置了savePoint(关键):
当BC事务成功commit时,只有AD也成功时BC才真正的提交,否则在3阶段出现异常导致AD回滚,则BC同样回滚,满足条件1,
当异常出现在2阶段时BC回滚,到B点因为savePoint设置了关键AD继续执行最后提交,并不影响AD的提交此时满足条件2
网友评论