Spring的事务
事务有以下特性(ACID):
-
原子性(Atomicity)
事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。 -
一致性(Consistency)
事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。 -
隔离性(Isolation)
多个事务并发执行时,一个事务的执行不应影响其他事务的执行。 -
持久性(Durability)
已被提交的事务对数据库的修改应该永久保存在数据库中
Spring 事务的传播属性。
七种传播属性。
事务传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
-
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
-
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
-
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
-
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
-
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
-
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
-
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
-
TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
-
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
-
TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
-
TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
-
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
Spring 的事务管理
Spring 的事务管理是基于 AOP 实现的,而 AOP 是以方法为单位的。Spring 的事务属性分别为传播行为、隔离级别、只读和超时属性,这些属性提供了事务应用的方法和描述策略。
在 Java EE 开发经常采用的分层模式中,Spring 的事务处理位于业务逻辑层,它提供了针对事务的解决方案。
在 Spring 解压包的 libs 目录中,包含一个名称为 spring-tx-x.x.xx.RELEASE.jar 的文件,该文件是 Spring 提供的用于事务管理的 JAR 包,其中包括事务管理的三个核心接口:
- PlatformTransactionManager
- TransactionDefinition
- TransactionStatus
PlatformTransactionManager
PlatformTransactionManager 接口是 Spring 提供的平台事务管理器,用于管理事务。该接口中提供了三个事务操作方法,具体如下。
- TransactionStatus getTransaction(TransactionDefinition definition):用于获取事务状态信息。
- void commit(TransactionStatus status):用于提交事务。
- void rollback(TransactionStatus status):用于回滚事务。
TransactionDefinition
TransactionDefinition 接口是事务定义(描述)的对象,它提供了事务相关信息获取的方法,其中包括五个操作,具体如下。
String getName():获取事务对象名称。
int getIsolationLevel():获取事务的隔离级别。
int getPropagationBehavior():获取事务的传播行为。
int getTimeout():获取事务的超时时间。
boolean isReadOnly():获取事务是否只读。
属性名称 | 值 | 描 述 |
---|---|---|
PROPAGATION_REQUIRED | required | 支持当前事务。如果 A 方法已经在事务中,则 B 事务将直接使用。否则将创建新事务 |
PROPAGATION_SUPPORTS | supports | 支持当前事务。如果 A 方法已经在事务中,则 B 事务将直接使用。否则将以非事务状态执行 |
PROPAGATION_MANDATORY | mandatory | 支持当前事务。如果 A 方法没有事务,则抛出异常 |
PROPAGATION_REQUIRES_NEW | requires_new | 将创建新的事务,如果 A 方法已经在事务中,则将 A 事务挂起 |
PROPAGATION_NOT_SUPPORTED | not_supported | 不支持当前事务,总是以非事务状态执行。如果 A 方法已经在事务中,则将其挂起 |
PROPAGATION_NEVER | never | 不支持当前事务,如果 A 方法在事务中,则抛出异常 |
PROPAGATION.NESTED | nested | 嵌套事务,底层将使用 Savepoint 形成嵌套事务 |
在事务管理过程中,传播行为可以控制是否需要创建事务以及如何创建事务。
通常情况下,数据的查询不会改变原数据,所以不需要进行事务管理,而对于数据的增加、修改和删除等操作,必须进行事务管理。如果没有指定事务的传播行为,则 Spring3 默认的传播行为是 required。
package org.springframework.transaction;
import org.springframework.lang.Nullable;
/**
* 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 {@link #PROPAGATION_REQUIRED},
* {@link #PROPAGATION_REQUIRES_NEW} and {@link #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 {@link #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 {@code Session}.
*
* @author Juergen Hoeller
* @since 08.05.2003
* @see PlatformTransactionManager#getTransaction(TransactionDefinition)
* @see org.springframework.transaction.support.DefaultTransactionDefinition
* @see org.springframework.transaction.interceptor.TransactionAttribute
*/
public interface 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,
* {@code 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 {@code Connection}, a
* Hibernate {@code 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 {@code PROPAGATION_SUPPORTS} with care! In particular, do
* not rely on {@code PROPAGATION_REQUIRED} or {@code PROPAGATION_REQUIRES_NEW}
* <i>within</i> a {@code 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").
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
* @see 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 {@code 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
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code javax.transaction.TransactionManager} to be
* made available it to it (which is server-specific in standard Java EE).
* <p>A {@code PROPAGATION_REQUIRES_NEW} scope always defines its own
* transaction synchronizations. Existing synchronizations will be suspended
* and resumed appropriately.
* @see 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
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code 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
* {@code PROPAGATION_NOT_SUPPORTED} scope. Existing synchronizations
* will be suspended and resumed appropriately.
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
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
* {@code PROPAGATION_NEVER} scope.
*/
int PROPAGATION_NEVER = 5;
/**
* Execute within a nested transaction if a current transaction exists,
* behave like {@link #PROPAGATION_REQUIRED} otherwise. 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
* {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
* when working on a JDBC 3.0 driver. Some JTA providers might support
* nested transactions as well.
* @see 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.
* @see 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.
* @see java.sql.Connection#TRANSACTION_READ_UNCOMMITTED
*/
int ISOLATION_READ_UNCOMMITTED = 1; // same as java.sql.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.
* @see java.sql.Connection#TRANSACTION_READ_COMMITTED
*/
int ISOLATION_READ_COMMITTED = 2; // same as java.sql.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").
* @see java.sql.Connection#TRANSACTION_REPEATABLE_READ
*/
int ISOLATION_REPEATABLE_READ = 4; // same as java.sql.Connection.TRANSACTION_REPEATABLE_READ;
/**
* Indicates that dirty reads, non-repeatable reads and phantom reads
* are prevented.
* <p>This level includes the prohibitions in {@link #ISOLATION_REPEATABLE_READ}
* and further prohibits the situation where one transaction reads all rows that
* satisfy a {@code WHERE} condition, a second transaction inserts a row
* that satisfies that {@code WHERE} condition, and the first transaction
* re-reads for the same condition, retrieving the additional "phantom" row
* in the second read.
* @see java.sql.Connection#TRANSACTION_SERIALIZABLE
*/
int ISOLATION_SERIALIZABLE = 8; // same as java.sql.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 {@code PROPAGATION_XXX} constants
* defined on {@link TransactionDefinition this interface}.
* <p>The default is {@link #PROPAGATION_REQUIRED}.
* @return the propagation behavior
* @see #PROPAGATION_REQUIRED
* @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive()
*/
default int getPropagationBehavior() {
return PROPAGATION_REQUIRED;
}
/**
* Return the isolation level.
* <p>Must return one of the {@code ISOLATION_XXX} constants defined on
* {@link TransactionDefinition this interface}. Those constants are designed
* to match the values of the same constants on {@link java.sql.Connection}.
* <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or
* {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started
* transactions. Consider switching the "validateExistingTransactions" flag to
* "true" on your transaction manager if you'd like isolation level declarations
* to get rejected when participating in an existing transaction with a different
* isolation level.
* <p>The default is {@link #ISOLATION_DEFAULT}. Note that a transaction manager
* that does not support custom isolation levels will throw an exception when
* given any other level than {@link #ISOLATION_DEFAULT}.
* @return the isolation level
* @see #ISOLATION_DEFAULT
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setValidateExistingTransaction
*/
default int getIsolationLevel() {
return ISOLATION_DEFAULT;
}
/**
* Return the transaction timeout.
* <p>Must return a number of seconds, or {@link #TIMEOUT_DEFAULT}.
* <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or
* {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started
* transactions.
* <p>Note that a transaction manager that does not support timeouts will throw
* an exception when given any other timeout than {@link #TIMEOUT_DEFAULT}.
* <p>The default is {@link #TIMEOUT_DEFAULT}.
* @return the transaction timeout
*/
default int getTimeout() {
return TIMEOUT_DEFAULT;
}
/**
* 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 ({@link #PROPAGATION_REQUIRED}/
* {@link #PROPAGATION_REQUIRES_NEW}) or operating non-transactionally at
* the resource level ({@link #PROPAGATION_SUPPORTS}). In the latter case,
* the flag will only apply to managed resources within the application,
* such as a Hibernate {@code 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.
* @return {@code true} if the transaction is to be optimized as read-only
* ({@code false} by default)
* @see org.springframework.transaction.support.TransactionSynchronization#beforeCommit(boolean)
* @see org.springframework.transaction.support.TransactionSynchronizationManager#isCurrentTransactionReadOnly()
*/
default boolean isReadOnly() {
return false;
}
/**
* Return the name of this transaction. Can be {@code 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 {@code fully-qualified class name + "." + method name} (by default).
* @return the name of this transaction ({@code null} by default}
* @see org.springframework.transaction.interceptor.TransactionAspectSupport
* @see org.springframework.transaction.support.TransactionSynchronizationManager#getCurrentTransactionName()
*/
@Nullable
default String getName() {
return null;
}
// Static builder methods
/**
* Return an unmodifiable {@code TransactionDefinition} with defaults.
* <p>For customization purposes, use the modifiable
* {@link org.springframework.transaction.support.DefaultTransactionDefinition}
* instead.
* @since 5.2
*/
static TransactionDefinition withDefaults() {
return StaticTransactionDefinition.INSTANCE;
}
}
TransactionStatus
TransactionStatus 接口是事务的状态,它描述了某一时间点上事务的状态信息。其中包含六个操作,具体如表 2 所示。
名称 | 说明 |
---|---|
void flush() | 刷新事务 |
boolean hasSavepoint() | 获取是否存在保存点 |
boolean isCompleted() | 获取事务是否完成 |
boolean isNewTransaction() | 获取是否是新事务 |
boolean isRollbackOnly() | 获取是否回滚 |
void setRollbackOnly() | 设置事务回滚 |
xml配置声明式事务
<!-- 事务管理器,依赖于数据源 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 编写通知:对事务进行增强(通知),需要编写切入点和具体执行事务的细节 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- 给切入点方法添加事务详情,name表示方法名称,*表示任意方法名称,propagation用于设置传播行为,read-only表示隔离级别,是否只读 -->
<tx:method name="find*" propagation="SUPPORTS"
rollback-for="Exception" />
<tx:method name="*" propagation="REQUIRED" isolation="DEFAULT"
read-only="false" />
</tx:attributes>
</tx:advice>
<!-- aop编写,让Spring自动对目标生成代理,需要使用AspectJ的表达式 -->
<aop:config>
<!-- 切入点 -->
<aop:pointcut expression="execution(* com.server.impl.*.*(..))"
id="txPointCut" />
<!-- 切面:将切入点与通知整合 -->
<aop:advisor pointcut-ref="txPointCut" advice-ref="txAdvice" />
</aop:config>
注解方式配置声明方事务
spring配置文件
<!-- 事务管理器,依赖于数据源 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 注册事务管理驱动 -->
<tx:annotation-driven transaction-manager="txManager"/>
service注解
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
public class UserServerImpl implements UserServer {
@Autowired
JdbcTemplate jdbcTemplate;
public void getUserMsg(User user) {
List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from user");
// System.out.println(user.toString());
System.out.println(list);
int i = 1 / 0;
jdbcTemplate.update("update user set username=?,password=?,datetime=?,admin=?"
, "user2", "user", new Date(), "user");
}
}
读写事务”和“只读事务
事务又会分为 “读写事务”和“只读事务”,“只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。Oracle默认情况下是没有事务控制的,即只能查询出语句执行前状态的数据,那么如果当你在做报表查询,两次查询结果可能会不一致。这个时候就可以用到只读事务。如果两条查询语句被一个只读事务包围,则两条查询结构就能保证一致性。也就是说只读事务开启期间数据库的变化是不会被查询出来