JPA只实现局部字段更新的解决办法(一)

JPA提供的save(S entity)可以实现更新,但是即使是null值,也会更新。不满足需求,所以对JPA的findById(Integer id)与save(S entity)进行封装,便可实现局部字段更新

核心:泛型方法和反射综合使用
1.封装局部更新工具类

package psn.kiko.util;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;

/**
 * 利用泛型和反射封装的动态更新工具类
 * created by kiko on 2022-6-12
 */
@Component
public class PartialUpdateUtil {
    /**
     * 根据主键id,实现JPA的动态更新
     *
     * @param id  主键,一般为Integer类型
     * @param src 收集的用户数据
     * @param dao JpaRepository对象
     * @param  src对象对应的实体类型
     * @param  主键参数对应的类型
     * @return 更新后的实体对象
     * @throws IllegalAccessException
     */
    public <T, I> T partialUpdate(I id, T src, JpaRepository<T, I> dao) throws IllegalAccessException {
        T orig = dao.findById(id).get();
        T dest = src;
        Field[] fields = src.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            //条件:对于某个Field,如果orig的值不为null,并且src的值为null
            if (field.get(orig) != null
                    && field.get(src) == null) {
                //操作:则将orig的当前Field的值,赋值给dest对应的Field
                field.set(dest, field.get(orig));
            }
        }
        T savedData = dao.save(dest);
        return savedData;
    }
}

2.测试示例

@SpringBootTest
public class UserRepositoryTest {
    @Autowired
    private UserRepository dao;

    @Autowired
    private PartialUpdateUtil updateUtil;
	 
	@Test
    void dynamicUpdate() throws IllegalAccessException {
        User update = updateUtil.partialUpdate(1, new User(1, "testname2222", null), dao);
        System.out.println(update);
    }
}

单元测试结果:测试时模拟的password属性为null值,经过动态更新后,其值不变,仍然为数据库中原来的值,从而实现了:当用户传入null值的时候,数据库中与此对应的字段不更新为null值。

但从打印的SQL语句就可以发现并没有从根本上解决问题
update tb_user set password=?, uname=? where id=?
|_+
期待的SQL语句应该是(如果某个字段为null,则SQL直接不更新它),当password没有传值时,SQL应该长这样:
update tb_user set uname=? where id=?

Hibernate: select user0_.id as id1_0_0_, user0_.password as password2_0_0_, user0_.uname as uname3_0_0_ from tb_user user0_ where user0_.id=?
Hibernate: select user0_.id as id1_0_0_, user0_.password as password2_0_0_, user0_.uname as uname3_0_0_ from tb_user user0_ where user0_.id=?
Hibernate: update tb_user set password=?, uname=? where id=?
User(id=1, uname=testname2222, password=kkkkpass)

补充代码信息
3.实体类

package psn.kiko.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "tb_user",schema = "hibernate")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Integer id;
    @Column(name = "uname")
    private String uname;
    @Column(name = "password")
    private String password;
}

4.dao:并不直接提供动态更新的方法,局部更新需要用到一开始封装的工具类

package psn.kiko.dao;

import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import psn.kiko.entity.User;

import java.util.List;

public interface UserRepository extends JpaRepository<User,Integer> {
    /**
     * 单个条件模糊查询
     * @param uname
     * @return
     */
    List<User> findByUnameLike(String uname);

    /**
     * 多个条件进行模糊查询
     * @param uname
     * @param password
     * @return
     */
    List<User> findByUnameLikeAndPasswordLike(String uname,String password);

    /**
     * 模糊查询
     * @param uname
     * @return
     */
    List<User> findByUnameContains(String uname);

    /**
     * 单个条件分页
     * @param uname
     * @param pageable
     * @return
     */
    List<User> findAllByUnameContains(String uname,Pageable pageable);

    /**
     * 多个条件分页
     * @param uname
     * @param password
     * @param pageable
     * @return
     */
    List<User> findAllByUnameContainsAndAndPasswordContains(String uname,String password,Pageable pageable);
}

你可能感兴趣的:(hibernate,java,jpa)