1、编程式事务

先配置事务管理器:

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">  
    <property name="jdbcUrl" value="${db.jdbcUrl}" />  
    <property name="user" value="${user}" />  
    <property name="password" value="${password}" />  
    <property name="driverClass" value="${db.driverClass}" />  
     <!--连接池中保留的最小连接数。 -->   
     <property name="minPoolSize">   
         <value>5</value>   
     </property>   
     <!--连接池中保留的最大连接数。Default: 15 -->   
     <property name="maxPoolSize">   
         <value>30</value>   
     </property>   
     <!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->   
     <property name="initialPoolSize">   
         <value>10</value>   
     </property>   
     <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->   
     <property name="maxIdleTime">   
         <value>60</value>   
     </property>   
     <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->   
     <property name="acquireIncrement">   
         <value>5</value>   
     </property>   
     <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。  如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0 -->   
     <property name="maxStatements">   
         <value>0</value>   
     </property>   
     <!--每60秒检查所有连接池中的空闲连接。Default: 0 -->   
     <property name="idleConnectionTestPeriod">   
         <value>60</value>   
     </property>   
     <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->   
     <property name="acquireRetryAttempts">   
         <value>30</value>   
     </property>   
     <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。Default: false -->   
     <property name="breakAfterAcquireFailure">   
         <value>true</value>   
     </property>   
     <!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的 时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable等方法来提升连接测试的性能。Default: false -->   
     <property name="testConnectionOnCheckout">   
         <value>false</value>   
     </property>   
</bean>  
<!--DataSourceTransactionManager位于org.springframework.jdbc.datasource包下,数据源事务管理类,提供对单个javax.sql.DataSource数据源的事务管理,主要用于JDBC,Mybatis框架事务管理。 -->  
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
    <property name="dataSource" ref="dataSource" />  
</bean>

 

1.1 PlatformTransactionManager

其业务代码:

@Resource  
    private PlatformTransactionManager txManager;  
    @Resource  
    private  DataSource dataSource;  
    private static JdbcTemplate jdbcTemplate;   
    private static final String INSERT_SQL = "insert into user(id) values(?)";  
    private static final String COUNT_SQL = "select count(*) from user";  
    @Test  
    public void testdelivery(){  
        //定义事务隔离级别,传播行为,  
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();    
        def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);    
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);    
        //事务状态类,通过PlatformTransactionManager的getTransaction方法根据事务定义获取;获取事务状态后,Spring根据传播行为来决定如何开启事务  
        TransactionStatus status = txManager.getTransaction(def);    
        jdbcTemplate = new JdbcTemplate(dataSource);  
        int i = jdbcTemplate.queryForInt(COUNT_SQL);    
        System.out.println("插入前表中记录总数:"+i);  
        try {    
            jdbcTemplate.update(INSERT_SQL, "1");    
            txManager.commit(status);  //提交status中绑定的事务  
        } catch (RuntimeException e) {    
            txManager.rollback(status);  //回滚  
        }    
        i = jdbcTemplate.queryForInt(COUNT_SQL);    
        System.out.println("插入后表中记录总数:"+i);  
    }

直接使用PlatformTransactionManager的优点:可以完全控制整个事务的过程。缺点也很明显,从应用程序开发的角度来看,其依然过于底层,全使用该方法管理事务,重复代码量也是惊人的。

鉴于使用PlatformTransactionManager进行事务管理流程比较固定,可以考虑像Spring的数据访问那样,使用模版方法模式与Callback相结合的方式,对直接使用PlatformTransactionManager的代码进行封装,于是就有了TransactionTemplate的管理事务的方式。

1.2 TransactionTemplate

jdbcTemplate = new JdbcTemplate(dataSource);  
    int i = jdbcTemplate.queryForInt(COUNT_SQL);    
    System.out.println("表中记录总数:"+i);  
    //构造函数初始化TransactionTemplate  
    TransactionTemplate template = new TransactionTemplate(txManager);  
    template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);    
    //重写execute方法实现事务管理  
    template.execute(new TransactionCallbackWithoutResult() {  
        @Override  
        protected void doInTransactionWithoutResult(TransactionStatus status) {  
            jdbcTemplate.update(INSERT_SQL, "中文");   //字段为int型,所以插入肯定失败报异常,自动回滚,代表TransactionTemplate自动管理事务  
        }}  
    );  
    i = jdbcTemplate.queryForInt(COUNT_SQL);    
    System.out.println("表中记录总数:"+i);

Spring针对TransactionTemplate提供了两个Callback接口,TransactionCallback和TransactionCallbackWithoutResult,二者的唯一区别式是否需要返回结果。

TransactionTemplate会捕捉事务操作中的unchecked exception并进行回滚事务,然后异常抛给上层处理。所以我们只需要处理特定于应用程序异常即可。

2.声明式事务

<tx:advice id="advice" transaction-manager="transactionManager">  
    <tx:attributes>  
        <!-- 拦截save开头的方法,事务传播行为为:REQUIRED:必须要有事务, 如果没有就在上下文创建一个 -->  
        <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" timeout="" read-only="false" no-rollback-for="" rollback-for=""/>  
        <!-- 支持,如果有就有,没有就没有 -->  
        <tx:method name="*" propagation="SUPPORTS"/>  
    </tx:attributes>  
</tx:advice>  
<!-- 定义切入点,指定impl包下的所有方法 -->  
<aop:config>  
    <aop:pointcut expression="execution(* com.lyx.*.service.impl.*.*(..))" id="pointcut"/>  
    <!--<aop:advisor>定义切入点,与通知,把tx与aop的配置关联,才是完整的声明事务配置 -->  
    <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>  
</aop:config>