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