首页 > 解决方案 > Spring Data JPA:org.hibernate.StaleStateException:批量更新从更新 [0] 返回了意外的行数;实际行数:0;预期:1

问题描述

每当我尝试更新我的实体时,我的日志中经常会出现此错误。有趣的是,当我检查我的数据库时,我看到实体已成功更新,其时间戳与日志的时间戳相同。当我尝试在本地点击相同的更新请求时,我也无法重现该问题。

实体如下:

MySqlAbstractEntity 的晚餐类

@MappedSuperclass
public abstract class MysqlAbstractEntity<ID extends Serializable> implements AbstractEntity<ID> {
    private static final long serialVersionUID = 8920812256547697337L;


    @Id
    @GeneratedValue(
        generator = "myGenerator",
        strategy = GenerationType.SEQUENCE
    )
    @GenericGenerator(
        name = "myGenerator",
        strategy = "com.****.MysqlLongIDGenerator"
    )
    @Column(
        name = "id",
        updatable = false,
        insertable = false
    )
    protected Long id;
    @Temporal(TemporalType.TIMESTAMP)
    @Column(
        name = "created_on",
        nullable = false,
        updatable = false
    )
    protected Date createdOn;
    @Temporal(TemporalType.TIMESTAMP)
    @Column(
        name = "last_modified_on",
        nullable = false
    )
    protected Date lastModifiedOn;
    @Version
    @Column(
        name = "version"
    )
    protected Long version = Long.valueOf(0L);

    public MysqlAbstractEntity() {
    }

    @PrePersist
    protected void onCreate() {
        this.lastModifiedOn = this.createdOn = this.createdOn == null?new Date():this.createdOn;
    }

    @PreUpdate
    protected void onUpdate() {
        this.lastModifiedOn = new Date();
    }

我的自定义 id 生成器如下所示:

public class MysqlLongIDGenerator extends IdentityGenerator {
    public MysqlLongIDGenerator() {
    }

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) {
        try {
            LongIDAbstractEntity entity = (LongIDAbstractEntity)object;
            if(entity.getId() != null && entity.getId().longValue() > 0L) {
                return entity.getId();
            }
        } catch (Exception var4) {
            ;
        }

        return super.generate(session, object);
    }
}

我的实际实体是

public class MyEntity extends MysqlAbstractEntity {
    @OneToOne(optional = false, fetch = FetchType.LAZY)
    @JoinColumn(name = "other_entity_id", nullable = false)
    private SomeOtherEntity user;

    @Column(name = "active")
    private Boolean active;

    @Column(name = "device_type")
    @Enumerated(EnumType.STRING)
    private DeviceType deviceType;

    @Column(name = "app_version")
    private String appVersion;

    @Column(name = "device_id")
    private String deviceId;

    @Column(name = "device_version")
    private String deviceVersion;

    @Column(name = "client_id")
    private String clientId;
}

我的存储库是

@Transactional(rollbackFor = Exception.class)
public class MyEntityRepository extends CrudRepository<MyEntity, Long> {
}

我的更新代码是这样的:

public MyEntity update(@NotNull MyEntity updatedMyEntity) {
        MyEntity entity = myEntityRepository.findByDeviceId(updatedMyEntity.getDeviceId());
        if (entity != null) {
            return myEntityRepository.save(updateEntity(entity, updatedMyEntity));
        } else {
            return null;
        }
}

updateEntity 方法实际上是更新字段值

这是我在日志中遇到的异常

ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1, stackTrace org.springframework.orm.ObjectOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:320)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:518)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy118.save(Unknown Source)

标签: javahibernatespring-data

解决方案


推荐阅读