JPA:仅更新特定字段

2022-08-31 12:39:46

有没有办法使用Spring Data JPA中的方法仅更新实体对象的某些字段save

例如,我有一个像这样的JPA实体:

@Entity
public class User {

  @Id
  private Long id;

  @NotNull
  private String login;

  @Id
  private String name;

  // getter / setter
  // ...
}

通过其 CRUD 存储库:

public interface UserRepository extends CrudRepository<User, Long> { }

Spring MVC中,我有一个控制器来获取一个用于更新它的对象:User

@RequestMapping(value = "/rest/user", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<?> updateUser(@RequestBody User user) {

   // Assuming that user have its id and it is already stored in the database,
   // and user.login is null since I don't want to change it,
   // while user.name have the new value

   // I would update only its name while the login value should keep the value 
   // in the database
   userRepository.save(user);

   // ...
}

我知道我可以使用加载用户,然后更改其名称并使用...但是,如果我有100个字段,并且我想更新其中的50个字段,则更改每个值可能会非常烦人。findOnesave

有没有办法告诉“保存对象时跳过所有空值”之类的东西?


答案 1

我有同样的问题,正如M. Deinum指出的那样,答案是否定的,你不能使用保存。主要问题是Spring Data不知道如何处理空值。是未设置 null 值,还是因为需要删除而设置 null 值?

现在从你的问题来看,我假设你也有和我一样的想法,那就是保存可以让我避免手动设置所有更改的值。

那么,是否有可能避免所有的曼努埃尔映射呢?好吧,如果您选择遵守 null 始终表示“未设置”的约定,并且您具有原始模型 ID,那么是的。您可以通过使用Springs BeanUtils自己避免任何映射。

您可以执行以下操作:

  1. 读取现有对象
  2. 使用 BeanUtils 复制值
  3. 保存对象

现在,Spring的BeanUtils实际不支持不复制空值,因此它将覆盖在现有模型对象上未设置为null的任何值。幸运的是,这里有一个解决方案:

如何使用弹簧框架BeanUtils copyProperties忽略空值?

所以把它们放在一起,你最终会得到这样的东西

@RequestMapping(value = "/rest/user", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<?> updateUser(@RequestBody User user) {

   User existing = userRepository.read(user.getId());
   copyNonNullProperties(user, existing);
   userRepository.save(existing);

   // ...
}

public static void copyNonNullProperties(Object src, Object target) {
    BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
}

public static String[] getNullPropertyNames (Object source) {
    final BeanWrapper src = new BeanWrapperImpl(source);
    java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

    Set<String> emptyNames = new HashSet<String>();
    for(java.beans.PropertyDescriptor pd : pds) {
        Object srcValue = src.getPropertyValue(pd.getName());
        if (srcValue == null) emptyNames.add(pd.getName());
    }
    String[] result = new String[emptyNames.size()];
    return emptyNames.toArray(result);
}

答案 2

使用JPA,你可以这样做。

CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaUpdate<User> criteria = builder.createCriteriaUpdate(User.class);
Root<User> root = criteria.from(User.class);
criteria.set(root.get("lastSeen"), date);
criteria.where(builder.equal(root.get("id"), user.getId()));
session.createQuery(criteria).executeUpdate();

推荐