处理 JPA 合并的最佳方法?

Posted

技术标签:

【中文标题】处理 JPA 合并的最佳方法?【英文标题】:Best way to handle JPA merge? 【发布时间】:2013-03-27 19:11:49 【问题描述】:

我是整个 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 处理程序并将新组添加到用户组列表中,还是应该在 so 组处理程序中使用持久性创建组并将其手动添加到我的用户对象?

希望这里有人有线索;)

【问题讨论】:

见***.com/questions/1069992/… 【参考方案1】:

问题 1

必须在分离的实体上调用merge 方法。 merge 方法会将合并的对象附加返回给 entityManager。

什么意思?

一旦您用来获取实体的实体管理器关闭,实体就会被分离。 (即大部分时间是因为您在之前的交易中获取它)。

在您的第二个代码示例中:用户已附加(因为您只是获取它),因此调用合并是无用的。 (顺便说一句:不是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 的生命周期)与对象的生命周期。

大多数时候:entityManager 是一个非常短时间的活对象。另一方面,您的业务对象可能存在的时间更长,因此您将不得不调用 merge(并注意 merge 不会修改传入参数的对象!!!)。

您可以决定在同一个事务中获取和修改您的业务对象(即使用同一个 entityManager):这意味着更多的数据库访问,并且出于性能原因,此策略通常必须与二级缓存相结合。但在这种情况下,您不必调用合并。

希望对你有所帮助。

【讨论】:

感谢您的回复。关于我的问题1,在调用JPA处理程序的方法中设置我的字段(如日期和新名称)并调用方法“updateUser(User user)”来合并用户对象和保存所有更改而不是为多个更新用例制定方法?【参考方案2】:

    这取决于您想要实现的目标以及您拥有多少关于您尝试合并的对象的来源的信息。

    首先,如果您在方法的第一行或最后调用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,第一种方法也可以(合并传递给业务方法的整个实体。)这真的取决于您的用例。

    我想这取决于您的级联设置。如果您想在 User 实体更改时自动保留/合并所有 Groups - 只需将其添加到用户的集合中是安全的(类似于 User#addGroup(Group g) groups.add(g)。如果您不想级联,您可以随时创建您自己的方法将传播到关系的另一端。可能类似于:User#addGroup(Group g),它会自动调用 g.addUser(this);

【讨论】:

以上是关于处理 JPA 合并的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

JPA 枚举类型映射。最佳方法

JPA 验证唯一约束的最佳方法

根据当前日期+时间查询 JPA 实体以获取实例的最佳方法是啥?

在 Spring JPA 中插入实体列表的最佳方法

在 JPA MySql 中存储与时区无关的时间的最佳方法

信创办公–基于WPS的EXCEL最佳实践系列 (处理合并单元格)