是否有办法使用 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);
// ...
}
我知道我可以使用findOne
加载用户,然后更改其名称并使用save
进行更新...但是如果我有100个字段并且我想要更新50个字段,那么它可以非常讨厌改变每个值..
有没有办法说出“在保存对象时跳过所有空值”之类的内容?
答案 0 :(得分:31)
我有同样的问题,正如M. Deinum指出的那样,答案是否定的,你不能使用保存。主要问题是Spring Data不知道如何处理null。是否设置了空值,或者是否设置了空值,因为需要将其删除?
现在从你的问题判断,我认为你也有同样的想法,即保存将允许我避免手动设置所有更改的值。
那么有可能避免所有的manuel映射吗?好吧,如果你选择遵守惯例,那么空值总是意味着“没有设置”&#39;你有原始模型ID,然后是。 您可以使用Springs BeanUtils来避免任何映射。
您可以执行以下操作:
现在,Spring的BeanUtils实际上并不支持不复制空值,因此它会覆盖现有模型对象上未设置为null的任何值。幸运的是,这里有一个解决方案:
How to ignore null values using springframework 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);
}
答案 1 :(得分:6)
使用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();
答案 2 :(得分:1)
问题不是spring数据jpa相关,而是你使用的jpa实现lib相关。 如果是休眠,你可以查看:
http://www.mkyong.com/hibernate/hibernate-dynamic-update-attribute-example/
答案 3 :(得分:0)
如果您正在以JSON字符串形式读取请求,则可以使用Jackson API完成此操作。这是下面的代码。代码比较现有的POJO元素并使用更新的字段创建新的POJO元素。使用新的POJO来保持。
public class TestJacksonUpdate {
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 {
TestJacksonUpdate o = new TestJacksonUpdate();
String input = "{\"code\":\"1000\",\"lastNm\":\"Smith\",\"comments\":\"Jackson 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);
}}
最终输出是,注意只有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=Jackson Update WOW]
答案 4 :(得分:0)
您可以编写类似
的内容@Modifying
@Query("update StudentXGroup iSxG set iSxG.deleteStatute = 1 where iSxG.groupId = ?1")
Integer deleteStudnetsFromDeltedGroup(Integer groupId);
或者,如果您只想更新已修改的字段,则可以使用注释
@DynamicUpdate
代码示例:
@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"})
答案 5 :(得分:0)
用于long,int和其他类型; 您可以使用以下代码;
if (srcValue == null|(src.getPropertyTypeDescriptor(pd.getName()).getType().equals(long.class) && srcValue.toString().equals("0")))
emptyNames.add(pd.getName());
答案 6 :(得分:0)
保存对象时跳过所有空值
正如其他人指出的那样,JPA中没有直接解决方案。
但是开箱即用,您可以使用MapStruct。
这意味着您使用正确的find()
方法从数据库中获取要更新的对象,仅覆盖非null属性,然后保存该对象。
您可以使用JPA,并且只需在Spring中使用MapStruct这样即可从数据库中仅更新对象的非null属性:
@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);
}
有关详细信息,请参见the MapStruct docu on that。
您可以像对待其他bean(HolidayDTOMapper
,Lombok等)一样注入@Autowired
。