写在前面

所谓的动态部分更新是指:并非对数据记录的所有字段整体更新,而是知道运行时才确定哪个或者哪些字段需要被更新。

1)Spring Data Jpa对于Entity的更新,是对数据表中Entity对应的除主键外的数据记录的所有字段整体更新,

      而不是仅仅更新前端传入的字段或者那些发生了变化的字段;

2)repository.save()的逻辑是:如果不存在Entity对应的数据记录则执行插入操作,否则则执行更新操作。同时,

在执行更新操作之前,此方法还会执行一步查询操作。源码如下:

    @Transactional
@Override
public <S extends T> S save(S entity) { if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}

3)对于字段更新时,如果使用@Query注解,通过写原生SQL的方法,确实可以实现字段的部分更新,但是使用@Query注解无法很好地实现字段的动态部分更新。

4)使用@DynamicUpdate注解,通过在Entity实体类上添加此注解,再结合repository.save()方法进行字段更新,此方法的确具有可行性,但是仍存在一个问题:当字段值为null值时,Jpa会将null值与原值作比较,如果原值不为null,那么原值将会被覆盖为null。

针对此问题,如何解决呢,这里提供一种方法。

对于动态部分更新,可以在@DynamicUpdate注解的基础上,可以书写一个Jpa工具类来避免null值对于动态部分更新的影响。

这里给出一个示例代码:

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapperImpl; import java.beans.PropertyDescriptor;
import java.util.stream.Stream; public class JpaUtil {
public static void copyNotNullProperties(Object src,Object target){
BeanUtils.copyProperties(src,target,getNullPropertyNames(src));
} private static String[] getNullPropertyNames(Object object) {
final BeanWrapperImpl wrapper = new BeanWrapperImpl(object);
return Stream.of(wrapper.getPropertyDescriptors())
.map(PropertyDescriptor::getName)
.filter(propertyName -> wrapper.getPropertyValue(propertyName) == null)
.toArray(String[]::new);
}
}

下面根据示例代码进行整合。

1> 数据准备

CREATE TABLE `tb_user` (
`id` int(32) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
`name` varchar(20) DEFAULT NULL COMMENT '用户名',
`age` int(10) DEFAULT NULL COMMENT '年龄',
`email` varchar(255) DEFAULT NULL COMMENT '邮箱',
`address` varchar(255) DEFAULT NULL COMMENT '地址',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `tb_user` VALUES (1, '张三', 22, '123456@qq.com', '北京市', '2020-02-16 13:18:21');
INSERT INTO `tb_user` VALUES (2, '李四', 23, '243456@qq.com', '天津市', '2020-02-16 13:18:58');
INSERT INTO `tb_user` VALUES (3, '王五', 22, '123597@qq.com', '重庆市', '2020-02-16 13:18:58');
INSERT INTO `tb_user` VALUES (4, '赵六', 21, '565345@qq.com', '武汉市', '2020-02-16 13:18:58');
INSERT INTO `tb_user` VALUES (5, '钱七', 24, '375654@qq.com', '杭州市', '2020-02-16 13:18:58');
INSERT INTO `tb_user` VALUES (6, '孙八', 26, '977842@qq.com', '上海市', '2020-02-16 13:18:58');
INSERT INTO `tb_user` VALUES (7, '周九', 24, '345342@qq.com', '深圳市', '2020-02-16 13:18:58');
INSERT INTO `tb_user` VALUES (8, '郑十', 25, '645564@qq.com', '广州市', '2020-02-16 13:18:58');

2> 创建工程,导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.darren</groupId>
<artifactId>springjpa-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springjpa-demo</name>
<description>Demo project for Spring Boot</description> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

3> 配置Application.yml文件

spring:
datasource:
url: jdbc:mysql:///springboottest?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2b8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
generate-ddl: true

4> 创建entity实体类

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.format.annotation.DateTimeFormat; import javax.persistence.*;
import java.util.Date; @Entity
@Data
@Table(name = "tb_user")
@DynamicUpdate
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; private String name; private Integer age; private String email; private String address; @Column(name = "create_time")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}

5> 创建Repository接口

import com.darren.springjpademo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository; @Repository
public interface UserRepository extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
}

6> 创建Service层接口及其实现类

6.1 创建service层接口

import com.darren.springjpademo.entity.User;

import java.util.List;

public interface UserService {
/**
* 查询所有用户信息
* @return
*/
List<User> queryList(); /**
* 更新用户信息
* @param user
* @return
*/
String updateUser(User user);
}

6.2 创建ServiceImpl实现类

import com.darren.springjpademo.repository.UserRepository;
import com.darren.springjpademo.entity.User;
import com.darren.springjpademo.service.UserService;
import com.darren.springjpademo.uitls.JpaUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import java.util.List;
import java.util.Optional; @Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository; @Override
public List<User> queryList() {
return userRepository.findAll();
} @Override
public String updateUser(User user) {
     User user = new User();
     user.setId(1);
     user.setName("张三");
     user.setAddress("北京");
if(user.getId() != null) {
Optional<User> originalUser = userRepository.findById(user.getId());
if (originalUser.isPresent()) {
JpaUtil.copyNotNullProperties(user, originalUser.get());
}
}
userRepository.save(user);
return user.getId()+" "+user.getName();
}
}

7> 创建Controller层

import com.darren.springjpademo.entity.User;
import com.darren.springjpademo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import java.util.List; @RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService; /**
* 查询所有的用户信息
* @return
*/
@GetMapping("/queryList")
public List<User> queryList(){
return this.userService.queryList();
} /**
* 更新用户信息
* @param user
* @return
*/
@PutMapping("/updateUser")
public ResponseEntity<String> updateUser(@RequestBody User user){
String result = userService.updateUser(user);
return ResponseEntity.ok(result);
}
}

SpringBoot+springDataJpa实现单表字段动态部分更新的更多相关文章

  1. MySQL 按照数据库表字段动态排序 查询列表信息

    MySQL 按照数据库表字段动态排序 查询列表信息 背景描述 项目中数据列表分页展示的时候,前端使用的Table组件,每列自带对当前页的数据进行升序或者降序的排序. 但是客户期望:随机点击某一列的时候 ...

  2. Django框架之第二篇--app注册、静态文件配置、form表单提交、pycharm连接数据库、django使用mysql数据库、表字段的增删改查、表数据的增删改查

    本节知识点大致为:静态文件配置.form表单提交数据后端如何获取.request方法.pycharm连接数据库,django使用mysql数据库.表字段的增删改查.表数据的增删改查 一.创建app,创 ...

  3. 关于解决SpringDataJpa框架实体类表字段创建顺序与数据库表字段展示顺序不一致的问题

    今天在公司的项目开发中,遇到一个问题: 后端对象实体类中写入字段顺序与数据库中的存储顺序不一致. 仔细观察到数据库中的表字段的排序方式是按照拼音字母的顺序abcdef......来存储的 而我的实体类 ...

  4. tp数据表字段缓存

    在维护一个tp写的项目,因为需要在产品表product中增加了一个字段status,但是不论如何就是无法给status赋值,查了资料才发现,原来是tp的数据表字段缓存在搞鬼. 在runtime> ...

  5. spring boot学习(7) SpringBoot 之表单验证

    第一节:SpringBoot 之表单验证@Valid 是spring-data-jpa的功能:   下面是添加学生的信息例子,要求姓名不能为空,年龄大于18岁.   贴下代码吧: Student实体: ...

  6. MySQL 基本语法(1.表字段操作,2表记录管理 3.运算符管理4.SQL查询 5.约束6.索引

    .表字段的操作 .语法:alter table 表名 执行动作; .添加字段(add) .添加到末尾 alter table 表名 add 字段名 数据类型; .添加到第一列 alter table ...

  7. 132_Power BI之建模必备要素&Power Query之数据表字段名称管理

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 近段时间比较忙,也没有看到很好的DAX素材,很久没有更新文章了,刚好有时间就来凑个热闹. 今天主题是Power ...

  8. mysql 数据库 表字段添加表情兼容

    项目中的几个需要支持Emoji表情符号,手机自带的表情,其实添加也很简单: 1 修改数据库 配置my.cnf  init-connect='SET NAMES utf8mb4'             ...

  9. 为了解决mysqlbing翻译表字段问题而分析frm文件(持续更新)

    出处:kelvin19840813 的博客 http://www.cnblogs.com/kelvin19840813/ 您的支持是对博主最大的鼓励,感谢您的认真阅读.本文版权归作者所有,欢迎转载,但 ...

随机推荐

  1. linux中安装nginx时查看修改80端口时没有iptables文件的内容?? 求解

  2. Maven快速入门使用

    1. Maven 介绍 1.1 为什么使用 Maven 由于 Java 的生态非常丰富,无论你想实现什么功能,都能找到对应的工具类,这些工具类都是以 jar 包的形式出现的,例如 Spring,Spr ...

  3. 2-1.了解Pod对象

    1.Pod参数定义 # 必填,版本号 apiVersion: string kind: Pod # 必填,元数据 metadata: # 必填,Pod对象的名称(命名规范需要符合RFC 1035规范) ...

  4. MyEclipse+Tamcat配置

    (尊重劳动成果,转载请注明出处:http://blog.csdn.NET/qq_25827845/article/details/53982209 冷血之心的博客) 一.Tomcat 1 Tomcat ...

  5. 使用 Hyper-V 替代 VMware

    目前我又用回了vmware,原因是,Hyper-V性能不如vmware,而且导出的虚拟机文件,再导入会出现奇怪的问题.不省心. Hyper-V是什么 Hyper-V硬件要求为Windows 10 企业 ...

  6. Edit页面返回ViewBag json字符串与前端js交互

    很多时候,在打开一个新页面的时候,后端需要同时传一个json字符串给前端页面,但是我们又不能把action的返回值改为string,这时候我们就需要用到ViewBag将json字符串传回到前端,前端j ...

  7. SQL语句中设置字段值取反操作

    1.对布尔值取反,使用 ~. 如 update set status=~status where id=2; status的值为true || false. 2.对0.1 数值取反,使用abs() 取 ...

  8. Word报表生成

    /// <summary> /// 生产报表 /// </summary> /// <param name="strTemplate">< ...

  9. jQuery---城市选择案例

    城市选择案例 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UT ...

  10. mybatis第一天02

    mybatis第二天02 1.映射文件之输入输出映射 1.1映射文件之输入映射类型(parameterType) 1.1.1简单类型 当parameterType为简单类型时,我们只需要直接填写“in ...