You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 06:45:30