我是整个JPA的新手,所以我对处理JPA合并和持久化的最佳方法有很多疑问。
我有一个应该更新的用户对象(某些值,如日期和名称)。我是否必须首先合并传递的对象,或者找到新对象更安全吗?
目前,我更新用户的代码如下所示:
public void updateUserName(User user, String name) {
// maybe first merge it?
user.setName(name);
user.setChangeDate(new Date());
em.merge(user);
}
如何在调用更新方法之前确定用户未被操作?做这样的事情更安全:
public void updateUserName(int userId, String name) {
User user = em.getReference(User.class, userId);
user.setName(name);
user.setChangeDate(new Date());
em.merge(user);
}
也许是其他解决方案?我观看了多个视频并看到了很多例子,但它们都不同,没有人解释最佳做法是什么。
将孩子添加到关系中的最佳方法是什么?例如,我的用户对象与多个组有连接。我应该为用户调用JPA处理程序并将新组添加到用户组列表中,还是应该使用persist在so组处理程序中创建组并将其手动添加到我的用户对象中?
希望有人在这里有线索;)
答案 0 :(得分:7)
这取决于您想要实现的目标以及您尝试合并的对象来源的信息量。
首先,如果您在方法的第一行调用em.merge(user)
或最终无关紧要。如果使用JTA和CMT,则在方法调用完成时将更新您的实体。唯一的区别是,如果在更改用户之前调用em.merge(user)
,则应使用返回的实例而不是参数,因此它是:
public void updateUserName(User user, String name) {
User managedUser = em.merge(user);
managedUser.setChangeDate(new Date());
// no need of additional em.merge(-) here.
// JTA automatically commits the transaction for this business method.
}
或
public void updateUserName(User user, String name) {
user.setChangeDate(new Date());
em.merge(user);
// JTA automatically commits the transaction for this business method.
}
现在关于更新实体
如果您只想更新实体中一些明确定义的字段 - 请使用第二种方法,因为它更安全。您无法确定方法的客户端是否未修改实体的某些其他字段。因此,em.merge(-)
也会更新它们,这可能不是您想要实现的目标。
另一方面 - 如果你想接受用户所做的所有更改,只是在你的例子中覆盖/添加一些属性,如changeDate
,第一种方法也很好(合并整个实体传递给业务方法) 。)这实际上取决于你的用例。
我想这取决于您的级联设置。如果您想在Groups
实体更改时自动保留/合并所有User
,只需将其添加到用户的集合(类似User#addGroup(Group g) { groups.add(g)}
)即可。如果您不想要级联,您始终可以创建自己的方法,这些方法将传播到关系的另一端。它可能类似于:User#addGroup(Group g)
自动调用g.addUser(this);
。
答案 1 :(得分:4)
问题1
merge
方法。merge
方法会将合并对象附加返回给entityManager。这是什么意思?
一旦您用于获取它的实体管理器关闭,实体就会被分离。 (即大多数时候因为你在之前的交易中获取它)。
在你的第二个代码示例中:用户被附加(因为你只是获取它)所以调用merge是没用的。 (顺便说一句:它不是getReference
而是find
)
在您的第一个示例中:我们不知道用户的状态(未分离的实体?)。如果它是分离的,则调用merge
是有意义的,但要小心merge
不要修改它作为参数传递的对象。所以这是我的第一个样本的版本:
/**
* @param user : a detached entity
* @return : the attached updated entity
**/
public User updateUserName(User user, String name) {
user.setName(name);
user.setChangeDate(new Date());
return em.merge(user);
}
问题2
也许一些代码示例来解释你对jpa处理程序的意思可以帮助我们理解你的顾虑。无论如何,我会尽力帮助你。
如果您有持久用户,则需要创建一个新组并将其与持久用户关联:
User user = em.find(User.class, userId);
Group group = new Group();
...
em.persist(group);
user.addToGroups(group);
group.addToUsers(user); //JPA won't update the other side of the relationship
//so you have to do it by hand OR being aware of that
如果您有持久用户和持久性组,则需要将它们关联起来:
User user = em.find(User.class, userId);
Group group = em.find(Group.class, groupId);
...
user.addToGroups(group);
group.addToUsers(user);
一般注意事项
关于所有这些的最佳实践实际上取决于您管理事务(以及实体管理器的生命周期)与对象的生命周期。
大多数情况下:entityManager是一个非常短时间的生活对象。另一方面,您的业务对象可能会存活更长时间,因此您必须调用merge(并注意合并不会修改参数中传递的对象!!!)。
您可以决定在同一事务中获取和修改业务对象(即使用相同的entityManager):这意味着需要更多的数据库访问,并且出于性能原因,此策略通常必须与二级缓存组合。但在这种情况下,您不必调用merge。
我希望这有帮助。