Spring data issue - org.hibernate.HibernateException: identifier of an instance of {Entity} was altered from 1 to 2

1/21/2019

I have this exception while changing parent for entity (@OneToMany relationship).

Entity parent update - org.hibernate.HibernateException: identifier of an instance of {Entity} was altered from 1 to 2

This exception occurs and can be reproduce only for service running in Kubernetes after some time. I mean that it isn't reproduced from the very begging of container life and some number of updates completed successfully.

The method that does the update on the entities looks like this:

@Transactional
    @Override
    public Optional<EntityT> update(EntityT entity) {

        entity.setIsConfirmed(true);

        return getRepository().findById(entity.getId())
                .map(entityToUpdate -> updateEntity(entity, entityToUpdate));
    }


private EntityT updateEntity(EntityT entity, EntityT entityToUpdate) {
        modelMapper.map(entity, entityToUpdate);

        getParentRepository().ifPresent(parentRepository ->
                entity.getParent().ifPresent(parentEntity ->
                        parentRepository.findById(parentEntity.getId()).ifPresent(entityToUpdate::setParent))
        );

        entityToUpdate.setVersionTs(getCurrentTime());
        return getRepository().save(entityToUpdate);
    }

Spring boot version - 2.1.2 Hibernate 5.3.7 also try 5.4.1 - the same result.

Also set spring jpa properties to

spring:
  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    generate-ddl: true
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        jdbc:
          batch_size: 100
        flushMode: "ALWAYS"
        order_inserts: true
        order_updates: true

Also tried different images for container open-jdk8 / oracle-jdk8

Could anybody advice some solution?

Thanks in advice.

-- Vlad
docker
hibernate
kubernetes
spring-data-jpa

2 Answers

1/21/2019

First of all, the updateEntity uses the redundant save-anti-pattern:

private EntityT updateEntity(EntityT entity, EntityT entityToUpdate) {
    modelMapper.map(entity, entityToUpdate);

    getParentRepository().ifPresent(parentRepository ->
            entity.getParent().ifPresent(parentEntity ->
                    parentRepository.findById(parentEntity.getId()).ifPresent(entityToUpdate::setParent))
    );

    entityToUpdate.setVersionTs(getCurrentTime());
    return getRepository().save(entityToUpdate);
}

Second, this part here is not needed:

getParentRepository().ifPresent(parentRepository ->
            entity.getParent().ifPresent(parentEntity ->
                    parentRepository.findById(parentEntity.getId()).ifPresent(entityToUpdate::setParent))
    );

The fact that you use both entity and entityToUpdate is a code smell which is probably related to using the lambdas that are also not needed.

So, you can probably reduce all your code to this:

@Transactional
@Override
public Optional<EntityT> update(EntityT entity) {

    entity.setIsConfirmed(true);
    entity.setVersionTs(getCurrentTime());
    return getRepository().save(entity);
}
-- Vlad Mihalcea
Source: StackOverflow

1/23/2019

The main issue was in mapper. Instead of parent replacing it change only id for fetched parent. Then we replace parent but fetched parent remains in cache (with new id) and Hibernate after some time try to flush this changes into DB.

-- Vlad
Source: StackOverflow