Spring事务
事务在实际开发中,重要性不言而喻。假设没有合理的事务控制,A向B发起了100元转账,A账户减100,B账户加100,但是转账中途因网络等因素导致程序异常(B账户更新记录没有成功,A账户更新成功),这就导致A账户无缘无故损失100元。。。这就是事务的一个简单例子,何时提交事务、何时事务回滚、合理设置事务的超时时间也是程序设计非常重要的一部分。
1、事务相关概念回顾
1.1、什么是事务
事务:数据库事务是指一组操作逻辑单元(不可分割的整体,这些操作要么一起成功,要么一起失败),使数据从一种状态变换到另一种状态。
在我们日常工作中,涉及到事务的场景非常多,一个service 中往往需要调用不同的dao 层的方法,为了确保数据库中的数据的一致性,这些方法要么同时成功要么同时失败。因此在service 层中我们一定要确保这一点。
事务的四大属性(ACID):
-
原子性(Atomicity ) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 -
一致性(Consistency ) 事务必须使数据库从一个一致性状态变换到另外一个一致性状态。 -
隔离性(Isolation ) 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致,也就是设置不同个隔离级别包括未提交读(Read Uncommitted)、提交读(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。 -
持久性(Durability ) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
1.2、事务转账案例实现(原生JDBC)
AA给BB转账100元,以MySQL 数据库为例
public class TransactionTest {
@Test
public void transferTest1(){
String sql1 = "update user_table set balance = balance - 100 where user = ?;";
update1_0(sql1,"AA");
System.out.println(10/0);
String sql2 = "update user_table set balance = balance + 100 where user = ?;";
update1_0(sql2,"BB");
System.out.println("转账成功!");
}
public int update1_0(String sql,Object ...args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = JDBCUtils.getConnection();
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
return preparedStatement.executeUpdate();
} catch(Exception e){
e.printStackTrace();
} finally {
JDBCUtils.closeResource(connection,preparedStatement);
}
return 0;
}
@Test
public void transactionTest2() {
Connection connection = null;
try {
connection = JDBCUtils.getConnection();
connection.setAutoCommit(false);
String sql1 = "update user_table set balance = balance - 100 where user = ?;";
update2_0(connection,sql1,"AA");
String sql2 = "update user_table set balance = balance + 100 where user = ?;";
update2_0(connection, sql2,"BB");
System.out.println("转账成功!");
connection.commit();
} catch (Exception e) {
e.printStackTrace();
try {
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}finally{
if(connection != null){
JDBCUtils.closeResource(connection,null);
}
}
}
public int update2_0(Connection connection,String sql,Object ...args) {
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
return preparedStatement.executeUpdate();
} catch(Exception e){
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null,preparedStatement);
}
return 0;
}
}
2、Spring中的事务
转账案例中我们发现可以手动控制事务的提交、是否自动提交、事务回滚等为我们开发带来了诸多便利,可灵活处理事务操作。然而Spring 的事务管理模块就是做这些事情(只是设计的更加高级),一切事务都由Spring 来管理,因此想要了解Spring 中的事务就得从它设计的类或接口开始,分别是PlatformTransactionManager 、TransactionDefinition 、TransactionStatus 。
2.1、三大基础组件
1、PlatformTransactionManager :事务处理的核心接口,定义了事务处理的三个最基本方法。规范事务处理的行为,具体实现细节都由各种子类实现,例如JDBC的DataSourceTransactionManager ,hibernate的HibernateTransactionManager ,jpa的JpaTransactionManager 等等
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
2、TransactionDefinition :也是一个接口,规范了事务的一些属性,例如事务的隔离级别(Isolation)、事务的传播行为(Propagation Behavior)、事务的超时时间(Timeout)、是否为只读事务(Readonly)。
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;
3、TransactionStatus :也是一个接口,规范一个事务的状态(也可以说是事务本身)。接口提供了控制事务执行和查询事务状态的方法。比如当前调用栈中是否已经存在了一个事务,就是通过该接口来判断的。该接口还可以管理和控制事务的执行,比如检查事务是否为一个新事务,或者是否只读,初始化回滚操作等。
boolean hasSavepoint();
void flush();
2.2、Spring中的编程式事务
在Spring 中,事务管理提供了两种实现方式,分别是编程式事务和声明式事务。
编程式事务言外之意就是在业务功能代码中嵌入事务管理的代码,手动控制事务的各种操作,属于侵入性事务管理。在Spring 中为了支持和简化编程式事务,专门提供了一个类TransactionTemplate ,在它的execute() 方法中就能实现事务的功能。
优点:
1、可以有效避免由于SpringAOP 的问题导致事务失效问题
2、能够更小粒度控制事务的范围,更直观更灵活
缺点:
1、每次都要单独实现,业务量大且功能复杂时,开发繁琐维护成本高
2、与业务代码耦合度高
说了这么多,如何使用编程式事务呢?
1、使用maven 搭建一个Spring 工程(略)
2、编写spring-dao.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:properties/database.properties"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${spring.datasource.driver-class-name}"/>
<property name="url" value="${spring.datasource.url}"/>
<property name="username" value="${spring.datasource.username}"/>
<property name="password" value="${spring.datasource.password}"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
3、编写测试代码进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:applicationContext.xml"})
public class Test {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private TransactionTemplate transactionTemplate;
@org.junit.Test
public void transactionTest() {
TransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus transaction = transactionManager.getTransaction(definition);
try {
int affectedRows = jdbcTemplate.update("UPDATE `TEST`.`TB_USER` U SET U.MONEY = U.MONEY - 100 WHERE U.USERNAME = ?", "AA");
transactionManager.commit(transaction);
System.out.println("update success");
} catch (Exception e) {
transactionManager.rollback(transaction);
e.printStackTrace();
}
}
}
2.3、Spring中的声明式事务
END
THANK YOU
|