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

添加@Version解决StaleObjectStateException后应用启动失败求助

问题背景

执行2次以上更新操作时触发StaleObjectStateException异常:行已被其他事务更新或删除(或未保存值映射不正确)。

手动设置DTO时,基础更新功能正常,但使用Mapper或BeanUtils.copyProperties会触发上述异常:

// Mapper
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public abstract void fromApi(CustomerDetails dto, @MappingTarget Customer entity );
// 或 BeanUtils
BeanUtils.copyProperties(dto, entity );

为解决该问题,我在MappedSuperClass中添加了@Version字段,却导致应用启动失败。以下是相关代码及启动异常信息,请帮忙指导如何正确调整:


更新逻辑代码

// 更新代码
public CustomerDetails updateCustomer(Long cid, CustomerDetails customerDetails) {
    
    Customer customer = custMenuRepository.findById(menuId).orElseThrow(
            () -> new ResourceNotFoundException("Not found"));
    custMapper.fromApi(customerDetails,customer);       
    
    customer = custRepository.saveAndFlush(customer);
    
    return custMapper.entityToApi(customer);
}

基础实体类

@MappedSuperclass
@EntityListeners({AuditingEntityListener.class, AuditTrailListener.class})
public class BaseEntity {
    @Column(name = "created_by", updatable = false)
    @CreatedBy
    private String createdBy;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_date", updatable = false)
    @CreatedDate
    private Date createdDate;

    @Column(name = "modified_by")
    @LastModifiedBy
    private String modifiedBy;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "modified_date")
    @LastModifiedDate
    private Date modifiedDate;

    @Column(name = "txn_id")
    private String transactionId;

    @Column(name = "ver_id")
    @Version
    private Integer version;
}

实际实体类

@Entity
@Table(name = "CUSTOMER")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Customer extends AbstractPersistableEntity implements Serializable {
    @Id
    @Column(name = "CUST_ID", updatable = false)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_CUST")
    @SequenceGenerator(name="SEQ_CUST", sequenceName="SEQ_CUST_ID")
    private Long custId;

    @Version
    @Column(name = "REVISION")
    private Integer revision;

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

    @Column(length = 300,name = "CUST_DESC")
    private String description;

    @Column(length = 255,name = "CUST_PHOTO")
    private String thumbnail;
}

数据库配置

public class CustomerDBConfig extends PersistenceConfig {
    @Override
    @Bean(name = "Customer_ds")
    @ConfigurationProperties(prefix="Customer.datasource")
    public DataSource dataSource() {
        DataSource ds = null;
        if (null != isJndiEnabled) {
            JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
            ds = dataSourceLookup.getDataSource(Preconditions.checkNotNull(CustomerJNDI));
        } else {
            ds = DataSourceBuilder.create().build();
        }
        return ds;
    }

    @Override
    @Bean(name = "Customer_em")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
            @Qualifier("Customer_ds") DataSource dataSource) {
        return builder.dataSource(dataSource).packages("com.testservicecustomer.persistence")
                .persistenceUnit("Customer_punit").build();
    }

    @Override
    @Bean(name = "Customer_txnMgr")
    public PlatformTransactionManager transactionManager(
            @Qualifier("Customer_em") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

启动异常信息

Caused by: org.springframework.beans.factory.BeanCreationException:
创建名为 'jpaMappingContext' 的 bean 失败:调用初始化方法出错;嵌套异常是
org.springframework.beans.factory.BeanCreationException: 创建名为 'customer_em' 的 bean 失败(定义在类路径资源 [com/testservicecustomer/config/customerDBConfig.class] 中):调用初始化方法出错;嵌套异常是
javax.persistence.PersistenceException: [持久化单元: customer_punit] 无法构建Hibernate SessionFactory;嵌套异常是 java.lang.IllegalArgumentException: 给定属性与声明的版本属性不匹配
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
        ... 104 个通用栈帧省略
Caused by: org.springframework.beans.factory.BeanCreationException: 创建名为 'customer_em' 的 bean 失败(定义在类路径资源 [com/testservicecustomer/config/customerDBConfig.class] 中):调用初始化方法出错;嵌套异常是
javax.persistence.PersistenceException: [持久化单元: customer_punit] 无法构建Hibernate SessionFactory;嵌套异常是 java.lang.IllegalArgumentException: 给定属性与声明的版本属性不匹配
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:671)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:659)
        at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1300)
        at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:329)
        at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.getMetamodels(JpaMetamodelMappingContextFactoryBean.java:102)
        at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:80)
        at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:44)
        at org.springframework.beans.factory.config.AbstractFactoryBean.afterPropertiesSet(AbstractFactoryBean.java:142)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
        ... 111 个通用栈帧省略
Caused by: javax.persistence.PersistenceException: [持久化单元: customer_punit] 无法构建Hibernate SessionFactory;嵌套异常是 java.lang.IllegalArgumentException: 给定属性与声明的版本属性不匹配
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:421)
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396)
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
        ... 127 个通用栈帧省略
Caused by: java.lang.IllegalArgumentException: 给定属性与声明的版本属性不匹配
        at org.hibernate.metamodel.internal.AttributeFactory$6.resolveMember(AttributeFactory.java:1094)
        at org.hibernate.metamodel.internal.AttributeFactory.determineAttributeMetadata(AttributeFactory.java:486)
        at org.hibernate.metamodel.internal.AttributeFactory.buildVersionAttribute(AttributeFactory.java:205)
        at org.hibernate.metamodel.internal.MetadataContext.applyVersionAttribute(MetadataContext.java:375)
        at org.hibernate.metamodel.internal.MetadataContext.wrapUp(MetadataContext.java:265)
        at org.hibernate.metamodel.internal.MetamodelImpl.initialize(MetamodelImpl.java:274)
        at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:303)
        at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:468)
        at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1259)
        at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409)
        ... 131 个通用栈帧省略

问题分析与解决步骤

1. 修复启动失败:移除重复的@Version字段

Hibernate不允许一个实体类存在多个版本属性,你的BaseEntity和Customer类同时定义了@Version字段,这是启动异常的直接原因。二选一调整:

  • 若数据库表CUSTOMER已有ver_id列:删除Customer类中的@Version注解和revision字段,保留BaseEntity中的version字段。
  • 若数据库表CUSTOMER已有REVISION列:删除BaseEntity中的@Version注解和version字段,仅保留Customer类中的revision字段及@Version注解。

2. 解决原始的StaleObjectStateException异常

异常核心原因是:映射工具将DTO中的null版本值覆盖了实体从数据库加载的有效版本号,导致Hibernate误判数据被修改。解决方法:

  • Mapper方式:显式忽略版本字段映射
    @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
    @Mapping(target = "version", ignore = true) // 若使用Customer的revision则改为target = "revision"
    public abstract void fromApi(CustomerDetails dto, @MappingTarget Customer entity );
    
  • BeanUtils方式:指定忽略版本字段
    BeanUtils.copyProperties(dto, entity, "version"); // 对应实际字段名调整为"revision"
    
  • 额外建议:DTO类中尽量不包含版本字段,避免不必要的映射风险。

3. 辅助检查

  • 确保updateCustomer方法处于Spring事务管理下(添加@Transactional注解)。
  • 确认通过findById获取的实体为持久化状态(当前代码逻辑符合要求)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 12:03:23