Spring Boot 2.7.7更新Account时密码被置为null的问题求助
Spring Boot 2.7.7 账户更新时密码被置为null的问题排查与解决
问题原因
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)仅控制JSON序列化逻辑:它只限制后端不把密码字段返回给前端,但前端发送更新请求时,请求体里不会携带password字段,Spring MVC将请求体转换为Account对象时,password字段会被默认赋值为null。- 直接调用
accountRepository.save(account)执行的是全量更新:前端传来的Account对象属于detached状态(未被JPA持久化上下文管理),JPA会将对象的所有字段(包括为null的password)覆盖写入数据库,导致原有密码被清空。
解决方案
方案1:先查后更,拷贝非空属性
从数据库查询原有账户实体,仅将前端传来的非null属性(或指定需要更新的属性)拷贝到原有实体,再执行保存操作,避免覆盖密码字段。
示例代码:
public Account updateAccount(Long id, Account account) throws CustomException { // 查询数据库中已存在的账户 Account existingAccount = accountRepository.findById(id) .orElseThrow(() -> new CustomException("账户不存在")); // 使用BeanUtils拷贝非null属性,忽略password和id等不需要更新的字段 BeanUtils.copyProperties(account, existingAccount, "password", "id"); // 也可以手动赋值需要更新的字段,比如title // existingAccount.setTitle(account.getTitle()); return accountRepository.save(existingAccount); }
注意:使用BeanUtils时需明确忽略敏感字段,避免误覆盖其他不需要更新的内容。
方案2:使用JPA自定义更新查询
在Repository层编写仅更新指定字段的JPQL或SQL,直接执行部分字段更新,完全不涉及密码字段。
示例代码:
@Repository public interface IAccountRepository extends JpaRepository<Account, Long> { @Modifying @Query("UPDATE Account a SET a.title = :title WHERE a.id = :id") int updateTitleById(@Param("id") Long id, @Param("title") String title); }
服务层调用该方法:
public void updateAccountTitle(Long id, String title) throws CustomException { int affectedRows = accountRepository.updateTitleById(id, title); if (affectedRows == 0) { throw new CustomException("账户不存在或未修改"); } }
方案3:结合@DynamicUpdate实现动态更新
在Account实体类上添加@DynamicUpdate注解,JPA会生成仅包含变化字段的更新SQL,但需配合“先查后更”的方式使用,否则前端传来的null字段仍会被更新。
修改实体类:
@Entity @Builder @Data @NoArgsConstructor @AllArgsConstructor @Table(name = "accounts") @DynamicUpdate // 添加该注解 public class Account extends BaseEntity implements Serializable { // ... 其他字段 @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) @Column(name = "password") private String password; }
服务层仍使用“先查后更”的逻辑,此时JPA生成的SQL只会更新被修改的字段,不会涉及密码字段。
内容的提问来源于stack exchange,提问作者nomadus
相关产品推荐
相关产品推荐

