【发布时间】:2015-07-18 13:46:56
【问题描述】:
我有一个非常特殊的情况,我需要更新 JPA(EclipseLink 2.6.0)不允许的主键。因此,实体首先被删除,然后插入新值。
所涉及的表具有 GlassFish Server 需要用于 JAAS 身份验证的预定义结构。
mysql> describe user_role_table;
+-------------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+-------+
| user_id | varchar(176) | NO | PRI | NULL | |
| password | varchar(255) | NO | | NULL | |
| row_version | bigint(20) unsigned | NO | | 0 | |
+-------------+---------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
mysql> describe group_table;
+---------------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------------------+------+-----+---------+-------+
| user_group_id | varchar(176) | NO | PRI | NULL | |
| group_id | varchar(15) | NO | PRI | NULL | |
| row_version | bigint(20) unsigned | NO | | 0 | |
+---------------+---------------------+------+-----+---------+-------+
3 rows in set (0.01 sec)
user_group_id 和group_id 一起构成一个复合主键。 group_table 中的 group_id 是引用 user_role_table 中的 user_id 的外键。 GroupTable 拥有来自 @Embeddable 类 GroupTablePK 的 @EmbeddedId。
很少需要此信息。因此,我没有发布所涉及的实体类。
尝试通过以下方式模拟更新:首先删除提供的实体GroupTable,然后使用新值group_id 保持相同的实体,如下所示(在使用CMT 的EJB 中)。
同样,这是一个非常特殊的情况,甚至更新用户的权限也很少见。只是值得预先提供功能。
public GroupTable update(GroupTable groupTable, String userId, String oldGroupId) {
String newGropuId = groupTable.getGroupTablePK().getGroupId();
groupTable.getGroupTablePK().setGroupId(oldGropuId);
if (delete(groupTable)) {
// entityManager.flush();
groupTable.setUserRoleTable(entityManager.getReference(UserRoleTable.class, userId));
groupTable.getGroupTablePK().setGroupId(newGropuId);
entityManager.persist(groupTable);
}
return groupTable;
}
public boolean delete(GroupTable groupTable) {
groupTable.setUserRoleTable(entityManager.getReference(UserRoleTable.class, groupTable.getUserRoleTable().getUserId()));
GroupTable managedGroupTable = entityManager.merge(groupTable);
managedGroupTable.getUserRoleTable().getGroupTableList().remove(groupTable);
entityManager.remove(managedGroupTable);
return !entityManager.contains(managedGroupTable);
}
这些方法在同一个事务中执行,它们的工作做得很好,但前提是 update() 方法中唯一的注释行未注释。否则,它会抱怨 group_table 中主键的重复条目 - 首先要删除的实体在持久化该实体之前不会被删除,从而导致重复插入产生。
为什么在持久化实体之前需要entityManager.flush();?这是到数据库的额外往返,应该避免。
【问题讨论】:
-
它是必需的,否则,persist 会取消您的删除调用,因此您只是更改主键。 JPA 中不允许更改主键,这就是您尝试使用删除/持久化来解决它的原因。克隆实体然后更新克隆的pk确实会更好。 EclipseLInk 具有克隆方法和策略来帮助实现这一点,并且使用克隆更准确地表示您正在做的事情 - 身份是实体的基础,因此新身份需要新实体。
-
这有点不像看起来那么直观,因为删除调用被暂停,即使在删除后尝试保持分离的实例。看起来像this。
标签: hibernate jpa ejb eclipselink