SpringBoot+springDataJpa实现单表字段动态部分更新
写在前面
所谓的动态部分更新是指:并非对数据记录的所有字段整体更新,而是知道运行时才确定哪个或者哪些字段需要被更新。
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实现单表字段动态部分更新的更多相关文章
- MySQL 按照数据库表字段动态排序 查询列表信息
MySQL 按照数据库表字段动态排序 查询列表信息 背景描述 项目中数据列表分页展示的时候,前端使用的Table组件,每列自带对当前页的数据进行升序或者降序的排序. 但是客户期望:随机点击某一列的时候 ...
- Django框架之第二篇--app注册、静态文件配置、form表单提交、pycharm连接数据库、django使用mysql数据库、表字段的增删改查、表数据的增删改查
本节知识点大致为:静态文件配置.form表单提交数据后端如何获取.request方法.pycharm连接数据库,django使用mysql数据库.表字段的增删改查.表数据的增删改查 一.创建app,创 ...
- 关于解决SpringDataJpa框架实体类表字段创建顺序与数据库表字段展示顺序不一致的问题
今天在公司的项目开发中,遇到一个问题: 后端对象实体类中写入字段顺序与数据库中的存储顺序不一致. 仔细观察到数据库中的表字段的排序方式是按照拼音字母的顺序abcdef......来存储的 而我的实体类 ...
- tp数据表字段缓存
在维护一个tp写的项目,因为需要在产品表product中增加了一个字段status,但是不论如何就是无法给status赋值,查了资料才发现,原来是tp的数据表字段缓存在搞鬼. 在runtime> ...
- spring boot学习(7) SpringBoot 之表单验证
第一节:SpringBoot 之表单验证@Valid 是spring-data-jpa的功能: 下面是添加学生的信息例子,要求姓名不能为空,年龄大于18岁. 贴下代码吧: Student实体: ...
- MySQL 基本语法(1.表字段操作,2表记录管理 3.运算符管理4.SQL查询 5.约束6.索引
.表字段的操作 .语法:alter table 表名 执行动作; .添加字段(add) .添加到末尾 alter table 表名 add 字段名 数据类型; .添加到第一列 alter table ...
- 132_Power BI之建模必备要素&Power Query之数据表字段名称管理
博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 近段时间比较忙,也没有看到很好的DAX素材,很久没有更新文章了,刚好有时间就来凑个热闹. 今天主题是Power ...
- mysql 数据库 表字段添加表情兼容
项目中的几个需要支持Emoji表情符号,手机自带的表情,其实添加也很简单: 1 修改数据库 配置my.cnf init-connect='SET NAMES utf8mb4' ...
- 为了解决mysqlbing翻译表字段问题而分析frm文件(持续更新)
出处:kelvin19840813 的博客 http://www.cnblogs.com/kelvin19840813/ 您的支持是对博主最大的鼓励,感谢您的认真阅读.本文版权归作者所有,欢迎转载,但 ...
随机推荐
- Wilson's Theorem
ProofsSuppose first that $p$ is composite. Then $p$ has a factor $d > 1$ that is less than or equ ...
- k8s 在Centos上 安装
k8s安装步骤: 1.所有机器上执行以下命令,准备安装环境:(注意是所有机器,主机master,从机node都要安装) 1.1.安装epel-release源(EPEL (Extra Packages ...
- TTradmin v2.1 【2019年12月12日更新】简单好用的临时远程协助软件
TTradmin 是一款免端口映射可直接穿透任何内网,基于VNC核心的即时远程协助软件.在使用的时候只需要保证“协助端”和“被协助端”使用同一个验证码即可实现安全便捷的远程控制,不需要进入路由 ...
- function_use
# 函数说明文档,help(len) def sum1(a, b): """ 求和函数sum1 :param a: 参数1 :param b: 参数2 :return: ...
- (一)Python GUI 创建及添加部件
1 开始创建Python GUI 实现代码: import tkinter as tk win = tk.Tk() win.title("Python GUI") win.main ...
- C# WPF 时钟动画(1/2)
微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. C# WPF 时钟动画(1/2) 内容目录 实现效果 业务场景 编码实现 本文参考 源码下载 ...
- simon effect (psychology experiment ) : build the function of wait4key()
## #the real experiment for simon effect #load the library which is our need import pygame import sy ...
- JAVA鼠标监听器
package howework_07; public class Example9_10 { public static void main(String args[]) { WindowMouse ...
- Centos7 修改/etc/profile错误后导致所有命令“not found”
因为Centos7中运行着两个版本的php,今天在设置环境变量时导致所有命令都 "not found". 修复方式: 第一:执行 /bin/vi /etc/profile 把文件修 ...
- 小白的java学习之路 “ 类和对象”
一.※ 万物皆对象 二.对象的两个特征: 属性:对象具有的各种特征 方法:对象执行的操作 对象:用来描述客观事物的一个实体,由一组属性和方法构成 三.封装: 对象同时具有属性和方法两项特性 对象的属性 ...