Spring事务控制
参考博客系列:
https://my.oschina.net/pingpangkuangmo/blog/416038
事务的实现类型:
本地事务模型
Connection conn=jdbcDao.getConnection();PreparedStatement ps=conn.prepareStatement("insert into user(name,age) value(?,?)");ps.setString(1,user.getName());ps.setInt(2,user.getAge());ps.execute();编程式事务模型
public static void main(String[] args) throws ClassNotFoundException, SQLException {String URL="jdbc:mysql://127.0.0.1:3306/ksc_order?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true";String USER="root";String PASSWORD="123456";//1.加载驱动程序Class.forName("com.mysql.jdbc.Driver");//2.获得数据库链接Connection conn=DriverManager.getConnection(URL, USER, PASSWORD);conn.setAutoCommit(false);try {PreparedStatement ps=conn.prepareStatement("insert into user_t(user_name,password,age) value(?,?,?)");ps.setString(1,"huangzs1138");ps.setString(2,"201805031138");ps.setInt(3,30);ps.execute();PreparedStatement ps2=conn.prepareStatement("update user_t set password=? where id=?");ps2.setString(1,"huangzs1145");ps2.setInt(2,1);ps2.execute();int[] arr={1,2,3};int test=arr[5];} catch (Exception e) {e.printStackTrace();conn.rollback();}finally{conn.commit();conn.close();}}声明式事务模型
@Transactionalpublic void save(User user){jdbcTemplate.update("insert into user(name,age) value(?,?)",user.getName(),user.getAge());}
事务5大隔离级别
ISOLATION_DEFAULT 默认和数据库隔离级别一样
ISOLATION_READ_UNCOMMITTED 允许脏读取
ISOLATION_READ_COMMITTED 读取提交后数据,这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
ISOLATION_REPEATABLE_READ 可重复读,可能出现幻读。
ISOLATION_SERIALIZABLE 最可靠的事务隔离级别
事务7大传播级别
模拟A事务PROPAGATION_REQUIRED,调用B事务
PROPAGATION_REQUIRED ,默认的spring事务传播级别。如果上下文中已经存在事务,那么就加入到事务中执行,如果不存在事务则新建。A或者B失败,都会全部回滚。
PROPAGATION_REQUIRES_NEW ,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。B成功,直接提交;B失败,Invoker A仍然成功,两个独立的事务。
PROPAGATION_NESTED 嵌套级别事务。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。涉及到 savepoint概念,B成功A不成功,都不提交,B失败,A会把事务回滚到调用B的savepoint之前,A成功的话,会单独提交A事务中的数据,而不提交B事务的数据。
PROPAGATION_SUPPORTS ,从字面意思就知道,supports,支持,该传播级别的特点是,如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。
PROPAGATION_NOT_SUPPORTED ,这个也可以从字面得知,not supported ,不支持,当前级别的特点就是上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。
这个级别有什么好处?可以帮助你将事务极可能的缩小。我们知道一个事务越大,它存在的风险也就越多。所以在处理事务的过程中,要保证尽可能的缩小范围。比如一段代码,是每次逻辑操作都必须调用的,比如循环1000次的某个非核心业务逻辑操作。这样的代码如果包在事务中,势必造成事务太大,导致出现一些难以考虑周全的异常情况。所以这个事务这个级别的传播级别就派上用场了。用当前级别的事务模板抱起来就可以了。PROPAGATION_MANDATORY , 该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。
PROPAGATION_NEVER ,该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!这个级别上辈子跟事务有仇。
require 、 require_new 、Nested 区别
- 两个require 事务调用,融为一体,为一个大的事务。
- require 调用 require_new,分为2个独立的事务,相互不影响
- require 调用 nested,包含的关系,nested为一个savepoint,nested不能影响require的提交
readonly属性
不同数据库的支持不同,mysql支持readonly,如果只读方法里面有DML操作,则DML失败且抛出异常。
申明注解式的配置
|
源码解读
PlatformTransactionManager 最核心接口,有3个关键的方法
java 分布式事务框架Atomikos 和Jotm
参考https://my.oschina.net/pingpangkuangmo/blog/423210
两种实现原理:
非XA方式,类似于2个Connection,分别设置为 setAutoCommit为false,然后都提交成功后,在分别都commit
Connection connA=dataSourceA.getConnection();Connection connB=dataSourceB.getConnection();Statement statementA=connA.createStatement();Statement statementB=connB.createStatement();String sql="insert into user(name,age) values('"+user.getName()+"',"+user.getAge()+")";try {connA.setAutoCommit(false);connB.setAutoCommit(false);statementA.execute(sql);statementB.execute(sql);//throw new RuntimeException();connA.commit();connB.commit();} catch (Exception e) {e.printStackTrace();statementA.close();statementB.close();connA.rollback();connB.rollback();}finally{connA.close();connB.close();}XA方式 2PC提交
资源管理器 分别start() end() prepare(),成功后,TM check所有RS状态,都OK再一起commit