您的位置:首页 > 编程语言 > Java开发

Spring声明型事务管理示例详解

2012-02-14 16:59 302 查看
Spring不直接实现管理事务,它只是管理哪些方法需要有事务,通过AOP的方式调用底层的事务管理器进行事务管理.有需要事务管理的类是被Spring代理创建的,代理类通过在连接点前后插入预处理过程(开始事务)和后处理过程(commit或rollbak)实现事务管理.
Spring定义了事务管理器(PlatformTransactionManager)预处理应该有些什么样的方法,在声明了事务的方法被调用的时候,通过调用PlatformTransactionManager的commit(TransactionStatus status)或者rollback(TransactionStatus status),commit和rollback由具体的TransactionManager提供实现,如下图的DataSourceTransactionManager,其真正的事务管理仍然依靠Connection的commit或者rollback来完成.(图片来源于Spring
in Action)



一,先用示例来说明一个JDBC的DataSourceTransactionManager:代码结构为在DAO中访问DB,在Service类中访问DAO,事务配置在Service类上.

Spring的配置如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
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-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" default-autowire="byName">
<bean id="jdbcTestDAO" class="com.test.spring.tx.jdbc.JDBCTestDAO">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="buzSingleService" class="com.test.spring.tx.jdbc.BuzSingleService"/>
<bean id="buzMultipleService" class="com.test.spring.tx.jdbc.BuzMultipleService"/>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@147.151.240.XX:1521:ORCL" />
<property name="username" value="but" />
<property name="password" value="but" />
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<aop:config>
<aop:pointcut id="serviceOperation"
expression="execution(* com.test.spring.tx.jdbc.*Service*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" />
</aop:config>
</beans>
**事务配置为 expression="execution(* com.test.spring.tx.jdbc.*Service*.*(..))",表示com.test.spring.tx.jdbc包中类名带有Service的所有方法都要在事务中运行.

DAO类:
public class JDBCTestDAO {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void testInsert(int id, String val) {
this.jdbcTemplate.update("insert into A (ID, VAL) values (?, ?)", id, val);
}
public void testUpdate(int id, String val) {
this.jdbcTemplate.update("update A set val = ? where id = ?", val, id);
}
public void testDelete(int id) {
this.jdbcTemplate.update("delete from A where id=?", id);
}
}
其中一个Service类如下:
public class BuzSingleService {
@Autowired
JDBCTestDAO jdbcTestDAO;
public void testTX1() throws Exception {
jdbcTestDAO.testInsert(0, "val0");
jdbcTestDAO.testInsert(1, "val1");
}
public void testTX2() throws Exception {
jdbcTestDAO.testInsert(2, "runtime exception nullpoint");
String nullStr = null;
nullStr.length();
}
public void testTX3() throws Exception {
jdbcTestDAO.testInsert(3, "checked exception CheckedException");
throw new CheckedException();
}
public JDBCTestDAO getJdbcTestDAO() {
return jdbcTestDAO;
}
public void setJdbcTestDAO(JDBCTestDAO jdbcTestDAO) {
this.jdbcTestDAO = jdbcTestDAO;
}
}
对于这个Service类里的每一个方法如果被一个客户端代码调用(该客服端代码不是在Spring配置的事务中运行),那么每一个方法就是一个独立的事务(propagation="REQUIRED"配置决定的,这里只从整体上说明Spring的事务管理,不对propagation的其他配置情况做介绍).下面举一些例子,以下代码假定都是在其他的一个main函数中运行:

a,数据库插入两条数据,serv.testTX1作为一个整体的事务.

BuzSingleService serv =(BuzSingleService)ctx.getBean("buzSingleService");

serv.testTX1();

b,serv.testTX2()方法中有"运行时"异常(RuntimeException)抛出,由于调用者不是运行在事务中,serv.testTX1();和serv.testTX2();是相互独立的,仍然有两条数据插入数据库.

BuzSingleService serv =(BuzSingleService)ctx.getBean("buzSingleService");

serv.testTX1();

serv.testTX2();

c,testTX1方法改为如下,在插入第二条数据后抛出"运行时"异常.没有数据插入到数据库,testTX1内部的所有数据库操作处于同一个事务.
public void testTX1() throws Exception {
jdbcTestDAO.testInsert(0, "val0");
jdbcTestDAO.testInsert(1, "val1");
String nullStr = null;
nullStr.length();
}
d,在默认情况下,抛出不是"运行时"异常类型的异常不会rollback事务.异常会被抛出到调用者,但是事务已经提交.

CheckedException 是一个用户自定义的异常:public class CheckedException extends Exception{}

BuzSingleService serv =(BuzSingleService)ctx.getBean("buzSingleService");

serv.testTX3();

e,在一个处于事务的方法中调用BuzSingleService的两个方法,被调用的两个方法会处于同一个事务中:
public class BuzMultipleService {
@Autowired
BuzSingleService buzSingleService;
public void testTX() throws Exception {
buzSingleService.testTX1();
buzSingleService.testTX2();
String nullStr = null;
nullStr.length();
}
public BuzSingleService getBuzSingleService() {
return buzSingleService;
}
public void setBuzSingleService(BuzSingleService buzSingleService) {
this.buzSingleService = buzSingleService;
}
}
在一个main方法中如下调用,没有数据会被插入到数据库.因为BuzMultipleService.testTX方法处于事务中,buzSingleService.testTX1 和buzSingleService.testTX2在一个有事务的方法中被调用的话,它们形成一个整体的事务.(变化和propagation的设置有关系)

        BuzMultipleService serv =(BuzMultipleService)ctx.getBean("buzMultipleService");

        serv.testTX();

二,集成Hibernate后通过HibernateTransactionManager来进行事务管理的方法和JDBC类似,Spring的配置如下:
<bean id="plainHibernateDAO" class="com.test.spring.tx.hibernate.PlainHibernateDAO">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
<bean id="hbSingleService" class="com.test.spring.tx.hibernate.HBSingleService"/>
<bean id="hbMultipleService" class="com.test.spring.tx.hibernate.HBMultipleService"/>
<bean id="mySessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:config/Hibernate.cfg.xml" />
</bean>
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="hbOperation"
expression="execution(* com.test.spring.tx.hibernate.*Service*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="hbOperation" />
</aop:config>
需要注意的是在Hibernate的配置文件中不能指定 <property name="current_session_context_class">thread</property>

**在同一个配置文件可以配置多个TransactionManager,用于不同的事务,如一个hbtxManager(通过id指定)用于管理Hibernate事务,一个jmstxManager用于管理JMS事务.

3,管理JMS事务,这里的MQ是ActiveMQ,事务commit和rollback的特性和JDBC的示例相同.Spring的配置如下:
<bean id="jmsAccessor" class="com.test.spring.tx.jms.JmsAccessor">
<property name="jmsTemplate" ref="jmsTemplate"/>
<property name="destination" ref="destination"/>
</bean>
<bean id="jmsSingleService" class="com.test.spring.tx.jms.JMSSingleService"/>
<bean id="jmstxManager" class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="jmsConnectionFactory"/>
</bean>
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="example.yorker" />
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsConnectionFactory"/>
<property name="defaultDestination" ref="destination"/>
<property name="receiveTimeout" value="10000"/>
</bean>
<tx:advice id="jmstxAdvice" transaction-manager="jmstxManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="jmsOperation"
expression="execution(* com.test.spring.tx.jms.*Service*.*(..))" />
<aop:advisor advice-ref="jmstxAdvice" pointcut-ref="jmsOperation" />
</aop:config>
JMS访问的JAVA类如下:
public class JmsAccessor {
JmsTemplate jmsTemplate;
Destination destination;
public void send() {
MessageCreator messageCreator = new MessageCreator() {
public Message createMessage(Session session) {
TextMessage message = null;
try {
message = session.createTextMessage("Hello message");
} catch (JMSException e) {
e.printStackTrace();
}
return message;
}
};
jmsTemplate.send(this.destination, messageCreator);
}

public void receive() {
TextMessage message = (TextMessage) jmsTemplate.receive();
try {
System.out.println("Message received:" + message.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public Destination getDestination() {
return destination;
}
public void setDestination(Destination destination) {
this.destination = destination;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息