1

When my Java application was running on Spring Boot 2.6.2, the entities for which Export/Import features applied were using the following custom id generator:

import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentityGenerator;

import java.io.Serializable;

public class CustomIdGenerator extends IdentityGenerator {

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object entity) {
        Serializable id = session.getEntityPersister(null, entity)
                .getClassMetadata().getIdentifier(entity, session);
        return id != null ? id : super.generate(session, entity);
    }
}

so that, when an Id was provided in the CSV file, the import was producing the very same entity in the database, with the very same Id.

In particular, I was able to save and restore the whole table by doing these actions in the following order:

  1. Export (to save as CSV)
  2. Import an empty CSV file (clears the table)
  3. Import the saved CSV file Result: after these 3 operations, the database content was exactly the same as before.

Now that I have migrated my application to Spring Boot 3.3.1, this doesn't work anymore. I was forced to dispose of CustomIdGenerator because this implementation is no more compatible with Spring Boot 3.3.1 (because of its Hibernate dependency). I tried to obtain a similar CustomIdGenerator the new way, but in vain after several attempts and a couple of evenings trying to achieve that...

To summarize: I need the Ids of my entity to be auto-generated (like with GenerationType.IDENTITY) when they are provided without Id, but I need the Ids to be strictly identical to those provided in the CSV file in case of an import action.

Any known way to do that with success?

--

For example, I tried to implement a CustomIdGenerator this way (Spring Boot 3.3.1):

public class NewCustomIdGenerator implements IdentifierGenerator {

@Override
public Object generate (SharedSessionContractImplementor session, Object entity) {
    var id = session.getEntityPersister(entity.getClass().toString(), entity).getIdentifier(entity, session);
    if (id != null) {
        return id;
    }

    //var res = super.generate(session, entity); // doesn't work
    var res = 0L; // doesn't work (here I'd like the auto-generation to take over)
    return res;
}

I also tried to extend IncrementGenerator or TableGenerator... in vain.

Any idea that works?

4
  • Instead of using EntityManager.persist you should try EntityManager.merge. This should in my understanding preserve the id of the entity that is being merged exactly as you demand. Commented Jul 8 at 5:10
  • I'm not using EntityManager.persit or EntityManager.merge, I'm using JpaRepository.saveAll - I'll check again if AbstractEntityInformation.isNew returns true or false when it should and if em.persist or em.merge is called. Then I'll come back to you. Commented Jul 8 at 9:17
  • I confirm that is it EntityManager.merge that's called. Right after this, I see, with Spring Boot 3.3.1, that the Ids of the new entities are not those provided by the CSV file, contrary to the behaviour I had with Spring Boot 2.6.2. Commented Jul 8 at 15:46
  • I can see that the ID that I set in the problematic entity before saveAll is changed before @PrePersist and @PostPersist with Spring Boot 3.3.1. It was not changed with Spring Boot 2.6.2. Commented Jul 8 at 21:14

2 Answers 2

0

I faced a similar issue after migrating to Spring Boot 3.x. Since Hibernate 6 has removed the old IdentityGenerator compatibility, custom ID generation became tricky.

Here's how I solved it without needing a custom IdentifierGenerator:

Entity Configuration

@Entity
public class MyEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // other fields...
}

Import Logic

if (csvHasId) {
    entity.setId(csvId); // Use provided ID from CSV
} else {
    entity.setId(null);  // Let Hibernate auto-generate ID
}
repository.save(entity);

This approach lets you reuse existing IDs from your CSV during import, and fallback to auto-generated IDs when no ID is supplied. Fully compatible with Spring Boot 3.3.1 + Hibernate 6.

Sign up to request clarification or add additional context in comments.

2 Comments

That's already what my application is doing, unfortunately.
This is what my application is doing. It was working fine with Spring Boot 2.6.2, but with Spring Boot 3.3.1, the case entity.setId(csvId) doesn't keep the provided csvId in case of a new entity.
0

I finally found this solution based upon hybrid ID generation:

import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.generator.EventType;
import org.hibernate.id.IdentityGenerator;

import static java.util.Objects.isNull;

public class CustomIdGenerator extends IdentityGenerator implements BeforeExecutionGenerator {

    @Override
    public Object generate(SharedSessionContractImplementor session, Object entity, Object currentValue, EventType eventType) {
        return getId(entity, session);
    }

    private static Object getId(Object entity, SharedSessionContractImplementor session) {
        return session.getEntityPersister(null, entity)
                .getIdentifier(entity, session);
    }

    /**
     * The generate() method above is called if no ID is provided!
     */
    @Override
    public boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) {
        Object id = getId(entity, session);
        return isNull(id);
    }

    @Override
    public boolean generatedOnExecution() {
        return true;
    }
}

The following post helped me a great deal:
https://discourse.hibernate.org/t/hybrid-id-generation-in-hibernate-6-2-is-difficult/7666/7

1 Comment

This solution seems to do the trick, but I need to test it thoroughly. In the meantime, feel free to post a better answer. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.