2013-03-29 39 views
0

我想更新不同事务中的同一行来理解Hibernate的乐观锁定。Hibernate乐观锁定:没有得到任何异常

但我没有收到任何StaleObjectStateException或任何其他异常。

public void updateUsingTwoThreads() throws InterruptedException { 
     Thread t1 = new Thread(new Runnable() { 
      public void run() { 
       Session session = null; 
       try { 
        session = HibernateUtil.getSessionFactory().openSession(); 
        org.hibernate.Transaction transaction = session 
          .beginTransaction(); 
        Airline airline = (Airline) session.get(Airline.class, 
          new Integer(1)); 
        System.out.println("getVersion in "+airline.getVersion()+"in "+ Thread.currentThread().getName()); 
        try { 
         Thread.sleep(2000); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 

        airline.setAirlineCode("asdasd234phle"); 
        session.saveOrUpdate(airline); 
        System.out.println("getVersion in "+airline.getVersion()+"in "+ Thread.currentThread().getName()); 
        transaction.commit(); 
       }catch(Throwable t){ 
        System.out.println(t); 

       }finally { 
        session.close(); 
       } 
      } 
     },"earlier"); 

     Thread t2 = new Thread(new Runnable() { 
      public void run() { 
       Session session = null; 
       try { 
        session = HibernateUtil.getSessionFactory().openSession(); 
        //session.clear(); 
        org.hibernate.Transaction transaction = session.beginTransaction(); 
        Airline airline = (Airline) session.get(Airline.class,new Integer(1)); 
        System.out.println("getVersion in "+airline.getVersion()+"in "+ Thread.currentThread().getName()); 
        try { 
         Thread.sleep(100000); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
        System.out.println("getVersion in "+airline.getVersion()+"in "+ Thread.currentThread().getName()); 
        airline.setAirlineCode("asdasdbaadmain"); 
        session.saveOrUpdate(airline); 
        transaction.commit(); 
       }catch(Throwable t){ 
        System.out.println(t); 

       } finally { 
        session.close(); 
       } 
      } 
     },"later"); 

     t1.start(); 
     t2.start(); 

     t1.join(); 
     t2.join(); 

    } 

我附上上面的代码,我试图做的。请让我知道..我错过了什么吗?

或乐观锁定与我正在尝试做的不同。

在上面的代码中,我启动了两个线程“早期”和“稍后”,它们都从sessionFactory获取会话对象,并从数据库加载相同的记录并同时更新它。

1)但没有例外,并且只有一个线程能够更新该行。

Airline类连接如下:

package tryouts.one_to_many; 

import java.io.Serializable; 
import java.util.HashSet; 
import java.util.Set; 

import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.GenerationType; 
import javax.persistence.Id; 
import javax.persistence.JoinColumn; 
import javax.persistence.OneToMany; 
import javax.persistence.Version; 

import org.hibernate.annotations.OptimisticLockType; 

@Entity 
@org.hibernate.annotations.Entity(dynamicUpdate=true, optimisticLock = OptimisticLockType.ALL) 
public class Airline implements Serializable { 

    private static final long serialVersionUID = 1L; 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    @Column(name = "AIRLINE_ID") 
    private Integer airlineId; 

    @Column(name = "AIRLINE_NAME") 
    private String airlineName; 

    @Column(name = "AIRLINE_CODE") 
    private String airlineCode; 

    @Version 
    private long version; 

    @OneToMany 
    @JoinColumn(name="airlineId") 
    private Set<AirlineFlight> airlineFlights = new HashSet<AirlineFlight>(); 

    public Integer getAirlineId() { 
     return airlineId; 
    } 

    public void setAirlineId(Integer airlineId) { 
     this.airlineId = airlineId; 
    } 

    public String getAirlineName() { 
     return airlineName; 
    } 

    public void setAirlineName(String airlineName) { 
     this.airlineName = airlineName; 
    } 

    public String getAirlineCode() { 
     return airlineCode; 
    } 

    public void setAirlineCode(String airlineCode) { 
     this.airlineCode = airlineCode; 
    } 

    public Set<AirlineFlight> getAirlineFlights() { 
     return airlineFlights; 
    } 

    public void setAirlineFlights(Set<AirlineFlight> airlineFlights) { 
     this.airlineFlights = airlineFlights; 
    } 

    public long getVersion() { 
     return version; 
    } 

    public void setVersion(long version) { 
     this.version = version; 
    } 

} 

程序输出:

04:25:29.656 [earlier] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13645311296 
04:25:29.656 [later] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13645311296 
04:25:29.657 [earlier] DEBUG o.h.transaction.JDBCTransaction - begin 
04:25:29.657 [later] DEBUG o.h.transaction.JDBCTransaction - begin 
04:25:29.657 [earlier] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection 
04:25:29.657 [later] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection 
04:25:29.657 [earlier] DEBUG o.h.transaction.JDBCTransaction - current autocommit status: false 
04:25:29.657 [later] DEBUG o.h.c.DriverManagerConnectionProvider - opening new JDBC connection 
04:25:29.659 [earlier] DEBUG org.hibernate.loader.Loader - loading entity: [tryouts.one_to_many.Airline#1] 
04:25:29.663 [earlier] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0) 
04:25:29.663 [earlier] DEBUG org.hibernate.SQL - select airline0_.AIRLINE_ID as AIRLINE1_0_0_, airline0_.AIRLINE_CODE as AIRLINE2_0_0_, airline0_.AIRLINE_NAME as AIRLINE3_0_0_, airline0_.version as version0_0_ from Airline airline0_ where airline0_.AIRLINE_ID=? 
Hibernate: select airline0_.AIRLINE_ID as AIRLINE1_0_0_, airline0_.AIRLINE_CODE as AIRLINE2_0_0_, airline0_.AIRLINE_NAME as AIRLINE3_0_0_, airline0_.version as version0_0_ from Airline airline0_ where airline0_.AIRLINE_ID=? 
04:25:29.680 [later] DEBUG o.h.c.DriverManagerConnectionProvider - created connection to: jdbc:mysql://localhost:3306/test, Isolation Level: 4 
04:25:29.681 [later] DEBUG o.h.transaction.JDBCTransaction - current autocommit status: false 
04:25:29.681 [later] DEBUG org.hibernate.loader.Loader - loading entity: [tryouts.one_to_many.Airline#1] 
04:25:29.681 [later] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 1) 
04:25:29.681 [later] DEBUG org.hibernate.SQL - select airline0_.AIRLINE_ID as AIRLINE1_0_0_, airline0_.AIRLINE_CODE as AIRLINE2_0_0_, airline0_.AIRLINE_NAME as AIRLINE3_0_0_, airline0_.version as version0_0_ from Airline airline0_ where airline0_.AIRLINE_ID=? 
Hibernate: select airline0_.AIRLINE_ID as AIRLINE1_0_0_, airline0_.AIRLINE_CODE as AIRLINE2_0_0_, airline0_.AIRLINE_NAME as AIRLINE3_0_0_, airline0_.version as version0_0_ from Airline airline0_ where airline0_.AIRLINE_ID=? 
04:25:29.682 [later] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open ResultSet (open ResultSets: 0, globally: 0) 
04:25:29.682 [earlier] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open ResultSet (open ResultSets: 0, globally: 0) 
04:25:29.683 [later] DEBUG org.hibernate.loader.Loader - result row: EntityKey[tryouts.one_to_many.Airline#1] 
04:25:29.683 [earlier] DEBUG org.hibernate.loader.Loader - result row: EntityKey[tryouts.one_to_many.Airline#1] 
04:25:29.687 [later] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close ResultSet (open ResultSets: 1, globally: 2) 
04:25:29.687 [earlier] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close ResultSet (open ResultSets: 1, globally: 2) 
04:25:29.687 [later] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 2) 
04:25:29.687 [earlier] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 2) 
04:25:29.688 [later] DEBUG org.hibernate.engine.TwoPhaseLoad - resolving associations for [tryouts.one_to_many.Airline#1] 
04:25:29.689 [earlier] DEBUG org.hibernate.engine.TwoPhaseLoad - resolving associations for [tryouts.one_to_many.Airline#1] 
04:25:29.695 [earlier] DEBUG org.hibernate.engine.TwoPhaseLoad - done materializing entity [tryouts.one_to_many.Airline#1] 
04:25:29.695 [later] DEBUG org.hibernate.engine.TwoPhaseLoad - done materializing entity [tryouts.one_to_many.Airline#1] 
04:25:29.695 [earlier] DEBUG o.h.e.StatefulPersistenceContext - initializing non-lazy collections 
04:25:29.695 [later] DEBUG o.h.e.StatefulPersistenceContext - initializing non-lazy collections 
04:25:29.695 [earlier] DEBUG org.hibernate.loader.Loader - done entity load 
04:25:29.695 [later] DEBUG org.hibernate.loader.Loader - done entity load 
getVersion in 53in earlier 
getVersion in 53in later 
getVersion in 53in earlier 
04:25:31.697 [earlier] DEBUG o.h.transaction.JDBCTransaction - commit 
04:25:31.697 [earlier] DEBUG o.h.e.d.AbstractFlushingEventListener - processing flush-time cascades 
04:25:31.699 [earlier] DEBUG o.h.e.d.AbstractFlushingEventListener - dirty checking collections 
04:25:31.704 [earlier] DEBUG org.hibernate.engine.Collections - Collection found: [tryouts.one_to_many.Airline.airlineFlights#1], was: [tryouts.one_to_many.Airline.airlineFlights#1] (uninitialized) 
04:25:31.704 [earlier] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects 
04:25:31.704 [earlier] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 1 collections 
04:25:31.705 [earlier] DEBUG org.hibernate.pretty.Printer - listing entities: 
04:25:31.705 [earlier] DEBUG org.hibernate.pretty.Printer - tryouts.one_to_many.Airline{airlineFlights=<uninitialized>, airlineCode=asdasd234phle, airlineName=Jet Airways, airlineId=1, version=53} 
04:25:31.707 [earlier] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0) 
04:25:31.707 [earlier] DEBUG org.hibernate.SQL - update Airline set AIRLINE_CODE=?, version=? where AIRLINE_ID=? and version=? 
Hibernate: update Airline set AIRLINE_CODE=?, version=? where AIRLINE_ID=? and version=? 
04:25:31.708 [earlier] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1) 
04:25:31.711 [earlier] DEBUG o.h.transaction.JDBCTransaction - committed JDBC Connection 
04:25:31.711 [earlier] DEBUG org.hibernate.jdbc.ConnectionManager - aggressively releasing JDBC connection 
04:25:31.711 [earlier] DEBUG org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)] 
getVersion in 53in later 
04:27:09.695 [later] DEBUG o.h.transaction.JDBCTransaction - commit 
04:27:09.696 [later] DEBUG o.h.e.d.AbstractFlushingEventListener - processing flush-time cascades 
04:27:09.696 [later] DEBUG o.h.e.d.AbstractFlushingEventListener - dirty checking collections 
04:27:09.696 [later] DEBUG org.hibernate.engine.Collections - Collection found: [tryouts.one_to_many.Airline.airlineFlights#1], was: [tryouts.one_to_many.Airline.airlineFlights#1] (uninitialized) 
04:27:09.696 [later] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 0 insertions, 0 updates, 0 deletions to 1 objects 
04:27:09.696 [later] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 1 collections 
04:27:09.696 [later] DEBUG org.hibernate.pretty.Printer - listing entities: 
04:27:09.696 [later] DEBUG org.hibernate.pretty.Printer - tryouts.one_to_many.Airline{airlineFlights=<uninitialized>, airlineCode=asdasdbaadmain, airlineName=Jet Airways, airlineId=1, version=53} 
04:27:09.697 [later] DEBUG o.h.transaction.JDBCTransaction - committed JDBC Connection 
04:27:09.697 [later] DEBUG org.hibernate.jdbc.ConnectionManager - aggressively releasing JDBC connection 
04:27:09.697 [later] DEBUG org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)] 
+0

您从“getVersion”日志行获得什么输出? – sharakan

+0

getVersion在在早期,52in 52in后 getVersion早期52in getVersion 52in后 getVersion –

+0

请找到输出 –

回答

2

这里发生的事情是脏检查,帮助你来的时候,你不要指望的情况下。我敢打赌,airlineCode的原始值是asdasdbaadmain,与您在“稍后”线程中设置的值相同。

Hibernate有一个称为脏检查的过程,在这里它检查每个字段以查看它是否从最初加载的状态进行了修改。在这种情况下,你没有改变任何东西。没有未更改的字段意味着没有更新,没有执行SQL,没有版本冲突,因此没有例外。

您获得异常,如果你调用session.evict()是由于这样的事实,如果你已经驱逐从Session实体再没有什么反对肮脏的检查,所以休眠的事实是被迫写数据库。

+1

是的问题是一个线程没有触发更新语句,因为airlineCode的值与该线程中设置的值相同,因此只有一个更新被触发。但这个问题不能用dynamicUpdate = false来解决,我已经在两个线程中使用了session.evict(airline),现在我得到的是staleobjectstateexception yiiiipppppiiiieeee :) –

+1

我觉得比较晚了,但是这是因为可见性数据库结果集和程序输入之间的值。 –

+0

@Cam我的错误,dynamicUpdate不是问题的一部分。我更新了答案,删除了这个,并告诉你为什么'evict'似乎可以解决它。就我个人而言,如果你有两个线程将值更改为新线程,比如说将线程名称附加到任何有线程名称,那么我会说这将是一个更令人信服的测试。 – sharakan