前言
本篇紧接着spring入门详细教程(三),建议阅读本篇前,先阅读第一篇,第二篇以及第三篇。链接如下:
Spring入门详细教程(一) https://www.cnblogs.com/jichi/p/10165538.html
Spring入门详细教程(二) https://www.cnblogs.com/jichi/p/10176601.html
Spring入门详细教程(三) https://www.cnblogs.com/jichi/p/10177004.html
Spring入门详细教程(四) https://www.cnblogs.com/jichi/p/10211475.html
本篇主要讲解spring的事务处理。
一、什么是事务
事务用白话来说比较好理解,我们举个例子。比如说你做两件事要达成一个目的。其中有一件事失败,你就相当于没做。如果两件事都成功,这件事你才算做的成功。用官方话来解释,就是事务是逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。
二、事务的特性
了解了事务的概念,我们就可以了解事务的特性了。
事务有四大特性:原子性,一致性,隔离性,持久性。
原子性:强调事务的不可分割。
一致性:事务的执行的前后数据的完整性保持一致。
隔离性:一个事务执行的过程中,不应受到其他事务的干扰。
持久性:事务一旦结束,数据就持久到数据库。
三、事务的并发问题
事务三大问题,脏读,幻读,不可重复读。
脏读:一个事务读到了另一个事务的未提交的数据。
不可重复读:一个事务读到了另一个事务已经提交的update的数据导致多次查询结果不一致。
虚幻读:一个事务读到了另一个事务已经提交的insert的数据导致多次查询结果不一致。
四、事务并发问题的解决:事务隔离级别
1、未提交读:脏读,不可重复读,虚幻读都有可能发生。
2、已提交读:避免脏读。不可重复读和虚幻读都可能发生。
3、可重复读:避免脏读和不可重复读。虚幻读可能发生。
4、串行化:避免以上所有的读问题。
事务的隔离级别越高,性能会降的越低。其实就是牺牲性能来提高准确性。在实际中,一般选取中间的隔离级别。
MysqL默认隔离级别:可重复读。
oracle默认隔离级别:已提交读。
五、spring封装事务
首先spring封装了我们需要进行的事务操作。事务操作是什么呢。比如说我们首先需要打开事务,进行操作后,提交事务。如果发生错误,回滚事务,如果中途未发生错误,则事务进行提交。如果spring没有对事务进行封装,我们需要没进行一次操作都重新写事务的处理代码。spring封装后,事务代码被封装,我们不用一遍遍的重复编写代码,配置好后,不用书写事务代码。接下来我们了解几个spring中事务的相关概念。
1、PlatformTransactionManager平台事务管理器
在不同的平台,操作代码的事务各不相同,spring为我们提供了一个接口。这个接口就是PlatformTransactionManager。
我们可以看一下里面的源码。提供了一个获得事务的方法,一个提交事务的方法以及一个回滚事务的方法。此处贴一下源码
/* * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License,Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing,software * distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.transaction; /** * This is the central interface in Spring's transaction infrastructure. * Applications can use this directly,but it is not primarily meant as API: * Typically,applications will work with either TransactionTemplate or * declarative transaction demarcation through AOP. * * <p>For implementors,it is recommended to derive from the provided * {@link org.springframework.transaction.support.AbstractPlatformTransactionManager} * class,which pre-implements the defined propagation behavior and takes care * of transaction synchronization handling. Subclasses have to implement * template methods for specific states of the underlying transaction,* for example: begin,suspend,resume,commit. * * <p>The default implementations of this strategy interface are * { org.springframework.transaction.jta.JtaTransactionManager} and * { org.springframework.jdbc.datasource.DataSourceTransactionManager},* which can serve as an implementation guide for other transaction strategies. * * @author Rod Johnson * Juergen Hoeller * @since 16.05.2003 * @see org.springframework.transaction.support.TransactionTemplate * org.springframework.transaction.interceptor.TransactionInterceptor * org.springframework.transaction.interceptor.TransactionProxyfactorybean */ public interface PlatformTransactionManager { * Return a currently active transaction or create a new one,according to * the specified propagation behavior. * <p>Note that parameters like isolation level or timeout will only be applied * to new transactions,and thus be ignored when participating in active ones. * <p>Furthermore,not all transaction definition settings will be supported * by every transaction manager: A proper transaction manager implementation * should throw an exception when unsupported settings are encountered. * <p>An exception to the above rule is the read-only flag,which should be * ignored if no explicit read-only mode is supported. Essentially,the * read-only flag is just a hint for potential optimization. * @param definition TransactionDefinition instance (can be {@code null} for defaults),* describing propagation behavior,isolation level,timeout etc. * @return transaction status object representing the new or current transaction * @throws TransactionException in case of lookup,creation,or system errors * IllegalTransactionStateException if the given transaction definition * cannot be executed (for example,if a currently active transaction is in * conflict with the specified propagation behavior) * TransactionDefinition#getPropagationBehavior * TransactionDefinition#getIsolationLevel * TransactionDefinition#getTimeout * TransactionDefinition#isReadOnly */ TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; * Commit the given transaction,with regard to its status. If the transaction * has been marked rollback-only programmatically,perform a rollback. * <p>If the transaction wasn't a new one,omit the commit for proper * participation in the surrounding transaction. If a prevIoUs transaction * has been suspended to be able to create a new one,resume the prevIoUs * transaction after committing the new one. * <p>Note that when the commit call completes,no matter if normally or * throwing an exception,the transaction must be fully completed and * cleaned up. No rollback call should be expected in such a case. * <p>If this method throws an exception other than a TransactionException,* then some before-commit error caused the commit attempt to fail. For * example,an O/R Mapping tool might have tried to flush changes to the * database right before commit,with the resulting DataAccessException * causing the transaction to fail. The original exception will be * propagated to the caller of this commit method in such a case. * status object returned by the { getTransaction} method * UnexpectedRollbackException in case of an unexpected rollback * that the transaction coordinator initiated * HeuristicCompletionException in case of a transaction failure * caused by a heuristic decision on the side of the transaction coordinator * TransactionSystemException in case of commit or system errors * (typically caused by fundamental resource failures) * IllegalTransactionStateException if the given transaction * is already completed (that is,committed or rolled back) * TransactionStatus#setRollbackOnly */ void commit(TransactionStatus status) * Perform a rollback of the given transaction. * <p>If the transaction wasn't a new one,just set it rollback-only for proper * participation in the surrounding transaction. If a prevIoUs transaction * has been suspended to be able to create a new one,resume the prevIoUs * transaction after rolling back the new one. * <p><b>Do not call rollback on a transaction if commit threw an exception.</b> * The transaction will already have been completed and cleaned up when commit * returns,even in case of a commit exception. Consequently,a rollback call * after commit failure will lead to an IllegalTransactionStateException. * TransactionSystemException in case of rollback or system errors * (typically caused by fundamental resource failures) * void rollback(TransactionStatus status) TransactionException; }
我们通过查看接口发现AbstractPlatformTransactionManager实现了这一接口。其中提供了一些方法。继续查看谁继承了该抽象类。
发现有两个:DataSourceTransactionManager与HibernateTransitionmanager。
DataSourceTransactionManager:这一事务管理类,在用spring的jdbc以及mybaties进行操作事务的时候使用。
HibernateTransitionmanager:在用hiberante进行操作事务的时候使用。
2、TransactionDefinition事务定义信息
主要记录了事务的定义信息相关。
记录了事务的隔离级别,事务传播行为,超时信息,只读信息的基本信息。此处可以看一下源码感受一下,这里面的一些信息,后面会讲解
* Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License,1)">import java.sql.Connection; * Interface that defines Spring-compliant transaction properties. * Based on the propagation behavior definitions analogous to EJB CMT attributes. * * <p>Note that isolation level and timeout settings will not get applied unless * an actual new transaction gets started. As only { #PROPAGATION_required},* { #PROPAGATION_REQUIRES_NEW} and { #PROPAGATION_NESTED} can cause * that,it usually doesn't make sense to specify those settings in other cases. * Furthermore,be aware that not all transaction managers will support those * advanced features and thus might throw corresponding exceptions when given * non-default values. * * <p>The { #isReadOnly() read-only flag} applies to any transaction context,* whether backed by an actual resource transaction or operating non-transactionally * at the resource level. In the latter case,the flag will only apply to managed * resources within the application,such as a Hibernate { Session}. * * 08.05.2003 * PlatformTransactionManager#getTransaction(TransactionDefinition) * org.springframework.transaction.support.DefaultTransactionDefinition * org.springframework.transaction.interceptor.TransactionAttribute TransactionDefinition { * Support a current transaction; create a new one if none exists. * Analogous to the EJB transaction attribute of the same name. * <p>This is typically the default setting of a transaction definition,* and typically defines a transaction synchronization scope. int PROPAGATION_required = 0; * Support a current transaction; execute non-transactionally if none exists. * Analogous to the EJB transaction attribute of the same name. * <p><b>NOTE:</b> For transaction managers with transaction synchronization,1)"> PROPAGATION_SUPPORTS} is slightly different from no transaction * at all,as it defines a transaction scope that synchronization might apply to. * As a consequence,the same resources (a JDBC { Connection},a * Hibernate { Session},etc) will be shared for the entire specified * scope. Note that the exact behavior depends on the actual synchronization * configuration of the transaction manager! * <p>In general,use { PROPAGATION_SUPPORTS} with care! In particular,do * not rely on { PROPAGATION_required} or { PROPAGATION_REQUIRES_NEW} * <i>within</i> a { PROPAGATION_SUPPORTS} scope (which may lead to * synchronization conflicts at runtime). If such nesting is unavoidable,make sure * to configure your transaction manager appropriately (typically switching to * "synchronization on actual transaction"). * org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization * org.springframework.transaction.support.AbstractPlatformTransactionManager#SYNCHRONIZATION_ON_ACTUAL_TRANSACTION int PROPAGATION_SUPPORTS = 1 * Support a current transaction; throw an exception if no current transaction * exists. Analogous to the EJB transaction attribute of the same name. * <p>Note that transaction synchronization within a { PROPAGATION_MANDATORY} * scope will always be driven by the surrounding transaction. int PROPAGATION_MANDATORY = 2 * Create a new transaction,suspending the current transaction if one exists. * Analogous to the EJB transaction attribute of the same name. * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-Box * on all transaction managers. This in particular applies to * { org.springframework.transaction.jta.JtaTransactionManager},* which requires the { javax.transaction.TransactionManager} to be * made available it to it (which is server-specific in standard Java EE). * <p>A { PROPAGATION_REQUIRES_NEW} scope always defines its own * transaction synchronizations. Existing synchronizations will be suspended * and resumed appropriately. * org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager int PROPAGATION_REQUIRES_NEW = 3 * Do not support a current transaction; rather always execute non-transactionally. * Analogous to the EJB transaction attribute of the same name. * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-Box * on all transaction managers. This in particular applies to * { javax.transaction.TransactionManager} to be * made available it to it (which is server-specific in standard Java EE). * <p>Note that transaction synchronization is <i>not</i> available within a * { PROPAGATION_NOT_SUPPORTED} scope. Existing synchronizations * will be suspended and resumed appropriately. * int PROPAGATION_NOT_SUPPORTED = 4 * Do not support a current transaction; throw an exception if a current transaction * exists. Analogous to the EJB transaction attribute of the same name. * <p>Note that transaction synchronization is <i>not</i> available within a * { PROPAGATION_NEVER} scope. int PROPAGATION_NEVER = 5 * Execute within a nested transaction if a current transaction exists,* behave like { #PROPAGATION_required} else. There is no analogous * feature in EJB. * <p><b>NOTE:</b> Actual creation of a nested transaction will only work on * specific transaction managers. Out of the Box,this only applies to the JDBC * { org.springframework.jdbc.datasource.DataSourceTransactionManager} * when working on a JDBC 3.0 driver. Some JTA providers might support * nested transactions as well. * org.springframework.jdbc.datasource.DataSourceTransactionManager int PROPAGATION_NESTED = 6; * Use the default isolation level of the underlying datastore. * All other levels correspond to the JDBC isolation levels. * java.sql.Connection int ISOLATION_DEFAULT = -1 * Indicates that dirty reads,non-repeatable reads and phantom reads * can occur. * <p>This level allows a row changed by one transaction to be read by another * transaction before any changes in that row have been committed (a "dirty read"). * If any of the changes are rolled back,the second transaction will have * retrieved an invalid row. * java.sql.Connection#TRANSACTION_READ_UNCOMMITTED int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED; * Indicates that dirty reads are prevented; non-repeatable reads and * phantom reads can occur. * <p>This level only prohibits a transaction from reading a row * with uncommitted changes in it. * java.sql.Connection#TRANSACTION_READ_COMMITTED int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED; * Indicates that dirty reads and non-repeatable reads are prevented; * phantom reads can occur. * <p>This level prohibits a transaction from reading a row with uncommitted changes * in it,and it also prohibits the situation where one transaction reads a row,* a second transaction alters the row,and the first transaction re-reads the row,* getting different values the second time (a "non-repeatable read"). * java.sql.Connection#TRANSACTION_REPEATABLE_READ int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ; #ISOLATION_REPEATABLE_READ} * and further prohibits the situation where one transaction reads all rows that * satisfy a { WHERE} condition,a second transaction inserts a row * that satisfies that { java.sql.Connection#TRANSACTION_SERIALIZABLE int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE; * Use the default timeout of the underlying transaction system,* or none if timeouts are not supported. int TIMEOUT_DEFAULT = -1 * Return the propagation behavior. * <p>Must return one of the { PROPAGATION_XXX} constants * defined on { TransactionDefinition this interface}. * the propagation behavior * #PROPAGATION_required * org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive() int getPropagationBehavior(); * Return the isolation level. * <p>Must return one of the { ISOLATION_XXX} constants * defined on { TransactionDefinition this interface}. * <p>Only makes sense in combination with { #PROPAGATION_required} * or { #PROPAGATION_REQUIRES_NEW}. * <p>Note that a transaction manager that does not support custom isolation levels * will throw an exception when given any other level than { #ISOLATION_DEFAULT}. * the isolation level getIsolationLevel(); * Return the transaction timeout. * <p>Must return a number of seconds,or { #TIMEOUT_DEFAULT}. * <p>Only makes sense in combination with { #PROPAGATION_REQUIRES_NEW}. * <p>Note that a transaction manager that does not support timeouts will throw * an exception when given any other timeout than { #TIMEOUT_DEFAULT}. * the transaction timeout getTimeout(); * Return whether to optimize as a read-only transaction. * <p>The read-only flag applies to any transaction context,whether * backed by an actual resource transaction * ({ #PROPAGATION_required}/{ #PROPAGATION_REQUIRES_NEW}) or * operating non-transactionally at the resource level * ({ #PROPAGATION_SUPPORTS}). In the latter case,the flag will * only apply to managed resources within the application,such as a * Hibernate { Session}. << * <p>This just serves as a hint for the actual transaction subsystem; * it will <i>not necessarily</i> cause failure of write access attempts. * A transaction manager which cannot interpret the read-only hint will * <i>not</i> throw an exception when asked for a read-only transaction. * { true} if the transaction is to be optimized as read-only * org.springframework.transaction.support.TransactionSynchronization#beforeCommit(boolean) * org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly() boolean isReadOnly(); * Return the name of this transaction. Can be { null}. * <p>This will be used as the transaction name to be shown in a * transaction monitor,if applicable (for example,WebLogic's). * <p>In case of Spring's declarative transactions,the exposed name will be * the { fully-qualified class name + "." + method name} (by default). * the name of this transaction * org.springframework.transaction.interceptor.TransactionAspectSupport * org.springframework.transaction.support.TransactionSynchronizationManager#getCurrentTransactionName() String getName(); }
3、TransactionStatus事务状态信息
记录事务的状态信息,可以看看源码。理解一下大神的思想。
* Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License,1)"> java.io.Flushable; * Representation of the status of a transaction. * * <p>Transactional code can use this to retrieve status information,* and to programmatically request a rollback (instead of throwing * an exception that causes an implicit rollback). * * <p>Derives from the SavepointManager interface to provide access * to savepoint management facilities. Note that savepoint management * is only available if supported by the underlying transaction manager. * * 27.03.2003 * #setRollbackOnly() * PlatformTransactionManager#getTransaction * org.springframework.transaction.support.TransactionCallback#doInTransaction * org.springframework.transaction.interceptor.TransactionInterceptor#currentTransactionStatus() interface TransactionStatus extends SavepointManager,Flushable { * Return whether the present transaction is new (else participating * in an existing transaction,or potentially not running in an * actual transaction in the first place). isNewTransaction(); * Return whether this transaction internally carries a savepoint,* that is,has been created as nested transaction based on a savepoint. * <p>This method is mainly here for diagnostic purposes,alongside * { #isNewTransaction()}. For programmatic handling of custom * savepoints,use SavepointManager's operations. * #isNewTransaction() * #createSavepoint * #rollbackToSavepoint(Object) * #releaseSavepoint(Object) hasSavepoint(); * Set the transaction rollback-only. This instructs the transaction manager * that the only possible outcome of the transaction may be a rollback,as * alternative to throwing an exception which would in turn trigger a rollback. * <p>This is mainly intended for transactions managed by * { org.springframework.transaction.support.TransactionTemplate} or * { org.springframework.transaction.interceptor.TransactionInterceptor},* where the actual commit/rollback decision is made by the container. * org.springframework.transaction.support.TransactionCallback#doInTransaction * org.springframework.transaction.interceptor.TransactionAttribute#rollbackOn void setRollbackOnly(); * Return whether the transaction has been marked as rollback-only * (either by the application or by the transaction infrastructure). isRollbackOnly(); * Flush the underlying session to the datastore,if applicable: * for example,all affected Hibernate/JPA sessions. @Override flush(); * Return whether this transaction is completed,that is,* whether it has already been committed or rolled back. * PlatformTransactionManager#commit * PlatformTransactionManager#rollback isCompleted(); }
4、事务的传播行为
一般来说我们都是一个业务层进行业务操作,如果涉及到一个业务层需要调用另一个业务层。这时就需要涉及到事务的传播行为用来规定在两个多个业务层中如何进行事务的管理。
(1)PROPAGATION_required:支持当前事务,如果不存在,则新建一个(默认)。
(2)PROPAGATION_SUPPORTS:支持当前事务,如果不存在,则不使用事务。
(3)PROPAGATION_MANDATORY:支持当前事务,如果不存在,则抛出异常。
(4)PROPAGATION_REQUIRES_NEW:如果当前事务不存在,则新建一个事务。如果当前事务存在,挂起这个事务再新建一个事务。
(5)PROPAGATION_NOT_SUPPORTED:不支持当前事务,以不使用事务的方式运行,如果当前存在事务,则挂起事务。
(6)PROPAGATION_NEVER:以非事务的方式运行,如果存在事务,抛出异常。
(7)PROPAGATION_NESTED:如果当前事务存在,则嵌套事务来执行。
六、spring的事务三种实现方式
三种方式分别为编码式,xml配置方式,注解配置方式。编码式了解即可,实际开发都是后两种。
1、编码式
(1)在spring容器中配置核心事务管理器,依赖于连接池,连接池的配置方式可参考之前的文章。
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource" ></property>
</bean>
(2)在spring容器中配置事务模板对象
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate" >
<property name="transactionManager" ref="transactionManager" ></property>
</bean>
(3)在业务层中配置事务模板对象
<bean name="accountDao" class="com.jichi.dao.AccountDaoImpl" > <property name="dataSource" ref="dataSource" ></property> </bean> <bean name="accountService" class="com.jichi.service.AccountServiceImpl" > <property name="ad" ref="accountDao" ></property> <property name="tt" ref="transactionTemplate" ></property> </bean>
(4)编写事务实现代码,在需要事务的方法中,用模板对象调用excute方法,实现其中的接口。重写接口中的方法,讲需要进行事务管理的代码放入其中。
class AccountServiceImpl implements AccountService { private AccountDao ad ; TransactionTemplate tt;
@Override void transfer(final Integer from,final Integer to,1)">final Double money) { tt.execute(new TransactionCallbackWithoutResult() { @Override protected doInTransactionWithoutResult(TransactionStatus arg0) { //减钱 ad.decreaseMoney(from,money); 加钱 ad.increaseMoney(to,money); } }); } }
2、xml方式
(1)导包
aopaliance,springaop,aspectJ,springaspectss
(2)配置事务管理器
<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> <!-- 以方法为单位,指定方法应用什么事务属性 isolation:隔离级别 propagation:传播行为 read-only:是否只读
timeout:过期时间
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>
(4)配置织入。首先配置切点。表示对哪些方法要进行增强。配置切面。pointcut-ref切点的名称,advice-ref通知的名称。
<aop:config > <aop:pointcut expression="execution(* com.jichi.service.*ServiceImpl.*(..))" id="txPc"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPc" /> </aop:config>
3、注解方式
(1)配置事务管理器
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource" ></property>
</bean>
(2)开启事务管理的注解
<tx:annotation-driven/>
(3)在使用事务的类上添加注解,在需要事务的方法上,打上注解,在注解中配置事务隔离级别,事务传播等信息。默认对该类中所有的方法,执行类上的配置,如果在某个方法上进行配置,则执行方法上的配置。
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.required,readOnly=true) TransactionTemplate tt; @Override @Transactional(isolation=Isolation.REPEATABLE_READ,1)">false) Double money) { int i = 1/0;