2010-06-16 95 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