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/ 您的支持是对博主最大的鼓励,感谢您的认真阅读.本文版权归作者所有,欢迎转载,但 ...
随机推荐
- Mybaits(10)N+1问题
N+1问题 从上面的例子日志中我们可以看到所有级联都成功了,但是引发了性能问题,例如我们在查询雇员的信息和工作任务信息,此时体检表和工牌信息就是多余,我们没必要查询一次.如果想日志体现的那样,取出了所 ...
- SSM开发健康信息管理系统
Spring+Spring MVC+MyBatis基于MVC架构的个人健康信息管理系统 采用ssm框架,包含 健康档案.健康预警(用户输入数据,系统根据范围自动判断给出不同颜色箭头显示). 健康分析. ...
- vue自定义插件
1.新建js文件 utils.js,自定义方法 let local = { say() { console.log('我是插件里面自定义的方法') } } export default { insta ...
- 在eclipse更新启动项目
说明:figfree是基于模块化开发,代码重用,可拆解性高. 功能模块分为:接口工程(*.Iface).接口实现工程(*.Impl).客户端工程(*.Client) 接口工程(*.Iface):对其他 ...
- 微信小程序直播资料整理
可以通过此脑图大概了解小程序直播内容:https://developers.weixin.qq.com/community/develop/article/doc/0002a62b3749f088fa ...
- 注解配置springMVC
在随笔“springMVC项目配置文件”的基础上,进行优化,使用注解配置,控制器类得以简化: 一.注解配置springMVC 1.在HelloController类中,去除实现的Controller接 ...
- 【你不知道的javaScript 中卷 笔记2】javaScript中的类型转换
1.1 对象内部属性 [[Class]] 常见的原生函数: String() Number() Boolean() Array() Object() Function() RegExp() Date( ...
- 安卓android eclipse运行提示no compatible targets were found
在eclipse中开发安卓应用,运行项目时,右击项目名称---Run As---Android Application时, 系统提示"No compatible targets were f ...
- 深入浅出Mybatis系列五-TypeHandler简介及配置(mybatis源码篇)
注:本文转载自南轲梦 注:博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 上篇文章<深入浅出Mybatis系列(四)---配置详解之typeAliase ...
- 【Unity|C#】基础篇(15)——异常处理(try/catch/throw)
[学习资料] <C#图解教程>(第22章):https://www.cnblogs.com/moonache/p/7687551.html 电子书下载:https://pan.baidu. ...