添加@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
相关产品推荐
相关产品推荐

