Spring Boot JPA操作MySQL更新实体时非空字段报错求助
解决Spring Boot JPA更新时忽略非空字段的问题
嘿,这个问题我太熟了!我之前在做Spring Boot项目的时候也踩过一模一样的坑——明明数据库里那个非空字段已经有值了,更新的时候没传它居然还报NOT NULL错误,简直离谱。结合你的实体类(已经加了@DynamicUpdate(true)和@JsonInclude(NON_EMPTY)),给你几个实用的解决方案,按需选就行:
1. 永久禁止该字段被更新(适合一旦创建就不能改的字段)
如果这个非空字段是创建后就永远不需要修改的(比如用户的注册时间、初始创建的标识字段),直接在字段上加上updatable = false属性,JPA会在生成UPDATE语句时完全忽略这个字段,根本不会碰它:
@Entity @Table(name = "user") @JsonInclude(value = JsonInclude.Include.NON_EMPTY) @DynamicUpdate(true) public class User { private static final long serialVersionUID = 1L; // 假设这个是你的非空字段,比如用户的手机号,创建后不能改 @Column(name = "phone", nullable = false, updatable = false) private String phone; // 其他字段... // getter和setter... }
2. 动态忽略null值(适合字段偶尔需要更新的场景)
如果这个字段有时候还是需要修改,只是这次更新不想传它,那最好用DTO(数据传输对象)配合BeanUtils来实现“只更新非null的字段”:
第一步:创建更新专用的DTO
只在DTO里放允许更新的字段,排除那个不想被覆盖的非空字段:
public class UserUpdateDTO { // 比如只允许更新用户名和邮箱,排除phone字段 private String username; private String email; // getter和setter... }
第二步:添加工具方法过滤null属性
写一个工具方法,找出DTO中为null的属性名,这样BeanUtils复制的时候会跳过这些属性:
import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import java.beans.PropertyDescriptor; import java.util.HashSet; import java.util.Set; public class BeanUtil { public static String[] getNullPropertyNames(Object source) { final BeanWrapper src = new BeanWrapperImpl(source); PropertyDescriptor[] pds = src.getPropertyDescriptors(); Set<String> emptyNames = new HashSet<>(); for (PropertyDescriptor pd : pds) { Object srcValue = src.getPropertyValue(pd.getName()); if (srcValue == null) { emptyNames.add(pd.getName()); } } return emptyNames.toArray(new String[0]); } }
第三步:在服务层实现更新逻辑
先从数据库查出原实体,再用DTO的非null属性覆盖原实体,最后保存:
@Service public class UserService { @Autowired private UserRepository userRepository; public User updateUser(Long userId, UserUpdateDTO updateDTO) { // 先获取数据库中已有的用户实体 User existingUser = userRepository.findById(userId) .orElseThrow(() -> new RuntimeException("用户不存在")); // 只复制DTO中非null的属性,跳过null值,原实体的非空字段不会被覆盖 BeanUtils.copyProperties(updateDTO, existingUser, BeanUtil.getNullPropertyNames(updateDTO)); return userRepository.save(existingUser); } }
3. 自定义更新SQL(最灵活的方式)
如果不想用DTO,也可以直接在Repository里写自定义的UPDATE语句,只更新你需要的字段,完全不涉及那个非空字段:
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @Repository public interface UserRepository extends JpaRepository<User, Long> { @Modifying @Query("UPDATE User u SET u.username = :username, u.email = :email WHERE u.id = :userId") int updateUserFields(@Param("userId") Long userId, @Param("username") String username, @Param("email") String email); }
调用这个方法时,直接传需要更新的字段就行,那个非空字段根本不会出现在SQL里,自然不会触发NOT NULL错误。
补充:为什么你的现有注解没起作用?
@DynamicUpdate(true):只会让JPA生成UPDATE语句时只包含实体中发生变化的字段,但如果前端没传某个字段,JSON反序列化后实体的该字段会被设为null,JPA会认为这个字段“变了”,还是会把null写到UPDATE语句里,导致数据库报错。@JsonInclude(NON_EMPTY):这是序列化时用的(把实体转成JSON时忽略空值),但反序列化(把前端传的JSON转成实体)时,没传的字段还是会被设为null,所以解决不了更新的问题。
内容的提问来源于stack exchange,提问作者Veera
相关产品推荐
相关产品推荐

