1、传播行为(Propagation behavior)
可以找到相对应的常数与说明,列出下列几个:
PROPAGATION_MANDATORY:方法必须在一个现存的事务中进行,否则丢出异常
PROPAGATION_NESTED:在一个嵌入的事务中进行
PROPAGATION_NEVER:不应在事务中进行,如果有则丢异常
PROPAGATION_NOT_SUPPORTED:不应再事务中进行,如果有就暂停现存的事务
PROPAGATION_REQUIRED:支持现在的事务,如果没有就建立一个新的事务
PROPAGATION_REQUIRES_NEW:建立一个新的事务,如果现存一个事务就暂停它
PROPAGATION_SUPPORTS:支持现在的事务,如果没有就以非事务的方式执行
2、隔离层级(Isolation level)
在一个应用程序中,可能有多个事务在同时进行,这些事务应当彼此之间互不知道另一个事务的存在,比如现在整个应用程序就只有一个事务存在,由于事务彼此之间独立,若读取的是同一个数据的话,就容易发生问题,比如:
Dirty read(脏读):某个事务已经更新了一份数据,另一份事务在此时读取了同一份数据,由于某些原因,前一个事务回滚了,则后一个事务读取的数据则是错误的。
Non-repeatable read(非重复读):在一个事务的两次查询中事务不一致,可能是因为两次查询过程中间插入了一个事务更新的原有数据。
Phantom read(幻象读):在一个事务的两次查询中数据笔数不一致。
解决以上问题的方法之一,就是在某个事务进行过程中锁定正在更新或查询的数据,但是这样会造成效率上的问题,别的事务必须等待当前事务解锁后才能进行。然而,根据需求的不同,并不用在事务进行时完全的锁定数据,隔离层级可以让您根据实际的需求,对数据的锁定进行设置。一下是几个隔离层级的参数说明:
ISOLATION_DEFAULT:使用底层数据库预设的隔离层级
ISOLATION_READ_COMMITTED:运行事务读取其他事务已经提交的数据字段,可以防止脏读问题
ISOLATION_READ_UNCOMMITTED:运行事务读取其他并行事务还没有提交的数据,会发生脏读、非重复读、幻象读等问题
ISOLATION_REPEATABLE_READ:要求多次读取的数据必须相同,除非事务本身更新数据,可以防止脏读、非重复读等问题
ISOLATION_SERIALIZABLE:完整的隔离层级,防止所有问题,会锁定数据对应的表,有效率问题
事实上,对于事务的传播特性,可以设置对应的隔离层级。在 中,我们用的最多的就是PROPAGATIOIN_REQUIRED这种传播行为。这个意思是,如果应用程序中已经存在一个事务了,当另一个事务进来时,会加入到这个事务中,如果没有事务存在,则开启一个新的事务。
请看一下对事务传播特性设置的配置文件
中的事务管理主要有编程式事务管理和声明式事务管理,这里主要介绍声明式事务管理。在 中,主要是通过AOP来完成声明式的事务管理。要了解 对事务的支持,首先要了解事务的属性。在 中事务属性主要分以下几方面:
class="language-java"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/aop"
xmlns:tx="http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/tx"
xsi:schemaLocation="
http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/beans http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/beans/
-beans-2.5.xsd
http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/aop http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/aop/
-aop-2.5.xsd
http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/tx http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/tx/
-tx-2.5.xsd">
<bean id="sessionFactory" class="org.class="tags" href="/tags/SPRING.html" title=spring>springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>
<bean id="transactionManager" class="org.class="tags" href="/tags/SPRING.html" title=spring>springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="allDaoMethod" expression="execution (* org.whatisjava.class="tags" href="/tags/DAO.html" title=dao>dao..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="allDaoMethod"/>
</aop:config>
</beans>
在<tx:method>中的属性设置,对于传播行为、隔离层级、只读、超时等,都有对应的"propagation"、"isolation"、"timeout"、"read-only"等等,这里设置的传播属性是"REQUIRED",则它对应的默认的隔离层级就是"DEFAULT","timeout"默是"-1","read-only"默认是"false"。我们也可以根据这些参数来选取不同的参数设置,比如
class="language-java">...
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED" isolation="SERIALIZABLE"/>
<tx:method name="del*" propagation="REQUIRED" isolation="SERIALIZABLE"/>
<tx:method name="update*" propagation="REQUIRED" isolation="SERIALIZABLE"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
....
这是基于xml配置文件进行的事务属性的传播控制,也可以基于注解方式的,请看如下代码
class="language-java">package org.whatisjava.class="tags" href="/tags/DAO.html" title=dao>dao.impl;
import java.util.List;
import org.class="tags" href="/tags/SPRING.html" title=spring>springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.class="tags" href="/tags/SPRING.html" title=spring>springframework.transaction.annotation.Propagation;
import org.class="tags" href="/tags/SPRING.html" title=spring>springframework.transaction.annotation.Transactional;
import org.whatisjava.class="tags" href="/tags/DAO.html" title=dao>dao.UserDao;
import org.whatisjava.po.User;
public class UserDaoBean extends HibernateDaoSupport implements UserDao {
@Transactional(propagation = Propagation.REQUIRED)
public void delUser(Integer id) {
getHibernateTemplate().delete((User)getHibernateTemplate().get(User.class, id));
}
@Transactional(readOnly = true)
public User findUser(Integer id) {
return (User)getHibernateTemplate().get(User.class, id);
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertUser(User user) {
getHibernateTemplate().save(user);
}
@Transactional(readOnly = true)
public List listUser() {
return getHibernateTemplate().find("from User user");
}
@Transactional(propagation = Propagation.REQUIRED)
public void updateUser(User user) {
getHibernateTemplate().update(user);
}
}
要想让这些注解配置生效,必须在配置文件里加入最后一行
class="language-java"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/aop"
xmlns:tx="http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/tx"
xsi:schemaLocation="
http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/beans http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/beans/
-beans-2.5.xsd
http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/aop http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/aop/
-aop-2.5.xsd
http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/tx http://www.class="tags" href="/tags/SPRING.html" title=spring>springframework.org/schema/tx/
-tx-2.5.xsd">
<bean id="sessionFactory" class="org.class="tags" href="/tags/SPRING.html" title=spring>springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>
<bean id="transactionManager" class="org.class="tags" href="/tags/SPRING.html" title=spring>springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
好了,以上就是 中的事务属性的介绍,以及 中如何以声明方式管理事务的。
()
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
附:
jquery中jquery对象取某元素的某属性:
$(obj).attr("xxx")
jquery对象获得dom对象 $(obj).obj()
this一般是dom对象,若要jquery对象则$(this)
()
浅析Spring提供的事务管理方法
传统的JDBC事务管理
以往使用JDBC进行数据操作时,一般采用DataSource,从数据源中得到Connection,我们知道数据源是线程安全的,而连接不是线程安全的,所以对每个请求都是从数据源中重新取出一个连接。一般的数据源由容器进行管理,包括连接池。例如TOMCAT,WEBSPHERE,WEBLOGIC等这些J2EE商业容器都提供了这个功能。
以往的我们使用JDBC在写代码时,事务管理可能会是这样:
Connection conn = null; try { conn = DBConnectionFactory.getConnection; conn.setAutoCommit(false); //do something conn.commit(); //commit transcation } catch(Exception e) { conn.rollback(); //do sth } finally { try { conn.close(); } catch(SQLException se){ //do sth.} //close ResultSet,PreparedStatement,Connection //notice:Maybe ocurr Exception when u close rs,pstmt,conn } |
按照以往的思路来写代码,代码量比较长,而且容易疏忽,忘掉一些try/catch,引发一些异常无法catch,虽然有时候我们会写DBTool类,来关闭这些资源,并且保证在关闭这些资源时,不向外抛异常。
Spring提供的编程式的事务处理
Spring提供了几个关于事务处理的类:
·TransactionDefinition //事务属性定义
·TranscationStatus //代表了当前的事务,可以提交,回滚。
·PlatformTransactionManager这个是class="tags" href="/tags/SPRING.html" title=spring>spring提供的用于管理事务的基础接口,其下有一个实现的抽象类AbstractPlatformTransactionManager,我们使用的事务管理类例如DataSourceTransactionManager等都是这个类的子类。
我们使用编程式的事务管理流程可能如下:
1 声明数据源
2 声明一个事务管理类,例如DataSourceTransactionManager,HibernateTransactionManger,JTATransactionManager等
3 在我们的代码中加入事务处理代码:
TransactionDefinition td = new TransactionDefinition(); TransactionStatus ts = transactionManager.getTransaction(td); try { //do sth transactionManager.commit(ts); } catch(Exception e){transactionManager.rollback(ts);} |
使用class="tags" href="/tags/SPRING.html" title=spring>spring提供的事务模板TransactionTemplate
void add() { transactionTemplate.execute( new TransactionCallback(){ pulic Object doInTransaction(TransactionStatus ts) { //do sth} } } |
TransactionTemplate也是为我们省去了部分事务提交、回滚代码;定义事务模板时,需注入事务管理对象.
Spring声明式事务处理
Spring声明式事务处理也主要使用了ioc,aop思想,提供了TransactionInterceptor拦截器和常用的代理类TransactionProxyFactoryBean,可以直接对组件进行事务代理。
使用TransactionInterceptor步骤
1.定义数据源,事务管理类
2.定义事务拦截器,such as:
<bean id = "transactionInterceptor" class="org.class="tags" href="/tags/SPRING.html" title=spring>springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager"><ref bean="transactionManager"/></property> <property name="transactionAttributeSource"> <value> com.test.UserManager.*r=PROPAGATION_REQUIRED </value> </property> </bean> |
3.为组件声明一个代理类:ProxyFactoryBean
<bean id="userManager" class="org.class="tags" href="/tags/SPRING.html" title=spring>springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><value>com.test.UserManager</value></property> <property name="interceptorNames"> <list> <idref local="transactionInterceptor"/> </list> </property> </bean> |
使用TransactionProxyFactoryBean:
<bean id="userManager" class="org.class="tags" href="/tags/SPRING.html" title=spring>springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"><ref bean="transactionManager"/></property> <property name="target"><ref local="userManagerTarget"/></property> <property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED</prop> <prop key="update*">PROPAGATION_REQUIRED</prop> <prop key="*">PROPAGATION_REQUIRED,readOnly</prop> </props> </property> </bean> |
TransactionProxyFactoryBean只是为组件的事务代理,如果我们要给组件添加一些业务方面的验证等,可以使用TransactionTemplate加拦截器方式,为组件添加多个拦截器,class="tags" href="/tags/SPRING.html" title=spring>spring AOP中提供了三类Advice,即前增强,后增强,抛出异常时的增强,可以灵活使用。
JOTM(Java Open
jotm的下载地址为http://jotm.objectweb. Manager)是ObjectWeb的一个开源 实现,本身也是开源应用程序服务器 JOnAS(Java Open Application Server)的一部分,为其提供 分 布式事务的功能。Spring对JOTM提供了较好的支持,提供了一个 . . . .JotmFactoryBean的支持类,在Spring2.0中也包含了JOTM相关的一些 library。
下载完成后解压缩,然后打开jotm下面conf文件夹,拷贝carol.properties文件到classpath中,并修改这个文件如下 ,最新版 本为2.0.10.
carol.properties
class="language-java"># do not use CAROL JNDI wrapper
carol.start.jndi=false
# do not start a name server
carol.start.ns=false
# Naming Factory
carol.jndi.java.naming.factory.url.pkgs=
.apache.naming
上面配置文件的目的是不使用JNDI的方式来加载JOTM的配置,当然也可以根据需要选择其它的一些配置,例如JTOM所提供的默认配置。
然后开始在Spring上下文中配置JOTM,在classpath中建立一个ApplicationContext-jotm.xml,配置如 下
ApplicationContext-jotm.xml
class="language-java"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.class="tags" href="/tags/SPRING.html" title=spring>spring-beans-2.0.xsd">
<bean id="jotm" class="
.
.
.
.JotmFactoryBean"/>
<bean id="txManager" class="
.
.
.
.JtaTransactionManager">
<property name="userTransaction" ref="jotm" />
</bean>
<bean id="ds1" class="
.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
<property name="dataSource">
<bean class="
.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
<property name="transactionManager" ref="jotm" />
<property name="driverName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:MySQL://localhost:3306/test" />
</bean>
</property>
<property name="user" value="root" />
<property name="password" value="admin" />
</bean>
<bean id="ds2" class="
.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
<property name="dataSource">
<bean class="
.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
<property name="transactionManager" ref="jotm" />
<property name="driverName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:MySQL://localhost:3306/test2" />
</bean>
</property>
<property name="user" value="root" />
<property name="password" value="admin" />
</bean>
<bean id="template1" class="
.
.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds1" />
</bean>
<bean id="template2" class="
.
.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds2" />
</bean>
<bean id="class="tags" href="/tags/DAO.html" title=dao>dao1" class="com.xa.class="tags" href="/tags/DAO.html" title=dao>dao.UserDao1">
<property name="jdbcTemplate">
<ref bean="template1"></ref>
</property>
</bean>
<bean id="class="tags" href="/tags/DAO.html" title=dao>dao2" class="com.xa.class="tags" href="/tags/DAO.html" title=dao>dao.UserDao2">
<property name="jdbcTemplate">
<ref bean="template2"></ref>
</property>
</bean>
<bean id="userServiceTarget" class="com.xa.service.UserServiceImpl">
<property name="class="tags" href="/tags/DAO.html" title=dao>dao1" ref="class="tags" href="/tags/DAO.html" title=dao>dao1"/>
<property name="class="tags" href="/tags/DAO.html" title=dao>dao2" ref="class="tags" href="/tags/DAO.html" title=dao>dao2"/>
</bean>
<bean id="userTest" class="
.
.
.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="txManager"/>
</property>
<property name="target">
<ref bean="userServiceTarget"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
</bean>
</beans>
.
/schema/beans"
xmlns:xsi="http://www.w3.
/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.
.
/schema/beans http://www.
.
/schema/beans/
上面是一个完整的Spring上下文配置,可以看第一个bean “jotm”,实际上引用了Spring内部所提供的对JOTM支持的工厂类,参考下面的配置代码段
class="language-java"><bean id="jotm" class="
.
.
.
.JotmFactoryBean"/>
随后,配置了
事务管理器,并且在管理器中使用上面所配置的jotm,如下面的 代码
class="language-java"><bean id="txManager" class="
.
.
.
.JtaTransactionManager">
<property name="userTransaction" ref="jotm" />
</bean>
再接下来就是配置多个数据源了,使用jotm提供的 jdbc.pool.StandardXAPoolDataSource 类,根据类名可以明确地看出它是用以配置多个数据源的啦,配置的代码如下
.enhydra.
class="language-java"><bean id="ds1" class="
.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
<property name="dataSource">
<bean class="
.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
<property name="transactionManager" ref="jotm" />
<property name="driverName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:MySQL://localhost:3306/test" />
</bean>
</property>
<property name="user" value="root" />
<property name="password" value="admin" />
</bean>
<bean id="ds2" class="
.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
<property name="dataSource">
<bean class="
.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
<property name="transactionManager" ref="jotm" />
<property name="driverName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:MySQL://localhost:3306/test2" />
</bean>
</property>
<property name="user" value="root" />
<property name="password" value="admin" />
</bean>
这里配置的两个数据源都连接到本地的mysql,实际上可以连接到不同的db server和不同类型的数据库,已经经过测试,这里为了方便,在本地建立了两个不同的数据库(test,test2)做测试。
随后的配置基本上和普通的Spring上下文配置相同了,根据不同的数据源配置两个jdbcTemplate,两个class="tags" href="/tags/DAO.html" title=dao>dao分别引用不同的 jdbcTemplate, 将两个class="tags" href="/tags/DAO.html" title=dao>dao注入到UserService中, 最后将service纳入事务管理,并在事务代理配置中配置回滚规则,意思为如遇异常,则强制回滚内容。配置如下所示
class="language-java"><property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
这样,一个使用JOTM
TestXa.java
事务的简单应用算大致成型了,最后,写一个 JUnit,来测试一下结果
class="language-java">package com.xa;
import class TestXa extends AbstractDependencyInjectionSpringContextTests
{
protected String[] getConfigLocations() {
return new String[] { "classpath:ApplicationContext-jotm.xml" };
}
public void testInsertBothDatabase() {
ApplicationContext ctx = this.getApplicationContext();
UserService ut = (UserService)ctx.getBean("userTest");
try {
ut.insertBothDatabase("1", null);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
.
.context.ApplicationContext;
import
.
.test.AbstractDependencyInjectionSpringContextTests;
import com.xa.service.UserService;
public
在test中,调用了UserService的insertBothDatabase方法,有两个参数,userId和UserName,另外在 方法的实现中调用了两个使用不同数据源class="tags" href="/tags/DAO.html" title=dao>dao,分别向两个不同的数据库插入输入,而test2数据库的xa_test表中,name字段是不允许为空的, 因此,在插入test2数据库时会失败.
运行这个test,然后察看数据库结果:),test和test2数据库中都没有插入成功,看serviceImpl中的代码可以知道,逻辑上 class="tags" href="/tags/DAO.html" title=dao>dao1会先于class="tags" href="/tags/DAO.html" title=dao>dao2执行,但是由于 class="tags" href="/tags/DAO.html" title=dao>dao2插入数据出现异常时整个事务被 回滚,由于事务被配置在service层,class="tags" href="/tags/DAO.html" title=dao>dao1和class="tags" href="/tags/DAO.html" title=dao>dao2都被纳入一个事务进行管理,呵呵。修改一下方法的参数,修改为
事务,在
class="language-java">ut.insertBothDatabase("1", "name1");
然后再试试test看数据库结果,如何?
第三节将简单介绍Atomikos在Spring中的配置和使用。