zoukankan      html  css  js  c++  java
  • 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);
        }
    }
  • 相关阅读:
    通过进程ID获取基地址
    怎样获得某个进程的内存基地址?
    Delphi来实现一个IP地址输入控件
    如何在Windows服务程序中添加U盘插拔的消息
    delphi Format格式化函数
    CRC8算法DELPHI源码
    实现控件的透明背景
    定制控件背景颜色与背景位图
    实现系统滚动条换肤功能
    Delphi 获取命令行输出的函数
  • 原文地址:https://www.cnblogs.com/cndarren/p/12342082.html
Copyright © 2011-2022 走看看