一、什么是事务?
事务(Transaction)是作为单个逻辑工作单元执行的一系列操作。这些操作作为一个整体向系统提交,要么都执行、要么都不执行。事务是一个不可分割的工作逻辑单元
转账操作A—>B:
begin transaction
1.更新帐户A的余额
2.记录帐户A的交易日志
3.更新帐户B的余额
4.记录帐户B的交易日志
end transaction
二、事务的特征(ACID)
原子性Atomicity
事务必须是原子工作单元。对于其数据修改,要么全执行,要么全都不执行。
一致性Consistency
事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有的内部数据结构都必须是正确的。在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
隔离性Isolation
由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。
持久性Durability
事务完成之后,它对于系统的影响是永久性的: 该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
三、事务管理策略
容器管理事务(CMT Container Manage Transaction)
Bean管理事务(BMT Bean Manage Transaction )
JTA: Java Transaction API
1.JTA是JavaEE提供的管理分布式事务的一系列接口,它提供了TransactionManager和ResourceManager之间沟通机制。
a.javax.transaction.UserTransaction提供给应用程序代码直接使用的接口,用于定义事务边界和获取事务状态。
b.javax.transaction.TransactionManager主要由应用服务器使用的接口,除了定义事务边界和获取事务状态,还可以挂起(suspend)或者重启(resume)事务。
四、事务属性
Required:
方法调用要求事务上下文,如果已经存在一个事务上下文,那么就是用这个,如果不存在,则创建一个新的如果不存在新事务,容器启动新的事务,如果存在事务,bean使用该事务。
RequiresNew:
容器在bean上的每个方法调用之前创建新事务,并在返回之前提交事务。调用bean方法的时候,总是启动新事务。如果事务已经存在,则悬挂该事务,直到新事务完成。
NotSupported:
bean运行在事务上下文的外部。在方法调用期间,悬挂起了现有事务。不能在事务内部调用bean。悬挂现有的事务,直到该bean中调用的方法完成。
Supports:
如果存在事务上下文,方法调用使用当前事务上下文。如果不存在,则不创建新的事务上下文。容器不启动新的事务。如果事务已存在,bean将包含在这个事务中。
注意:使用这个属性,没有事务,bean也能运行。
Mandatory:
方法调用需要事务上下文。如果不存在,抛出异常。必须已经存在活动事务。如果不存在事务,则抛出javax.ejb.TransactionRequiredException。
Never:
方法调用要求不存在事务上下文。如果存在,则抛出异常。bean必须永远不同事务同时运行。如果存在事务,则抛出java.rmi.RemoteException。
事务属性-事务传播
五、事务隔离级别
目的:在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别。
问题:
更新丢失(Lost update)
两个事务都同时更新一行数据,但是第二个事务却中途失败退出,导致对数据的两个修改都失效了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
脏读(Dirty Reads)
一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交。这是相当危险的,因为很可能所有的操作都被回滚。
不可重复读(Non-repeatable Reads)
一个事务对同一行数据重复读取两次,但是却得到了不同的结果。它包括以下情况:
(1) 事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。
(2) 幻读(Phantom Reads):事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据(这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。
未授权读取ISOLATION_READ_UNCOMMITED
允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
授权读取ISOLATION_READ_COMMITTED
允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
可重复读取ISOLATION_REPEATABLE_READ
禁止不可重复读取和脏读取,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
序列化ISOLATION_SERIALIZABLE
这个常量表示脏读取,非重复读取和不真实读取都被禁止。
这个级别包括在TRANSACTION_REPEATABLE_READ阻止的事情,以及防止一个事务在满足WHERE条件下读所有的行记录,第二个事务在满足WHERE条件下插入一行,随后前一个事务在同样的条件下重复读取,这是获得的是不真实的行记录。
ISOLATION_DEFAULT(Spring)
默认隔离级别是与具体的数据库相关的,采取的是具体数据库的默认隔离级别,不同的数据库是不一样的
事务隔离策略
Weblogic:
weblogic-ejb-jar.xml
…
<transaction-isolation>
<isolation-level>TransactionSerializable</isolation-level>
<method>
<ejb-name>TradingService</ejb-name>
<method-inf>Remote</method-inf>
<method-name>placeTrade</method-name>
</method>
</transaction-isolation>
Spring:
…
<bean id=“tradingService" class=“org.springframework.transaction.interceptor. TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref local=" tradingService Target"/></property>
<property name="transactionAttributes">
<props>
<prop key=“insert*">PROPAGATION_REQUIRED,ISOLATION_SERIALIZABLE</prop>
</props>
</property>
</bean>
六、事务类型
本地事务模型:Local Transaction Model
程序定制事务模型:Programmatic Transaction Model
申明式事务模型:Declarative Transaction Model
七、分布式事务
多个数据源
两段提交协议
第一步,事务控制器询问各资源管理器是否准备好提交;
第二步,如果所有资源管理器都准备好提交,则事务管理器要求所有进行提交。
Spring事务处理
1.声明式事务管理
2.编程式事务管理
Spring声明式事务不提供高端应用服务器提供的跨越远程调用的事务上下文传播。如果你需要这些特性,我们推荐你使用EJB。 然而,不要轻易使用这些特性。因为通常我们并不希望事务跨越远程调用。
Spring中事务处理-申明式事务管理
事务属性
PROPAGATION_REQUIRED
PROPAGATION_SUPPORTS
PROPAGATION_MANDATORY
PROPAGATION_REQUIRES_NEW
PROPAGATION_NOT_SUPPORTED
PROPAGATION_NEVER
PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚
Isolation隔离级别
read-only是否只读,默认为false
大多数Spring用户选择声明式事务管理。这是对应用代码影响最小的选择,因此也最符合 非侵入式 轻量级容器的理念。
java
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
xml
<tx:annotation-driven transaction-manager="txManager"/>
<!-- a PlatformTransactionManager is still required -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
</bean>
Spring中事务处理-编程式事务管理
使用 TransactionTemplate (推荐)
直接使用一个 PlatformTransactionManager 实现
使用 TransactionTemplate
private final TransactionTemplate transactionTemplate;
// use constructor-injection to supply the PlatformTransactionManager
public SimpleService(PlatformTransactionManager transactionManager) { Assert.notNull(transactionManager, "The ‘transactionManager‘ argument must not be null.");
this.transactionTemplate = new TransactionTemplate(transactionManager);
// the transaction settings can be set here explicitly if so desired this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED); //设定事务隔离级别
this.transactionTemplate.setTimeout(30); // 事务超时
}
…
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
updateOperation1();
updateOperation2();
} catch (SomeBusinessExeption ex) {
status.setRollbackOnly(); //回滚
}
}
});
使用 PlatformTransactionManager
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can only be done programmatically
def.setName("SomeTxName"); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
// execute your business logic here
} catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>