Spring中的事务管理

Spring中的事务管理

事务的概念

事务

是逻辑上一组操作,要么全都成功,要么全都失败。

事务特性-ACID

原子性:事务不可分割

一致性:事务执行的前后,数据完整性保持一致.

隔离性:一个事务执行的时候,不应该受到其他事务的打扰

持久性:一旦结束,数据就永久的保存到数据库.

脏读、不可重复读虚读

如果不考虑隔离性

脏读:一个事务读到另一个事务未提交数据。

不可重复读:一个事务读到另一个事务已经提交数据(update)导致一个事务多次查询结果不一致

虚读:一个事务读到另一个事务已经提交数据(insert)导致一个事务多次查询结果不一致

事务的隔离级别

未提交读:以上情况都有可能发生。

已提交读:避免脏读,但不可重复读,虚读是有可能发生。

可重复读:避免脏读,不可重复读,但是虚读有可能发生。

串行的:避免以上所有情况。

事务管理之事务开发常用API的详解

分层开发:事务处在Service层。

Spring事务管理高层抽象主要包括3个接口,它们的常见方法:

(1)PlatformTransactionManager:平台事务管理器

 

Method Summary
 void commit(TransactionStatus status)
         提交
 TransactionStatus getTransaction(TransactionDefinition definition)
         获取事务
 void rollback(TransactionStatus status)
       回滚

(2)TransactionDefinition:事务定义

ISOLation_XXX:设置事务的隔离级别

PROPAGATION_XXX:设置事务的传播行为。不是JDBC中有的,是为了解决实际开发中的问题,后面会详细的说。

TIMEOUT_DEFAULT:过期时间

 

Field Summary
static int ISOLATION_DEFAULT
          Use the default isolation level of the underlying datastore.
static int ISOLATION_READ_COMMITTED
          Indicates that dirty reads are prevented; non-repeatable reads and phantom reads can occur.
static int ISOLATION_READ_UNCOMMITTED
          Indicates that dirty reads, non-repeatable reads and phantom reads can occur.
static int ISOLATION_REPEATABLE_READ
          Indicates that dirty reads and non-repeatable reads are prevented; phantom reads can occur.
static int ISOLATION_SERIALIZABLE
          Indicates that dirty reads, non-repeatable reads and phantom reads are prevented.
static int PROPAGATION_MANDATORY
          Support a current transaction; throw an exception if no current transaction exists.
static int PROPAGATION_NESTED
          Execute within a nested transaction if a current transaction exists, behave likePROPAGATION_REQUIRED else.
static int PROPAGATION_NEVER
          Do not support a current transaction; throw an exception if a current transaction exists.
static int PROPAGATION_NOT_SUPPORTED
          Do not support a current transaction; rather always execute non-transactionally.
static int PROPAGATION_REQUIRED
          Support a current transaction; create a new one if none exists.
static int PROPAGATION_REQUIRES_NEW
          Create a new transaction, suspending the current transaction if one exists.
static int PROPAGATION_SUPPORTS
          Support a current transaction; execute non-transactionally if none exists.
static int TIMEOUT_DEFAULT 
          Use the default timeout of the underlying transaction system, or none if timeouts are not supported.

 

 

Method Summary
 int getIsolationLevel()
          Return the isolation level.
 String getName() 
          Return the name of this transaction.
 int getPropagationBehavior()
          Return the propagation behavior.
 int getTimeout() 
          Return the transaction timeout.
 boolean isReadOnly() 
          Return whether to optimize as a read-only transaction.

 

(3)TransactionStatus:事务状态

是否有保存点,是否一个新事务,是否已经提交,是否已经回滚

 

Method Summary
 void flush() 
          Flush the underlying session to the datastore, if applicable: for example, all affected Hibernate/JPA sessions.
 boolean hasSavepoint() 
          Return whether this transaction internally carries a savepoint, that is, has been created as nested transaction based on a savepoint.
 boolean isCompleted() 
          Return whether this transaction is completed, that is, whether it has already been committed or rolled back.
 boolean isNewTransaction()
          Return whether the present transaction is new (else participating in an existing transaction, or potentially not running in an actual transaction in the first place).
 boolean isRollbackOnly()
          Return whether the transaction has been marked as rollback-only (either by the application or by the transaction infrastructure).
 void setRollbackOnly()
          Set the transaction rollback-only.

 

这三个接口的关系:

PlatformTransactionManager通过TransactionDefinition设置事务相关信息管理事务,管理事务过程中,产生一些事务状态:状态由TransactionStatus记录.

API详解:

PlatformTransactionManager:接口.

Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现

 org.springframework.jdbc.datasource.DataSourceTransactionManager :  使用SpringJDBC或iBatis进行持久化数据时使用

org.springframework.orm.hibernate3.HibernateTransactionManager       :  使用Hibernate3.0版本进行持久化数据时使用

org.springframework.orm.jpa.JpaTransactionManager    使用JPA进行持久化时使用

org.springframework.jdo.JdoTransactionManager    当持久化机制是Jdo时使用

org.springframework.transaction.jta.JtaTransactionManager       使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用

 

TransactionDefinition:接口

ISOLation_DEFAULT:默认级别 MySQL:repeatable_read;Oracle:read_commited

 

事务的传播行为:不是JDBC事务管理,解决业务层之间的调用的事务的关系

PROPAGATION_REQUIRED:支持当前事务,如果不存在就新建一个

* A,B    如果A有事务,B使用A的事务,如果A没有事务,B就开启一个新的事务.(A,B是在一个事务中。)

PROPAGATION_SUPPORTS     :支持当前事务,如果不存在,就不使用事务

* A,B    如果A有事务,B使用A的事务,如果A没有事务,B就不使用事务.

PROPAGATION_MANDATORY    :支持当前事务,如果不存在,抛出异常

* A,B    如果A有事务,B使用A的事务,如果A没有事务,抛出异常.

PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务

* A,B    如果A有事务,B将A的事务挂起,重新创建一个新的事务.(A,B不在一个事务中.事务互不影响.)

PROPAGATION_NOT_SUPPORTED    以非事务方式运行,如果有事务存在,挂起当前事务

* A,B    非事务的方式运行,A有事务,就会挂起当前的事务.

PROPAGATION_NEVER    以非事务方式运行,如果有事务存在,抛出异常

PROPAGATION_NESTED   如果当前事务存在,则嵌套事务执行

* 基于SavePoint技术

* A,B    A有事务,A执行之后,将A事务执行之后的内容保存到SavePoint.B事务有异常的话,用户需要自己设置事务提交还是回滚

事务管理快速入门实例

Spring的事务管理分成两类:

1、 编程式事务管理:手动编写代码完成事务管理。在实际应用中很少使用,通过TransactionTemplate手动管理事务。

2、声明式事务管理:不需要手动编写代码,配置。使用XML配置声明式事务,开发中推荐使用(代码侵入性最小),Spring的声明式事务是通过AOP实现的。

以转账案例为基本场景,下面介绍转账案例的环境准备:

(1)创建数据库表account:

CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `money` double DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `account` VALUES ('1', 'aaa', '1000');
INSERT INTO `account` VALUES ('2', 'bbb', '1000');
INSERT INTO `account` VALUES ('3', 'ccc', '1000');

(2)创建一个web项目,导入相应的jar包(基本开发包、mysql驱动、C3P0连接池)

(3)src下建立配置文件applicationContext.xml、jdbc.properties、log4j.properties

jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://127.0.0.1:3306/springjdbc?characterEncoding=utf-8
jdbc.name = root
jdbc.password = 123456

(4)新建两个接口

package com.js.demo2;


public interface AccountDao {
    /**
     * 转出的方法
     * @param from:转出得人
     * @param money:转账金额
     */
    public void out(String from,Double money);
    /**
     * 转入的方法
     * @param to:转入的人
     * @param money:转账金额
     */
    public void in(String to,double money);
}
package com.js.demo2;
/**
 * 账户的业务层的接口
 * @author JiangShuai
 *
 */
public interface AccountService {
    /**
     * 转账的方法
     * @param from:从哪转出
     * @param to:转入目标
     * @param money:转账金额
     */
    public void transfer(String from,String to,double money);
}

(6)编写两个实现类

package com.js.demo2;

import org.springframework.jdbc.core.support.JdbcDaoSupport;


public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

    /**
     * 转出的方法
     * @param from:转出得人
     * @param money:转账金额
     */
    public void out(String from, Double money) {
        String sql = "update account set money = money - ? where name = ?";
        this.getJdbcTemplate().update(sql,money,from);
    }
    /**
     * 转入的方法
     * @param to:转入的人
     * @param money:转账金额
     */
    public void in(String to, double money) {
        String sql = "update account set money = money + ? where name = ?";
        this.getJdbcTemplate().update(sql,money,to);
    }


}
package com.js.demo2;

public class AccountServiceImpl implements AccountService{
    
    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
    /**
     * 转账的方法
     * @param from:从哪转出
     * @param to:转入目标
     * @param money:转账金额
     */
    public void transfer(String from, String to, double money) {
        accountDao.out(from, money);
//        int i = 1/0;   //制造异常
        accountDao.in(to, money);
        System.out.println("用户"+from+"向用户"+to+"转账"+money+"元");
    }
}

(7)在配置文件中配置bean

<?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:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 第1种,配置属性文件:配置bean,暂时注释 -->
<!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:jdbc.properties"></property>
</bean> -->

<!-- 第2种,配置属性文件:引入context约束,然后解析属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置C3P0连接池 --> 
<bean id="C3P0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.name}"></property>
<property name="password" value="${jdbc.password}">
</property> </bean>

<!-- 定义JDBC的模板类 -->
<bean id="C3P0JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="C3P0DataSource"></property> </bean>

<!-- 业务层类 --> <bean id="accountService" class="com.js.demo2.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property> </bean>

<!-- 持久层类 --> <bean id="accountDao" class="com.js.demo2.AccountDaoImpl">
<!-- 注入连接池对象,通过连接池对象去创建JDBC模板 -->
<property name="dataSource" ref="C3P0DataSource"></property>
</bean>
</beans>

(8)测试一下这个场景的功能

package com.js.demo2;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
/**
 * 测试转账功能
 * @author hdb
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class C3P0DataSourceAccountTest {
    
    @Autowired
    @Qualifier("accountService")
    private AccountService accountService;
    
    @Test
    public void demo(){
        //完成转账
        accountService.transfer("aaa", "bbb", 100d);
    }
}

 查看数据库:

金额改变正确,说明功能正常。

这时候,系统内不存在事务,如果在转账过程中有异常发生,取消

AccountServiceImpl 中注释//int i = 1/0; //制造异常

重新运行测试:

发现“aaa”的钱扣了,但是“bbb”的钱没增加。

显然,这时候就需要有事务的管理了。

Spring提供了事务管理的模板,也就是一个工具类,可以省写一些代码。

第一步,注册事务管理器、事务管理模板,并在业务层注入事务管理模板,基于applicationContext.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:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 第1种,配置属性文件:配置bean,暂时注释 -->
<!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:jdbc.properties"></property>
</bean> -->

<!-- 第2种,配置属性文件:引入context约束,然后解析属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置C3P0连接池 -->
<bean id="C3P0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driver}"></property>
    <property name="jdbcUrl" value="${jdbc.url}"></property>
    <property name="user" value="${jdbc.name}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 定义JDBC的模板类 -->
<bean id="C3P0JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="C3P0DataSource"></property>
</bean>

<!-- 业务层类 -->
<bean id="accountService" class="com.js.demo2.AccountServiceImpl">
    <!-- 在业务层注入Dao -->
    <property name="accountDao" ref="accountDao"></property>
    <!-- 在业务层注入事务管理模板 -->
    <property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
<!-- 持久层类 -->
<bean id="accountDao" class="com.js.demo2.AccountDaoImpl">
    <!-- 注入连接池对象,通过连接池对象去创建JDBC模板 -->
    <property name="dataSource" ref="C3P0DataSource"></property>
</bean>
 
<!-- 事务管理的模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager"></property>
</bean> 
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 需要注入连接池,通过连接池获得连接 -->
    <property name="dataSource" ref="C3P0DataSource"></property>
</bean>
</beans>

第二步:修改业务类,添加事务模板属性、编写事务。

package com.js.demo2;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

public class AccountServiceImpl implements AccountService{
    
    private AccountDao accountDao;
    //添加事务模板属性
    private TransactionTemplate transactionTemplate;
    
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }
    
    /**
     * 转账的方法
     * @param from:从哪转出
     * @param to:转入目标
     * @param money:转账金额
     */
    public void transfer(final  String from,final  String to,final  double money) {
        //编写事务
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                
                try{
                    accountDao.out(from, money);
                    int i = 1/0;   //制造异常
                    accountDao.in(to, money);
                    System.out.println("用户"+from+"向用户"+to+"转账"+money+"元");
                    
                }catch (Exception e) {
                    e.printStackTrace();
                }                
            }
        });            
    }
}

第三步:进行测试,和上面的一样。

第四步:测试有异常发生的情况下,事务的管理是否生效。

如果在转账过程中有异常发生,取消

AccountServiceImpl 中注释//int i = 1/0; //制造异常

第五步:重新运行测试。

抛出异常,金额没有发生变化,说明事务管理成功。

这就是使用手动编码的方式来实现事务的管理。

手动编码方式的缺点:代码量增加,代码有侵入性。业务层代码中嵌入了dao代码

事务管理之声明式事务-基于TransactionProxyBean

声明式事务管理(原始方式,基于TransactionProxyBean,它可以生成一个基于事务的对象代理,在实际应用中很少使用)的的具体步骤:

另外,还需要引入aop包和aop联盟的包

1、配置applicationContext.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:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 第1种,配置属性文件:配置bean,暂时注释 -->
<!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:jdbc.properties"></property>
</bean> -->

<!-- 第2种,配置属性文件:引入context约束,然后解析属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!-- 配置C3P0连接池 -->
<bean id="C3P0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driver}"></property>
    <property name="jdbcUrl" value="${jdbc.url}"></property>
    <property name="user" value="${jdbc.name}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 定义JDBC的模板类 -->
<bean id="C3P0JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="C3P0DataSource"></property>
</bean>

<!-- 业务层类 -->
<bean id="accountService" class="com.js.demo2.AccountServiceImpl">
    <!-- 在业务层注入Dao -->
    <property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 持久层类 -->
<bean id="accountDao" class="com.js.demo2.AccountDaoImpl">
    <!-- 注入连接池对象,通过连接池对象去创建JDBC模板 -->
    <property name="dataSource" ref="C3P0DataSource"></property>
</bean>
 
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 需要注入连接池,通过连接池获得连接 -->
    <property name="dataSource" ref="C3P0DataSource"></property>
</bean>

<!-- 配置生成代理对象 -->
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <!-- 目标对象 -->
    <property name="target" ref="accountService"></property>
    <!-- 注入事务管理器 -->
    <property name="transactionManager" ref="transactionManager"></property>
    <!-- 设置事务的一些属性 -->
    <property name="transactionAttributes">
        <props>
            <prop key="transfer">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>
</beans>

【补充】:prop参数配置的格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception

-顺序:传播行为、隔离级别、事务是否只读、发生哪些异常可以回滚事务(所有的异常都回滚)、发生了哪些异常不回滚

2、编写测试类

package com.js.demo2;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
/**
 * 测试转账功能
 * @author hdb
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class C3P0DataSourceAccountTest {
    
    @Autowired
    @Qualifier("accountServiceProxy")
    private AccountService accountService;
    
    @Test
    public void demo(){
        //完成转账
        accountService.transfer("aaa", "bbb", 100d);
    }

}
View Code

测试有异常发生的情况下,事务的管理是否生效,详细参考上面。

事务管理之声明式事务-基于切面自动代理

声明式事务的管理:(自动代理,基于切面),这种方式需要重点掌握。

第1步:导入相应的jar包。aspectJ包(两个)

第2步:引入相应的约束 aop约束、tx约束,配置事务管理器,配置增强、切面、切点

<?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:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

<!-- 纯用注解配置bean的情况下context:annotation-config可以省略,当混合使用xml和注解配置bean的时候就不能省略了-->
<!-- 作用是使@Resource、@PostConstruct、@PreDestroy、@AutoWired生效-->
<context:annotation-config></context:annotation-config>

<!-- 第1种,配置属性文件:配置bean,暂时注释 -->
<!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:jdbc.properties"></property>
</bean> -->

<!-- 第2种,配置属性文件:引入context约束,然后解析属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>


<!-- 配置C3P0连接池 -->
<bean id="C3P0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driver}"></property>
    <property name="jdbcUrl" value="${jdbc.url}"></property>
    <property name="user" value="${jdbc.name}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 定义JDBC的模板类 -->
<bean id="C3P0JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="C3P0DataSource"></property>
</bean>

<!-- 业务层类 -->
<bean id="accountService" class="com.js.demo2.AccountServiceImpl">
    <!-- 在业务层注入Dao -->
    <property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 持久层类 -->
<bean id="accountDao" class="com.js.demo2.AccountDaoImpl">
    <!-- 注入连接池对象,通过连接池对象去创建JDBC模板 -->
    <property name="dataSource" ref="C3P0DataSource"></property>
</bean>
 
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 需要注入连接池,通过连接池获得连接 -->
    <property name="dataSource" ref="C3P0DataSource"></property>
</bean>

<!-- 定义一个增强 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- 增强事务的属性的配置 -->
    <!-- isolation:事务的隔离级别
         propagation:事务的传播行为
         read-only:false,不是只读
         timeout:-1不过期
         no-rollback-for:发生哪些异常不回滚
         rollback-for:发生哪些异常回滚
     -->
    <tx:attributes>
        <tx:method name="transfer" ></tx:method>
    </tx:attributes>
</tx:advice>

<!-- aop配置定义切面 和切点的信息--> 
<aop:config>
    <!-- 定义切点:哪些类的哪些方法应用增强 -->
    <aop:pointcut expression="execution(* com.js.demo2.AccountService+.*(..))" id="mypointcut"/>
    <!-- 定义切面 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="mypointcut"/>
</aop:config> 

</beans>

 

第3步:编写测试类

package com.js.demo2;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
/**
 * 测试转账功能
 * @author hdb
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class C3P0DataSourceAccountTest {
    
    @Autowired
    @Qualifier("accountService")
    private AccountService accountService;
    
    @Test
    public void demo(){
        //完成转账
        accountService.transfer("aaa", "bbb", 100d);
    }

}
View Code

测试有异常发生的情况下,事务的管理是否生效,详细参考上面。

事务管理之声明式事务-基于注解

第1步:导入相应的jar包。

第2步:配置事务管理器、并开启注解的事务管理

<?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:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

<!-- 纯用注解配置bean的情况下context:annotation-config可以省略,当混合使用xml和注解配置bean的时候就不能省略了-->
<!-- 作用是使@Resource、@PostConstruct、@PreDestroy、@AutoWired生效-->
<context:annotation-config></context:annotation-config>

<!-- 第1种,配置属性文件:配置bean,暂时注释 -->
<!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:jdbc.properties"></property>
</bean> -->

<!-- 第2种,配置属性文件:引入context约束,然后解析属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!-- 配置C3P0连接池 -->
<bean id="C3P0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driver}"></property>
    <property name="jdbcUrl" value="${jdbc.url}"></property>
    <property name="user" value="${jdbc.name}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 定义JDBC的模板类 -->
<bean id="C3P0JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="C3P0DataSource"></property>
</bean>

<!-- 业务层类 -->
<bean id="accountService" class="com.js.demo2.AccountServiceImpl">
    <!-- 在业务层注入Dao -->
    <property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 持久层类 -->
<bean id="accountDao" class="com.js.demo2.AccountDaoImpl">
    <!-- 注入连接池对象,通过连接池对象去创建JDBC模板 -->
    <property name="dataSource" ref="C3P0DataSource"></property>
</bean>
 
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 需要注入连接池,通过连接池获得连接 -->
    <property name="dataSource" ref="C3P0DataSource"></property>
</bean>

<!-- 开启注解的事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

第3步:在Service上使用注解AccountServiceImpl:@Transactional

package com.js.demo2;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;


@Transactional
public class AccountServiceImpl implements AccountService{
    
    private AccountDao accountDao;
    //添加事务模板属性
    private TransactionTemplate transactionTemplate;
    
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }
    
    /**
     * 转账的方法
     * @param from:从哪转出
     * @param to:转入目标
     * @param money:转账金额
     */
    public void transfer(final  String from,final  String to,final  double money) {
        //编写事务
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                
                try{
                    accountDao.out(from, money);
//                    int i = 1/0;   //制造异常
                    accountDao.in(to, money);
                    System.out.println("用户"+from+"向用户"+to+"转账"+money+"元");
                    
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
            
    }
}
View Code

第4步:编写测试类

package com.js.demo2;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
/**
 * 测试转账功能
 * @author hdb
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class C3P0DataSourceAccountTest {
    
    @Autowired
    @Qualifier("accountService")
    private AccountService accountService;
    
    @Test
    public void demo(){
        //完成转账
        accountService.transfer("aaa", "bbb", 100d);
    }

}
View Code

测试有异常发生的情况下,事务的管理是否生效,详细参考上面。

posted @ 2018-08-17 01:25  Coosee  阅读(290)  评论(0编辑  收藏  举报