Java JPA:仅更新特定字段

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27818334/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 05:06:24  来源:igfitidea点击:

JPA: update only specific fields

javaspringjpaspring-data-jpa

提问by Andrea

Is there a way for updating only some fieldsof an entity object using the method savefrom Spring Data JPA?

是否有更新的方式只有一些领域使用该方法的实体对象save春数据JPA

For example I have a JPA entity like this:

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

@Entity
public class User {

  @Id
  private Long id;

  @NotNull
  private String login;

  @Id
  private String name;

  // getter / setter
  // ...
}

With its CRUD repo:

使用其 CRUD 存储库:

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

In Spring MVCI have a controller that get an Userobject for update it:

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);

   // ...
}

I know that I could load the user using findOne, then change its name and update it using save... But if I have 100 fields and I want to update 50 of them it could be very annoying change each value..

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

Is there no way to tell something like "skip all null values when save the object"?

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

回答by Pirulino

the problem is not spring data jpa related but the jpa implementation lib that you are using related. In case of hibernate you may have a look at:

问题不是与 spring 数据 jpa 相关,而是与您正在使用的 jpa 实现库相关。在休眠的情况下,您可以查看:

http://www.mkyong.com/hibernate/hibernate-dynamic-update-attribute-example/

http://www.mkyong.com/hibernate/hibernate-dynamic-update-attribute-example/

回答by William

I had the same question and as M. Deinum points out, the answer is no, you can't use save. The main problem being that Spring Data wouldn't know what to do with nulls. Is the null value not set or is it set because it needs to be deleted?

我有同样的问题,正如 Deinum 先生指出的那样,答案是否定的,您不能使用 save。主要问题是 Spring Data 不知道如何处理空值。是没有设置空值还是因为需要删除而设置?

Now judging from you question, I assume you also had the same thought that I had, which was that save would allow me to avoid manually setting all the changed values.

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

So is it possible to avoid all the manuel mapping then? Well, if you choose to adhere to the convention that nulls always means 'not set' and you have the original model id, then yes. You can avoid any mapping yourself by using Springs BeanUtils.

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

You could do the following:

您可以执行以下操作:

  1. Read the existing object
  2. Use BeanUtils to copy values
  3. Save the object
  1. 读取现有对象
  2. 使用 BeanUtils 复制值
  3. 保存对象

Now, Spring's BeanUtils actual doesn't support not copying null values, so it will overwrite any values not set with null on the exiting model object. Luckily, there is a solution here:

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

How to ignore null values using springframework BeanUtils copyProperties?

如何使用 springframework BeanUtils copyProperties 忽略空值?

So putting it all together you would end up with something like this

所以把它们放在一起你会得到这样的结果

@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);
}

回答by Arjun Nayak

Using JPA you can do it this way.

使用 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();

回答by Anand

If you are reading request as JSON String, this could be done using Hymanson API. Here is code below. Code compares an existing POJO Elements and create new one with updated fields. Use the new POJO to persist.

如果您将请求作为 JSON 字符串读取,则可以使用 Hymanson API 来完成。这是下面的代码。代码比较现有的 POJO 元素并创建具有更新字段的新元素。使用新的 POJO 进行持久化。

public class TestHymansonUpdate {

class Person implements Serializable {
    private static final long serialVersionUID = -7207591780123645266L;
    public String code = "1000";
    public String firstNm = "John";
    public String lastNm;
    public Integer age;
    public String comments = "Old Comments";

    @Override
    public String toString() {
        return "Person [code=" + code + ", firstNm=" + firstNm + ", lastNm=" + lastNm + ", age=" + age
                + ", comments=" + comments + "]";
    }
}

public static void main(String[] args) throws JsonProcessingException, IOException {
    TestHymansonUpdate o = new TestHymansonUpdate();

    String input = "{\"code\":\"1000\",\"lastNm\":\"Smith\",\"comments\":\"Hymanson Update WOW\"}";
    Person persist = o.new Person();

    System.out.println("persist: " + persist);

    ObjectMapper mapper = new ObjectMapper();
    Person finalPerson = mapper.readerForUpdating(persist).readValue(input);

    System.out.println("Final: " + finalPerson);
}}

Final output would be, Notice only lastNm and Comments are reflecting changes.

最终输出将是,注意只有 lastNm 和 Comments 反映了更改。

persist: Person [code=1000, firstNm=John, lastNm=null, age=null, comments=Old Comments]
Final: Person [code=1000, firstNm=John, lastNm=Smith, age=null, comments=Hymanson Update WOW]

回答by Андрей Затворницкий

You are able to write something like

你可以写一些类似的东西

@Modifying
    @Query("update StudentXGroup iSxG set iSxG.deleteStatute = 1 where iSxG.groupId = ?1")
    Integer deleteStudnetsFromDeltedGroup(Integer groupId);

Or If you want to update only the fields that were modified you can use annotation

或者如果您只想更新已修改的字段,您可以使用注释

@DynamicUpdate

Code example:

代码示例:

@Entity
@Table(name = "lesson", schema = "oma")
@Where(clause = "delete_statute = 0")
@DynamicUpdate
@SQLDelete(sql = "update oma.lesson set delete_statute = 1, "
        + "delete_date = CURRENT_TIMESTAMP, "
        + "delete_user = '@currentUser' "
        + "where lesson_id = ?")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})

回答by bmck

For long,int and other types; you can use the following code;

对于long、int等类型;您可以使用以下代码;

            if (srcValue == null|(src.getPropertyTypeDescriptor(pd.getName()).getType().equals(long.class) && srcValue.toString().equals("0")))
            emptyNames.add(pd.getName());

回答by Sandro

skip all null values when save the object

保存对象时跳过所有空值

As others have pointed out, there is not straight forward solution in JPA.

正如其他人所指出的,JPA 中没有直接的解决方案。

But thinking out of the box you can use MapStruct for that.

但是开箱即用,您可以使用 MapStruct。

This means you use the right find()method to get the object to update from the DB, overwrite only the non-null properties and then save the object.

这意味着您使用正确的find()方法从数据库中获取要更新的对象,仅覆盖非空属性,然后保存该对象。

You can use JPA as you know and just use MapStruct like this in Spring to update only the non-null properties of the object from the DB:

您可以使用 JPA,并在 Spring 中像这样使用 MapStruct 来仅更新数据库中对象的非空属性:

@Mapper(componentModel = "spring")
public interface HolidayDTOMapper {

    /**
     * Null values in the fields of the DTO will not be set as null in the target. They will be ignored instead.
     *
     * @return The target Holiday object
     */
    @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
    Holiday updateWithNullAsNoChange(HolidayDTO holidayDTO, @MappingTarget Holiday holiday);

}

See the MapStruct docu on thatfor details.

有关详细信息请参阅MapStruct 文档

You can inject the HolidayDTOMapperthe same way you do it with other beans (@Autowired, Lombok,...) .

您可以HolidayDTOMapper使用与其他 bean ( @Autowired, Lombok,...)相同的方式注入。