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);
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM