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

如何在Hibernate类层级中实现多表独立版本属性管控

解决方案

方法一:利用Hibernate注解实现表级乐观锁控制

针对JOINED类型的表级继承(每个实体对应独立数据表),通过@DynamicUpdate和@OptimisticLocking注解组合,让Hibernate仅更新脏属性所在表的version字段:

  1. 实体类配置如下:
@Entity
@Table(name = "human")
@Inheritance(strategy = InheritanceType.JOINED)
@DynamicUpdate
@OptimisticLocking(type = OptimisticLockType.DIRTY)
class Human {
    @Id
    private Integer id;
    private String name;
    @Version
    @Column(name = "version")
    private int humanVersion;

    // getter、setter方法省略
}

@Entity
@Table(name = "developer")
@DynamicUpdate
@OptimisticLocking(type = OptimisticLockType.DIRTY)
class Developer extends Human {
    private String title;
    @Version
    @Column(name = "version")
    private int developerVersion;

    // getter、setter方法省略
}
  • @DynamicUpdate:让Hibernate只生成包含脏属性的UPDATE语句,不会更新未修改的字段
  • @OptimisticLocking(type = OptimisticLockType.DIRTY):仅针对脏属性对应的实体表进行乐观锁检查和version更新

当修改Developer的title时,Hibernate只会执行developer表的更新语句并递增其version,不会触及human表的version字段。

方法二:自定义PreUpdate事件监听器手动控制version更新

如果注解方式无法满足需求,可以通过自定义Hibernate事件监听器,在更新前判断脏属性所属层级,手动更新对应表的version:

1. 调整实体类(移除@Version注解)

@Entity
@Table(name = "human")
@Inheritance(strategy = InheritanceType.JOINED)
class Human {
    @Id
    private Integer id;
    private String name;
    @Column(name = "version")
    private int humanVersion;

    // getter、setter方法省略
}

@Entity
@Table(name = "developer")
class Developer extends Human {
    private String title;
    @Column(name = "version")
    private int developerVersion;

    // getter、setter方法省略
}

2. 实现PreUpdateEventListener

public class TableLevelVersionListener implements PreUpdateEventListener {
    @Override
    public boolean onPreUpdate(PreUpdateEvent event) {
        Object entity = event.getEntity();
        SessionImplementor session = event.getSession();
        String[] dirtyProperties = event.getDirtyProperties();
        EntityPersister persister = event.getPersister();

        if (entity instanceof Developer) {
            Developer developer = (Developer) entity;
            // 判断脏属性是否属于Developer子类(而非父类Human)
            boolean isSubclassDirty = false;
            for (String prop : dirtyProperties) {
                int propIndex = persister.getPropertyIndex(prop);
                // 子类属性索引大于等于父类属性数量
                if (propIndex >= persister.getSubclassPropertySpan()) {
                    isSubclassDirty = true;
                    break;
                }
            }
            if (isSubclassDirty) {
                developer.setDeveloperVersion(developer.getDeveloperVersion() + 1);
                session.getPersistenceContext().getEntry(entity).addDirtyProperty("developerVersion");
            } else {
                // 仅父类属性修改,更新Human的version
                Human human = (Human) entity;
                human.setHumanVersion(human.getHumanVersion() + 1);
                session.getPersistenceContext().getEntry(entity).addDirtyProperty("humanVersion");
            }
        } else if (entity instanceof Human) {
            Human human = (Human) entity;
            human.setHumanVersion(human.getHumanVersion() + 1);
            session.getPersistenceContext().getEntry(entity).addDirtyProperty("humanVersion");
        }
        return false; // 返回false允许Hibernate继续执行后续流程
    }
}

3. 注册监听器

  • 在persistence.xml中配置:
<property name="hibernate.ejb.event.pre-update" value="com.yourpackage.TableLevelVersionListener"/>
  • 若使用Spring Boot,可通过配置类注册:
@Configuration
public class HibernateConfig {
    @Bean
    public HibernatePropertiesCustomizer hibernatePropertiesCustomizer() {
        return properties -> properties.put(
            "hibernate.event.pre_update",
            "com.yourpackage.TableLevelVersionListener"
        );
    }
}

这种方式可以更精细地控制version字段的更新逻辑,完全匹配业务中"仅修改对应层级数据时更新该表版本"的要求。


内容的提问来源于stack exchange,提问作者Brandon Flowers

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 02:12:32