EntityManager尝试在不提示的情况下插入实体

时间:2011-11-09 21:06:42

标签: jpa eclipselink

也许我只是给你一小段代码而过度简化了这一点(如果是这样的话,我会发布更多的代码)但我认为,最初,越少越好:

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

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

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

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

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

我的问题是......为什么这个Location对象甚至试图被持久化?我正在做的就是在实体中设置一个变量。

之前一切正常,但我们只是转而使用Eclipselink和Java EE 6,这个问题就出现了。

解决方案?:我使用了从下面“分离”的想法并进行了以下更改:

Location location = asset.getLocation();

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

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

3 个答案:

答案 0 :(得分:2)

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

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

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

编辑:

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

答案 1 :(得分:0)

那么,该位置是现有位置还是新对象?

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

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

答案 2 :(得分:0)

从代码中看起来您正在使用非托管版本的位置并将其与ChildAsset相关联。如果您的childAsset-> Location关系被标记为cascade persist,那么将在flush或commit上的Location上调用spec要求perist。由于Location不是托管对象,因此在持久化时需要例外。

管理位置时(例如,当您在ChildAsset上调用merge时,或者如果您使用了从位置返回的位置的托管实例) em.merge(位置);调用),对ChildAsset的持续操作 - >位置是无操作。

不要将非托管实体与标记为级联持久的关系相关联。