使用Spring Data JPA更新实体的正确方法是什么?

3

我有一个包含对另一张表的外键的Account表。

选项1:

@Override
@Transactional(readOnly = false)
public Account changePassword(Account existingAccount, String newPassword){
    existingAccount.setPassword(newPassword);
    return accountDAO.save(existingAccount);
}

每当我调用save()方法时,都会进行一次连接以检索另一个表的ID,然后才进行更新操作。
请注意,即使只更改密码,此处也会更新所有字段。

编辑:我发现之所以进行连接是因为我将实体(existingAccount)作为参数传递。如果我在changePassword方法中找到它,它可以正常工作(没有连接)。问题是,我需要在控制器中使用此实体进行验证,因此两次进行相同的数据库调用是无意义的。

选项2:

@Modifying
@Query("UPDATE Account SET password=(:pass) WHERE username=(:username)")
public void changePassword(@Param("username")String username, @Param("pass")String pass);

这样,只有@Query内的自定义更新被执行。

我认为如果考虑性能,选项2可能更好,但选项1感觉更像jpa。欢迎任何建议。


你应该添加 AccountAccountRepository 接口的代码。 - user180100
真的没有什么可补充的了。选项1是服务层代码,存储库中没有任何内容,而选项2是存储库接口代码。服务层只需调用它即可。 - Sikor
1个回答

3
两种方法都可以。如果你想使用第一种方法,但是不想更新除密码以外的字段,你可以使用注释@DynamicUpdate(value=true)配置动态更新

dynamic-update(可选,默认为false):指定在运行时生成UPDATE SQL,并且只包含值已更改的那些列。

你需要使用select-before-update或者乐观锁定。其中select-before-update可能比更新所有字段性能更差。
显然,这是特定实现而非JPA的一部分

目前正在查找,@org.hibernate.annotations.Entity已被弃用,但@DynamicUpdate(value = true)可以代替使用。它像你说的那样工作,但唯一的问题是第一个选项仍然在更新之前执行连接(即使现在只更新1个字段 - 密码)。 - Sikor
@Sikor:有趣。也许将关联到连接的事物变为惰性? - Nathan Hughes
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "pilot_id"),这是懒加载的 :D - Sikor
我想我会坚持选项2。那样做感觉我对自己在做什么有更多的控制权,而且我不必在更新一个字段之前执行任何选择操作。虽然我仍然不知道为什么Hibernate在选项1中执行连接语句。这很奇怪。 - Sikor
@Sikor:是的,这里没有完全描述懒惰问题,我认为这会成为一个很好的单独问题。 - Nathan Hughes
我找到了为什么Hibernate会进行连接的原因。看一下我的选项1中的参数。我从控制器传递实体(existingAccount),因为我需要在那里进行验证。现在,如果我只传递用户名并在changePassword方法中查找帐户,则可以正常工作,即没有连接。但这并不能真正解决我的问题,因为我需要在控制器中使用existingAccount,并在服务层中创建另一个完全相同的SQL是毫无意义的,所以我肯定会坚持选项2。 - Sikor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接