How to write a custom sequence-based String Id generator with JPA and Hibernate

You can write a String identifier generator like this one:

public class StringSequenceIdentifier implements 
        IdentifierGenerator, Configurable {

    public static final String SEQUENCE_PREFIX = "sequence_prefix";

    private String sequencePrefix;

    private String sequenceCallSyntax;

    @Override
    public void configure(
            Type type,
            Properties params,
            ServiceRegistry serviceRegistry)
        throws MappingException {

        final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService(
                JdbcEnvironment.class
        );

        final Dialect dialect = jdbcEnvironment.getDialect();

        final ConfigurationService configurationService = serviceRegistry.getService(
                ConfigurationService.class
        );

        String globalEntityIdentifierPrefix = configurationService.getSetting(
            "entity.identifier.prefix",
            String.class,
            "SEQ_"
        );

        sequencePrefix = ConfigurationHelper.getString(
            SEQUENCE_PREFIX,
            params,
            globalEntityIdentifierPrefix
        );

        final String sequencePerEntitySuffix = ConfigurationHelper.getString(
            SequenceStyleGenerator.CONFIG_SEQUENCE_PER_ENTITY_SUFFIX,
            params,
            SequenceStyleGenerator.DEF_SEQUENCE_SUFFIX
        );

        boolean preferSequencePerEntity = ConfigurationHelper.getBoolean(
            SequenceStyleGenerator.CONFIG_PREFER_SEQUENCE_PER_ENTITY,
            params,
            false
        );

        final String defaultSequenceName = preferSequencePerEntity
                ? params.getProperty(JPA_ENTITY_NAME) + sequencePerEntitySuffix
                : SequenceStyleGenerator.DEF_SEQUENCE_NAME;

        sequenceCallSyntax = dialect.getSequenceNextValString(
            ConfigurationHelper.getString(
                SequenceStyleGenerator.SEQUENCE_PARAM,
                params,
                defaultSequenceName
            )
        );
    }

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object obj) {
        if (obj instanceof Identifiable) {
            Identifiable identifiable = (Identifiable) obj;
            Serializable id = identifiable.getId();

            if (id != null) {
                return id;
            }
        }

        long seqValue = ((Number)
                Session.class.cast(session)
                .createNativeQuery(sequenceCallSyntax)
                .uniqueResult()
        ).longValue();

        return sequencePrefix + String.format("%011d%s", 0 ,seqValue);
    }
}

And map it like this:

@Id
@GenericGenerator(
	name = "assigned-sequence",
	strategy = "com.vladmihalcea.book.hpjp.hibernate.identifier.StringSequenceIdentifier",
	parameters = {
		@org.hibernate.annotations.Parameter(
			name = "sequence_name", value = "hibernate_sequence"),
		@org.hibernate.annotations.Parameter(
			name = "sequence_prefix", value = "CTC_"),
	}
)
@GeneratedValue(
	generator = "assigned-sequence",
	strategy = GenerationType.SEQUENCE)
private String id;

Check out this article for more details.