Fork me on GitHub

【Spring】编程式事务和声明式事务

一、概述

Spring的事务管理分成两类:

  1. 编程式事务管理(手动编写代码完成事务管理)
  2. 声明式事务管理(不需要手动编写代码,需要配置)

二、准备工作

1. 创建表

 CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `money` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB

2. 创建项目并引入Maven依赖

  创建项目并引入如下Maven依赖:

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>   
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>
    
    <!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
    
    
    <!-- Spring 整合测试的包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    

结构目录如下:

3. 编写实体类

public class Account {
    private Integer id;
    private String name;
    private Integer money;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getMoney() {
        return money;
    }
    public void setMoney(Integer money) {
        this.money = money;
    }
    @Override
    public String toString() {
        return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
    }
}

4. 编写Dao层

编写接口


/**
 * 银行账户的相关操作 此处用了@see注释,在实现类可以继承接口中方法的注释内容
 * 
 * @author hao
 * @see AccountDaoImpl
 * 
 */
public interface IAccountDao {

    /**
     * 添加账户
     * 
     * @param account 要添加的账户
     */
    public void add(Account account);

    /**
     * 转出的方法
     * 
     * @param from  :转出的账户,打出钱
     * @param money :要转账金额
     */
    public void out(Account from, Integer money);
    
    /**
     * 转出的方法
     * 
     * @param to    转入的账户,收到钱
     * @param money 要转账金额
     */
    public void in(Account to, Integer money);
    
    /**
     * 通过名字查询账户
     * @param name 账户名
     * @return
     */
    public Account selectOneByName(String name);
    
}

实现类

public class AccountDaoImpl implements IAccountDao{

    
    @Autowired
    @Qualifier("jdbcTemplate")
    private JdbcTemplate jdbcTemplate;
    
    @Override
    public void add(Account account) {
        String sql = "insert into account values(null,?,?)";
        jdbcTemplate.update(sql,account.getName(),account.getMoney());
    }
    
    
    @Override
    public Account selectOneByName(String name) {
        String sql = "select * from account where name = ?";
        Account account = null;
        try {//这里需要捕获结果集为空时的异常
            account = jdbcTemplate.queryForObject(sql, new RowMapper<Account>() {

                @Override
                public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
                    Account account = new Account();
                    account.setId(rs.getInt("id"));
                    account.setName(rs.getString("name"));
                    account.setMoney(rs.getInt("money"));
                    return account;
                }
                
            }, name);
        } catch (EmptyResultDataAccessException e) {
            e.printStackTrace();
            return null;
        }
        
        return account;
    }
    
    
    public void out(Account from, Integer money) {
        String sql = "update account set money = money-? where name =? ";
        jdbcTemplate.update(sql, money,from.getName());
    }

    public void in(Account to, Integer money) {
        String sql ="update account set money = money+? where name =?";
        jdbcTemplate.update(sql,money,to.getName());
    }
    
}

5. 业务层

接口

/**
 * 
 * @author hao
 * @see AccountServiceImpl
 */
public interface IAccountService {

    /**
     * 向数据库中添加用户
     * 
     * @param account 要添加的用户对象
     */
    public void addAccount(Account account);

    /**
     * 转账的方法
     * 
     * @param from  转出的账户
     * @param to    转入的账户
     * @param money 转账金额
     */
    public void transfer(Account from, Account to, Integer money);
    
    /**
     * 
     * @param name 需要查询的账户名
     * @return
     */
    public Account findAccountByName(String name);
}

实现类

/**
 * 没有添加事务
 * @author hao
 *
 */
public class AccountServiceImpl implements IAccountService {
    // 注入 accountDao 利用setter方法注入属性
    private IAccountDao accountDao;

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public void addAccount(Account account) {
        accountDao.add(account);
    }
    
    @Override
    public Account findAccountByName(String name) {
        Account account = accountDao.selectOneByName(name);
        return account;
    }


    @Override
    public void transfer(Account from, Account to, Integer money) {
        accountDao.out(from, money);// 转出钱
        int i= 1/0 //出现异常时
        accountDao.in(to, money);// 收入钱
    }
}

6. 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"
       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">
    
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 配置c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean> 
    
    <!-- 这里需要注入数据源 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 此处声明Dao层类,用注解的方式将jdbcTemplate注入到了accountDao中,也可用其他方式  -->  
    <bean id="accountDao" class="com.hao.tx.dao.impl.AccountDaoImpl"></bean>
    
    
    <!-- 此处声明业务层类,用setter的方式注入了accountDao -->
    <bean id="accountService" class="com.hao.tx.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

</beans>

7. 测试

@Test
    public void test02() {
        System.out.println("转账之前....");
        Account a = accountService.findAccountByName("a");
        if(a != null) {
            System.out.println(a);
        }
        
        Account b = accountService.findAccountByName("b");
        if(b!=null) {
            System.out.println(b);
        }
        
        try {//这里捕获转账时出现的异常
            System.out.println("开始转账.....");
            accountService.transfer(a, b, 100);
        }catch(ArithmeticException e) {
            e.printStackTrace();
        }finally {
            System.out.println("转账之后....");
            Account a_trans = accountService.findAccountByName("a");
            if(a != null) {
                System.out.println(a_trans);
            }
            Account b_trans = accountService.findAccountByName("b");
            if(b!=null) {
                System.out.println(b_trans);
            }
        }
    }

结果分析:
  运行测试类中test02 之后会发现,如果没有事务处理的话,即使出现异常,也会改变数据库中的数据
  结果如下:
  
  

  
  

三、编程式事务

  上面程序中并没有添加事务,下面用手动编码的方式完成事务管理,需要事务管理器去真正管理事务对象,Spring提供了事务管理的模板(工具类)
  关于平台事务管理器,可以查阅【Spring】事务

1. 在业务层代码上使用事务模板

  创建业务类并使用编程式事务去进行事务管理,Spring 中提供了管理事务的模版

/**
 * 编程式事务
 * 
 * @author hao
 *
 */
public class AccountServiceImpl_Program implements IAccountService {

    // 注入 accountDao 利用setter方法注入属性
    private IAccountDao accountDao;

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    // 注入 transactionTemplate 利用setter方法注入属性
    private TransactionTemplate transactionTemplate;

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    @Override
    public void addAccount(Account account) {
        accountDao.add(account);
    }
    @Override
    public Account findAccountByName(String name) {
        Account account = accountDao.selectOneByName(name);
        return account;
    }

    @Override
    public void transfer(Account from, Account to, Integer money) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                accountDao.out(from, money);
                int d = 1 / 0;
                accountDao.in(to, money);
            }
        });
    }

}

2. 在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"
    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">

    <context:property-placeholder location="classpath:jdbc.properties" />
    <!-- 配置c3p0连接池 -->
    <bean id="dataSource"
        class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    
    <!-- 在容器中声明jdbc模版,这里需要注入数据源 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 此处声明Dao层类,用注解的方式将jdbcTemplate注入到了accountDao中,也可用其他方式 -->
    <bean id="accountDao" class="com.hao.tx.dao.impl.AccountDaoImpl"></bean>

    <!-- (三)、在业务层注入模板类管理事务 -->
    <!-- 此处声明业务层类,用setter的方式注入了accountDao 和 管理事务的模版 -->
    <bean id="accountService" class="com.hao.tx.service.impl.AccountServiceImpl_Program">
        <property name="accountDao" ref="accountDao"></property>
        <property name="transactionTemplate" ref="transactionTemplate"></property>
    </bean>
    
    
    <!-- (一)、注册事务管理器: -->
    
    <!-- 配置事务管理器 这里DataSourceXXX...可以用做Spring自己的或者MyBatis的事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 需要注入连接池,通过连接池获得连接 -->
        <property name="dataSource" ref="dataSource" />
    </bean>
    
    <!-- (二)、注册事务模板类: -->
    <!-- 事务管理的模板 -->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager" />
    </bean>
</beans>

3. 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext1.xml")
public class TxAcTest001 {

    @Autowired
    @Qualifier("accountService") 
    private IAccountService accountService;

    @Test
    public void test01() {
        System.out.println("转账之前....");
        Account a = accountService.findAccountByName("txa");
        if (a != null) {
            System.out.println(a);
        }

        Account b = accountService.findAccountByName("txb");
        if (b != null) {
            System.out.println(b);
        }
        
        try {// 这里捕获转账时出现的异常
            System.out.println("开始转账.....");
            accountService.transfer(a, b, 100); //会出现异常
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("转账之后....");
            Account a_trans = accountService.findAccountByName("txa");
            if (a != null) {
                System.out.println(a_trans);
            }
            Account b_trans = accountService.findAccountByName("txb");
            if (b != null) {
                System.out.println(b_trans);
            }
        }
    }
}

结果如下:

4. 手动编码方式缺点:

  代码量增加,代码有侵入性.

四、声明式事务

声明式事务管理:(原始方式)

基于TransactionProxyFactoryBean.

1. 业务类

  创建AccountServiceImpl_DeclaProxyF类,这里是为了学习时测试使用,其实无需改变业务类的任何逻辑,可以直接copy准备中的业务类。
  

2. 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"
    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">

    <context:property-placeholder
        location="classpath:jdbc.properties" />
    <!-- 配置c3p0连接池 -->
    <bean id="dataSource"
        class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    
    <!-- 在容器中声明jdbc模版,这里需要注入数据源 -->
    <bean id="jdbcTemplate"
        class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 此处声明Dao层类,用注解的方式将jdbcTemplate注入到了accountDao中,也可用其他方式 -->
    <bean id="accountDao" class="com.hao.tx.dao.impl.AccountDaoImpl"></bean>

    <!-- 此处声明业务层类,用setter的方式注入了accountDao -->
    <bean id="accountService"
        class="com.hao.tx.service.impl.AccountServiceImpl_DeclaProxyF">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    

    <!-- (一)、注册事务管理器: -->

    <!-- 配置事务管理器 这里DataSourceXXX...可以用做Spring自己的或者MyBatis的事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 需要注入连接池,通过连接池获得连接 -->
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- (二)、创建业务层代理对象: 配置生成代理对象 -->
    <bean id="accountServiceProxy"
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <!-- 目标对象 -->
        <property name="target" ref="accountService" />
        <!-- 注入事务管理器 -->
        <property name="transactionManager" ref="transactionManager" />
        <!-- 事务的属性设置 -->
        <property name="transactionAttributes">
            <props> <!-- key值表示要进行事务管理的方法 -->
                <prop key="transfer">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>

</beans>

3. 测试

  注意在测试中要注入的是代理对象

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class TxAcTest002 {

    @Autowired
    @Qualifier("accountServiceProxy") // 这里注入的代理对象
    private IAccountService accountService;

    @Test
    public void test01() {
        System.out.println("转账之前....");
        Account a = accountService.findAccountByName("txa");
        if (a != null) {
            System.out.println(a);
        }

        Account b = accountService.findAccountByName("txb");
        if (b != null) {
            System.out.println(b);
        }
        
        try {// 这里捕获转账时出现的异常
            System.out.println("开始转账.....");
            accountService.transfer(a, b, 100); //会出现异常
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("转账之后....");
            Account a_trans = accountService.findAccountByName("txa");
            if (a != null) {
                System.out.println(a_trans);
            }
            Account b_trans = accountService.findAccountByName("txb");
            if (b != null) {
                System.out.println(b_trans);
            }
        }
    }
}

4. 注意事项以及缺点

注意事项:配置代理对象时:prop格式:PROPAGATION , ISOLATION , readOnly, -Exception, +Exception 依次是:传播行为、隔离级别、事务是否只读、发生哪些异常可以回滚事务(所有的异常都回滚)、发生了哪些异常不回滚。

缺点:就是需要为每一个管理事务的类生成代理.需要为每个类都需要进行配置。

声明值事务-基于AspectJ XML方式

1. 业务类

  创建AccountServiceImpl_DeclaAspecJ类,这里是为了学习时测试使用,其实无需改变业务类的任何逻辑,可以直接copy准备中的业务类。
  

2. XML 中的配置

 <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: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/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">
            
    <context:property-placeholder location="classpath:jdbc.properties" />
    <!-- 配置c3p0连接池 -->
    <bean id="dataSource"
        class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <!-- 在容器中声明jdbc模版,这里需要注入数据源 -->
    <bean id="jdbcTemplate"
        class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 此处声明Dao层类,用注解的方式将jdbcTemplate注入到了accountDao中,也可用其他方式 -->
    <bean id="accountDao" class="com.hao.tx.dao.impl.AccountDaoImpl"></bean>

    <!-- 此处声明业务层类,用setter的方式注入了accountDao -->
    <bean id="accountService"
        class="com.hao.tx.service.impl.AccountServiceImpl_DeclaAspecJ">
        <property name="accountDao" ref="accountDao"></property>
    </bean>


    <!-- (一)、注册事务管理器: -->

    <!-- 配置事务管理器 这里DataSourceXXX...可以用做Spring自己的或者MyBatis的事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 需要注入连接池,通过连接池获得连接 -->
        <property name="dataSource" ref="dataSource" />
    </bean>


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

    
    <!-- (三)、定义aop的配置(切点和通知的组合) -->
    <!-- aop配置定义切面和切点的信息 -->
    <aop:config>
        <!-- 定义切点:哪些类的哪些方法应用增强 -->
        <aop:pointcut
            expression="execution(* com.hao.tx.service.impl.AccountServiceImpl_DeclaAspecJ+.*(..))"
            id="mypointcut" />
        <!-- 定义切面: -->
        <aop:advisor advice-ref="txAdvice"  pointcut-ref="mypointcut" />
    </aop:config>
</beans>

3. 测试

  这里无需注入代理对象

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext3.xml")
public class TxAcTest003 {

    @Autowired
    @Qualifier("accountService") //  注入Service对象,不需要注入代理对象(生成这个类的时候,已经是代理对象.)
    private IAccountService accountService;

    @Test
    public void test01() {
        System.out.println("转账之前....");
        Account a = accountService.findAccountByName("txa");
        if (a != null) {
            System.out.println(a);
        }

        Account b = accountService.findAccountByName("txb");
        if (b != null) {
            System.out.println(b);
        }
        
        try {// 这里捕获转账时出现的异常
            System.out.println("开始转账.....");
            accountService.transfer(a, b, 100); //会出现异常
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("转账之后....");
            Account a_trans = accountService.findAccountByName("txa");
            if (a != null) {
                System.out.println(a_trans);
            }
            Account b_trans = accountService.findAccountByName("txb");
            if (b != null) {
                System.out.println(b_trans);
            }
        }
    }
}

声明值事务-基于AspectJ 注解方式

1. 业务类

  • 在Service上使用注解@Transactional,此注解可以标注在方法上,还可以标注在类上:
    • 注解中有属性值:
    • isolation
    • propagation
    • readOnly
      ...
/**
 * 声明式事务-基于AspectJ 注解方式
 * 
 * @author hao
 *
 */
public class AccountServiceImpl_DeclaAspecJAnno implements IAccountService {
    // 注入 accountDao 利用setter方法注入属性
    private IAccountDao accountDao;

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public void addAccount(Account account) {
        accountDao.add(account);
    }
    
    @Override
    public Account findAccountByName(String name) {
        
        Account account = accountDao.selectOneByName(name);
        return account;
    }
    
    
    @Override
    @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false)
    public void transfer(Account from, Account to, Integer money) {
        accountDao.out(from, money);// 转出钱
        int i =1/0;   // 中间出现异常
        accountDao.in(to, money);// 收入钱
    }
    
}

2. XML中开启注解

 <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: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/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">
            
    <context:property-placeholder location="classpath:jdbc.properties" />
    <!-- 配置c3p0连接池 -->
    <bean id="dataSource"
        class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <!-- 在容器中声明jdbc模版,这里需要注入数据源 -->
    <bean id="jdbcTemplate"
        class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 此处声明Dao层类,用注解的方式将jdbcTemplate注入到了accountDao中,也可用其他方式 -->
    <bean id="accountDao" class="com.hao.tx.dao.impl.AccountDaoImpl"></bean>

    <!-- 此处声明业务层类,用setter的方式注入了accountDao 和 管理事务的模版 -->
    <bean id="accountService"
        class="com.hao.tx.service.impl.AccountServiceImpl_DeclaAspecJAnno">
        <property name="accountDao" ref="accountDao"></property>
    </bean>


    <!-- (一)、注册事务管理器: -->

    <!-- 配置事务管理器 这里DataSourceXXX...可以用做Spring自己的或者MyBatis的事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 需要注入连接池,通过连接池获得连接 -->
        <property name="dataSource" ref="dataSource" />
    </bean>

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

3. 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext4.xml")
public class TxAcTest004 {

    @Autowired
    @Qualifier("accountService") //  注入Service对象,不需要注入代理对象(生成这个类的时候,已经是代理对象.)
    private IAccountService accountService;

    @Test
    public void test01() {
        System.out.println("转账之前....");
        Account a = accountService.findAccountByName("txa");
        if (a != null) {
            System.out.println(a);
        }

        Account b = accountService.findAccountByName("txb");
        if (b != null) {
            System.out.println(b);
        }
        
        try {// 这里捕获转账时出现的异常
            System.out.println("开始转账.....");
            accountService.transfer(a, b, 100); //会出现异常
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("转账之后....");
            Account a_trans = accountService.findAccountByName("txa");
            if (a != null) {
                System.out.println(a_trans);
            }
            Account b_trans = accountService.findAccountByName("txb");
            if (b != null) {
                System.out.println(b_trans);
            }
        }
    }
}
posted @ 2019-09-25 14:02  这个世界~  阅读(2378)  评论(0编辑  收藏  举报