|

Sequence naming strategies in Hibernate 6


Take your skills to the next level!

The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.


Hibernate 6 introduced a new configuration parameter and an interface to define the implicit naming strategy for database sequences and tables used to generate primary key values. When you migrate an existing application to Hibernate 6, you quickly recognize that change because the default naming strategy has changed. Due to that, Hibernate might try using a sequence that doesn’t exist in your database. But that’s not the only situation in which you should be familiar with this new setting. It can also be helpful to map a legacy database with strangely named sequences or if you need to follow some internal naming conventions.

Since Hibernate 6, you can use the configuration property hibernate.id.db_structure_naming_strategy to define which naming strategy Hibernate shall use if you don’t explicitly define a sequence name in your mapping.

<persistence>
    <persistence-unit name="my-persistence-unit">
        ...
        <properties>
            <property name="hibernate.id.db_structure_naming_strategy" value="standard" />
			...
        </properties>
    </persistence-unit>
</persistence>

Naming strategies supported by Hibernate 6

Previous Hibernate versions provided 1 default behavior, and you had to specify the sequence name if you wanted to use a different one. With Hibernate 6, you can choose between 4 implicit naming strategies for database sequences:

  • standard
    This is the new default in Hibernate 6.
    It concatenates the configured sequence suffix, which is _SEQ by default, to the name of the table that’s mapped by the entity class.
  • legacy
    This naming strategy provides you the same behavior as the Hibernate versions >= 5.3 but <6 used by default. 
    The sequence name depends on your entity mapping definition:
    • Hibernate uses the generator name if you referenced a generator without defining a sequence name. This simplifies the mapping if you only want to define the sequence name and was an optimization introduced in Hibernate 5.3.
    • If your mapping doesn’t reference a generator, Hibernate uses its default sequence name hibernate_sequence.
  • single
    This naming strategy provides you the same behavior as Hibernate used in version <5.3 by default.
    It always uses the Hibernate’s default sequence name hibernate_sequence.
  • the fully qualified class name of an ImplicitDatabaseObjectNamingStrategy implementation
    This enables you to provide your own naming strategy. I will show you how to do that at the end of this article.

Let’s take a closer look at all 4 naming strategies.

ID_DB_STRUCTURE_NAMING_STRATEGY= standard

In contrast to previous Hibernate versions, Hibernate 6 uses a separate database sequence for each entity class by default. The name of that sequence consists of the name of the database table to which the entity class gets mapped and the postfix _SEQ.

Implicit table mapping

If you don’t specify the name of the database table, Hibernate uses its implicit naming strategy. The default strategy uses the simple class name of the entity class as the table name.

@Entity
public class ChessPlayer {

	@Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
	private Long id;

    private String firstName;
    
    private String lastName;

    private LocalDate birthDate;

    @Version
    private int version;
	
	...
}

So, the ChessPlayer entity class gets mapped to the ChessPlayer table. And if you’re using Hibernate 6’s standard naming strategy for database sequences, Hibernate uses the sequence ChessPlayer_SEQ to generate primary key values.

ChessPlayer player = new ChessPlayer();
player.setFirstName("Thorben");
player.setLastName("Janssen");
em.persist(player);
16:15:04,917 DEBUG [org.hibernate.SQL] - 
    select
        nextval('ChessPlayer_SEQ')
16:15:04,947 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        ChessPlayer
        (birthDate, firstName, lastName, version, id) 
    values
        (?, ?, ?, ?, ?)

Custom table mapping

You can customize the table mapping by annotating your entity class with a @Table annotation and setting the name of the database table.

@Entity
@Table(name="player")
public class ChessPlayer {

	@Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
	private Long id;

	...
}

Let’s use that mapping with the previous test case. You can see in the log output that Hibernate now calls the database sequence player_SEQ to generate primary key values. It also persists the ChessPlayer entity object to the player table.

16:17:04,094 DEBUG [org.hibernate.SQL] - 
    select
        nextval('player_SEQ')
16:17:04,120 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        player
        (birthDate, firstName, lastName, version, id) 
    values
        (?, ?, ?, ?, ?)

ID_DB_STRUCTURE_NAMING_STRATEGY = legacy

The legacy naming strategy gets you the same strategy as Hibernate used in versions >=5.3 and <6. You can activate it by setting the property hibernate.id.db_structure_naming_strategy in your persistence.xml configuration to legacy.

<persistence>
    <persistence-unit name="my-persistence-unit">
        ...
        <properties>
            <property name="hibernate.id.db_structure_naming_strategy" value="legacy" />
			...
        </properties>
    </persistence-unit>
</persistence>

The behavior of this naming strategy depends on your entity mappings.

Mappings without a generator reference

Hibernate uses 1 default sequence for all primary key attributes annotated with @GeneratedValue or @GeneratedValue(strategy = GenerationType.SEQUENCE). The important thing about these 2 mappings is that they don’t reference a generator.

@Entity
public class ChessPlayer {

	@Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
	private Long id;

    private String firstName;
    
    private String lastName;

    private LocalDate birthDate;

    @Version
    private int version;
	
	...
}

When you persist this ChessPlayer entity using the legacy naming strategy, Hibernate uses the database sequence hibernate_sequence to generate primary key values.

16:51:10,742 DEBUG [org.hibernate.SQL] - 
    select
        nextval('hibernate_sequence')
16:51:10,771 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        ChessPlayer
        (birthDate, firstName, lastName, version, id) 
    values
        (?, ?, ?, ?, ?)

Mappings with a generator reference but without a sequence name

If your primary key mapping references a generator that doesn’t exist or doesn’t define a sequenceName, Hibernate uses the generator’s name as the sequence name. This Hibernate-specific optimization was introduced in version 5.3 to simplify the most commonly used mapping definition, which only customizes the name of the database sequence.

@Entity
public class ChessPlayer {

	@Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="player_seq")
	private Long id;

    private String firstName;
    
    private String lastName;

    private LocalDate birthDate;

    @Version
    private int version;
	
	...
}

When you persist an object of this entity class, Hibernate uses the database sequence player_seq to generate primary key values.

16:51:50,304 DEBUG [org.hibernate.SQL] - 
    select
        nextval('player_seq')
16:51:50,343 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        ChessPlayer
        (birthDate, firstName, lastName, version, id) 
    values
        (?, ?, ?, ?, ?)

ID_DB_STRUCTURE_NAMING_STRATEGY = single

The naming strategy single is a simpler version of the legacy strategy and gets you the default naming of Hibernate in versions <5.3. You can activate it by setting the property hibernate.id.db_structure_naming_strategy in your persistence.xml configuration to single.

<persistence>
    <persistence-unit name="my-persistence-unit">
        ...
        <properties>
            <property name="hibernate.id.db_structure_naming_strategy" value="single" />
			...
        </properties>
    </persistence-unit>
</persistence>

This strategy always uses the database sequence hibernate_sequence if you don’t specify a sequence name in your mapping definition.

@Entity
public class ChessPlayer {

	@Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
	private Long id;

    private String firstName;
    
    private String lastName;

    private LocalDate birthDate;

    @Version
    private int version;
	
	...
}

You can see that in Hibernate’s log output, if you persist an object of this ChessPlayer class using the naming strategy single.

16:57:15,706 DEBUG [org.hibernate.SQL] - 
    select
        nextval('hibernate_sequence')
16:57:15,734 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        ChessPlayer
        (birthDate, firstName, lastName, version, id) 
    values
        (?, ?, ?, ?, ?)

ID_DB_STRUCTURE_NAMING_STRATEGY = custom class

I showed you Hibernate’s 3 standard naming strategies for database sequences in the previous sections. You can use the same mechanism to provide your own naming strategy. You only need to provide a custom implementation of the ImplicitDatabaseObjectNamingStrategy interface and configure it in your persistence.xml.

The implementation of the ImplicitDatabaseObjectNamingStrategy interface doesn’t have to be complex. The interface only defines 2 methods, which both return a QualifiedName object.

public class MyImplicitDatabaseObjectNamingStrategy implements ImplicitDatabaseObjectNamingStrategy {
	public static final String STRATEGY_NAME = "custom";

    @Override
    public QualifiedName determineSequenceName(Identifier catalogName, Identifier schemaName, Map<?, ?> configValues,
            ServiceRegistry serviceRegistry) {
        final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService(JdbcEnvironment.class);

        String seqName = "seq_".concat(((String) configValues.get("jpa_entity_name")));

        return new QualifiedSequenceName(
                catalogName,
                schemaName,
                jdbcEnvironment.getIdentifierHelper().toIdentifier(seqName));
    }

    @Override
    public QualifiedName determineTableName(Identifier catalogName, Identifier schemaName, Map<?, ?> configValues,
            ServiceRegistry serviceRegistry) {
        final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService(JdbcEnvironment.class);

        return new QualifiedNameParser.NameParts(
                catalogName,
                schemaName,
                jdbcEnvironment.getIdentifierHelper().toIdentifier(DEF_TABLE));
    }

}

The determineSequenceName method returns the name of the database sequence Hibernate shall use. The determineTableName method returns the name of the database table that Hibernate shall use to simulate a sequence.

I don’t get into any details about implementing the determineTableName method in this article. You could customize it in the same way as the name resolution for database sequences. But the simulation of a sequence causes lots of scalability issues, and all modern databases support sequences or autoincremented columns. This mechanism is, therefore, no longer practically relevant. Please stick to the default implementation that returns Hibernate’s default table name and use a sequence or autoincremented column to generate your primary key values.

The implementation of the determineSequenceName methods depends entirely on your table model and application requirements. The Map<?, ?> configValues method parameter contains several mapping information about the entity class and database table that you can use to generate your sequence name. In this example, I implemented a simple naming strategy that uses seq_ as the prefix for all sequence names and concatenates it with the logical name of my entity class.

The logical name of the entity class is either the simple class name of your entity class or the name you defined in your @Entity annotation.

@Entity(name="Player")
public class ChessPlayer {

	@Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
	private Long id;

    private String firstName;
    
    private String lastName;

    private LocalDate birthDate;

    @Version
    private int version;
	
	...
}

After implementing the ImplicitDatabaseObjectNamingStrategy interface, you need to reference it in your configuration. You do that by setting the configuration property hibernate.id.db_structure_naming_strategy to your interface implementation’s fully qualified class name.

<persistence>
    <persistence-unit name="my-persistence-unit">
        ...
        <properties>
            <property name="hibernate.id.db_structure_naming_strategy" value="com.thorben.janssen.sample.model.MyImplicitDatabaseObjectNamingStrategy" />
			...
        </properties>
    </persistence-unit>
</persistence>

When you use the same test case as in the previous examples to persist a ChessPlayer entity object, you can see that Hibernate now uses the database sequence seq_Player to generate primary key values.

17:06:51,325 DEBUG [org.hibernate.SQL] - 
    select
        nextval('seq_Player')
17:06:51,352 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        Player
        (birthDate, firstName, lastName, version, id) 
    values
        (?, ?, ?, ?, ?)

Fixing migration issues in Hibernate 6

When you’re migrating an existing application to Hibernate 6, the default naming strategy changes from single, if you’ve been using Hibernate <5.3, or legacy, if you’ve been using Hibernate >=5.3, to standard. And as I described earlier, this changes the name of the sequence Hibernate uses to generate your primary key values.

If you’re running into that problem, you can fix it by explicitly defining the sequence name in your mapping, migrating your database schema, or configuring the old naming strategy in your persistence.xml.

<persistence>
    <persistence-unit name="my-persistence-unit">
        ...
        <properties>
            <property name="hibernate.id.db_structure_naming_strategy" value="single" />
			...
        </properties>
    </persistence-unit>
</persistence>

Conclusion

The ImplicitDatabaseObjectNamingStrategy interface and the configuration property hibernate.id.db_structure_naming_strategy introduce a new implicit naming strategy in Hibernate 6. It defines how Hibernate determines the name of a database sequence or the database table used to simulate a sequence if you don’t specify their name in your entity mapping definition.

Most developers will need to use this configuration when migrating their application to Hibernate 6 because Hibernate’s default implicit naming strategy has changed. Instead of using 1 default sequence for all entity classes that don’t specify a sequence, Hibernate now generates an entity-specific default name. You can tell Hibernate to use the old naming strategy by setting the configuration property hibernate.id.db_structure_naming_strategy to single if you’re migrating from a Hibernate version <5.3, or to legacy if you’re migrating from a Hibernate version >=5.3.

You can also provide your own naming strategy for database sequences. To do that, you need to implement the ImplicitDatabaseObjectNamingStrategy interface and provide the fully-qualified class name as the value of the configuration property hibernate.id.db_structure_naming_strategy.