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

如何通过JPA/Hibernate/Spring Boot独立更新嵌套实体的更新字段

JPA/Hibernate实体独立更新审计字段问题

我有一对一对一关联的嵌套实体EntityA和EntityB,需求是仅当实体自身字段发生变更时,独立更新该实体的username和lastUpdateDatetime,关联实体的对应字段不更新:

  • 若EntityB自身字段变更,仅更新EntityB的username和lastUpdateDatetime
  • 若EntityA自身字段变更,仅更新EntityA的username和lastUpdateDatetime

实体代码

EntityA

@Entity(name = "EntityA")
public class EntityA {
    
    @Column(name = "EntityAColumn1")
    private String entityAColumn1;

    @Column(name = "EntityAColumn2")
    private String entityAColumn2;     

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "EntityA_EntityB")
    private EntityB entityB;

    @Column(name = "username", length = 100)
    private String username;

    @Column(name = "last_update_datetime")
    private ZonedDateTime lastUpdateDatetime;
}

EntityB

@Entity(name = "EntityB")
public class EntityB {
    
    @Column(name = "EntityBColumn1")
    private String entityBColumn1;

    @Column(name = "EntityBColumn2")
    private String entityBColumn2;     

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "EntityB", fetch = FetchType.LAZY)
    private EntityA entityA;

    @Column(name = "username", length = 100)
    private String username;

    @Column(name = "last_update_datetime")
    private ZonedDateTime lastUpdateDatetime;
}

控制器代码

@RestController
public class EntityAController {

    private final EntityARepository entityARepository;

    @PutMapping("/update")
    public void updateEntityA(
        @RequestBody ObjectA objectA,
        @RequestHeader("username") String username
    ) {
        EntityA entityA = EntityMapper.mapToEntity(objectA);

        entityARepository.save(entityA);
    }
}

注:EntityMapper.mapToEntity(objectA)仅用于将Web请求映射为实体


仓库代码

@Repository("EntityARepository")
public interface ShippingOrderRepository extends CrudRepository<EntityA, Long> {
    
}

数据示例

更新前数据库数据(JSON格式便于阅读)

{
    "entityAColumn1": "val1",
    "entityAColumn2": "val2",
    "entityB": {
        "entityBColumn1": "val3",
        "entityBColumn2": "val4",
        "username": "username1",
        "lastUpdateDatetime": "2023-09-20T07:09:30Z"
    },
    "username": "username1",
    "lastUpdateDatetime": "2023-09-20T07:09:30Z"
}

更新请求内容

{
    "entityAColumn1": "val1",
    "entityAColumn2": "val2",
    "entityB": {
        "entityBColumn1": "val3",
        "entityBColumn2": "val5", // 值已变更
        "username": "username1",
        "lastUpdateDatetime": "2023-09-20T07:09:30Z"
    },
    "username": "username1",
    "lastUpdateDatetime": "2023-09-20T07:09:30Z"
}
  • 仅EntityB的entityBColumn2字段发生变更
  • 请求头中的username为"username2"

期望更新结果

{
    "entityAColumn1": "val1",
    "entityAColumn2": "val2",
    "entityB": {
        "entityBColumn1": "val3",
        "entityBColumn2": "val5", // 已更新
        "username": "username2", // 已更新
        "lastUpdateDatetime": "2023-10-13T07:09:30Z" // 已更新
    },
    "username": "username1",
    "lastUpdateDatetime": "2023-09-20T07:09:30Z"
}

仅EntityB的username和lastUpdateDatetime被更新,因其是唯一发生变更的实体


已尝试的无效方案

  1. 使用@PreUpdate注解方法:即使仅一个实体变更,两个实体的审计字段都会被更新
@PreUpdate
public void preUpdate() {
    this.username = UsernameUtil.getUserName();
    this.lastUpdateDatetime = ZonedDateTime.now();
}
  1. 使用@UpdateTimestamp注解更新lastUpdateDatetime:同样会导致两个实体的对应字段都被更新
@UpdateTimestamp
@Column(name = "last_update_datetime")
private ZonedDateTime lastUpdateDatetime;

注:已实现从请求头获取并存储username的机制,此处省略相关代码


核心问题

是否存在JPA/Hibernate原生机制,能够实现仅当实体自身字段变更时独立更新其username和lastUpdateDatetime,无需编写大量字段对比方法(实体字段较多时该方式不切实际)?


内容的提问来源于stack exchange,提问作者Vanblerk Havenga

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 20:23:08