2021年10月15日

MySql事务

1.事务介绍

l 事务的概念

事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功

l 数据库开启事务命令

start transaction 开启事务 (等同于set autocommit = off )

Rollback 回滚事务

Commit 提交事务

2.Mysql中使用事务

1.创建表

create table account(

id int primary key auto_increment,

name varchar(20),

money double

);

insert into account values(null,'aaa',1000);

insert into account values(null,'bbb',1000);

insert into account values(null,'ccc',1000);

2、MySQL中事务默认自动提交的,每当执行一条SQL,就会提交一个事务 (一条SQL 就是一个事务)

Oracle 中事务默认 不自动提交,需要在执行SQL 语句后 通过commint 手动提交事务

3、mysql管理事务

方式一 :同时事务管理SQL 语句

start transaction 开启事务

rollback 回滚事务 (将数据恢复到事务开始时状态)

commit 提交事务 (对事务中进行操作,进行确认操作,事务在提交后,数据就不可恢复)

方式二:数据库中存在一个自动提交变量 ,通过 show variables like '%commit%'; ---- autocommint 值是 on,说明开启自动提交

关闭自动提交 set autocommit = off / set autocommit = 0

如果设置autocommit 为 off,意味着以后每条SQL 都会处于一个事务中,相当于每条SQL执行前 都会执行

start transaction

补充:Oracle中autocommit 默认是 off

3.Jdbc使用事务

l 当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列语句:

l JDBC控制事务语句

Connection.setAutoCommit(false); // 相当于start transaction

Connection.rollback(); rollback

Connection.commit(); commit

4.事务特性(ACID)

原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

一致性(Consistency) 事务前后数据的完整性必须保持一致。

隔离性(Isolation) 事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。

持久性(Durability)

持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

5.隔离级别

多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性

数据库内部定义了四种隔离级别,用于解决三种隔离问题

1 Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)

2 Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读(幻读)

3 Read committed:可避免脏读情况发生(读已提交)

4 Read uncommitted:最低级别,以上情况均无法保证。(读未提交)

怎样设置事务的隔离级别?

mysql中设置

1.查看事务隔离级别

select @@tx_isolation 查询当前事务隔离级别

mysql中默认的事务隔离级别是 Repeatable read.

扩展:oracle 中默认的事务隔离级别是 Read committed

 

2.mysql中怎样设置事务隔离级别

set session transaction isolation level 设置事务隔离级别

 

jdbc中设置

JDBC程序中能否指定事务的隔离级别 ?

Connection接口中定义事务隔离级别四个常量:

static int TRANSACTION_READ_COMMITTED

指示不可以发生脏读的常量;不可重复读和虚读可以发生。

static int TRANSACTION_READ_UNCOMMITTED

指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。

static int TRANSACTION_REPEATABLE_READ

指示不可以发生脏读和不可重复读的常量;虚读可以发生。

static int TRANSACTION_SERIALIZABLE

指示不可以发生脏读、不可重复读和虚读的常量。

通过 void setTransactionIsolation(int level) 设置数据库隔离级别

如果不考虑隔离性,可能会引发如下问题:

1、脏读 :指一个事务读取另一个事务未提交的数据

A 转账 给B 100,未提交

B 查询账户多了100

A 回滚

B 查询账户那100不见了

2、不可重复读:在一个事务先后两次读取发生数据不一致情况,第二次读取到另一个事务已经提交数据 (强调数据更新 update)

A 查询账户 5000

B 向 A 账户转入 5000

A 查询账户 10000

3、虚读(幻读) : 在一个事务中,第二次读取发生数据记录数的不同 ,读取到另一个事务已经提交数据 (强调数据记录变化 insert )

A 第一次读取 存在5条记录

B 向 A 插入一条新的记录

A 第二次读取 存在6条记录

4、丢失更新:两个事务对同一条记录进行操作,后提交的事务,将先提交的事务的修改覆盖了。

演示:脏读

一个事务读取到另一个事务的未提交数据

设置A,B事务隔离级别为 Read uncommitted​

set session transaction isolation level read uncommitted;

在A事务中

start transaction;

update account set money=money-500 where name='aaa';

update account set money=money+500 where name='bbb';​

在B事务中

start transaction;

select * from account;

 

这时,B事务读取时,会发现,钱已经汇完。那么就出现了脏读。

当A事务提交前,执行rollback,在commit, B事务在查询,就会发现,钱恢复成原样

也出现了两次查询结果不一致问题,出现了不可重复读.

 

解决脏读问题

将事务的隔离级别设置为 read committed来解决脏读

设置A,B事务隔离级别为 Read committed

set session transaction isolation level read committed;

 

在A事务中

start transaction;

update account set money=money-500 where name='aaa';

update account set money=money+500 where name='bbb';​

在B事务中

start transaction;

select * from account;

 

这时B事务中,读取信息时,是不能读到A事务未提交的数据的,也就解决了脏读。 让A事务,提交数据 commit;

这时,在查询,这次结果与上一次查询结果又不一样了,还存在不可重复读。

 

解决不可重复读

将事务的隔离级别设置为Repeatable read来解决不可重复读。

设置A,B事务隔离级别为 Repeatable read;

set session transaction isolation level Repeatable read;

 

1.在A事务中

start transaction;

update account set money=money-500 where name='aaa';

update account set money=money+500 where name='bbb';

2.在B事务中

start transaction;

select * from account;

当A事务提交后commit;B事务在查询,与上次查询结果一致,解决了不可重复读。

 

设置事务隔离级别 Serializable ,它可以解决所有问题

set session transaction isolation level Serializable;

如果设置成这种隔离级别,那么会出现锁表。也就是说,一个事务在对表进行操作时,其它事务操作不了。

 

总结:

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

不可重复读:两次读取数据不一致(读提交数据)---update

虚读:两次读取数据不一致(读提交数据)----insert

事务隔离级别:

read uncommitted 什么问题也解决不了.

read committed 可以解决脏读,其它解决不了.

Repeatable read 可以解决脏读,可以解决不可重复读,不能解决虚读.

Serializable 它会锁表,可以解决所有问题.

安全性:serializable > repeatable read > read committed > read uncommitted

性能:serializable < repeatable read < read committed < read uncommitted

结论: 实际开发中,通常不会选择 serializable 和 read uncommitted ,

mysql默认隔离级别 repeatable read ,oracle默认隔离级别 read committed

丢失更新:多个事务对同一条记录进行了操作,后提交的事务将先提交的事务操作覆盖了。

问题:怎样解决丢失更新问题?

解决丢失更新可以采用两种方式:

1.悲观锁

悲观锁 (假设丢失更新一定会发生 ) ----- 利用数据库内部锁机制,开启事务之后提供的锁机制

1.共享锁( 读锁 )

select * from 表名 lock in share mode( 在读取数据时加锁 )

加锁后可以使用delete语句进行测试

2.排它锁( 写锁 )

select * from 表名 for update ( 在修改数据时加锁 )

注:update语句默认添加排它锁

 

 

2.乐观锁

乐观锁 (假设丢失更新不会发生)------- 采用程序中添加版本字段解决丢失更新问题

 

CREATE TABLE product(
  id INT,
  NAME VARCHAR(20),
  updatetime TIMESTAMP
)

insert into product values(1,'冰箱',null);

update product set name='洗衣机' where id = 1;

解决丢失更新:在数据表添加版本字段,每次修改过记录后,版本字段都会更新,如果读取是版本字段,与修改时版本字段不一致,说明别人进行修改过数据 (重改)

 

Spring中的事务

1.事务回顾

事务:是逻辑上一组操作,要么全都成功,要么全都失败. 事务特性:ACID

原子性:事务不可分割 一致性:事务执行的前后,数据完整性保持一致. 隔离性:一个事务执行的时候,不应该受到其他事务的打扰 持久性:一旦结束,数据就永久的保存到数据库.

如果不考虑隔离性: 脏读:一个事务读到另一个事务未提交数据 不可重复读:一个事务读到另一个事务已经提交数据(update)导致一个事务多次查询结果不一致 虚读:一个事务读到另一个事务已经提交数据(insert)导致一个事务多次查询结果不一致

事务的隔离级别: 未提交读:以上情况都有可能发生。 已提交读:避免脏读,但不可重复读,虚读是有可能发生。 可重复读:避免脏读,不可重复读,但是虚读有可能发生。 串行的:避免以上所有情况.

2.Spring中的事务

Spring中事务管理 分层开发:事务处在Service层.

Spring提供事务管理API

PlatformTransactionManager:平台事务管理器. ​ getTransaction(TransactionDefinition definition) ​ rollback(TransactionStatus status) ​ commit(TransactionStatus status) TransactionDefinition:事务定义 ​ ISOLation_XXX:事务隔离级别. ​ PROPAGATION_XXX:事务的传播行为. TransactionStatus:事务状态 ​ 是否有保存点 ​ 是否是一个新的事务 ​ 事务是否已经提交 关系:PlatformTransactionManager通过TransactionDefinition设置事务相关信息管理事务,管理事务过程中,产生一些事务状态,状态由TransactionStatus记录。

API详解: ​ PlatformTransactionManager:接口. ​ Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现

使用Spring JDBC或iBatis 进行持久化数据时使用(重点) org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Hibernate进行持久化数据时使用 org.springframework.orm.hibernate.HibernateTransactionManager 使用JPA进行持久化时使用 org.springframework.orm.jpa.JpaTransactionManager 当持久化机制是Jdo时使用 org.springframework.jdo.JdoTransactionManager 使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用 org.springframework.transaction.jta.JtaTransactionManager TransactionDefinition: ISOLATION_DEFAULT:默认级别. Mysql --> repeatable_read | Oracle -->> read_commited

级别如下:

ISOLATION_READ_UNCOMMITTED ISOLATION_READ_COMMITTED ISOLATION_REPEATABLE_READ ISOLATION_SERIALIZABLE

3.Sping中事务的传播行为

事务的传播行为:(不是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事务有异常的话,用户需要自己设置事务提交还是回滚.

  • 常用:(重点) PROPAGATION_REQUIRED PROPAGATION_REQUIRES_NEW PROPAGATION_NESTED

4.Spring的事务管理

Spring的事务管理分成两类 编程式事务管理: 手动编写代码完成事务管理.(了解) 声明式事务管理: 不需要手动编写代码,配置.

5.Spring的事务案例

事务操作的环境搭建

5.1创建表
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=3 DEFAULT CHARSET=utf8;

INSERT INTO `account` VALUES ('1', 'aaa', '1000');
INSERT INTO `account` VALUES ('2', 'bbb', '1000');
5.2导入依赖
<dependencies>
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
       <version>5.0.2.RELEASE</version>
   </dependency>

   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-tx</artifactId>
       <version>5.0.2.RELEASE</version>
   </dependency>

   <dependency>
       <groupId>org.aspectj</groupId>
       <artifactId>aspectjweaver</artifactId>
       <version>1.8.13</version>
   </dependency>

   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-jdbc</artifactId>
       <version>5.0.2.RELEASE</version>
   </dependency>

   <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>druid</artifactId>
       <version>1.0.9</version>
   </dependency>

   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>5.1.32</version>
   </dependency>

   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.12</version>
   </dependency>
</dependencies>

<build>
   <plugins>
       <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <configuration>
               <target>1.8</target>
               <source>1.8</source>
               <encoding>UTF-8</encoding>
           </configuration>
       </plugin>
   </plugins>
</build>
5.3代码编写

AccountDao

public interface AccountDao {

       // 加钱
       void increaseMoney(Integer id, Double money);

       // 减钱
       void decreaseMoney(Integer id, Double money);
}

AccountDaoImpl

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao  {

       @Override
       public void increaseMoney(Integer id, Double money) {

           getJdbcTemplate().update("update account set money = money+? where id = ? ", money,id);

      }

       @Override
       public void decreaseMoney(Integer id, Double money) {

           getJdbcTemplate().update("update account set money = money-? where id = ? ", money,id);
      }

}

AccountService

public interface AccountService {

       //转账方法
       void transfer(Integer from,Integer to,Double money);

}

AccountServiceImpl

public class AccountServiceImpl implements AccountService {

       private AccountDao accountDao;

       @Override
       public void transfer(Integer from, Integer to, Double money) {
           // 减钱
           accountDao.decreaseMoney(from, money);
           // int i = 1 / 0;// 如果发生异常数据(钱)会丢失
           // 加钱
           accountDao.increaseMoney(to, money);
      }

       public void setAccountDao(AccountDao accountDao) {
           this.accountDao = accountDao;
      }
}
5.4创建applicationContext.xml引入事务(tx)约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://www.springframework.org/schema/beans" 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-4.2.xsd
                          http://www.springframework.org/schema/context
                          http://www.springframework.org/schema/context/spring-context-4.2.xsd
                          http://www.springframework.org/schema/aop
                          http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
                          http://www.springframework.org/schema/tx
                          http://www.springframework.org/schema/tx/spring-tx-4.2.xsd ">


   <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
       <property name="url" value="jdbc:mysql://localhost:3306/mydb"></property>
       <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
       <property name="username" value="root"></property>
       <property name="password" value="root"></property>
   </bean>

   <bean name="accountDao" class="com.AccountDaoImpl">
       <property name="dataSource" ref="dataSource"></property>
   </bean>

   <bean name="accountService" class="com.AccountServiceImpl">
       <property name="accountDao" ref="accountDao"></property>
   </bean>

</beans>
5.5测试
public class Demo {

   @Test
   public void test(){
       ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

       AccountService accountService = (AccountService)context.getBean("accountService");

       accountService.transfer(1,2,100d);

  }
}

上述测试中,如果在转账方法中出现异常后,数据前后会产生不一致,此时,我们需要用Spring的事务管理来解决这一问题。

5.6手动编码的方式完成事务管理(了解)

缺点:代码量增加,代码有侵入性.

修改AccountServiceImpl

public class AccountServiceImpl implements AccountService {

   private AccountDao accountDao;

   private TransactionTemplate transactionTemplate;

   @Override
   public void transfer(final Integer from,final Integer to,final Double money) {

       transactionTemplate.execute(new TransactionCallbackWithoutResult() {

           @Override
           protected void doInTransactionWithoutResult(TransactionStatus arg0) {
               // 减钱
               accountDao.decreaseMoney(from, money);
               int i = 1 / 0;// 如果发生异常数据(钱)不会丢失
               // 加钱
               accountDao.increaseMoney(to, money);

          }
      });
  }

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

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

修改applicationContext.xml

<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
   <property name="url" value="jdbc:mysql://localhost:3306/mydb"></property>
   <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
   <property name="username" value="root"></property>
   <property name="password" value="root"></property>
</bean>

<!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 -->
<bean name="transactionManager"
     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 事务模板对象 -->
<bean name="transactionTemplate"
     class="org.springframework.transaction.support.TransactionTemplate">
   <property name="transactionManager" ref="transactionManager"></property>
</bean>

<bean name="accountDao" class="com.AccountDaoImpl">
   <property name="dataSource" ref="dataSource"></property>
</bean>

<bean name="accountService" class="com.AccountServiceImpl">
   <property name="accountDao" ref="accountDao"></property>
   <property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
5.7Xml配置(aop)的方式完成事务管理

修改AccountServiceImpl

public class AccountServiceImpl implements AccountService {

       private AccountDao accountDao;

       @Override
       public void transfer(Integer from, Integer to, Double money) {
           // 减钱
           accountDao.decreaseMoney(from, money);
           // int i = 1 / 0;// 如果发生异常数据(钱)会丢失
           // 加钱
           accountDao.increaseMoney(to, money);
      }

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

修改applicationContext.xml

<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
   <property name="url" value="jdbc:mysql://localhost:3306/db_name?serverTimezone=Asia/Shanghai"></property>
   <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
   <property name="username" value="root"></property>
   <property name="password" value="root"></property>
</bean>

<!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 -->
<bean name="transactionManager"
     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
       <!-- 企业中配置CRUD方法一般使用方法名+通配符*的形式配置通知,此时类中的方法名要和配置的方法名一致 -->
       <!-- 以方法为单位,指定方法应用什么事务属性
               isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
               propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
               read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
               timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
               rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
               no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。
            -->
       <tx:method name="save*" isolation="REPEATABLE_READ"
                  propagation="REQUIRED" read-only="false" />
       <tx:method name="persist*" isolation="REPEATABLE_READ"
                  propagation="REQUIRED" read-only="false" />
       <tx:method name="update*" isolation="REPEATABLE_READ"
                  propagation="REQUIRED" read-only="false" />
       <tx:method name="modify*" isolation="REPEATABLE_READ"
                  propagation="REQUIRED" read-only="false" />
       <tx:method name="delete*" isolation="REPEATABLE_READ"
                  propagation="REQUIRED" read-only="false" />
       <tx:method name="remove*" isolation="REPEATABLE_READ"
                  propagation="REQUIRED" read-only="false" />
       <tx:method name="get*" isolation="REPEATABLE_READ"
                  propagation="REQUIRED" read-only="true" />
       <tx:method name="find*" isolation="REPEATABLE_READ"
                  propagation="REQUIRED" read-only="true" />
       <tx:method name="transfer" isolation="REPEATABLE_READ"
                  propagation="REQUIRED" read-only="false" />
   </tx:attributes>
</tx:advice>

<!-- 配置织入 -->
<aop:config>
   <!-- 配置切点表达式 -->
   <aop:pointcut expression="execution(* com.qf.service.*ServiceImpl.*(..))" id="txPc" />
   <!-- 配置切面 : 通知+切点 advice-ref:通知的名称 pointcut-ref:切点的名称 -->
   <aop:advisor advice-ref="txAdvice" pointcut-ref="txPc" />
</aop:config>

<bean name="accountDao" class="com.AccountDaoImpl">
   <property name="dataSource" ref="dataSource"></property>
</bean>

<bean name="accountService" class="com.AccountServiceImpl">
   <property name="accountDao" ref="accountDao"></property>
</bean>
5.8注解配置(aop)的方式完成事务管理

修改applicationContext.xml

<bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
   <property name="url" value="jdbc:mysql://localhost:3306/mydb"></property>
   <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
   <property name="username" value="root"></property>
   <property name="password" value="root"></property>
</bean>

<!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 -->
<bean name="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 开启spring对注解事务的支持-->
<tx:annotation-driven />

<bean name="accountDao" class="com.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>

<bean name="accountService" class="com.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>

修改AccountServiceImpl

@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=true)
public class AccountServiceImpl implements AccountService {

   private AccountDao accountDao;

   @Override
   //如果该方法与类名上的配置不同,可以单独在这个方法上配置注解
   @Transactional(isolation= Isolation.REPEATABLE_READ,propagation= Propagation.REQUIRED,readOnly=false)
   public void transfer(Integer from, Integer to, Double money) {
       // 减钱
       accountDao.decreaseMoney(from, money);
       int i = 1 / 0;// 如果发生异常数据(钱)会丢失
       // 加钱
       accountDao.increaseMoney(to, money);
  }

   public void setAccountDao(AccountDao accountDao) {
       this.accountDao = accountDao;
  }
}
posted @ 2021-10-15 19:10  张三疯321  阅读(60)  评论(0)    收藏  举报