Spring-事务管理

14 篇文章 0 订阅

什么是事务

Spring事务控制的目的是为了达到控制一组操作,要么都执行要么都不执行的一种操作。
ACID四大特性:

  • 原子性 :最小执行单位,不可分割,要么都完成要么无效。
  • 一致性 :保证数据库数据整体完整性,事务成功则整体数据修改,事务失败则数据回滚到之前的状态。
  • 隔离性 :各事务之间是独立的,多个事务操作同一对象时,会串行等待方式执行,保证事务之间互不干扰。
  • 持久性 :事务提交后数据会保存到数据库,之后的数据基于此次保存的数据为基础。

Spring事务控制API

1.PlatformTransactionManager

此接口是Spring的事务管理器。它提供了常用操作事务的方法。

//获取事务状态信息
TransactionStatus getTransaction(TransactionDefinition definition)
//提交事务
Void commit(TransactionStatus status) throws TransactionException;  
//回滚事务
Void rollback(TransactionStatus status) throws TransactionException;  

现实开发中使用的是它的实现类进行数据操作,具体有:

  1. org.springframework.jdbc.datasource.DataSourceTransactionManager 使用SpringJDBC 或者iBatis进行持久化数据时使用。
  2. org.springframework.orm.hibernate5.HibernateTransactionManager 使用Hibernate进行持久化数据时使用。
  3. org.springframework.orm.jpa.JpaTransactionManager 使用JPA进行持久化数据时使用。
  4. org.springframework.transaction.jta.JtaTransactionManager 使用一个JTA来实现分布式事务管理,在一个事务跨越多个资源时使用。

2.TransactionDefinition

事务管理器接口 PlatformTransactionManager 通过 getTransaction(TransactionDefinition definition) 方法来得到一个事务,这个方法里面的参数是 TransactionDefinition类,这个类就定义了一些基本的事务属性。

方法如下:

public interface TransactionDefinition {
// 返回事务的传播行为
int getPropagationBehavior(); 
// 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
int getIsolationLevel(); 
// 返回事务超时时间
int getTimeout();
//返回事务的名字
String getName()// 返回是否为只读事务。 
boolean isReadOnly();
} 
1.并发事务影响

事务的隔离级别反映并发操作时受其他事务影响的程度,其中可能带来的问题有:

  • 脏读 :又叫做读未提交,指的是当前事务对一数据进行了修改,但是并没有提交到数据库中,此时另一事务此时也访问了这个数据,并且使用了数据,而这时当前事务因为异常进行了数据回滚,此时另一事务读到的数据就是"脏数据",根据脏数据是没法进行正确的操作的。
  • 不可重复读 :在一个事务内,多次读取同一数据,而两次读取数据中间由另一事务对当前数据进行了修改,导致两次读取数据的值不一样。
  • 幻读 : 与不可重复读类似,指的是,在一个事务内读取了若干行数据,接着另一事务插入了一些数据,并提交。在当前事务再次进行查询时会发现出现了一些本不存在的数据,就像幻觉一样。
  • 值丢失 : 在当前事务修改某一数据时,另一事务并发修改同一数据,导致晚提交的事务覆盖了上一提交的事务,造成值丢失。(例如 A=10,事务1更新A=A+20,事务2更新A=A-10,最后导致A=0的与预期值A=20不符)

幻读 和 不可重复读的区别:
幻读重点在于新增和删除,而不可重复读的重点在于查询+修改。
不可重复读 和 脏读的区别:
脏读是当前事务读取了另一事务未提交的数据,而不可重复读是多次查询返回不同的数据值,因为当前数据被另一事务并发更改,并提交。

2.事务隔离级别

数据库中定义了4种隔离级别,分别是:

  1. read uncommitted 读未提交 所有事务可以看到未提交事务数据。
  2. read committed 读提交 只有事务提交后才可以被其他数据查询到。 Oracle 默认级别
  3. repeatable read 可重复读 (会产生幻读) Mysql默认级别
  4. Serializable 序列化 最高级别,强制事务按照顺序执行,在读的数据行上加上共享锁,可能产生大量的超时现象和锁竞争

TransactionDefinition接口定义了5个隔离等级的常量:

  • ISOLATION_DEFAULT : 默认 使用数据库默认的隔离级别
  • ISOLATION_READ_UNCOMMITTED : 读未提交(可能会导致可能会导致脏读、幻读或不可重复读)
  • ISOLATION_READ_COMMITTED 读已提交 (可避免脏读,不可避免幻读和不可重复读)
  • ISOLATION_REPEATABLE_READ :可重复读,同一字段多次读取结果一致,除非数据被本身事务进行修改(不可避免不可重复读)
  • ISOLATION_SERIALIZABLE: 最高隔离界别 序列化操作(影响性能,慎用!)
3.事务传播

**事务方法被另一事务调用时,必须指定事务应该如何传播。**事务的嵌套,内层事务既可以开启新的事务,也可以将当前事务合并到外层事务中来。
具体有:
支持当前事务型

  • PROPAGATION_REQUIRED:如果当前没有事务就新建事务,如果已有事务则加入该事务。
  • PROPAGATION_SUPPORTS:如果当前没有事务就加入该事务,如果没有事务,则以没有事务方式运行。
  • PROPAGATION_MANDATORY:如果当前存在事务加入该事务,如果没有事务则抛出异常。

不支持当前事务型

  • PROPAGATION_REQUIRES_NEW:创建一个新事物,如果当前存在事务,则挂起当前事务。
  • PROPAGATION_NOT_SUPPORTED:以没有事务方式运行,如果有事务则将当前事务挂起。
  • PROPAGATION_NEVER: 以非事务方式运行,如果存在事务则抛出异常。

其他

  • PROPAGATION_NESTED:当前存在事务,则创建事务作为当前事务的嵌套事务,如果没有则取值等价 PROPAGATION_REQUIRED。

PROPAGATION_NESTED 是Spring独有的,其启动的事务如果外部有事务的话内嵌于外部事务,此时内嵌事务不属于独立事务,它依赖外部事物,只有外部事物提交才能引起内部事务提交,内嵌子事务不得独立提交。当然外部事务回滚也会导致内部事务回滚。

4.超时时间

超时时间指事务允许执行的最大时间,如果超过限制时间事务还没有完成,则自动回滚事务。默认值是-1,没有超时限制。如果有,以秒为单位进行设置。

5.只读属性

事务只读属性指当前事务对事务访问资源进行只读操作还是读写操作。只读操作可提高事务处理性能,可在适当的地方使用。

6.回滚

默认情况下事务在运行期异常时会回滚,在检查型异常并不会回滚,但是可以声明事务遇到特定异常时回滚或不回滚,即使该异常是检查型异常。

3.TransactionStatus

TransactionStatus 接口用来记录事务的状态,该接口定义了一组方法可以获得某个时间点上事务对象的状态信息。
PlatformTransactionManager.getTransaction()方法返回一个TransactionStatus对象,可能是一个新的事务或者已经存在的事务。
接口方法如下:

  • boolean isNewTransaction() 是否新事务
  • boolean hasSavepoint() 是否有恢复点
  • void flush() 刷新事务
  • boolean isRollbackOnly() 是否回滚
  • void setRollbackOnly() 设置为只回滚
  • boolean isCompleted()是否已完成*

配置方式

XML配置方式

1.准备工作

jar包依赖

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

配置文件约束配置导入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: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/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">
</beans>

配置数据源

<context:property-placeholder location="db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!--连接数据库的必备信息-->
    <property name="driverClass" value="${driverClass}"></property>
    <property name="jdbcUrl" value="${jdbcUrl}"></property>
    <property name="user" value="${jdbcUser}"></property>
    <property name="password" value="${password}"></property>
</bean>
2.事务配置步骤
2.1 配置事务管理器
<!-- 配置一个事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入 DataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
2.2 配置事务通知引用事务管理器
<!-- 事务的配置 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
</tx:advice>
2.3 配置事务属性
<tx:attributes>
<!-- 指定方法名称:是业务核心方法
read-only:是否是只读事务。默认 false,不只读。
isolation:指定事务的隔离级别。默认值是使用数据库的默认隔离级别。
propagation:指定事务的传播行为。
timeout:指定超时时间。默认值为:-1。永不超时。
rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异常,事务不回滚。
没有默认值,任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回
滚。没有默认值,任何异常都回滚。
-->
<tx:method name="*" read-only="false" propagation="REQUIRED"/><!-- REQUIRED 有事务就加入,没有则新建 -->
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/> <!-- 在这里对find开头的查询方法使用只读事务 ;SUPPORTS 当前有事务,则加入,没有则无事务-->
</tx:attributes>
2.4 配置切入点表达式
<!-- 配置 aop -->
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut expression="execution(* com.lfc.service.impl.*.*(..))" id="pt1"/>
</aop:config>
2.5 配置切入点表达式和事务通知的对应关系、
<!-- 在aop:config 标签内部:建立事务的通知和切入点表达式的关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>

汇总

<!-- 配置账户的持久层-->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置数据源-->
<context:property-placeholder location="db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--连接数据库的必备信息-->
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="user" value="${jdbcUser}"></property>
<property name="password" value="${password}"></property>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED" read-only="false"/>
        <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
    </tx:attributes>
</tx:advice>
<!-- 配置aop-->
<aop:config>
    <!-- 配置切入点表达式-->
    <aop:pointcut id="pt1" expression="execution(* com.lfc.service.impl.*.*(..))"></aop:pointcut>
    <!--建立切入点表达式和事务通知的对应关系 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>

注解配置方式

1.准备工作
<!-- 配置 spring 创建容器时要扫描的包 -->
<context:component-scan base-package="com.lfc"/>

数据源同xml配置

2.事务配置步骤
2.1 配置事务管理器注入数据源
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
2.2 业务层使用 @Transactional 注解
@Service("accountService")
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountDao accountDao;
    @Override
    public Account findAccountById(Integer id) {
    return accountDao.findAccountById(id);
    }
    @Override
    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    public void transfer(String id, String name) {
    }
}

该注解的属性和 xml 中的属性含义一致。该注解可以出现在接口上,类上和方法上。
出现接口上,表示该接口的所有实现类都有事务支持。
出现在类上,表示类中所有方法有事务支持
出现在方法上,表示方法有事务支持。
以上三个位置的优先级:方法>类>接口

2.3 配置文件中开启Spring对注解事务的支持
<!-- 开启 spring 对注解事务的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
纯注解式配置(不使用xml配置文件)

主配置类 替代bean.xml

@Configuration    //该类作为spring的xml配置文件中的<beans>
@ComponentScan("com.lfc")    //扫描配置包下被注解过的类
@Import({JdbcConfig.class,TransactionConfig.class}) //导入另外两个配置类
@PropertySource("db.properties")
@EnableTransactionManagement  //事务注解
public class SpringConfigurantion {
}

数据源 配置类


public class JdbcConfig {
    @Value("${driverClass}")
    private String driver;

    @Value("${jdbcUrl}")
    private String url;

    @Value("${jdbcUser}")
    private String username;

    @Value("${password}")
    private String password;

    @Bean(name = "jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    /**
     * 创建数据源对象
     * @return
     */
    @Bean(name="dataSource")
    public DataSource createDataSource(){
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}

事务相关配置类

public class TransactionConfig {
    @Bean(name="transactionManager")
    public PlatformTransactionManager reateTransactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值