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

晨曦之光 发布于 2012/04/25 16:16
阅读 2K+
收藏 3
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;
    }
}


原文链接:http://blog.csdn.net/kkdelta/article/details/7258050
加载中
0
光石头
光石头
不建议使用hibernate了。 http://www.oschina.net/p/9iuspring
返回顶部
顶部