才高行厚的hibernate(5)---hibernate的事务管理

事务的概念

事物的个基本特性

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作。事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。 

  1. 原子性(atomic),事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行

  2. 一致性(consistent),事务在完成时,必须使所有的数据都保持一致状态。

  3. 隔离性(insulation),由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。

  4. 持久性(Duration),事务完成之后,它对于系统的影响是永久性的。

数据库事务并发可能带来的问题

如果没有锁定且多个用户同时访问一个数据库,则当他们的事务同时使用相同的数据时可能会发生问题。由于并发操作带来的数据不一致性包括:(丢失更新)丢失数据修改、(脏读)读”脏”数据、不可重复读、(幻读)产生幽灵数据:
lost update(丢失更新)
事务T1读取了数据,并执行了一些操作,然后更新数据。事务T2也做相同的事,则T1和T2更新数据时可能会覆盖对方的更新,从而引起错误。
dirty read(脏读)
事务T1更新了数据还未提交,这时事务T2来读取相同的数据,则T2读到的数据其实是错误的数据,即脏数据。基于脏数据所作的操作是不可能正确的
unrepeatable read(不可重复读)
一个事务的两次读取中,读取相同的资源得到不同的值。当事务T2在事务T1的两次读取之间更新数据,则会发生此种错误(重点在修改)
phantom read(幻读)
事务T1对一定范围内执行操作,T2对相同的范围内执行不兼容的操作,这时会发生幻读。 
如:T1删除符合条件C1的所有数据,T2又插入了一些符合条件C1的数据,则在T1中再次查找符合条件C1的数据还是可以查到,这对T1来说好像是幻觉一样,这时的读取操作称为幻读。(重点在新增或删除)

数据库的隔离级别

为了解决上面的4种问题,就出现了4种隔离级别,不同的数据库默认使用不同的隔离级别

1.read uncommit(未提交读)

当事务A更新某条数据时,不容许其他事务来更新该数据,但可以读取。

2.read commit(已提交读)

当事务A更新某条数据时,不容许其他事务进行任何操作包括读取,但事务A读取时,其他事务可以进行读取、更新

3.read repeatable(可重复读)

当事务A更新数据时,不容许其他事务进行任何操作,但当事务A进行读取时,其他事务只能读取,不能更新。

4.serializable(可序列化)

最严格的隔离级别,工作方式类似于可重复读。但它不仅会锁定受影响的数据,还会锁定这个范围。这就阻止了新数据插入查询所涉及的范围,事务必须依次进行。

Hibernate乐观锁策略,认为很少出现同时读取、更新的情况,在数据库隔离级别一般设为read commit,会导致出现lost update的问题

hibernate的事务

对jdbc的封装

hibernate是jdbc轻量级的封装,本身不具备事务管理的能力,在事物管理层面,一般是委托于底层的jdbc和jta来完成调度的。默认事物处理机制是基于jdbc transaction的,当然,我们也能通过配置jta来实现:
Xml代码
<hibernate-configuration> 
    <session-factory> 
        ........ 
            <property name="hibernate.transaction.factory_class"> 
                     org.hibernate.engine.transaction.internal.jta.JTATransactionFactory //JTA 
                    <!--org.hibernate.engine.transaction.internal.jdbc.JDBCTransactionFactory-->   //JDBC 
            </property> 
        ........ 
     </sesssion-factoty> 
</hibernate-configuration> 
  将事物委托给jdbc处理,无疑是最简单的方式,hibernate支持它做默认方式,其事物的封装,也非常简单,比如当我们在处理这段代码时:
Java代码

try{
session = sessionFactory.openSession(); 
Transaction tx=session.beginTransaction(); 
...... 
tx.commit(); }catch(Exception e){

e.printStackTrace();

tx.rollback();

}
 
  而从jdbc事物层面而言,实际上做的是:
Java代码
try{

Connection dbconn=getConnection(); 
dbconn.setAutoCommit(false); 

...... 
dbconn.commit();

}catch(Exception e){

e.printStackTrace();

dbcon.rollback();

}

hibernate对Jta事务的支持

需要配置对jta的支持,属性配置文件中

hibernate.transaction.factory_class =org.hibernate.engine.transaction.internal.jta.JTATransactionFactory

hibernate.current_session_context_class=jta

hibernate.transaction.manager_lookup_class=classname.of.TransactionManagerLookup
或xml中

  <property name="hibernate.transaction.factory_class"> 
                     org.hibernate.engine.transaction.internal.jta.JTATransactionFactory 
            </property> 

 <property name="hibernate.current_session_context_class"> 
                    jta
            </property> 

 <property name="hibernate.transaction.manager_lookup_class"> 
                   classname.of.TransactionManagerLookup
            </property> 

hibernate的jta的java代码

try {
    UserTransaction tx = (UserTransaction)new InitialContext()
                            .lookup("java:comp/UserTransaction");

    tx.begin();

    // Do some work on Session bound to transaction
    factory.getCurrentSession().load(...);
    factory.getCurrentSession().persist(...);

    tx.commit();
}
catch (RuntimeException e) {
    tx.rollback();
    throw e; // or display error message
}

hibernate的悲观锁和乐观锁

当多个人对同一数据同时进行修改的时候,会发生脏数据,造成数据的不一致性,hibernate的解决办法是通过悲观锁和乐观锁来实现.

Hibernate悲观锁: 在数据有加载的时候就给其进行加锁,直到该锁被释放掉,其他用户才可以进行修改

          优点:数据的一致性保持得很好

          缺点:不适合多个用户并发访问。当一个锁住的资源不被释放掉的时候,这个资源永远不会被其他用户进行修改,容易造成无限期的等待。

          实现:悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保                   证外部系统不会修改数据)。 

Hibernate乐观锁:就是在对数据进行修改的时候,对数据采取版本或者时间戳等方式来比较,数据是否一致性来实现加锁。 

           优点比较好。

           实现:版本或时间戳等,大多是基于数据版本(Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过                  为数据库表增加一个“version”字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应                  记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

Hibernate的加锁模式有: 
Ø LockMode.NONE : 无锁机制。 
Ø LockMode.WRITE :Hibernate在Insert和Update记录的时候会自动 
获取。 
Ø LockMode.READ : Hibernate在读取记录的时候会自动获取。 
以上这三种锁机制一般由Hibernate内部使用,如Hibernate为了保证Update 
过程中对象不会被外界修改,会在save方法实现中自动为目标对象加上WRITE锁。 
Ø LockMode.UPGRADE :利用数据库的for update子句加锁。 
Ø LockMode. UPGRADE_NOWAIT :Oracle的特定实现,利用Oracle的for update nowait子句实现加锁。 

悲观锁的使用:

    String hqlStr  =   " from TUser as user where user.name=’Erica’ " 
        Query query  =  session.createQuery(hqlStr); 
        query.setLockMode( " user " ,LockMode.UPGRADE);  // 加锁  
        List userList  =  query.list(); // 执行查询

    注意,只有在查询开始之前(也就是Hiberate 生成SQL 之前)设定加锁,才会 真正通过数据库的锁机制进行加锁处理,否则,数据已经通过不包含for update 子句的                 Select SQL加载进来,所谓数据库加锁也就无从谈起

       这里Hibernate通过使用数据库的for update子句实现了悲观锁机制。 

     上面这种锁机制是我们在应用层较为常用的,加锁一般通过以下方法实现: 

         Criteria.setLockMode 
         Query.setLockMode 
         Session.lock 

乐观锁的使用:

java代码:

省略getter和setter方法

package hibernate.senssic.relevance;


public class Person {


private int id;


private String name;


private int version;

}

xml映射文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping>
      <class name="hibernate.senssic.relevance.Person" optimistic-lock="version">
        <id name="id">
          <generator class="identity">
          </generator>
        </id>
        <!-- 必须位于id节点之后 -->
        <version name="version"></version>
        <property name="name"></property>
    
      </class>
    </hibernate-mapping>
每次对表进行更新的时候,我们可以发现,数据库中的version都在递增。通过比较version就可以解决脏读数据了

乐观锁的注解配置:

@Version

 private int version;


悲观锁与乐观锁的比较: 

悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受; 
相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。乐观锁机制往往基于系统中的数据存储逻辑,因此也具备一定的局限性,如在上例中,由于乐观锁机制是在我们的系统中实现,来自外部系统的更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。在 
系统设计阶段,我们应该充分考虑到这些情况出现的可能性,并进行相应调整(如将乐观锁策略在数据库存储过程中实现,对外只开放基于此存储过程的数据更新途径,而不是将数据库表直接对外公开)
Hibernate中可以通过class描述符的optimistic-lock属性结合version描述符指定。 
optimistic-lock属性有如下可选取值: 
Ø none 
无乐观锁 
Ø version 
通过版本机制实现乐观锁 
Ø dirty 
通过检查发生变动过的属性实现乐观锁 
Ø all 
通过检查所有属性实现乐观锁 
其中通过version实现的乐观锁机制是Hibernate官方推荐的乐观锁实现,同时也是Hibernate中,目前唯一在数据对象脱离Session发生修改的情况下依然有效的锁机制。因此,一般情况下,我们都选择version方式作为Hibernate乐观锁实现机制。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值