ACID
数据库事务的特性:
- 原子性:组成一个事务的多个数据库操作是一个不可分割的原子单元。只有所有操作执行成功,事务才能提交。有任何一个数据库操作执行失败,已经执行的操作必须撤销。
- 一致性:事务操作成功后,数据库所处的状态和它的业务规则是一致的。即数据不会被破坏。
- 隔离性。不同的事务拥有各自的数据操作空间,他们的操作不会对对方造成影响。数据库规定了多种数据隔离级别。不同的隔离级别对应不同的干扰程度。隔离级别越高,数据一致性越好,但并发性余越弱。
- 持久性:一旦事务提交成功,事务中的所有数据库操作都必须持久到数据库中,即使在事务提交时,数据库马上奔溃,在数据库重启时,也必须能保证通过某种机制恢复数据。
数据库管理系统一般采用重执行日志来保证原子性,一致性和持久性。
数据并发的问题
- 脏读:A事务读取了B事务尚未提交的数据,并在这个数据的基础上进行操作。如果B事务回滚,那么A读到的数据就是脏的。
- 不可重复读:A事务读取两次数据,第一次,B事务尚未提交更改。第二次,B事务已经提交更改。导致前后两次读取不一致
- 幻读(phantom read):A事务读取B事务提交的新增数据。幻读一般发生在计算机统计数据的事务中。第一次读,B事务尚未提交,第二次读,B事务新增数据并提交了。前后两次读不一致。
- 第一类丢失更新:A事务撤销时,把已经提交的B事务的更新数据覆盖了。
- 第二类丢失更新:A事务覆盖B事务提交的数据,造成B事务所做操作丢失。
幻读和不可重复读比较像,但是前者指的是读到了其他已经提交事务的新增数据,后者是读到了已经提交事务的更改数据。防止读到更改数据,只需对操作的数据添加行级锁,反之读到新增数据,往往需要添加表级锁。
数据库锁机制
一般分为表锁定和行锁定。从并发事务锁定的关系上看,可以分为共享锁定和独占锁定。共享锁定会防止独占锁定,但允许其他的共享锁定。而独占锁定既防止其它的独占锁定又反之共享锁定。为了更改数据,数据库必须在进行更改的行上施加行级独占锁定,Insert,update,delete,select for update语句都会隐式的采用必要的行级锁定。Oracle数据库常用的5中锁定:
- 行共享锁定:一般通过select for update语句隐式获得行共享锁定。行共享锁定并不防止对数据行进行更改操作,但是可以防止其他会话获取独占性数据表锁定。允许进行多个并发的行共享和行独占锁定。还允许进行数据表的共享或者采用共享行独占锁定。
- 行独占锁定:通过一条insert,update或者delete语句隐式获取。这种锁定可以防止其它会话获取一个共享锁定,共享行独占锁定或者独占锁定。
- 表共享锁定:通过LOCK TABLE IN SHARE MODE语句显示获得。
- 表共享行独占锁定:通过LOCK TABLE IN SHARE ROW EXCLUSIVE MODE语句显示获得。
- 表独占锁定:通过LOCK TABLE IN EXCLUSIVE MODE语句显示获得。防止其它会话对该表的任何其它锁定。
事务隔离级别
用户指定会话的事务隔离级别,数据库就会分析事务中的SQL语句,然后自动为事务操作的数据资源添加适合的锁。此外,数据库还会维护这些锁。当一个资源上的锁数目太多时,自动进行锁升级以提高系统的运行性能。
oracle使用多版本机制彻底解决了在非阻塞读时读到脏数据的问题并保持读的一致性。而Mysql的默认事务隔离级别是:Repeatable Read。
事务管理抽象
主要有3个接口:PlatformTransactionManager,TransactionDefinition,TransactionStatus。
TransactionDefinition
定义了一些事务属性。
- 事务隔离:TransactionDefinition
接口定义了4个隔离级别。 - 事务传播:
- 事务超时:事务在超时前能运行多久,超过时间后,事务被回滚。
- 只读状态:只读事务不修改任何数据,
TransactionStatus
代表一个事务的具体运行状态。继承SavepointManager接口,SavepointManager接口基于JDBC3.0保存点的分段事务控制能力提供了嵌套事务的机制。
boolean isNewTransaction();
boolean hasSavepoint();
boolean isRollbackOnly();
boolean isCompleted();
void setRollbackOnly():将当前事务设置为rollback-only,通过该标识通知事务管理器只能将事务回滚。
PlatformTransactionManager
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
spring事务管理实现类
将事务管理委托给底层具体的持久化实现框架来完成。例如jpa,hibernate等都有对应的事物管理器。而这些事务管理器都对特定事务实现框架的代理。
spring jdbc和mybatis
他们都基于数据源的Connection访问数据库,可以使用Data
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc2.driver}"/>
<property name="url" value="${jdbc2.url}"></property>
<property name="username" value="${jdbc2.username}"></property>
<property name="password" value="${jdbc2.password}"></property>
</bean>
<!-- 定义事务 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
事务同步管理器
JDBC的Connection,Hibernate的Session,称为资源,这些资源是不能多线程共享的,spring的事务同步管理器TransactionSynchronizationManager使用ThreadLocal为不同事务线程提供了独立的资源副本。
spring框架为不同持久化技术提供了一套从TransactionSynchronizationManager获取对应线程绑定的资源的工具类。
- DataSourceUtils
- SessionFactoryUtils
- EntityManagerFactoryUtils
- PersistenceManagerFactoryUtils
这些工具类都提供了静态方法,如:DataSourceUtils.getConnection(DataSource dataSource)方法可以获取和当前线程绑定的Connection。当手工操作底层持久化技术的原生API时,就需要通过这些工具类来获取,而不应该直接从DataSource或者SessionFactory获取。这些工具类还有一个重要的用途,将特定异常转换为spring的DAO异常。
spring为不同的持久化技术提供了模板类,模板类在内部通过资源获取工具类简介访问TransactionSynchronizationManager中的线程绑定资源。TransactionSynchronizationManager将DAO,Service类中影响线程安全的所有“状态”统一抽取到该类中,并用ThreadLocal进行替换。所以Dao,service类可以是singleton的。
事务传播行为
当调用一个基于spring的Service接口方法时,它将运行与spring管理的事务环境中,Service接口方法可能会在内部调用其他的Service接口方法以共同完成一个完整的业务逻辑。spring通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中。
spring在TransactionDefinition中定义了7中事务传播行为。他们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播。
- PROPAGATION_REQUIRED:如果当前没有事务,则新建一个事务,如果已经存在事务,则加入到这个事务中。
- PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,则以非事务方式执行。
- PROPAGATION_MANDATORY:使用当前事务,如果当前没有事务则抛出异常
- PROPAGATION_REQUIRES_NEW:新建事务。如果当前存在事务,则把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则把当前事务挂起。
- PROPAGATION_NEVER:以非事务方式执行操作,如果当前存在事务则抛出异常
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
编程式的事务管理
实际应用中很少使用编程式事务管理。模板类,TransactionTemplate有两个主要方法:
void setTransactionManager(PlatformTransactionManager transactionManager)
T execute(TransactionCallback<T> action)
TransactionCallback只有一个方法:T doInTransaction(TransactionStatus status);如果操作不会返回结果,则可以使用TransactionCallback的子接口TransactionCallbackWithoutResult。例如:
@Service
public class UserService{
//......
public TransactionTemplate template;
//autowire setTemplate...
public void addUser(User user){
template.execute(new TransactionCallbackWithoutResult(){
protected void doInTransactionWithoutResult(TransactionStatus status){
userDao.addUser(user);
}
}
}
}
使用XML配置声明式事务
Spring的声明式事务管理是通过spring AOP实现的。默认情况下,spring只有在遇到运行期异常时自动回滚。
使用原始的TransactionProxyFactoryBean
在spring的早期版本,必须通过TransactionProxyFactoryBean对需要事务管理的业务类进行代理。例如:
<bean id="xxService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="target" ref="targetxxService"></property>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
异常回滚/提交规则
默认情况下,遇到运行期异常时自动回滚,遇到检查型异常时既不会滚也不提交。
使用aop/tx命名空间
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<aop:config>
<!-- 通过aop定义事务增强切面 -->
<aop:pointcut id="serviceMethod"
expression="execution(* com.mucfc.dao.LibraryDaoImpl.*(..))" />
<!-- 引用事务增强 -->
<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />
</aop:config>
<!--事务增强 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- 事务属性定义 -->
<tx:attributes>
<tx:method name="get*" read-only="false" />
<tx:method name="add*" rollback-for="Exception" />
<tx:method name="del*" />
</tx:attributes>
</tx:advice>
<tx:method >
元素拥有的属性:
属性 | 是否必须 | 默认值 | 描述 |
---|---|---|---|
name | 是 | 与事务属性关联的方法名,可使用通配符(*) | 如“get*” |
propagation | 否 | REQUIRED | 事务传播行为 |
islation | 否 | DEFAULT | 事务隔离级别 |
timeout | 否 | -1 | 事务超时时间,以秒为单位,-1表示由底层的事务系统决定 |
read-only | 否 | false | 事务是否只读 |
rollback-for | 否 | 所有运行期期异常回滚 | 触发事务回滚的Exception,用异常名称的片段进行匹配,可以设置多个,用逗号分隔。 |
no-rollback-for | 异常 | 所有检查期异常不回滚 | 不触发事务回滚的Exception。 |
使用@Transactional注解
@Service
@Transactional
public class UserService{
}
//对标注@Transactional注解的bean进行加工处理,以织入事务管理切面
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false"/>
@Transactional注解可以应用于接口定义和接口方法,类定义和pubic方法上。spring建议在类上添加注解。方法出的注解会覆盖类定义处的注解。
使用不同的事务管理器
如果一个应用要使用多个事务管理器,则通过如下方法实现:
@Service
public class UserService{
@Transactional("user1")
public void addUser(){...}
@Transactional("user2")
public void updateUser(){...}
}
<bean id="user1txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="user1DataSource" />
<qualifier value="user1"/>
</bean>
<bean id="user2txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="user2DataSource" />
<qualifier value="user2"/>
</bean>