1、事务隔离级别和事务传播特性
数据库系统中多个事务的并发可能发生以下5种现象:
第一类丢失更新:撤消一个事务时,把其它事务已提交的更新的数据覆盖了。
脏读:一个事务读到另一个事务未提交的更新数据。
幻读:一个事务执行两次查询,但第二次查询比第一次查询多出了一些数据行。
不可重复读:一个事务两次读同一行数据,可是这两次读到的数据不一样。
第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另一个事务已提交的更新数据。
JDBC的数据库连接使用数据库默认的事务隔离级别,hibernate可以显式设置事务隔离级别</pre><pre name="code" class="html" style="font-size:14px;"><session-factory>
<!-- 设置JDBC的隔离级别 -->
<property name="hibernate.connection.isolation">2</property>
</session-factory>
每一种事务隔离级别对应一个正整数:
Serializable:串行化。隔离级别最高8
Repeatable Read:可重复读。4
Read Committed:读已提交数据。2
Read Uncommitted:读未提交数据。隔离级别最差。1
隔离级别 |
脏读 (Dirty Read) |
不可重复读(NonRepeatable Read) |
幻读 (Phantom Read) |
读操作未提交 (Read uncommitted) |
可能 |
可能 |
可能 |
读操作已提交 (Read commited) |
不可能 |
可能 |
可能 |
可重复读 (Repeatable read) |
不可能 |
不可能 |
可能 |
可串行化 (Serializable) |
不可能 |
不可能 |
不可能 |
以下是7种事务传播特性
1.PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启
2.PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行3.PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
4.PROPAGATION_REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
5.PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
6.PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
7.PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
2、HibernateTranscationManager
通常在Spring和Hibernate的集成中,hibernate的事务管理被委托给HibernateTranscationManager。以下通过HibernateTranscationManager的部分源码对Spring和Hihernate集成后的事务管理进行分析。
public class HibernateTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {
首先该类继承了AbstractPlatformTransactionManager类且重写了doGetTransaction()方法和doBegin(),doCommit(),这才是我们主要要讨论的。从父类的getTransaction(TransactionDefinition
definition)可以看出会先走doGetTransaction(),而后在走doBegin(),我们来看doGetTransaction()方法源码:protected Object doGetTransaction() {
HibernateTransactionObject txObject = new HibernateTransactionObject();//这里new一个HibernateTransactionObject对象
txObject.setSavepointAllowed(isNestedTransactionAllowed());//设置一个保存点
//从当前线程当中以sessionFacoty为key去取相对应的sessionHolder,
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
if (sessionHolder != null) {
if (logger.isDebugEnabled()) {
logger.debug("Found thread-bound Session [" +
SessionFactoryUtils.toString(sessionHolder.getSession()) + "] for Hibernate transaction");
}
txObject.setSessionHolder(sessionHolder);//把sessionHolder设置到txObject当中
}
else if (this.hibernateManagedSession) {//这是判断有没有设置在当前上下文,比如在配置文件中的thread,或Spring上下文
try {
Session session = getSessionFactory().getCurrentSession();//有就直接从当前上下文去取
if (logger.isDebugEnabled()) {
logger.debug("Found Hibernate-managed Session [" +
SessionFactoryUtils.toString(session) + "] for Spring-managed transaction");
}
txObject.setExistingSession(session);//和上面一样设置sessionHolder到txObject当中,该set方法中又把session包装了下
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException(
"Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
}
}
//在事务对象中设置DataSource,其中有个afterPropertiesSet()将从sessionFactory中获取DataSource
if (getDataSource() != null) {
ConnectionHolder conHolder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(getDataSource());//从当前线程中获取绑定的数据库连接,它是在doBegin()方法绑定的
txObject.setConnectionHolder(conHolder);//把从线程中取得的sessionHolder设置到txObject中
}
return txObject;
}
上面这个doGetTransaction()方法走完了就创建了HibernateTransactionObject
txObject对象,这个对象也是主角,且往这个对象中填充了两个属性,为这两个属性赋好值,一个是sessionholder,另一个是connectionHolder,也就是session和connect。下一个就是doBegin()方法,引入一个新对象TransactionDefinition事物描述:
protected void doBegin(Object transaction, TransactionDefinition definition) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;//取得txObject对象,把事务强转HibernateTransactionObject
// 如果sessionHolder没有创建,那么这里将会创建hibernate里面的session,并把这个session放到SessionHolder中
Session session = null;
try {//判断txObject上的sessionHolder值是否为空
if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
Interceptor entityInterceptor = getEntityInterceptor();//一个实体拦截器
Session newSession = (entityInterceptor != null ?
getSessionFactory().openSession(entityInterceptor) : getSessionFactory().openSession());
if (logger.isDebugEnabled()) {
logger.debug("Opened new Session [" + SessionFactoryUtils.toString(newSession) +
"] for Hibernate transaction");
}
txObject.setSession(newSession);
}
//这里从sessionHolder中取出session,为hibernateTransaction做准备
session = txObject.getSessionHolder().getSession();
if (this.prepareConnection && isSameConnectionForEntireSession(session)) {
// We're allowed to change the transaction settings of the JDBC Connection.
if (logger.isDebugEnabled()) {
logger.debug(
"Preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");
}
Connection con = session.connection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
}
else {
//这里是设置Aop里面你配置的isolation属性
// Not allowed to change the transaction settings of the JDBC Connection.
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
// We should set a specific isolation level but are not allowed to...
throw new InvalidIsolationLevelException(
"HibernateTransactionManager is not allowed to support custom isolation levels: " +
"make sure that its 'prepareConnection' flag is on (the default) and that the " +
"Hibernate connection release mode is set to 'on_close' (SpringTransactionFactory's default). " +
"Make sure that your LocalSessionFactoryBean actually uses SpringTransactionFactory: Your " +
"Hibernate properties should *not* include a 'hibernate.transaction.factory_class' property!");
}
if (logger.isDebugEnabled()) {
logger.debug(
"Not preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");
}
}
//这里是设置Aop里面你配置的read-only属性,
if (definition.isReadOnly() && txObject.isNewSession()) {
// Just set to NEVER in case of a new Session for this transaction.
session.setFlushMode(FlushMode.MANUAL);
}
if (!definition.isReadOnly() && !txObject.isNewSession()) {
//判断事物是否只读,是否是一个新的session,也就是当前线程里面存不存在session,不存在则为true(OpenSessionView)
// We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = session.getFlushMode();
if (flushMode.lessThan(FlushMode.COMMIT)) {
session.setFlushMode(FlushMode.AUTO);
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
}
}
Transaction hibTx;
// Register transaction timeout.
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
hibTx = session.getTransaction();
//设置一个事务超时机制,设置时间hibTx.begin();
hibTx.setTimeout(timeout);
}else {
//将hibernate的事务设置到txObject的sessionHolder的里面,这个sessionHolder会和线程绑定.
hibTx = session.beginTransaction();
}
//将Transaction hibTx设置到txObject中,给txObject事务赋值,主要是一个已经开启的事务
txObject.getSessionHolder().setTransaction(hibTx);
// Register the Hibernate Session's JDBC Connection for the DataSource, if set.
if (getDataSource() != null) {
Connection con = session.connection();
ConnectionHolder conHolder = new ConnectionHolder(con);//包装connection
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
conHolder.setTimeoutInSeconds(timeout);
}
if (logger.isDebugEnabled()) {
logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]");
}
//把当前的数据库connection绑定在当前线程当中.
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
//在这里给txObject的ConnectionHolder赋值,以保证在con不为null
txObject.setConnectionHolder(conHolder);
}
//如果是新的sessionHolder,将它和当前线程绑定// Bind the session holder to the thread.
if (txObject.isNewSessionHolder()) {
//判断在当前线程里面有没有这个sessionHolder,当前里面有则为false,open则是true
TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
//绑定到当前线程当中
}
//在sessionHolder中进行状态标志,标识事务已经开始。
txObject.getSessionHolder().setSynchronizedWithTransaction(true);
}
}
上面这个doBegin方法,首先是得到一个在上面doGetTransaction()里面创建的hibernateTransactionObject对象txObject,还有个实体拦截器,entityInterceptor,该拦截器的作用就相当于一个代理,要访问被代理的对象,先走这个拦截器,我们在doGetTransaction方法中,先判断了在线程和当前上下文能不能取得到session,并把其设置到txObject中,并且还在doBegin()中为了保证sessionHolder不为null,判断txObject.getSessionHolder()如果为null,则通过sessionfactory打开一个session,并把它传到txObject封装成sessionHolder,且在opensession方法中传入拦截器。现在这个主角类HibernateTransactionObject对象txObject已经包含了sessionHolder,conHolder,已经开启了的Transaction。doCommit()众所周知是事务提交,当然在提交之前,也做了些许多事情,比如清空session,这些方法就没一一列出.可以查看父类AbstractPlatformTransactionManager中的processCommit()方法,
可以看到是先跑一些prepareForCommit准备提交的方法等等......
protected void doCommit(DefaultTransactionStatus status) {//事务提交
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Committing Hibernate transaction on Session [" +
SessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "]");
}
try {
txObject.getSessionHolder().getTransaction().commit();//得到sessionHolder的事务,直接提交
}