JPA/Hibernate - 不需要的部分回滚和会话处理
JPA/Hibernate - Undesired Partial Rollback and Session Handling
我正在使用无状态 EJB class 来更新位于数据库中的持久性实体。 EJB 中的方法调用完成工作的实现 class。我认为导致问题的原因是名为 Foo
的实体与实体 Bar
具有一对多关系。事情已经完成,Session 更新为 Foo
,'cascades' 到 Bar
。当 StaleObjectStateException
发生时,事务没有被完全回滚,这会导致错误,原因很明显。
EJB:
private Session getSession() throws BusinessException {
if( this.sess == null ) {
ServiceLocator locator = new ServiceLocator();
SessionFactory sf = locator.getHibernateSessionFactory();
this.sess = sf.openSession();
}
return this.sess;
}
private ProductionOrderImpl getImpl() throws BusinessException {
if( this.impl == null ) {
this.impl = new ProductionOrderImpl( getSession() );
}
return this.impl;
}
public void cutoffOrders( ) throws Exception {
Transaction tx = null;
try {
tx = getSession().beginTransaction();
getImpl().cutOffFoos(fooTime);
tx.commit();
} catch (StaleObjectStateException e1){
if (tx != null) tx.rollback();
logger.error( "Failed to cutoff order : " + e1 );
throw new Exception( LocaleMgr.getMessage());
}
finally {
// reset implementation object, close session,
// and reset session object
impl = null;
sess.close();
sess = null;
}
}
实施:
public ProductionOrderImpl(Session sess) {
this.sess = sess;
}
public void cutoffFoos( Timestamp fooTime) throws Exception {
... Code that gets fooList ...
if( fooList != null ) {
for( Foo foo: fooList ) {
for( Bar bar : foo.getBarList() ) {
... Code that does things with existing Barlist ...
if( ... ) {
... Code that makes new Bar object ...
foo.getBarList().add(bar2);
}
}
sess.update( foo );
}
}
}
相关Foo代码:
@OneToMany(cascade=CascadeType.ALL, mappedBy="foo")
@OrderBy("startTime DESC")
Set<Bar> barList;
所以基本上,当事务试图回滚时,被更改的 Bar 部分被回滚,但新的 Bar(代码中的 bar2)记录仍然存在..
如有任何指导,我们将不胜感激。就像我说的,我相信这里的错误与 sess.update(foo)
; 有关。可能与 autocommit
有关,但默认情况下它应该关闭。
我相信正在发生的事情是 Session.Update(foo) 依次创建两个单独的事务。具体来说,Foo
已更新(SQL UPDATE),但 Bar
已保存(SQL INSERT)。由于事务上下文只会真正看到 SQL UPDATE,所以这就是它的全部反转。将不得不对此进行更多研究..
我已尝试将 Session.FlushMode
更改为 COMMIT
,但似乎仍无法解决问题。但是,它确实部分解决了问题。它会正确回滚条目,但导致 StaleObjectStateException 的特定条目除外。该特定条目实际上已从数据库中删除...
好吧,我设法解决了我的问题。我会等着接受它,以防其他人发布更好的东西,更多的东西……值得赏金。
基本上,通过将 FlushMode
更改为手动,并在整个过程中手动刷新,我可以更早地捕获 StaleObjectException
,从而更快地退出代码。我仍然有部分回滚记录的工件。但是,此方法按计划每 2 分钟运行一次,因此在第二遍期间它修复了所有问题。
我将我的 EJB 更改为具有以下内容:
public void cutoffOrders( ) throws Exception {
Transaction tx = null;
try {
tx = getSession().beginTransaction();
getSession().setFlushMode(FlushMode.MANUAL);
getImpl().cutOffFoos(fooTime);
getSession().flush();
tx.commit();
} catch (StaleObjectStateException e1){
if (tx != null) tx.rollback();
logger.error( "Failed to cutoff order : " + e1 );
throw new Exception( LocaleMgr.getMessage());
}
finally {
// reset implementation object, close session,
// and reset session object
impl = null;
sess.close();
sess = null;
}
}
那么实现代码如下:
public void cutoffFoos( Timestamp fooTime) throws Exception {
... Code that gets fooList ...
if( fooList != null ) {
for( Foo foo: fooList ) {
for( Bar bar : foo.getBarList() ) {
... Code that does things with existing Barlist ...
sess.flush();
if( ... ) {
... Code that makes new Bar object ...
foo.getBarList().add(bar2);
}
}
sess.flush();
sess.update( foo );
}
}
}
好吧,这是我的两分钱,因为这也与 JPA 有关:
在 Spring Data JPA 中,您可以只使用以下内容:
1.调用存储库之前的@Transactional 注释(处理回滚)
2.Use JPA 存储库 saveAndFlush 方法,即:
@Service
public class ProductionOrderServiceImpl extends ProductionOrderService{
@Autowired
ProductionOrderRepository jpaRepository;
@Transactional
public void cutoffOrders( Timestamp fooTime ){
... Code that gets fooList ...
if( fooList != null ) {
for( Foo foo: fooList ) {
for( Bar bar : foo.getBarList() ) {
... Code that does things with existing Barlist ...
{Call another similar method with @transactional..}//saveAndFlush(BarList);
if( ... ) {
... Code that makes new Bar object ...
foo.getBarList().add(bar2);
}
}
jpaRepository.saveAndFlush(foo);
}
}
}
}
save 和 flush 内部做的是这样的:
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
*/
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
然后 em.flush();
万一在此之后,如果您 运行 进入@Audited 版本控制问题,删除的记录不会显示,则设置 org.hibernate.envers.store_data_at_delete=true。
希望增加对解决方案的看法。
我正在使用无状态 EJB class 来更新位于数据库中的持久性实体。 EJB 中的方法调用完成工作的实现 class。我认为导致问题的原因是名为 Foo
的实体与实体 Bar
具有一对多关系。事情已经完成,Session 更新为 Foo
,'cascades' 到 Bar
。当 StaleObjectStateException
发生时,事务没有被完全回滚,这会导致错误,原因很明显。
EJB:
private Session getSession() throws BusinessException {
if( this.sess == null ) {
ServiceLocator locator = new ServiceLocator();
SessionFactory sf = locator.getHibernateSessionFactory();
this.sess = sf.openSession();
}
return this.sess;
}
private ProductionOrderImpl getImpl() throws BusinessException {
if( this.impl == null ) {
this.impl = new ProductionOrderImpl( getSession() );
}
return this.impl;
}
public void cutoffOrders( ) throws Exception {
Transaction tx = null;
try {
tx = getSession().beginTransaction();
getImpl().cutOffFoos(fooTime);
tx.commit();
} catch (StaleObjectStateException e1){
if (tx != null) tx.rollback();
logger.error( "Failed to cutoff order : " + e1 );
throw new Exception( LocaleMgr.getMessage());
}
finally {
// reset implementation object, close session,
// and reset session object
impl = null;
sess.close();
sess = null;
}
}
实施:
public ProductionOrderImpl(Session sess) {
this.sess = sess;
}
public void cutoffFoos( Timestamp fooTime) throws Exception {
... Code that gets fooList ...
if( fooList != null ) {
for( Foo foo: fooList ) {
for( Bar bar : foo.getBarList() ) {
... Code that does things with existing Barlist ...
if( ... ) {
... Code that makes new Bar object ...
foo.getBarList().add(bar2);
}
}
sess.update( foo );
}
}
}
相关Foo代码:
@OneToMany(cascade=CascadeType.ALL, mappedBy="foo")
@OrderBy("startTime DESC")
Set<Bar> barList;
所以基本上,当事务试图回滚时,被更改的 Bar 部分被回滚,但新的 Bar(代码中的 bar2)记录仍然存在..
如有任何指导,我们将不胜感激。就像我说的,我相信这里的错误与 sess.update(foo)
; 有关。可能与 autocommit
有关,但默认情况下它应该关闭。
我相信正在发生的事情是 Session.Update(foo) 依次创建两个单独的事务。具体来说,Foo
已更新(SQL UPDATE),但 Bar
已保存(SQL INSERT)。由于事务上下文只会真正看到 SQL UPDATE,所以这就是它的全部反转。将不得不对此进行更多研究..
我已尝试将 Session.FlushMode
更改为 COMMIT
,但似乎仍无法解决问题。但是,它确实部分解决了问题。它会正确回滚条目,但导致 StaleObjectStateException 的特定条目除外。该特定条目实际上已从数据库中删除...
好吧,我设法解决了我的问题。我会等着接受它,以防其他人发布更好的东西,更多的东西……值得赏金。
基本上,通过将 FlushMode
更改为手动,并在整个过程中手动刷新,我可以更早地捕获 StaleObjectException
,从而更快地退出代码。我仍然有部分回滚记录的工件。但是,此方法按计划每 2 分钟运行一次,因此在第二遍期间它修复了所有问题。
我将我的 EJB 更改为具有以下内容:
public void cutoffOrders( ) throws Exception {
Transaction tx = null;
try {
tx = getSession().beginTransaction();
getSession().setFlushMode(FlushMode.MANUAL);
getImpl().cutOffFoos(fooTime);
getSession().flush();
tx.commit();
} catch (StaleObjectStateException e1){
if (tx != null) tx.rollback();
logger.error( "Failed to cutoff order : " + e1 );
throw new Exception( LocaleMgr.getMessage());
}
finally {
// reset implementation object, close session,
// and reset session object
impl = null;
sess.close();
sess = null;
}
}
那么实现代码如下:
public void cutoffFoos( Timestamp fooTime) throws Exception {
... Code that gets fooList ...
if( fooList != null ) {
for( Foo foo: fooList ) {
for( Bar bar : foo.getBarList() ) {
... Code that does things with existing Barlist ...
sess.flush();
if( ... ) {
... Code that makes new Bar object ...
foo.getBarList().add(bar2);
}
}
sess.flush();
sess.update( foo );
}
}
}
好吧,这是我的两分钱,因为这也与 JPA 有关:
在 Spring Data JPA 中,您可以只使用以下内容:
1.调用存储库之前的@Transactional 注释(处理回滚)
2.Use JPA 存储库 saveAndFlush 方法,即:
@Service
public class ProductionOrderServiceImpl extends ProductionOrderService{
@Autowired
ProductionOrderRepository jpaRepository;
@Transactional
public void cutoffOrders( Timestamp fooTime ){
... Code that gets fooList ...
if( fooList != null ) {
for( Foo foo: fooList ) {
for( Bar bar : foo.getBarList() ) {
... Code that does things with existing Barlist ...
{Call another similar method with @transactional..}//saveAndFlush(BarList);
if( ... ) {
... Code that makes new Bar object ...
foo.getBarList().add(bar2);
}
}
jpaRepository.saveAndFlush(foo);
}
}
}
}
save 和 flush 内部做的是这样的:
/*
* (non-Javadoc)
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
*/
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
然后 em.flush();
万一在此之后,如果您 运行 进入@Audited 版本控制问题,删除的记录不会显示,则设置 org.hibernate.envers.store_data_at_delete=true。
希望增加对解决方案的看法。