2011-11-09 18 views
1

也许我只是给你一小段代码而过度简化了这一点(如果是这种情况,我会发布更多的代码),但我认为,最初,更少更好:EntityManager尝试插入实体而不被提示

我有一个资产类型的实体,它有一个位置类型的字段,它也是一个实体。当我设置资产的位置时,我还必须设置其子项的位置。

Location location = asset.getLocation(); 
em.merge(location); 
em.flush(); 

childAsset.setLocation(asset.getLocation()); 
em.flush(); 

当我运行的flush(),我得到以下异常:

内部异常:java.sql.SQLIntegrityConstraintViolationException:ORA-00001:唯一约束(SWRADMIN.LOCATION_PK)违反

我的问题是...为什么这个位置对象甚至试图被持续?我所做的只是在一个实体中设置一个变量。

这工作很好,但我们只是切换到使用Eclipselink和Java EE 6,这个问题弹出。

解决方案?:我用“分离”的想法从下面并提出了以下变化:

Location location = asset.getLocation(); 

em.detach(childAsset); 
childAsset.setLocation(asset.getLocation()); 
em.merge(); 
em.flush(); 

和它的工作!我很困惑,为什么,但是......你会认为自动同步会做同样的事情。

回答

2

如果对象处于受管理状态,则实体管理器将通过隐式持久对象(可能在事务结束时)或明确调用em.flush()方法时将其与底层数据库同步。

您可以使用em.detach(entity)分离单个实体或em.clear()以分离所有实体。然后,对实体/实体所做的更改将不会反映在数据库中。

为了更好地处理此问题,您可以使用BMT(Bean Managed Transaction),您必须手动处理实体持久性事务。

编辑:

Location location = asset.getLocation(); 
childAsset.setLocation(location); 
em.merge(childAsset); 
em.flush(); 
+0

所以,我做了一个em.detach(childAsset),然后childAsset.setLocation(位置),然后em.merge(childAsset),它的工作!我的问题是...为什么?这不是同步开始时应该做的吗? – wsaxton

+0

看着你原来的代码,em.merge(location)然后em.flush()会在数据库中持久化'location',然后在childAsset中设置它,然后再刷新'SQLIntegrityConstraintViolationException',因为'location'已经存在于数据库中。修改答案,参考编辑部分。 –

0

所以,位置是一个现有的位置,还是一个新的对象?

它是如何读取的,是从另一个事务或实体管理器读取还是以某种方式分离?如果是这样,那么你需要重新阅读(查找)或合并它。

如果它是新的并且与托管对象相关,则是,flush必须写入它。

+0

嗨,詹姆斯。我在帖子中添加了更多代码。该位置是现有位置。为了确保,我对位置对象执行了merge(),然后执行了flush()。它运行良好。当我将其分配给childAsset,然后执行另一个flush()时,我得到相同的错误。 – wsaxton

0

从代码,它看起来像您正在使用位置的非托管版本,是相关联的ChildAsset。如果您的childAsset-> Location关系标记为cascade persist,则规范要求perist将在刷新或提交时在位置上调用。由于“位置”不是托管对象,因此它需要持久存在异常。

当位置被管理时(比如当你在ChildAsset上调用merge时,或者你已经使用了从 em.merge(location); call)返回的位置的托管实例时,ChildAsset-> Location上的持久操作是一个没有操作。

不要将非托管实体关联到标记级联持久化的关系。

相关问题