以下内容也是spring in action2的内容,因为感觉实在没太多难点,就没写demo
事物在软件中扮演一个重要的角色,用户确保数据和资源永远不会处在一种不一致的状态下。
没有实物,就可以会出现数据被破坏,或者是应用程序中的业务规则缺乏在一致性。
ACID
原子性:事物由一个或多个行为捆绑在一起组成,好像是一个单独的工作单位。
一致性:一但一个事物结束了(不管成功与否),系统所处的状态和它的业务规则是一直的。
隔离性:事物应该允许多名用户操作同一个数据,一名用户的操作不会和其他用户的操作相混淆。
持久性:一但事物完成,事物的结果应该持久化,这样不管什么样的系统崩溃,他们都将幸免于难。
1、JDBC事务
如果你正在为应用程序的持久性使用纯的JDBC,那么DataSourceTransactionManager将为你处理事务的边界
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
2、Hibernate事务
如果你的应用程序的持久性是由Hibernate处理的,那么你就应该使用HibernateTransactionManager
<!-- 对于Hibernate2.X 如下:(注意:spring-framework-2.0.8以后就没hibernate这个包了) -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- hibernate3.X以后如下: -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
HibernateTransactionManager把事务管理委托给一个从当前Hibernate会话中检索到的org.hibernate.Transaction对象
当一个事务成功完成时,HibernateTransactionManager将调用哪个Transaction对象的commit()方法。
类似的,当一个事物失败时,那个Transaction对象的rollback()方法将被调用。
3、JPA事务
Jpa只需要被装配一个JPA实体管理器工厂 entity manager factory,JpaTransaction将与当前工厂生成的JPAEntityManager合作事务
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
<!-- Start the JVM specifying the Java agent to be used-->
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
</bean>
那么 loadTimeWeaver 到底有什么作用呢?
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
作为一个高级特性,JpaTemplate、JpaTransactionManager 和 AbstractEntityManagerFactoryBean 的子类支持用户自定义的 JpaDialect 作为"jpaDialect"的bean属性进行注入。 在这种情况下,DAO将不再接收 EntityManagerFactory 的引用作为参数,而是接收一个完整的 JpaTemplate(也就是将它注入到 JpaDaoSupport 的"jpaTemplate"属性中去一个 JpaDialect 实现能够激活一些由Spring支持的高级特性,这通常由特定的实现供应商指定: 一个 JpaDialect 实现能够激活一些由Spring支持的高级特性,这通常由特定的实现供应商指定:
使用特定的事务语义(例如用户自定义的事务隔离级别和事务超时)
获取具备事务功能的Connection对象(暴露给基于JDBC的DAO)
从 PersistenceExceptions 到Spring的 DataAccessExceptions 高级转化
这对于特殊的事务语义和异常的高级转化这样的高级特性比较有价值。注意默认的实现(使用DefaultJpaDialect)并不提供任何特殊的功能。如果你需要上述的特殊功能,你必须指定合适的Dialect。
<!--
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="jpaDialect" ref="jpaDialect"/>
</bean>
-->
那么jpaDialect 到底是什么呢?
4、另外还有JDO 和JTA(事务横跨多个事务资源)
传播实务:
事务的第一个方面的传播行为。传播行为定义关于客户端和被调用方法的事务边界。
事务创建时或者是现有事务可以被使用时所定义的传播规则
----------------------------------------------------------
传播行为 | 意义
----------------------------------------------------------------
PROPAGATION_MANDATORY | 标识该方法必须运行在一个事务中。如果当前没有事务正在运行,将抛出一个异常。
----------------------------------------------------------------
PROPAGATION_NESTED | 表示如果当前正有一个事务在运行中,则该方法应当运行在一个嵌套式事务中。
| 被嵌套的事务可以独立于封装事务进行提交或回滚。如果事务不存在,行为就像
| PROPAGATION_REQUIRED一样。请注意各厂商对于这种传播行为的支持是参错不齐的
----------------------------------------------------------------
PROPAGATION_NEVER | 表示当前的方法不应该在一个事务性上下文中运行。如果当前正有一个事务在运行,则会抛出异常。
----------------------------------------------------------------
PROPAGATION_NOT_SUPPORTED| 表示该方法不应该在一个事务中运行。如果一个现有的事务正在运行中,它将在该
| 事务方法的运行期间被挂起。如果正在使用JTATransactionManager,则需要访问TransactionManager
----------------------------------------------------------------
PROPAGATION_REQUIRED | 表示当前方法必须在一个事务中运行。如果一个现有的事务正在运行中,该方法将
| 会在那个事务中运行。否则的话,就要开始一个新的事务
----------------------------------------------------------------
PROPAGATION_REQUIRES_NEW | 表示当前方法必须在它自己的事务中运行。一个新的事务将被启动,而且如果有一个
| 现有的事务正在运行的话,则将在这个方法运行期间被挂起。如果正在使用JTATransactionManager,则需要访问TransactionManager
----------------------------------------------------------------
PROPAGATION_SUPPORTS | 表示当前方法不需要事务性上下文,但是如果有一个事务正在运行的话,它也可以在这个事务中运行
----------------------------------------------------------------
隔离级别:
脏读(Dirty read):脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会被无效的。
不可重复读(Nonrepeatable read): 不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每一次查询结果不同。
这通常是由于另一个并发事物在两次查询之间更新了数据。
幻读(Phantom reads):幻读和不可重复读类似。当一个事务(T1)读取几行记录后,另一个并发事物(T2)插入了一些记录时,幻读就发生了。
在后来查询中,第一个事务(T1)就会发现一些原来没有的额外记录。
Spring事务隔离级别:
------------------------------------------------------------
隔离级别 | 含义
------------------------------------------------------------
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别
------------------------------------------------------------
ISOLATION_READ_UNCOMMITTED | 允许读取尚未提交的修改。可能导致脏读、不可重复读和幻读
------------------------------------------------------------
ISOLATION_READ_COMMITTED | 允许从已经提交的并发事物读取。可防止脏读,但是不可重复读和幻读不可避免
------------------------------------------------------------
ISOLATION_REPEATABLE_READ | 对相同字段的多次读取结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但是不避免幻读
------------------------------------------------------------
ISOLATION_SERIALIIZABLE | 完全服从ACID的隔离级别,确保不发生任何情况。
------------------------------------------------------------
只读:
如果一个事务只对后端数据库执行读操作,那么该数据库就可能可以利用那个事物的只读特性,采取某些优化措施。
如果使用hibernate,讲使hibernate的flush模式被设置为FLUSH_NEVER
事务超时:
假设你的事务的运行时间变得格外长。因为事务可能涉及对后端数据库的锁定,所以长时间运行的事务会不必要地占用数据库资源。你可以声明一个事务,在特定秒数之后自动回滚,而不必等他座机结束。
回滚规则:
在默认设置下,事务只在出现运行异常(runtime exception)时回滚,而在出现受阻异常(checked exception)时不回滚。不过你可以单独声明回滚形式。
代理事务(spring2.0之前):
<!--
The original, pre-Spring 2.0, proxy for rantService to make it
transactional. As shown in Listing 6.3.
I only list it here for educational purposes. I no longer use
this and favor other mechanisms for declarative transaction such
as annotations.
-->
<!--
<bean id="rantService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="target"
ref="rantServiceTarget" />
<property name="proxyInterfaces"
value="com.roadrantz.service.RantService" />
<property name="transactionManager"
ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
</bean>
-->
上面需要说明的一点是,id为rantService,即为我们原本的service的id,也就是你代码中调用的bean id,原有id改名为rantServiceTarget,因为这样,我们让这部分声明起作用
在spring2.0里声明事务:
isolation:指定事务隔离级别
no-rollback-for:指定对于哪些异常当前事务应该继续执行而不要回滚
propagation:定义事物的传播规则
read-only:指定一个事务为只读
rollback-for:指定对于哪些受阻异常应该回滚事务而不要提交
timeout:为一个长时间运行的事务定义超时
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
为事务切面
<aop:config>
<aop:advisor pointcut="execution(* *..*Service.*(..))" advice-ref="txAdvice" />
</aop:config>
定义注解声明事务:
<tx:annotation-driven/>
或者指定特定的事务管理器
<tx:annotation-driven transaction-manager="transactionManager"/>
在类或者方法上声明事务如:
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
可以定义在类上,类里面的所有方法就默认以这种声明形式,但是也可以方法上单独指定,也可以声明到接口上。
<?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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <import resource="classpath:bean-jdbc.xml" /> <!-- ++++++++++++++++++++++++++++++++++ 各种事务 +++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- 如果你正在为应用程序的持久性使用纯的JDBC,那么DataSourceTransactionManager将为你处理事务的边界 --> <!-- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> --> <!-- 如果你的应用程序的持久性是由Hibernate处理的,那么你就应该使用HibernateTransactionManager。 --> <!-- 对于Hibernate2.X 如下:(注意:spring-framework-2.0.8以后就没hibernate这个包了) --> <!-- <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> --> <!-- hibernate3.X以后如下: --> <!-- <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> --> <!-- HibernateTransactionManager把事务管理委托给一个从当前Hibernate会话中检索到的org.hibernate.Transaction对象 当一个事务成功完成时,HibernateTransactionManager将调用哪个Transaction对象的commit()方法。 类似的,当一个事物失败时,那个Transaction对象的rollback()方法将被调用。 --> <!-- Jpa只需要被装配一个JPA实体管理器工厂 entity manager factory,JpaTransaction将与当前工厂生成的JPAEntityManager合作事务 --> <!-- <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" /> < ! - - Start the JVM specifying the Java agent to be used - - > <property name="loadTimeWeaver"> <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> --> <!-- 作为一个高级特性,JpaTemplate、JpaTransactionManager 和 AbstractEntityManagerFactoryBean 的子类支持用户自定义的 JpaDialect 作为"jpaDialect"的bean属性进行注入。 在这种情况下,DAO将不再接收 EntityManagerFactory 的引用作为参数,而是接收一个完整的 JpaTemplate(也就是将它注入到 JpaDaoSupport 的"jpaTemplate"属性中去一个 JpaDialect 实现能够激活一些由Spring支持的高级特性,这通常由特定的实现供应商指定: 一个 JpaDialect 实现能够激活一些由Spring支持的高级特性,这通常由特定的实现供应商指定: 使用特定的事务语义(例如用户自定义的事务隔离级别和事务超时) 获取具备事务功能的Connection对象(暴露给基于JDBC的DAO) 从 PersistenceExceptions 到Spring的 DataAccessExceptions 高级转化 这对于特殊的事务语义和异常的高级转化这样的高级特性比较有价值。注意默认的实现(使用DefaultJpaDialect)并不提供任何特殊的功能。如果你需要上述的特殊功能,你必须指定合适的Dialect。 --> <!-- <bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> <property name="jpaDialect" ref="jpaDialect"/> </bean> --> <!-- ++++++++++++++++++++++++++++++++++ 声明事务 +++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- The original, pre-Spring 2.0, proxy for rantService to make it transactional. As shown in Listing 6.3. I only list it here for educational purposes. I no longer use this and favor other mechanisms for declarative transaction such as annotations. --> <!-- <bean id="rantService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="target" ref="rantServiceTarget" /> <property name="proxyInterfaces" value="com.roadrantz.service.RantService" /> <property name="transactionManager" ref="transactionManager" /> <property name="transactionAttributes"> <props> <prop key="add*">PROPAGATION_REQUIRED</prop> <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop> </props> </property> </bean> --> <!-- 2.0里声明事务 --> <!-- isolation:指定事务隔离级别 no-rollback-for:指定对于哪些异常当前事务应该继续执行而不要回滚 propagation:定义事物的传播规则 read-only:指定一个事务为只读 rollback-for:指定对于哪些受阻异常应该回滚事务而不要提交 timeout:为一个长时间运行的事务定义超时 --> <!-- <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> <! - - 为事务切面 - - > <aop:config> <aop:advisor pointcut="execution(* *..*Service.*(..))" advice-ref="txAdvice" /> </aop:config> --> <!-- 定义注解声明事务 --> <!-- <tx:annotation-driven/> 或者指定特定的事务管理器 <tx:annotation-driven transaction-manager="transactionManager"/> 在类或者方法上声明事务如: @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) 可以定义在类上,类里面的所有方法就默认以这种声明形式,但是也可以方法上单独指定,也可以声明到接口上。 --> </beans>