spring-事务管理
<h1 id="事务支持">事务支持</h1><h2 id="什么是事务">什么是事务?</h2>
<p>在一个业务流程中,需要多条DML(insert、delete、update)语句联合才能完成。这些语句必须同时成功或者同时失败。这样才能保证数据安全。</p>
<p>多条DML同时成功或者同时失败,叫做事务。</p>
<h3 id="事务处理的四个过程">事务处理的四个过程</h3>
<ol>
<li>开启事务</li>
<li>执行业务代码</li>
<li>提交事务(没出现异常,提交成功。commit transaction)</li>
<li>回滚事务(出现异常。执行回滚事务. rollback transaction)</li>
</ol>
<h3 id="事务的四个特性acid">事务的四个特性(ACID)</h3>
<ol>
<li>A原子性:事务是最小的工作单元,不可分</li>
<li>C一致性:事务要么同时成功,要么同时失败</li>
<li>I隔离性:事务与事务之间保证和互不干扰</li>
<li>D持久性:持久性是事务结束的标志。</li>
</ol>
<h2 id="spring对事务的支持">spring对事务的支持</h2>
<p>spring实现事务的2种方式:</p>
<ol>
<li>编程式事务:通过编写代码的方式来实现事务管理</li>
<li>声明式事务:基于注解方式和基于xml方式(推荐使用)</li>
</ol>
<h3 id="spring事务管理api">spring事务管理api</h3>
<p>spring对事务的管理是基于aop实现的。所以spring专门针对事务开发了一套api,其核心接口如下:PlatformTransactionManager 接口。</p>
<h3 id="声明式事务基于注解方式实现">声明式事务基于注解方式实现</h3>
<p>需要配置xml文件</p>
<pre><code class="language-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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd" >
<!-- 组件扫描-->
<context:component-scan base-package="com.ali" />
<!-- 配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/bank"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<!-- 配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事务注解驱动器,开启事务注解,需要加上tx的命名空间-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
</code></pre>
<p>可以在类和方法上加@Transactional 开启事务</p>
<ul>
<li>加在类上表示这个类上的所有方法都开启事务</li>
<li>加在方法方法上表示只有这个方法开启事务</li>
</ul>
<h3 id="事务的传播行为">事务的传播行为</h3>
<p>什么是事务的传播行为?</p>
<p>在service种有a()和b()2个方法,a()上有事务,b()上也有事务,当a()在执行过程中调用了b(),事务是如何传递的?是合并到一个事务?还是开启一个新事务?这就是事务的传播行为。</p>
<p>一共有7种传播行为:</p>
<ol>
<li>REQUIRD:支持当前事务,如果不存在就新建一个事务(默认)【没有就新建,有就加入】</li>
<li>SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行【有就加入,没有就不管了】</li>
<li>MANDATORY:必须运行在一个事务中,如果当前没有事务发生,将抛出异常【有就加入,没有就抛异常】</li>
<li>REQUIRES_NEW:开启一个新事务,如果一个事务已经存在,则将这个存在的事务挂起【不管有没有。直接开启一个新事务。新事务和旧事务不存在嵌套关系,旧事务被挂起了】</li>
<li>NOT_SUPPORTED:以非事务方式运行。如果有事务。则挂起当前事务【不支持事务,存在就挂起】</li>
<li>NEVER:以非事务方式运行。如果有事务。则抛异常【不支持事务,存在就抛异常】</li>
<li>NESTED:如果当前有一个事务在进行中,则该方法应当运行在一个嵌套事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在。行为就像REQUIRD一样【有事务的话,就在这个事务里嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRD一样】</li>
</ol>
<p>在代码中设置事务的传播行为:</p>
<pre><code class="language-java">@Transactional(propagation = Propagation.MANDATORY)
</code></pre>
<h3 id="事务隔离级别">事务隔离级别</h3>
<p>数据库中读取数据存在三大问题:</p>
<ol>
<li>脏读:读取到没有提交到数据库的数据</li>
<li>不可重复读:在同一个事务中,第一次和第二次读取的数据不一样</li>
<li>幻读:读到的数据是假的()</li>
</ol>
<p>事务的隔离级别有4个:</p>
<ol>
<li>读未提交READ_UNCOMMITTED: 存在脏读、不可重复读、幻读问题</li>
<li>读提交READ_COMMITTED:事务提交之后才读到。存在不可重复读、幻读问题</li>
<li>可重复读REPEATABLE_READ:解决不可重复读的问题,存在幻读问题</li>
<li>序列化SERIALIZABLE:解决幻读问题,事务排队执行。不支持并发。</li>
</ol>
<p><strong>MySQL默认可重复读,Oracle默认读提交</strong></p>
<p>仅在读的事务中设置隔离级别就行,写的事务没必要设置</p>
<p>代码中设置事务的隔离级别:</p>
<pre><code class="language-java">@Transactional(isolation = Isolation.DEFAULT)
</code></pre>
<h3 id="事务超时">事务超时</h3>
<pre><code class="language-java">@Transactional(timeout = 10)
</code></pre>
<p>以上代码设置事务超时时间为10s</p>
<p>表示超过10s,如果事务中所有的DML语句还没有执行完毕的话,最终结果会回滚。</p>
<p>默认值-1,表示没有时间限制。</p>
<p><strong>事务的超时时间值得是哪段时间?</strong></p>
<p>在当前事务中,最后一条DML语句执行之前的时间,如果最后一条DML语句后面有很多业务逻辑,这些业务代码执行的时间不被计入超时时间。</p>
<h3 id="只读事务">只读事务</h3>
<pre><code class="language-java">@Transactional(readOnly = true)
</code></pre>
<p>将当前事务设为只读事务,在该事务中只允许执行select 语句。</p>
<p>该特性的作用是:启动spring的优化策略。提高select语句的执行效率。</p>
<h3 id="设置哪些异常回滚事务">设置哪些异常回滚事务</h3>
<pre><code class="language-java">@Transactional(rollbackFor = RuntimeException.class)
</code></pre>
<p>表示发生RuntimeException异常或该异常的子类异常才回滚</p>
<h3 id="设置哪些异常不回滚事务">设置哪些异常不回滚事务</h3>
<pre><code class="language-java">@Transactional(noRollbackFor = NullPointerException.class)
</code></pre>
<p>表示发生NullPointerException异常或该异常的子类不回滚,其他异常才回滚</p>
<h3 id="事务的全注解式开发">事务的全注解式开发</h3>
<p>编写配置类</p>
<pre><code class="language-java">@Configuration // 代替xml配置文件
@ComponentScan("com.ali") // 扫描com.ali包下的所有类
@EnableTransactionManagement // 开启事务管理
public class Spring6Config {
// @Bean注解用于将方法的返回值注册为Spring容器中的一个Bean
@Bean(name = "druidDataSource")
public DruidDataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://localhost:3306/spring6?useSSL=false&serverTimezone=UTC");
druidDataSource.setUsername("root");
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setPassword("123456");
return druidDataSource;
}
@Bean(name = "transactionManager")
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
@Bean(name = "jdbcTemplate")
// 该方法的参数DataSource dataSource会自动从Spring容器中找到类型为DataSource的Bean并注入
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
}
</code></pre>
<p>使用时和其他方式一样。</p>
</div>
<div id="MySignature" role="contentinfo">
<p>本文来自博客园,作者:NE_STOP,转载请注明原文链接:https://www.cnblogs.com/alineverstop/p/19598826</p><br><br>
来源:https://www.cnblogs.com/alineverstop/p/19598826
頁:
[1]