Spring事务管理3

来源:互联网 发布:协议端口号 编辑:程序博客网 时间:2024/06/11 14:09

如何正确配置Spring事务管理已经清楚了,接下来就是如何使用了。此时有个疑问,如何将资源与Spring的事务管理同步起来,如果我们使用DataSource作为持久层,在没有Spring事务管理情况下,我们这样写代码

Connection connection = null;try {<span style="white-space:pre"></span>String sql = "insert into person_d (name) values ('hl') ";connection = dataSource.getConnection();connection.setAutoCommit(false);PreparedStatement ps = connection.prepareStatement(sql);ps.execute();connection.commit();} catch (SQLException e) {try {if (connection != null) {connection.rollback();connection.close();}} catch (SQLException e1) {e1.printStackTrace();}e.printStackTrace();}

现在事务通过Spring控制了,我们就要用Spring提供的类来进行数据操做,以达到和Spring事务管理同步起来。

推荐的方式是使用Spring提供的模版,例如JdbcTemplate。还有一中方式是使用各种工具类,DataSourceUtils (for JDBC), EntityManagerFactoryUtils (for JPA),
SessionFactoryUtils (for Hibernate), PersistenceManagerFactoryUtils (for JDO)等,但是这种方式比较偏底层,脱离了Spring事务管理,完全由自己来控制事务也可以很好的工作,所以不推荐。还有另外一种方式是使用TransactionAwareDataSourceProxy类,它是DataSource的代理类,封装了DataSource并加入了事务管理。这个类最底层,几乎不可能用到,强烈建议不要使用此类。

使用模版例子

@Transactional()public void insertFoo(Foo foo) {String sql = "insert into person_d (name) values ('hl') ";jdbcTemplate.execute(sql);//容器注入}

使用工具类例子

@Transactional()public void insertFoo(Foo foo) {Connection connection = null;try {String sql = "insert into person_d (name) values ('hl') ";connection = DataSourceUtils.getConnection(dataSource);//dataSource通过容器注入connection.setAutoCommit(false);//可以不使用Spring事务管理,这也是不推荐使用这种方式的原因PreparedStatement ps = connection.prepareStatement(sql);ps.execute();connection.commit();} catch (SQLException e) {try {if (connection != null) {connection.rollback();connection.close();}} catch (SQLException e1) {e1.printStackTrace();}e.printStackTrace();}}

下面我们看上面例子的配置。众所周知Spring事务管理有两种使用方式,一个是声明式一个是编程式。先来看看最被推荐的声明式。

定义一个服务接口

public interface FooService {Foo getFoo(String fooName);Foo getFoo(String fooName, String barName);void insertFoo(Foo foo);void updateFoo(Foo foo);}

实现,

public class DefaultFooService implements FooService {public Foo getFoo(String fooName) {throw new UnsupportedOperationException();}public Foo getFoo(String fooName, String barName) {throw new UnsupportedOperationException();}public void insertFoo(Foo foo) {throw new UnsupportedOperationException();}public void updateFoo(Foo foo) {throw new UnsupportedOperationException();}}

配置事务切入点

<?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:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 要被加入事务的对象 --><bean id="fooService" class="org.chl.spring.transaction.service.impl.DefaultFooService" ><property name="dataSource" ref="dataSource"></property><property name="jdbcTemplate" ref="jdbcTemplate"></property></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean><!-- 配置事务的行为 相当于aop中的advice --><tx:advice id="txAdvice" transaction-manager="txManager"><!-- the transactional semantics... --><tx:attributes><!-- 所有以get开头的方法只读 --><tx:method name="get*" read-only="true"/><!-- 其他的所有方法使用默认的 读写 RuntimeException异常回滚--><tx:method name="*"/></tx:attributes></tx:advice><!-- 定义切入点 --><aop:config><aop:pointcut id="fooServiceOperation" expression="execution( x.y.service.FooService.*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/></aop:config><!-- 数据源 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/><property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/><property name="username" value="scott"/><property name="password" value="tiger"/></bean><!-- 事务管理 --><bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean></beans>
上面的例子,我们想要给fooService增加事务能力,下面写个测试类,测试一下

public final class Boot {public static void main(final String[] args) throws Exception {ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");FooService fooService = (FooService) ctx.getBean("fooService");fooService.insertFoo (new Foo());}}
配置好日志系统,会得到如下日志

<!-- Spring 容器启动... -->[AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy for bean fooServicewith 0 common interceptors and 1 specific interceptors<!-- DefaultFooService 被代理 -->[JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService]<!-- ... insertFoo(..) 在代理上被调用 -->[TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo<!-- 事务... -->[DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo][DataSourceTransactionManager] - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@a53de4]for JDBC transaction<!-- the insertFoo(..) 抛出异常... -->[RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should rollback onjava.lang.UnsupportedOperationException[TransactionInterceptor] - Invoking rollback for transaction on x.y.service.FooService.insertFoo due tothrowable [java.lang.UnsupportedOperationException]<!-- 事务回滚 -->[DataSourceTransactionManager] - Rolling back JDBC transaction on Connection[org.apache.commons.dbcp.PoolableConnection@a53de4][DataSourceTransactionManager] - Releasing JDBC Connection after transaction[DataSourceUtils] - Returning JDBC Connection to DataSourceException in thread "main" java.lang.UnsupportedOperationException atx.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14)<!-- AOP infrastructure stack trace elements removed for clarity -->at $Proxy0.insertFoo(Unknown Source)at Boot.main(Boot.java:11)
如果觉得xml配置事务麻烦的话,可以使用注解方式替代。

需要修改实现类,添加如下注解

@Transactionalpublic class DefaultFooService implements FooService {Foo getFoo(String fooName);Foo getFoo(String fooName, String barName);void insertFoo(Foo foo);void updateFoo(Foo foo);}

xml配置文件也要修改

<?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:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- this is the service object that we want to make transactional --><bean id="fooService" class="org.chl.spring.transaction.service.impl.DefaultFooService" ><property name="dataSource" ref="dataSource"></property><property name="jdbcTemplate" ref="jdbcTemplate"></property></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean><!--开启事务注解支持--><tx:annotation-driven transaction-manager="txManager"/><!-- don't forget the DataSource --><bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"destroy-method="close"><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://localhost/test" /><property name="username" value="root" /><property name="password" value="root" /></bean><!-- similarly, don't forget the PlatformTransactionManager --><bean id="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!-- other <bean/> definitions here --></beans>


@Transactional可以写在一个接口上,接口的方法上(基于接口的动态代理,jdk代理),类上,类的public方法上。注意@Transactional注解只对public方法起作用。Spring不推荐将@Transactional写在接口里面。

在proxy模式中,在目标类中,如果一个没有事务的方法调用了该类中一个有事务的方法,那么有事务的方法也将不会起作用。因为只用通过代理调用的方法才能被拦截







0 0
原创粉丝点击