上一篇写的是Spring中的JDBC操作,今天说一说Spring事务编程.
Spring事务
事务有关知识请参考事务介绍的博客,附上链接:
Spring管理事务的流程:平台事务管理器根据事务定义进行事务管理,事务管理产生的状态记录在事务状态信息里
事务有关的API
- Platform Transaction Manager
平台事务管理器 - TransactionDefinition
事务定义信息:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读 - TransactionStatus
事务状态:用于记录在事务管理过程中,事务的状态的对象。
事务的传播行为
在我们的分层模式中事务应该处于那一层呢,应该在service层。如果遇到很复杂的问题不仅仅是service层调用dao层,也需要service层之间的互相调用,那么事务的传播行为就是用来解决复杂的业务层之间的相互调用
Spring中的七种事务的传播行为:
-
保证多个操作在同一个事务中
-
- PROPAGATION_REQUIRED :默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来
-
- PROPAGATION_SUPPORTS :支持事务,如果A中有事务,使用A中的事务。如果A没有事务,不使用事务。
-
- PROPAGATION_MANDATORY :如果A中有事务,使用A中的事务。如果A没有事务,抛出异常。
-
保证多个操作不在同一个事务中
-
- PROPAGATION_REQUIRES_NEW :如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没有事务,创建一个新事务,包含自身操作。
-
- PROPAGATION_NOT_SUPPORTED :如果A中有事务,将A的事务挂起。不使用事务管理。
-
- PROPAGATION_NEVER :如果A中有事务,报异常。
-
嵌套式事务
-
- PROPAGATION_NESTED :嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点。
了解了事务的这写概念,那就上手来演练一下子(光说不练假把式)
在日常生活中事务的用途很多,下边就以转账为例来深入了解事务
建立转账的Dao和Service层,这个很简单就直接附上代码
Dao
package Transactiom;
public interface Dao {
public void outMoney(String from ,Double money);
public void inMoney(String to ,Double money);
}
DaoImpl,注意在Dao的实现中继承了JdbcDaoSupport,里边封装着Jdbc模板的set方法,只需要传入datasource就可以自动获得Jdbc模板(this.getJdbcTemplate),所以就可以在xml中注入datasource属性就ok或者在xml配置好模板注入到dao中使用,但是后者的使用还需要再dao中注解声明,不如直接传入datasource简单方便。
package Transactiom;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class DaoImpl extends JdbcDaoSupport implements Dao {
/**
* 继承这个类只需要datasourse就可以自动创建JDBC模板,也可以自己配置模板 '
*
*/
@Override
public void outMoney(String from, Double money) {
this.getJdbcTemplate().update("update spring1 set money = money - ? where name = ?", money,from);
}
@Override
public void inMoney(String to, Double money) {
this.getJdbcTemplate().update("update spring1 set money = money + ? where name = ?", money ,to);
}
}
转账前:
转账后:
但是,如果中途由于一些原因导致不能完整的执行转账这一操作,就会是下边这个场景
转账前:
转账后:
会明显的发现前者转账过去而后者未接收到转账。这就是需要事务管理的理由。
但是通过事务模板给转账加上事务测试后和事务的性质完全吻合!
下边是实现事务模板的execute方法,利用接口回调实现事务
public void transfer(final String from, final String to, final Double money) {
trsactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
dao.outMoney(from, money);
int d = 999/ 0;
dao.inMoney(to, money);
}
});
也要在service注入transactionTemplate模板
<!--配置事务管理平台-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务管理模板简化事务操作-->
<bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
执行成功时会显示正常数据,失败时就会回滚到初始状态!这种是编程式事务,用的不是很多。
下边就说一说非编程式事务
用xml配置
用tx标签配置事务,底层还是我上次写的AOP(环绕通知),因为事务的通知写法是固定的,所以Spring把它导到固定标签<tx…/>中。所以在开始就得引入 <t x … />标签。
我是这么理解的:就是tx里放的就是一个事务的初始化(看作一个加强版的bean),然后再aop的配置中配置切点和切面。
<tx:advice transaction-manager="transactionManager" id="interceptor">
<tx:attributes>
<!--propagation设置传播行为-->
<!--<tx:method name="*"/> 代表所有方法,用法和sql中的模糊查询相似-->
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="transfer" expression="execution(* Transactiom.ServiceImpl.transfer())"/>
<!--配置单个用advisor,多个用aspect-->
<aop:advisor advice-ref="interceptor" pointcut-ref="transfer"/>
</aop:config>
恢复到最初的转账异常测试,发现配置成功。
public void transfer(final String from, final String to, final Double money) {
dao.outMoney(from, money);
int i = 1/0;
dao.inMoney(to, money);
}
因为数据库前后对比相同就不放图了。
可以在isolation中设置隔离级别
可以设置只读,回滚,和超时等初始化操作。
这些配置都是很简单的,如果有人不明白那些隔离级别,我也会在后边写一些隔离级别实现的博客。
用注解配置
必须要在tx中开启注解事务,然后再service类上加一个事务标签就ok
<bean class="Transactiom.DaoImpl" id="dao">
<!--<property name="jdbcTemplate" ref="jdbcTemplate"/>-->
<property name="dataSource" ref="dataSource"/>
</bean>
<bean class="Transactiom.ServiceImpl" id="service">
<property name="dao" ref="dao"/>
<!--<property name="trsactionTemplate" ref="transactionTemplate"/>-->
</bean>
<!--配置jdbc-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--jdbc模板-->
<!--<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">-->
<!--<property name="dataSource" ref="dataSource"/>-->
<!--</bean>-->
<!--配置事务管理平台-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务注释模式-->
<tx:annotation-driven transaction-manager="transactionManager" />
可以再Transaction()里边加上初始化属性和xml配置完全一样
@Transactional()
public class ServiceImpl implements Service
这就是Spring中的事务处理,可以说是很简洁了。后续我还是会继续深入Spring的分析,下一篇就回顾回顾钱前面学的知识并进入到MyBatis的学习。
加油!