Spring之事务管理

1.Spring事务管理的两种方式

Java EE应用的事务策略分为全局事务和局部事务。大多数情况下,我们都使用局部事务,所以这篇文章就不说全局事务了。

而Spring 框架为局部事务提供了两种管理方式,分别如下

(1)编程式事务管理:通过编程实现,但我们太懒了,所以一般不用。

(2)声明式事务管理:通过声明实现。

而声明式事务管理又可以通过以下两种方式实现

  • 基于配置文件实现
  • 基于注解的实现

2.Spring事务管理的API

Spring框架提供的事务管理抽象层主要由org.springframework.transaction包下的三个接口组成:

  • TransactionDefinition接口:用于描述事务的隔离级别、传播规则、超时时间、是否为只读事务等特性,可以编程设置这些特性,也可以通过XML文件或者注解配置。

  • TransactionStatus接口:用于描述事务的状态,事务管理器通过该接口可以获取事务的运行期状态信息,也可以通过该接口间接的回滚事务,它相比于在抛出异常时回滚事务的方式更具有可控性。

  • PlatformTransactionManager接口:该接口代表事务管理器,是Spring事务管理的核心接口。

针对不同的持久化技术,Spring提供了PlatformTransactionManager接口的不同实现类:

  • JpaTransactionManager:使用JPA持久化的事务管理器。

  • HibernateTransactionManager:使用Hibernate持久化的事务管理器。

  • DataSourceTransactionManager:使用Spring JDBC、iBatis等DataSource数据源持久化时的事务管理器。

  • JdoTransactionManager:使用JDO持久化的事务管理器。

  • JtaTransactionManager:使用全局事务的事务管理器。

3.步骤

无论是使用XML配置事务还是使用注解配置事务,都有以下几步
(1)配置数据源
(2)配置事务管理器
(3)配置事务增强
(4)配置事务增强的切面

准备工作

导jar包,这里我们使用了c3p0连接池,所以导入了c3p0的jar包
9.png

引入约束

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

新建数据库account
11.png

新建dao和service

public class AccountDao {
    // JdbcTemplate实例
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 账户减少钱
     */
    public void lessAccount() {
        String sql = "update account set money = money - ? where name = ?";
        jdbcTemplate.update(sql, 1000, "CodeTiger");
    }

    /**
     * 账户增加钱
     */
    public void moreAccount() {
        String sql = "update account set money = money + ? where name = ?";
        jdbcTemplate.update(sql, 1000, "xu");
    }
}
public class AccountService {
    private AccountDao accountDao;

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

    public void accountMoney() {
        // 一个账户减少钱
        accountDao.lessAccount();

        // 提现事务的功能
        int a = 10 / 0;
        System.out.println(a);

        // 一个账户增加钱
        accountDao.moreAccount();
    }
}

基于XML配置文件的事务管理

(1)配置数据源

    <!-- 配置c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/spring"></property>
        <property name="user" value="root"></property>
        <property name="password" value="1311664842"></property>
    </bean>

配置了c3p0连接池一些最基本的参数,仅用于连接。

(2)配置事务管理器

    <!-- 配置jdbc的事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

不同的持久化技术,使用的事务管理器不一样,这里我们使用JDBC的事务管理器。

(3)配置事务增强

使用<tx:advice>标签,可以为一个或一批(通过通配符的方式进行方法名称的匹配)方法配置事务增强。

<tx:advice>标签有两个属性

  • id:唯一标识。

  • transaction-manager:已经配置的事务管理器bean的id,如果事务管理器bean的id值为transactionManager,则可以省略此属性。

<tx:advice>标签具有<tx:attributes>子标签,通过<tx:attributes>标签的子标签<tx:method>可以配置需要被事务增强的方法,以及事务的传播、隔离、超时、只读事务、对指定异常回滚和对指定异常不会滚的属性。<tx:method>标签的属性如下:

  • name:配置需要被事务增强的方法名,可以使用通配符*。

  • propagation:配置事务的传播类型。可选择REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED之一,默认为REQUIRED。

  • isolation:配置事务的隔离级别。可选择DEFAULT、READ_UNCOMMITED、READ_COMMITED、REPEATABLE_READ、SERIALIZABLE之一,默认为DEFAULT。

  • timeout:配置事务超时时间(以秒为单位),默认为-1,表示事务超时的时间由底层的事务系统决定。

  • read-only:配置是否为只读事务,默认为false。

  • rollback-for:配置需要自动回滚事务的异常类型,默认所有RuntimeException都会回滚。

  • no-rollback-for:配置不触发自动回滚事务的异常类型,默认所有CheckedException都不会回滚。

下面的代码配置事务增强

    <!-- 配置事务增强 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 做事务操作 -->
        <tx:attributes>
            <!-- 设置进行事务操作的方法匹配原则 -->
            <tx:method name="account*"/>
        </tx:attributes>
    </tx:advice>

(4)配置事务增强的切面

这一步将事务增强与切入点关联在一起。配置ide方法与普通的切面配置相同。

    <!-- 配置切面 -->
    <aop:config>
        <!-- 配置切入点,注意这里的切入点是service,不是dao -->
        <aop:pointcut expression="execution(* com.codeliu.service.AccountService.*(..))" id="pointcut1"/>
        <!-- 把增强用到切入点上 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
    </aop:config>

事务管理也使用了AOP的原理,即把事务增强看成切面,service看成切点(当然也可以把dao看成切点,这时事务增强的方法匹配规则就得相应的进行修改)

当然,配置文件中还少了一些东西,比如service和dao的实例化,以及获取操作数据库的实例对象。把它们也加上。

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.codeliu"></context:component-scan>

    <!-- 开启aop操作 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    <!-- 配置Jdbc Template -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="accountDao" class="com.codeliu.dao.AccountDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>
    <bean id="accountService" class="com.codeliu.service.AccountService">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

最后写一个测试方法进行测试

    @Test
    public void accountTest() {
        @SuppressWarnings("resource")
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService service = (AccountService)context.getBean("accountService");
        service.accountMoney();
    }

运行测试,出现除0异常,刷新数据库,账户余额没有发生变化,说明事务管理生效了。

基于注解的事务管理

(1)启用注解式事务配置

使用注解配置事务,首先需要在XML配置文档中开启事务的注解

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

该标签有三个属性

  • transaction-manager:已经配置好的事务管理器Bean的id,如果未指定,则会查找id为transactionManager的事务管理器Bean。
  • proxy-target-class:是否通过cglib创建子类代理对象。
  • order:如果业务类除事务切面外,还需要织入其他的切面,通过该属性可以控制事务切面在目标连接点的织入顺序。

(2)使用@Transactional注解

业务类或业务类的方法如果需要被增强,则需要使用@Transactional注解进行配置。

@Transactional注解的参数和前面讲的<tx:method>标签的属性非常类似。通常情况都使用默认值,不需要改动。

@Transactional注解可用于接口、接口的方法、类、类的public方法上,由于注解的不可继承性,通常建议在类上使用该注解。

如下,给service类加注解

// 开启事务,对该类的所有方法都生效
@Transactional
public class AccountService {
    private AccountDao accountDao;

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

    @Transactional(readOnly = true)
    public void accountMoney() {
        // 一个账户减少钱
        accountDao.lessAccount();

        // 提现事务的功能
        int a = 10 / 0;
        System.out.println(a);

        // 一个账户增加钱
        accountDao.moreAccount();
    }
}

上面的代码中,给AccountService 类加了@Transactional注解,给accountMoney方法也加了@Transactional注解并设置为只读。因此最终的配置结果就是AccountService 类的所有方法都会被增强,并且accountMoney方法使用的是只读事务。(覆盖了类上的注解)

猜你喜欢

转载自blog.csdn.net/a_helloword/article/details/80530055