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/ 您的支持是对博主最大的鼓励,感谢您的认真阅读.本文版权归作者所有,欢迎转载,但 ...
随机推荐
- 破局AI落地难,数据标注行业需率先变革丨曼孚科技
2019年,国内人工智能领域的投融资热情大幅降低,相当数量的AI企业彻底消失在了历史的长河中,“人工智能寒潮已至”甚至成为行业年度热词. 与前几年创业与投资热情齐头并进的盛况相比,近段时间的AI行业 ...
- [TJOI2008] 小偷
TJOI2008小偷 题目背景 一位著名的小偷进入了一个充满宝石的储藏室,这个储藏室是由一连串房间构成的,房间的标号从0开始,想进入第i个房间就必须从第i-1个房间进入,如图: 题目描述 上图为三个房 ...
- 吴裕雄--天生自然HADOOP操作实验学习笔记:分布式及RPC通信简介
实验目的 掌握GOF设计模式的代理模式 了解掌握socket编程.java反射.动态代理 了解NIO.多线程 掌握hadoop的RPC框架使用API 实验原理 1.什么是RPC 在hadoop出现以前 ...
- BSP与HAL关系(转)
板级支持包(BSP)(Board Support Package)是介于主板硬件和操作系统中驱动层程序之间的一层,一般认为它属于操作系统一部分,主要是实现对操作系统的支持,为上层的驱动程序提供访问硬件 ...
- Pr制作音乐相册
Pr制作音乐相册 设置蒙版效果
- PAT (Advanced Level) Practice 1055 The World's Richest (25 分) (结构体排序)
Forbes magazine publishes every year its list of billionaires based on the annual ranking of the wor ...
- 关于全球唯一标识GUID的生成
1.c#生成GUID的几种方式 (1)生成标准的标志符 (36位标准) var strguid = Guid.NewGuid().ToString(); 结果:B2A5AB40-EE29-4791-9 ...
- windows10 找回windows照片查看器的方法
突然发现windows10自带的图片查看器打开预览查看速度还是可以的,但是却找不到了,,,,, 下面就是如何找回 windows 图片查看器的操作了,只需要运行一个bat程序即可!!!!!! 随便新建 ...
- Python instagram 爬虫项目
直接介绍一下具体的步骤以及注意点: instagram 爬虫注意点 instagram 的首页数据是 服务端渲染的,所以首页出现的 11 或 12 条数据是以 html 中的一个 json 结构存在的 ...
- jenkins - docker搭建jenkins
jenkins镜像拉取 docker pull jenkins/jenkins 为jenkins镜像分配容器 docker run -d --name jenkins \ -p 8080:8080 \ ...