Spring 事务管理

1 什么是事务

事务是指逻辑上的一组操作,要么全部成功,要么全部失败

2 事务的特性

  • 原子性
    • 原子性是指事务是一个不可分割 的工作单位,事务中的操作要么全部都发生,要么都不发生。
  • 一致性
    • 一致性是指事务前后的数据的完整性必须保持一致
  • 隔离性
    • 隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离
  • 持久性
    • 持久性是指一个事物一旦被提交,它对数据库中数据的改变就是永久性的,即使数据库发生故障也不应对其有任何影响

3 Spring事务管理的API

Spring事务管理高层抽象主要包括3个接口

  • PlatformTransactionManager:事务管理器
  • TransactionDefinition:事务定义信息(隔离,传播,超时,只读)
  • TransactionStatus:事务具体运行状态

3.1 PlatformTransactionManager接口

3.1.1 Spring为不同的框架提供了不同的PlatformTransactionManager接口实现

事务说明
org.springframework.jdbc.datasource.DataSourceTransactionManager使用Spring JDBC或iBatis进行持久化数据时使用
org.springframework.jdbc.orm.hibernate3.HibernateTransactionManager使用Hibernate3.0版本进行持久化数据时使用
org.springframework.orm.jpa.JpaTransactionManager使用JPA进行持久化时的使用
org.springframework.jdo.JdaoTransactionManager当持久化机制是Jdo时使用
org.springframework.transaction.jta.JtaTransactionManager使用一个JTA实现管理事务,在一个事务跨越多个资源时必须使用

3.2 TransactionDefinition接口

3.2.1 几个概念:

  • 脏读:
    • 一个事务读取了另一个事务改写但是还未提交的数据,如果这些数据被回滚,则读到的数据是无效的
  • 不可重复读:
    • 在同一个事务中,多次读取同一数据返回的结果有所不同
  • 幻读:
    • 一个事务读取了几行记录后,另一个事务插入一些记录。幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录

3.2.2 事务隔离级别

隔离级别含义
DEFAULT使用后端数据库默认的隔离级别(Spring中的选择项)
READ_UNCOMMITTED允许你读取还未提交的但是改变了的数据。可能是脏读,幻读,不可重复读
READ_COMMITED允许在并发事务已经提交后读取。可防止脏读,但是幻读,不可重复读仍可发生
REPEATABLE_READ对于相同的字段的多次读取是一致的,除非数据被事务本身改变。可防止脏读,幻读,不可重复读
SERIALIZABLE完全服从ACID的隔离级别,确保不会发生脏,幻,不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的

3.2.3 事务传播行为

事务传播行为类型说明
PROPAGATION_REQUIRED支持当前事务,如果不存在就新建一个
PROPAGATION_SUPPORTS支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY支持当前事务,如果不存在,就抛出异常
PROPAGATION_REQUIRED_NEW如果有事务存在,挂起当期事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTED以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED如果当前事务存在,则嵌套事务执行

实例应用

银行转账问题的Demo

eg.:

当A账户向B账户转账时,应该保证A的钱转出去后,B一定能收到,如果任意一方出错,则整个转账过程就是不成功的。确保双方账户的安全性。这就好比一个事务一样,要么全部成功,要么全部失败。

初始状态,没做处理

  1. 先创建数据库springdemo和表格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');

测试前数据内容

+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | aaa  |  1000 |
|  2 | bbb  |  1000 |
|  3 | ccc  |  1000 |
+----+------+-------+
  1. 创建两个接口
    • AccountDao:实现对数据库的操作
package com.bart.dao;

public interface AccountDao {

    /**
     * @param in
     * @param money
     */
    public void inMoney(String in,Double money);

    /**
     * @param out
     * @param money
     */
    public void outMoney(String out,Double money);
}
====================分割线============================
package com.bart.dao;

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

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao    {

    /**
     * @param in
     * @param money
     */
    public void inMoney(String in, Double money) {
        String sql = "update account set money = money+? where name = ? ";
        this.getJdbcTemplate().update(sql,money,in);
    }

    /**
     * @param out
     * @param money
     */
    public void outMoney(String out, Double money) {
        String sql = "update account set money = money-? where name = ? ";
        this.getJdbcTemplate().update(sql,money,out);
    }
}
  • AccountService:实现业务操作,例如把aaa账户欠款打到bbb账户
package com.bart.demo;

public interface AccountService {

    /**
     * @param in
     * @param out
     * @param money
     */
    public void transfer(String in,String out,Double money); 
}
==========================分割线==============================
package com.bart.demo;

import org.springframework.transaction.support.TransactionTemplate;
import com.bart.dao.AccountDao;
import com.sun.istack.internal.FinalArrayList;

public class AccountServiceImpl implements AccountService {

    //注入accountDaoImpl
    private AccountDao accountDao;

    /**
     * @param in
     * @param out
     * @param money
     */
    public void transfer(String in, String out, Double money) {
        accountDao.outMoney(out, money);
        accountDao.inMoney(in, money);
    }


    //注入必须配置set方法
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

}
  1. 配置数据库属性
    jdbc.properties
jdbc.Driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/springdemo
jdbc.password=
jdbc.username=root
  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:aop="http://www.springframework.org/schema/aop"
    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       
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.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.username}" />
        <property name="password"  value="${jdbc.password}" />
    </bean>

    <!-- 配置业务类 -->
    <bean id="accountService" class="com.bart.demo.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!-- 配置DAO类 -->
    <bean id="accountDao" class="com.bart.dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>
  1. 测试
@Test
public void demo1() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    accountService=(AccountService) context.getBean("accountService");
    accountService.transfer("aaa", "bbb", 200.00);
}
  1. 测试结果
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | aaa  |  1200 |
|  2 | bbb  |   800 |
|  3 | ccc  |  1000 |
+----+------+-------+

可以看出开,正常情况下通过测试aaa账户的200元确实转账到了bbb账户里,但是如果在转账过程中发生了异常会导致什么情况呢?下面测试一下。
首先把所有的钱全部恢复默认值,都是1000元。修改AccountServiceImpl类的transfer方法,添加一个异常。

/**
     * @param in
     * @param out
     * @param money
     */
    public void transfer(String in, String out, Double money) {
        accountDao.outMoney(out, money);
        int i=1/0;
        accountDao.inMoney(in, money);
    }

再次进行测试。发现钱“转丢了”。。。

+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | aaa  |  1000 |
|  2 | bbb  |   800 |
|  3 | ccc  |  1000 |
+----+------+-------+

bbb的钱转出去了200元,但是由于手动添加了一个异常aaa并没有收到bbb转过来的200元。这种情况是不允许出现的。为了解决这个问题,需要用到事务处理,Spring框架给我们提供了几种方法。

编程式事务管理

编程式事务管理使用spring框架提供的org.springframework.transaction.support.TransactionTemplate模板来进行事务的管理。
  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:aop="http://www.springframework.org/schema/aop"
    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       
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.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.username}" />
        <property name="password"  value="${jdbc.password}" />
    </bean>

    <!-- 配置业务类 -->
    <bean id="accountService" class="com.bart.demo.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="transactionTemplate" ref="transactionTemplate"/>
    </bean>

    <!-- 配置DAO类 -->
    <bean id="accountDao" class="com.bart.dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

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

    <!-- 配置事务管理的模板:Spring为了简化事务管理的代码而提供的类 -->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
    </bean>

</beans>
  1. 修改AccountServiceImpl
package com.bart.demo;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.bart.dao.AccountDao;

public class AccountServiceImpl implements AccountService {

    //注入accountDaoImpl
    private AccountDao accountDao;

    //注入一个transactionTemplate模板,基于编程的事务管理
    private TransactionTemplate transactionTemplate;

    /**
      * 1. 编程式的事务管理
      * */
    public void transfer(final String in, final String out, final Double money) {   

        transactionTemplate.execute(new TransactionCallbackWithoutResult(){

            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                accountDao.outMoney(out, money);
                //设置异常,测试转账中断
                int i=1/0;
                accountDao.inMoney(in, money);
            }

        });
    }

//注入必须配置set方法
    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    //注入必须配置set方法
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

}
  1. 测试
@Test
public void demo1() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    accountService=(AccountService) context.getBean("accountService");
    accountService.transfer("aaa", "bbb", 200.00);
}
  1. 测试结果:
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | aaa  |  1000 |
|  2 | bbb  |  1000 |
|  3 | ccc  |  1000 |
+----+------+-------+

在有异常的情况下,不会出现转丢失的情况,两个动作绑在了一起,成为一个事务,所以一起成功,一起失败。

声明式事务管理

三种方式:

  • 基于TransactionProxyFactoryBean的方式
  • 基于AspectJ的XML方式
  • 基于注解的方式

基于TransactionProxyFactoryBean的方式

类似Spring的Aop里面配置事务的方法。
1. 修改后的AccountServiceImpl

package com.bart.demo;

import org.springframework.transaction.support.TransactionTemplate;
import com.bart.dao.AccountDao;

public class AccountServiceImpl implements AccountService {


    //注入accountDaoImpl
    private AccountDao accountDao;

    /**
      * 2. 基于AOP思想的声明式的事务管理
      * */
    public void transfer(String in, String out, Double money) {
        accountDao.outMoney(out, money);
        //int i=1/0;
        accountDao.inMoney(in, money);
    }

    //注入必须配置set方法
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

}
  1. 配置applicationContextAop.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: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       
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.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.username}" />
        <property name="password"  value="${jdbc.password}" />
    </bean>

    <!-- 配置业务类 -->
    <bean id="accountService" class="com.bart.demo.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!-- 配置DAO类 -->
    <bean id="accountDao" class="com.bart.dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 配置事务管理器 -->
    <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>
            <!-- 
                prop的格式:
                * PROPAGATION   :事务的传播行为
                * ISOLATIN      :事务的传播行为
                * readOnly      :只读
                * -Exception    :发生哪些异常时回滚事务
                * +Exception    :发生哪些异常时事务不回滚           
             -->
                <prop key="transfer">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>

</beans>
  1. 测试
@Test
public void dedom2() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContextAop.xml");
accountService=(AccountService) context.getBean("accountServiceProxy");     accountService.transfer("aaa", "bbb", 200.00);
}
  1. 测试结果
    此时不管在AccountServiceImpl的transfer方法中如果有异常,则转账失败,不会出现钱转丢的情况
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | aaa  |  1000 |
|  2 | bbb  |   800 |
|  3 | ccc  |  1000 |
+----+------+-------+

基于AspectJ的XML方式

  1. 配置ApplicationContextAspectJ.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:context="http://www.springframework.org/schema/context"
    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.username}" />
        <property name="password"  value="${jdbc.password}" />
    </bean>

    <!-- 配置业务类 -->
    <bean id="accountService" class="com.bart.demo.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!-- 配置DAO类 -->
    <bean id="accountDao" class="com.bart.dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

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

    <!-- 配置事务的增强(Advice通知) -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 
                propagation :事务传播行为
                isolation   :事务隔离级别
                read-only   :只读
                rollback-for:发生哪些异常时候回滚
                no-rollback-for:发生哪些异常不回滚
             -->
            <tx:method name="transfer" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!-- 配置切面 -->
    <aop:config>
    <!-- 第一个*表示任意类型返回值,第二个+表示该类的所有子类,第三个*表示所有的方法(..)表示所有参数 -->
        <aop:pointcut id="pointcut" expression="execution(* com.bart.demo.AccountServiceImpl+.*(..))"/>
        <!-- 配置通知 -->   
        <aop:advisor pointcut-ref="pointcut" advice-ref="txAdvice" />
    </aop:config>

</beans>
  1. AccountServiceImpl不做改变, 测试
@Test
public void dedom3() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContextAspectJ.xml");
    accountService=(AccountService) context.getBean("accountService");
    accountService.transfer("aaa", "bbb", 200.00);
    }
  1. 测试结果
    和基于TransactionProxyFactoryBean的方式效果相同

基于注解的方式

  1. 修改AccountServiceImpl:
package com.bart.demo;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.bart.dao.AccountDao;
import com.sun.istack.internal.FinalArrayList;

 /** Transaction:注解事务管理
   * propagation    :事务的传播行为
   * isolation  :事务的隔离级别
   * readOnly   :只读
   * rollbackFor    :发生哪些异常时回滚
   * notRollbackFor :发生哪些异常时不回滚
   **/
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT)
public class AccountServiceImpl implements AccountService {

    //注入accountDaoImpl
    private AccountDao accountDao;

    /**
      * 2. 基于AOP思想的声明式的事务管理
      * */
    public void transfer(String in, String out, Double money) {
        accountDao.outMoney(out, money);
        //int i=1/0;
        accountDao.inMoney(in, money);
    }

    //注入必须配置set方法
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

}
  1. 配置applicationContextAnnotation.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:context="http://www.springframework.org/schema/context"
    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.username}" />
        <property name="password"  value="${jdbc.password}" />
    </bean>

    <!-- 配置业务类 -->
    <bean id="accountService" class="com.bart.demo.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!-- 配置DAO类 -->
    <bean id="accountDao" class="com.bart.dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

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

    <!-- 开启事务管理 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
  1. 测试
    效果和前两个一样
    @Test
public void demo4() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContextAnnotation.xml");
    accountService = (AccountService) context.getBean("accountService");
    accountService.transfer("aaa", "bbb", 200d);
}

总结

Spring将事务管理分成了两类:

  • 编程式事务管理:
    • 手动编写代码进行事务管理。(很少使用)
  • 声明式事务管理:
    • 基于TransactionProxyFactoryBean的方式。(很少使用)
      -需要为每个进行事务管理
    • 基于AspectJ的XML配置方式。(经常使用)
      • 一旦配置好之后,类上不需要添加任何东西
    • 基于注解方式。(经常使用)
      • 配置简单,需要在业务层上添加一个@transactional的注解
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值