ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

06-Spring之基于AOP的事务管理(针对数据库操作)

2020-12-21 09:35:26  阅读:219  来源: 互联网

标签:事务管理 事务 06 读取 Spring methodB 操作 数据 public


事务管理(针对数据库操作)

注意spring中的事务控制是基于AOP的
事务控制应该针对Service层进行配置(比如银行转账的一个案例,A转账给B 100元,这里涉及到了两个操作,A账户的资金减少100元以及B账户资金增加100元,如果不添加事务操作,B账户资金增加时发生错误,增加失败,而A操作正常发生,这时A的账户会减少100元,但是B账户没有增加100元,这明显是不可取的。因此需要引入事务控制,只有当两个操作都成功时,才真正地进行操作,任意一个操作失败时,发生回滚,即两个操作都撤回)

简介

定义:事务管理是针对于一系列对数据库操作的管理(这是数据库的概念),一个事务包含一条或多条SQL语句
假设增加一条数据需要进行的操作:查询数据库查看是否已经存在这条数据,再将这条数据插入(注意这里是假设).这样子的一个操作可以看做事务(单纯的查找数据或是插入数据也可以看做一个事务).
我们在实际业务场景中,经常会遇到数据频繁修改读取的问题。在同一时刻,不同的业务逻辑对同一个表数据进行修改,这种冲突很可能造成数据不可挽回的错乱,所以我们需要用事务来对数据进行管理.
关键属性ACID:
原子性(atomicity):事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的.
一致性(consistency):表明应用系统从一个正确的状态到另一个正确的状态,比如转账中,我们在数据库中约束余额不能为负,因此转账前后账户的余额都不为负(注意这是数据库约束的),否则会产生回滚。
隔离性(isolation):可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏.
持久性(durability):一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除.

一个事务的简单流程

1--开始事务
2--执行一系列的SQL语句
3--如果操作都成功,则执行提交操作,否则回滚所有操作.

事务类型

编程式事务--通过编码方式实现事务,即类似于JDBC编程实现事务管理
声明式事务--管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务.

数据库并发的问题

- 脏读(dirty reads):当事务读取还未被提交的数据时,就会发生这种事件。举例来说:Transaction1 修改了一行数据,然后Transaction2在Transaction1还未提交修改操作之前读取了被修改的行。如果Transaction1回滚了修改操作,那么Transaction2读取的数据就可以看作是从未存在过的。        
- 不可重复的读(non-repeatable reads):当事务两次读取同一行数据,但每次得到的数据都不一样时,就会发生这种事件。举例来说:Transaction1读取一行数据,然后Transaction2修改或删除该行并提交修改操作。当Transaction1试图重新读取该行时,它就会得到不同的数据值(如果该行被更新)或发现该行不再存在(如果该行被删除)。           
- 虚读(phantom read):如果符合搜索条件的一行数据在后面的读取操作中出现,但该行数据却不属于最初的数据,就会发生这种事件。举例来说:Transaction1读取满足某种搜索条件的一些行,然后Transaction2插入了符合Transaction1的搜索条件的一个新行。如果Transaction1重新执行产生原来那些行的查询,就会得到不同的行。

事务的隔离级别

- ISOLATION_DEFAULT
	- 默认级别,归属下列某一种
- ISOLATION_READ_UNCOMMITTED
	- 表明可以发生误读、不可重复读和虚读。(可以读取未提交的数据)
- ISOLATION_READ_COMMITTED
	- 表明能够阻止误读;可以发生不可重复读和虚读。(只能读取已提交的数据,解决脏读问题(Oracle默认级别))
- ISOLATION_REPEATABLE_READ
	- 表明能够阻止误读和不可重复读;可以发生虚读。(是否读取其他事务提交后的数据,解决不可重复读的问题(MYSQL默认级别))
- ISOLATION_SERIALIZABLE
	- 表明能够阻止误读、不可重复读和虚读。(是否读取其他事务提交添加后的数据,解决幻影读问题)

事务的传播行为

1、REQUIRED

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
   methodB();
// do something
}
 
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // do something
}

REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择----单独调用methodB()时,因为不存在事务,因此会开启一个新的事务,调用methodA()时,开启一个新的事务,而执行到methodB()时,由于已经存在一个事务了,因此会加入到这个事务中

2、SUPPORTS

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}
 
// 事务属性为SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
    // do something
}

SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行----单独调用methodB()时,不存在事务,但methodB()是可以非事务执行的,因此以非事务执行,调用methodA()时,会开启一个新的事务,而执行到methodB()时,会加入到methodA()的事务

3、MANDATORY

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}
 
// 事务属性为MANDATORY
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
    // do something
}
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。单独调用methodB()时,因为当前没有事务,会抛出异常,调用methodA()不会发生错误

4、REQUIRES_NEW

新建事务,如果当前存在事务,把当前事务挂起。

5、NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6、NEVER

以非事务方式执行,如果当前存在事务,则抛出异常。

7、NESTED

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作。

超时时间和是否是只读事务

超时时间:默认为-1,没有超时限制。如果有,以秒为单位进行设置。
是否是只读事务:建议查询时设置为只读。

基于xml的声明式事务控制

注意:如果不进行事务配置,那么一个操作是一个sqlSession(即一个mapper.function就是一个sqlSession)

	<!--Spring中基于xml的声明式事务控制
		1.配置事务管理器
		2.配置事务通知
			<tx:advice>标签配置事务通知
				属性:
					id:给事务通知提供一个唯一标志
					transaction-manager:给事务通知提供一个事务管理器引用
		3.配置AOP中的通用切入点表达式
		4.建立事务通知和切入点表达式的对应关系
		5.配置事务属性
			isolation属性:用于指定事务隔离级别,默认值DEFAULT,表示使用数据库的默认级别。
			propagation属性:用于指定事务的传播行为,默认值REQUIRED,表示一定会有事务,增删改的选择,查询可选SUPPORTS。
			read-only属性:设置事务是否只读,只用查询方法才能设置为true,默认值时false便是读写。
			timeout属性:用于指定事务的超时时间,默认值-1,如果制定了时间以s为单位。
			rollback-for属性:用于指定一个异常,当产生该异常时事务回滚,产生其他异常时,事务不回滚。没有默认值,表示任何异常都回滚。
			no-rollback-for属性:用于指定一个异常,当产生该异常时事务不回滚,产生其他异常时,事务回滚。没有默认值,表示任何异常都回滚。
	-->
需要的命名空间:
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
<!--1.配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!--2.配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--5.配置事务属性-->
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED" read-only="false"/>
        <!--优先级高-->
        <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
    </tx:attributes>
</tx:advice>
<!--配置AOP-->
<aop:config>
    <!--3.配置切入点表达式-->
    <aop:pointcut id="pt1" expression="execution(* com.whether.service.impl.*.*(..))"></aop:pointcut>
    <!--4.建立事务通知和切入点表达式的对应关系-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>

基于注解的声明式事务控制(重点)

	<!--Spring中基于注解的声明式事务控制
		1.配置事务管理器
		2.开启Spring对注解事务的支持
		3.在需要事务支持的地方使用@Transactional注解
	-->
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启Spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
@Service("accountService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)//只读型事务配置
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountDao accountDao;

    @Override
    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void transfer(String sourceName, String targetName, Float money) {
        System.out.println("transfer....");
        //2.1根据名称查询转出账户
        Account source = accountDao.findAccountByName(sourceName);
        //2.2根据名称查询转入账户
        Account target = accountDao.findAccountByName(targetName);
        //2.3转出账户减钱
        source.setMoney(source.getMoney() - money);
        //2.4转入账户加钱
        target.setMoney(target.getMoney() + money);
        //2.5更新转出账户
        accountDao.updateAccount(source);
        int i = 1 / 0;
        //2.6更新转入账户
        accountDao.updateAccount(target);
    }
}

标签:事务管理,事务,06,读取,Spring,methodB,操作,数据,public
来源: https://www.cnblogs.com/whether/p/14166422.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有