使用@DynamicUpdate时如何避免更新随机生成的指定字段?
JPA动态更新时避免修改指定随机生成字段的解决方案
问题描述
我在使用JPA的@DynamicUpdate注解时遇到字段更新问题:需要避免更新通过随机函数生成的StaffSectionCode和StudentSectionCode字段,仅在新增实体时生成并设置它们的值,更新时保持原有值不变。
实体类定义
@Data @Entity @Table(name = "Classes") @NoArgsConstructor @AllArgsConstructor @Getter @Setter @JsonInclude(JsonInclude.Include.NON_NULL) @ToString @DynamicUpdate public class Classes { @Id @Column(name = "ClassId") private Long classId; @Column(name = "CourseSourceId") private String courseSourceId; @Column(name = "Name") private String name; @Column(name = "Grades",updatable = false) private String grades; @Column(name = "CourseId") private String courseId; @Column(name = "ClassCode",updatable = false) private String classCode; @Column(name = "Subjects") private String subjects; @Column(name = "SubjectCodes") private String subjectCodes; @Column(name = "Periods") private String periods; @Column(name = "StaffSectionCode",updatable = false) private String staffSectionCode; @Column(name = "StudentSectionCode",updatable = false) private String studentSectionCode; }
我已经为目标字段设置了updatable=false,但由于Controller中每次请求都会生成新的随机值,且EventHandler不分场景赋值,导致更新操作时这两个字段仍被修改。
Controller代码(同时处理新增/更新)
@PostMapping(value = "/addClasses") @PreAuthorize("hasAuthority('Write')") public ResponseEntity<Object> saveCLasses(@Valid @RequestBody AddClassesCommand command, @RequestHeader(ClassesConstants.AUTHORIZATION) String token) { try { log.info("saveCLasses {} Service Calling..."); Long id = UniqueIdentifierGenerate.get64MostSignificantBitsForVersion1(); command.setId(id); Date date = new Date(); Long time = date.getTime() + (int) (Math.random() * 100); command.getClasses().setStaffAccessCode(ApplicationConstants.STAFF_SECTION_CODE + time); Long std = date.getTime() + (int) (Math.random() * 100); command.getClasses().setStudentAccessCode(ApplicationConstants.STUDENT_SECTION_CODE + std); command.setUctx(TokenConversion.getUserContextJson(token)); commandGateway.sendAndWait(command); command.setUctx(null); log.info("saveClasses {} Service Complete successfully..."); return new ResponseEntity<>(new CreatedResponse(id, ClassesConstants.getSuccessMessage (ClassesConstants.CLASSES, ClassesConstants.CREATE), command, true), HttpStatus.CREATED); } catch (Exception e) { logger.info(e.getMessage()); var safeErrorMessage = ClassesConstants.CREATE_ERROR_MSG; return new ResponseEntity<>(safeErrorMessage, HttpStatus.INTERNAL_SERVER_ERROR); } }
EventHandler处理逻辑
private Classes saveClasses(AddClassesEvent event, ActiveSessionInfo activeSessionInfo) { try { log.info("saveClasses {} calling..."); /** if (event.getClasses().getClassId() != null) { guid = event.getClasses().getClassId(); } else { guid = event.getId().toString(); } var existingId = classesRepository.findByGuid(guid); if (existingId != null) { cls = existingId; } else { cls.setGuid(event.getId().toString()); cls.setClassId(classesRepository.findNextValFromClasses_Seq()+100); classesHelper.updateSeqValueInDb(ApplicationConstants.DEFAULT_CLASSES_SEQUENCE, cls.getClassId(), 1l); } **/ // 希望在上述判断的if分支(更新场景)中不更新指定字段,else分支(新增场景)中创建字段数据 cls.setName(event.getClasses().getClassName()); cls.setOrgId(event.getClasses().getOrgId()); if (activeSessionInfo != null && activeSessionInfo.getYearId() != null) { cls.setSchoolYearId(activeSessionInfo.getYearId()); } cls.setPeriods(event.getClasses().getPeriod()); cls.setCourseId(event.getClasses().getCourseId()); if (activeSessionInfo != null && activeSessionInfo.getTenantId() != null) { cls.setTenantId(activeSessionInfo.getTenantId()); } cls.setStaffSectionCode(event.getClasses().getStaffAccessCode()); cls.setStudentSectionCode(event.getClasses().getStudentAccessCode()); classesRepository.saveAndFlush(cls); saveCoTeacherEnrollmentDetails(event, cls.getClassId(), activeSessionInfo); log.info("saveClasses {} calling end..."); return cls; } catch (Exception ex) { log.error(ex.getMessage()); return null; } }
解决方案
1. 区分新增/更新场景,仅在新增时赋值目标字段
在EventHandler的saveClasses方法中,利用现有实体判断逻辑,仅在新增分支中设置随机字段值,更新分支跳过赋值:
private Classes saveClasses(AddClassesEvent event, ActiveSessionInfo activeSessionInfo) { try { log.info("saveClasses {} calling..."); String guid; Classes cls; if (event.getClasses().getClassId() != null) { guid = event.getClasses().getClassId().toString(); } else { guid = event.getId().toString(); } var existingId = classesRepository.findByGuid(guid); if (existingId != null) { // 更新场景:复用现有实体,不修改目标随机字段 cls = existingId; } else { // 新增场景:初始化新实体并设置随机字段 cls = new Classes(); cls.setGuid(event.getId().toString()); cls.setClassId(classesRepository.findNextValFromClasses_Seq()+100); classesHelper.updateSeqValueInDb(ApplicationConstants.DEFAULT_CLASSES_SEQUENCE, cls.getClassId(), 1l); // 仅新增时赋值随机生成的字段 cls.setStaffSectionCode(event.getClasses().getStaffAccessCode()); cls.setStudentSectionCode(event.getClasses().getStudentAccessCode()); } // 通用字段赋值(新增/更新都执行) cls.setName(event.getClasses().getClassName()); cls.setOrgId(event.getClasses().getOrgId()); if (activeSessionInfo != null && activeSessionInfo.getYearId() != null) { cls.setSchoolYearId(activeSessionInfo.getYearId()); } cls.setPeriods(event.getClasses().getPeriod()); cls.setCourseId(event.getClasses().getCourseId()); if (activeSessionInfo != null && activeSessionInfo.getTenantId() != null) { cls.setTenantId(activeSessionInfo.getTenantId()); } classesRepository.saveAndFlush(cls); saveCoTeacherEnrollmentDetails(event, cls.getClassId(), activeSessionInfo); log.info("saveClasses {} calling end..."); return cls; } catch (Exception ex) { log.error(ex.getMessage()); return null; } }
2. 优化Controller逻辑,避免更新请求生成随机值
通过判断Command中是否携带ClassId区分场景,仅在新增时生成随机值:
@PostMapping(value = "/addClasses") @PreAuthorize("hasAuthority('Write')") public ResponseEntity<Object> saveCLasses(@Valid @RequestBody AddClassesCommand command, @RequestHeader(ClassesConstants.AUTHORIZATION) String token) { try { log.info("saveCLasses {} Service Calling..."); Long id = UniqueIdentifierGenerate.get64MostSignificantBitsForVersion1(); command.setId(id); // 仅新增场景(ClassId为空)生成随机字段值 if (command.getClasses().getClassId() == null) { Date date = new Date(); Long time = date.getTime() + (int) (Math.random() * 100); command.getClasses().setStaffAccessCode(ApplicationConstants.STAFF_SECTION_CODE + time); Long std = date.getTime() + (int) (Math.random() * 100); command.getClasses().setStudentAccessCode(ApplicationConstants.STUDENT_SECTION_CODE + std); } command.setUctx(TokenConversion.getUserContextJson(token)); commandGateway.sendAndWait(command); command.setUctx(null); log.info("saveClasses {} Service Complete successfully..."); return new ResponseEntity<>(new CreatedResponse(id, ClassesConstants.getSuccessMessage (ClassesConstants.CLASSES, ClassesConstants.CREATE), command, true), HttpStatus.CREATED); } catch (Exception e) { logger.info(e.getMessage()); var safeErrorMessage = ClassesConstants.CREATE_ERROR_MSG; return new ResponseEntity<>(safeErrorMessage, HttpStatus.INTERNAL_SERVER_ERROR); } }
3. 确保@Column(updatable=false)生效
- 检查实体类字段与数据库字段的映射是否一致,避免拼写错误
- 确认没有使用原生SQL或自定义更新语句绕过JPA的字段更新限制
问题根源分析
之前设置的updatable=false本身可以阻止JPA生成包含目标字段的UPDATE语句,但由于Controller每次请求(包括更新)都会生成新的随机值,且EventHandler不分场景赋值,导致内存中的实体值被覆盖。通过上述修改,既能保证数据库字段不被更新,也能避免内存实体值被不必要修改。
内容的提问来源于stack exchange,提问作者Mahrukh Saeed
相关产品推荐
相关产品推荐

