Table of Contents

    Book an Appointment

    How Did We Encounter the Hibernate Custom ID Generator Challenge?

    While working on a large-scale logistics SaaS platform, we were tasked with modernizing the core backend infrastructure. The goal was to upgrade the system to Java 21 and Spring Boot 3.5.x, leveraging the latest performance enhancements and security patches. However, this upgrade introduced Hibernate 6 as a dependency, which brought massive internal API and SPI changes. During our integration testing phase, we noticed a critical failure in our data synchronization pipeline.

    The platform aggregates data from offline edge devices (mobile scanners used in warehouses) and an online administrative web portal. When offline devices sync their data to the cloud, they send records with pre-assigned unique identifiers. Conversely, when users create new records directly in the web portal, the system must rely on the database to generate an identifier. This dynamic required a reliable hibernate custom id generator to inspect incoming entities.

    In production, failing to handle this correctly means either overwriting existing offline records or crashing the system with duplicate key exceptions. We quickly realized our legacy identifier logic was completely broken in Hibernate 6. This challenge inspired this article so other engineering teams can avoid the same modernization pitfalls when managing a hibernate auto increment id strategy.

    Why Is a Hybrid Hibernate Auto Increment ID Vital for This Architecture?

    In a standard web application, entity ID generation is typically delegated entirely to the database or a sequence generator. You apply a basic annotation and the framework handles the rest. But in distributed systems where edge devices operate offline, clients often generate and assign their own identifiers before syncing with the central database.

    Our business use case dictated a strict set of rules for the persistence layer:

    • If the entity arrives at the API with an ID already set, the database must respect and use that specific ID.
    • If the entity arrives with a null ID, the persistence layer must delegate the generation to the databases native AUTO_INCREMENT (or IDENTITY) feature.

    In older versions of Hibernate, this was managed by extending the IdentityGenerator class. The application would check if the identifier existed; if it did, it returned the value. If not, it triggered the default database behavior. However, the migration to Hibernate 6 fundamentally altered how identifier generators are implemented and registered, rendering our previous implementation obsolete.

    What Caused the Hibernate Custom ID Generator to Fail?

    The symptoms appeared immediately upon booting the upgraded Spring Boot application. The application context failed to initialize, throwing mapping exceptions related to unknown identifier generators. Once we temporarily resolved the boot failures by tweaking annotations, we hit runtime exceptions during database inserts.

    The logs revealed two distinct failure modes:

    • Online records (null IDs) were throwing exceptions because Hibernate was attempting to insert null values into the primary key column instead of triggering the database AUTO_INCREMENT.
    • Offline records (pre-assigned IDs) were being rejected because Hibernate treated them as detached entities rather than new entities, triggering update statements instead of insert statements.

    The root cause was the deprecation and removal of several internal Hibernate SPIs. Our legacy code relied on Session implementors and ClassMetadata APIs that simply do not exist or function the same way in Hibernate 6. Furthermore, the internal mechanics of how Hibernate signals the database to use an identity column had shifted, meaning our old hibernate auto increment id fallback was returning incorrect markers during the persistence lifecycle.

    How Did We Approach Building a Resilient Hibernate Custom ID Generator?

    When you hire software developer teams to handle enterprise modernization, you expect them to look beyond quick patches and analyze the long-term architectural impact. We gathered our backend engineers to map out potential solutions that would align with Hibernate 6 best practices while protecting our existing database schema.

    Did We Consider Alternative Strategies for Hibernate Auto Increment ID?

    Before writing a custom implementation from scratch, we evaluated several alternative approaches to see if out-of-the-box features could solve the problem:

    • Using SequenceStyleGenerator: We initially considered moving away from IDENTITY columns and using a centralized Hibernate sequence. However, our legacy tables had drastically different max ID values (e.g., the accounts table max ID was 300, the inventory table was 50,000 and the shipping records table was 120,000). A shared sequence would force all new inserts to start at 120,001, ruining the natural increments of the smaller tables.
    • Standard IDENTITY Annotations with Lifecycle Hooks: We attempted to use the standard GenerationType.IDENTITY and manage the pre-assigned IDs using @PrePersist hooks. This failed because Hibernate natively ignores manually set IDs when the strategy is strictly set to IDENTITY.
    • Separate Entities for Read and Write: We considered using the Command Query Responsibility Segregation (CQRS) pattern to separate our DTOs and entities, handling the ID logic at the service layer. We abandoned this because it required rewriting thousands of lines of legacy business logic.

    Ultimately, we concluded that writing a new hibernate custom id generator tailored for Hibernate 6 was the only way to preserve our database schema and avoid a massive codebase rewrite.

    How Did We Implement the Final Hibernate Custom ID Generator Solution?

    To solve this in Hibernate 6, we needed an implementation of IdentifierGenerator that correctly interfaces with the new SharedSessionContractImplementor. The critical piece of knowledge was understanding how Hibernate 6 signals an IDENTITY insert: it uses a specific constant called POST_INSERT_INDICATOR.

    Here is the sanitized, generalized version of the implementation we deployed:

    import org.hibernate.HibernateException;
    import org.hibernate.engine.spi.SharedSessionContractImplementor;
    import org.hibernate.id.IdentifierGenerator;
    import org.hibernate.id.IdentifierGeneratorHelper;
    import java.io.Serializable;
    public class HybridIdentityGenerator implements IdentifierGenerator {
        
        @Override
        public Object generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
            Serializable id = (Serializable) session.getEntityPersister(null, object)
                                                    .getIdentifier(object, session);
            
            if (id != null) {
                // Use the pre-assigned ID
                return id;
            }
            
            // Delegate to the database native AUTO_INCREMENT / IDENTITY
            return IdentifierGeneratorHelper.POST_INSERT_INDICATOR;
        }
    }
    

    To use this in our entities, we mapped the generator as follows:

    import jakarta.persistence.Entity;
    import jakarta.persistence.Id;
    import jakarta.persistence.GeneratedValue;
    import org.hibernate.annotations.GenericGenerator;
    @Entity
    public class LogisticsRecord {
        
        @Id
        @GenericGenerator(
            name = "hybrid_id_generator",
            strategy = "com.example.infrastructure.HybridIdentityGenerator"
        )
        @GeneratedValue(generator = "hybrid_id_generator")
        private Long id;
        // Additional fields, getters and setters
    }
    

    Validation Steps: We ran extensive integration tests using Testcontainers. We verified that records syncing from edge devices preserved their assigned IDs and records created via the API successfully triggered the MySQL AUTO_INCREMENT capability. By returning POST_INSERT_INDICATOR, Hibernate defers the ID resolution to the actual SQL INSERT statement, ensuring thread safety and data integrity.

    What Can Engineering Teams Learn About Handling Hibernate Auto Increment ID?

    Complex framework upgrades often reveal hidden technical debt. Here are the actionable insights we extracted from this experience:

    • Avoid Shared Sequences on Legacy Schemas: Never force a SequenceStyleGenerator onto existing tables with vastly different AUTO_INCREMENT values unless you are prepared to manage multiple independent sequence tables in your database.
    • Understand SPI vs. API: Framework APIs are generally stable, but Service Provider Interfaces (SPIs) like IdentifierGenerator are subject to massive rewrites between major versions. Always isolate SPI implementations in your codebase.
    • Leverage the Post-Insert Indicator: When building a hibernate custom id generator that relies on native database capabilities, remember that Hibernate needs a specific return type to know it should defer generation.
    • Write Comprehensive Data Creation Tests: Upgrades should not be certified without integration tests that validate both explicit entity state and framework-generated entity state.
    • Plan for Persistence Changes: If your enterprise is heavily reliant on older ORM behaviors, you should hire java developers for backend modernization who understand the underlying mechanics of database dialects.

    How Can You Ensure a Smooth Migration with a Hibernate Custom ID Generator?

    Upgrading an enterprise application to Java 21 and Spring Boot 3 is a strategic move that delivers immense value in execution speed and cloud efficiency. However, as we discovered with our logistics platform, the devil is in the details of the persistence layer. By carefully rewriting our hibernate custom id generator to return the proper indicators, we maintained backwards compatibility with offline data syncs while properly utilizing the native hibernate auto increment id feature for new records.

    This level of diagnostic thinking is exactly what technical leaders demand when they hire spring boot developers for scalable systems. If your organization is planning a major architectural modernization, our teams are ready to help you navigate the complexities of legacy data migration and framework upgrades. contact us to explore how we can support your next enterprise initiative.

    Social Hashtags

    #Hibernate6 #Hibernate #SpringBoot #SpringBoot3 #Java21 #JavaDevelopment #JavaDeveloper #BackendDevelopment #SoftwareEngineering #EnterpriseSoftware #DatabaseDevelopment #ORM #Microservices #SaaSDevelopment #LegacyModernization

     

    Frequently Asked Questions

    Success Stories That Inspire

    See how our team takes complex business challenges and turns them into powerful, scalable digital solutions. From custom software and web applications to automation, integrations, and cloud-ready systems, each project reflects our commitment to innovation, performance, and long-term value.