2010-06-16 93 views
6

如果我有@Cascade(CascadeType.SAVE_UPDATE)一@OneToMany關係如下如何在我的Hibernate事務由Spring管理時啓用Hibernate Interceptor?

public class One { 

    private Integer id; 

    private List<Many> manyList = new ArrayList<Many>(); 

    @Id 
    @GeneratedValue 
    public Integer getId() { 
     return this.id; 
    } 

    @OneToMany 
    @JoinColumn(name="ONE_ID", updateable=false, nullable=false) 
    @Cascade(CascadeType.SAVE_UPDATE) 
    public List<Many> getManyList() { 
     return this.manyList; 
    }   

} 

和許多類

public class Many { 

    private Integer id; 

    /** 
     * required no-arg constructor 
     */ 
    public Many() {} 

    public Many(Integer uniqueId) { 
     this.id = uniqueId 
    } 

    /** 
     * Without @GeneratedValue annotation 
     * Hibernate will use assigned Strategy 
     */ 
    @Id 
    public Integer getId() { 
     return this.id; 
    } 

} 

,如果我有以下情形

One one = new One(); 

/** 
    * generateUniqueId method will Take care of assigning unique id for each Many instance 
    */ 
one.getManyList().add(new Many(generateUniqueId())); 
one.getManyList().add(new Many(generateUniqueId())); 
one.getManyList().add(new Many(generateUniqueId())); 
one.getManyList().add(new Many(generateUniqueId())); 

而且我打電話

sessionFactory.getCurrentSession().save(one); 

之前

會根據Transitive persistence Hibernate參考文檔,你可以看到

如果父被save(),更新()或者saveOrUpdate(),所有兒童通過saveOrUpdate()

好的。現在,讓我們來看看Java持久性有了約saveOrUpdate方法Hibernate的書會談

休眠查詢多個表給定id,並一旦發現,休眠更新該行如果找不到,插入新行是必需和完成的。

可根據

INSERT INTO ONE (ID) VALUES (?) 

/** 
    * I have four Many instances added To One instance 
    * So four select-before-saving 
    * 
    * I DO NOT NEED select-before-saving 
    * Because i know i have a Fresh Transient instance 
    */ 
SELECT * FROM MANY WHERE MANY.ID = ? 
SELECT * FROM MANY WHERE MANY.ID = ? 
SELECT * FROM MANY WHERE MANY.ID = ? 
SELECT * FROM MANY WHERE MANY.ID = ? 

INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?) 
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?) 
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?) 
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?) 

任何解決方法爲了避免前選擇保存翻譯???是的,您可以

  • 添加@Version列(不適用)
  • 由Hibernate攔截器提供isTransient方法(選項我有

這樣的一種方式來在使用這種級聯時,避免使用select-before-saving默認行爲,我通過將Hibernate攔截器分配給休眠會話來改進我的代碼,這些會話的事務由Spring管理。

這裏去我的倉庫

之前(沒有任何的Hibernate攔截):它工作正常!

@Repository 
public class SomeEntityRepository extends AbstractRepository<SomeEntity, Integer> { 

    @Autowired 
    private SessionFactory sessionFactory; 

    @Override 
    public void add(SomeEntity instance) { 
     sessionFactory.getCurrentSession().save(instance); 
    } 

} 

(使用Hibernate Inteceptor):不順心的事(不執行SQL查詢 - 既不是INSERT也不選擇先於儲蓄)

@Repository 
public class SomeEntityRepository extends AbstractRepository<SomeEntity, Integer> { 

    @Autowired 
    private SessionFactory sessionFactory; 

    @Override 
    public void add(SomeEntity instance) { 
     sessionFactory.openSession(new EmptyInterceptor() { 
      /** 
       * To avoid select-before-saving 
       */ 
      @Override 
      public Boolean isTransient(Object o) { 
       return true; 
      } 
     }).save(instance); 
    } 

} 

我的問題是:爲什麼春節不在使用Hibernate Interceptor時堅持我的實體及其關係,我應該如何解決這個問題?

回答

3

春天保持當前的會話和當前的交易(見SessionFactoryUtils.java)。由於已經有當前DAO方法調用相關的會話,你必須使用這個會話,或採取的暴跌之間的關聯參與將新會話與以前的事務上下文相關聯的模糊細節。這可能是可能的,但具有相當大的風險,並且絕對不推薦。在休眠中,如果你的session已經打開,那麼應該使用它。儘管如此,您可能可以讓Spring爲您創建一個新會話並將其與當前事務上下文相關聯。使用SessionFactoryUtils.getNewSession(SessionFactory, Interceptor)。如果你使用這個而不是hibernate的sessionFactory,那麼這應該保持與事務的關聯。

最初,您可以直接在DAO中對其進行編碼。當它經過測試並希望能夠發揮作用時,可以採取措施將彈出代碼從DAO中移出,例如使用AOP向add()方法添加建議,以創建和清理新會話。

另一種選擇是使用全局攔截器。即使它是全球性的,你也可以給它本地可控的行爲。 TransientInterceptor包含一個threadLocal<Boolean>。這是當前線程的標誌,用於指示攔截器是否應該爲isTransient返回true。在add()方法的開始處將其設置爲true,並在最後清除它。例如。

class TransientInterceptor extends EntityInterceptor { 
     ThreadLocal<Boolean> transientFlag = new ThreadLocal<Boolean)(); 
     public boolean isTransient() { 
     return transientFlag.get()==Boolean.TRUE; 
     } 
     static public setTransient(boolean b) { 
      transientFlag.set(b); 
     } 
    } 

然後在你的DAO:

@Override 
public void add(SomeEntity instance) { 
    try { 
     TransientInterceptor.set(true); 
     sessionFactory.getCurrentSession().save(instance); 
    } 
    finally { 
     TransientInterceptor.set(false); 
    } 
} 

然後,您可以設置TransientInterceptor作爲對SessionFactory的一個全球性的攔截器(如:LocalSessionFactoryBean)爲了使這個創傷小,你可以創建一個圍繞建議的AOP在適當的情況下將此行爲應用於所有DAO添加方法。

0

在'after'方法中,您正在創建一個新會話而不是刷新它,因此沒有更新發送到數據庫。這與Spring沒有關係,但是是純粹的Hibernate行爲。

你可能想要的是將一個(實體)攔截器添加到sessionFactory中,可能使用Spring進行配置。然後,您可以像以前一樣保留存儲庫的add()方法。 見http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/orm/hibernate3/LocalSessionFactoryBean.html#setEntityInterceptor%28org.hibernate.Interceptor%29

+0

如上所述:*您可以將一個攔截器添加到SessionFactory *中。它發生在我是否將一個攔截器添加到SessionFactory中,我將得到**全局**行爲。我只想添加一個攔截器到由add(SomeEntity實例)方法使用的Session實例。任何解決方法??? – 2010-06-22 05:00:41