Development

Custom Id Generators For Typed Id Values With Hibernate

OK, so you have your strongly typed values, but how can you generate them so they can be used as a numeric primary key?

Assuming you set up your typed ids as in: Using Typed Id Values With Hibernate, you don’t have to do much to make them from sequences.

This example uses oracle sequences, but I’m sure the method applies to other stuff too.

Annotate your class to use custom sequence generator

This uses the hibernate-specific @GenericGenerator annotation.

public class Foo {
 
    //Id needed for PK column
    @Id
    
    @GeneratedValue(generator = "typed-sequence")
    @GenericGenerator(
        name = "typed-sequence",
        parameters = {
        @Parameter(name = "sequence", value = "FooSeq")
            },
        strategy = "net.time4tea.persist.generator.TypedSequenceGenerator")
 
    // Type and Nullable just the same as any custom type
    @Type(type = "net.time4tea.persist.types.id.PersistentFooId")
    @Nullable
    private FooId id;
 
 
}

Using an hbm file

<class name="Foo">
        <id name="id" column="barrier_id">
            <type name="net.time4tea.persist.types.id.PersistentFooId"/>
            <generator class="net.time4tea.persist.generator.TypedSequenceGenerator">
                <param name="sequence">FooSeq</param>
            </generator>
        </id>
</class>

Implement your sequence generator

Just delegate to the original implementation to get the number, then strongly type it.

public class TypedSequenceGenerator implements PersistentIdentifierGenerator, Configurable {
 
    private final SequenceHiLoGenerator delegate = new SequenceHiLoGenerator();
    private Constructor constructor;
 
    public void configure(Type type, Properties params, Dialect d) throws MappingException {
 
        delegate.configure(Hibernate.LONG, params, d);
 
        try {
            constructor = type.getReturnedClass().getConstructor(long.class);
        } catch (NoSuchMethodException e) {
            throw new Defect("TypedSequenceGenerator is only applicable for types deriving from AbstractPersistenLongId", e);
        }
    }
 
    public synchronized Serializable generate(SessionImplementor session, Object obj) throws HibernateException {
        try {
            Long val = (Long) delegate.generate(session, obj);
            return typedIdForValue(val);
        } catch (ClassCastException e) {
            throw new HibernateException("delegate didn't return a long", e);
        }
    }
 
    private Serializable typedIdForValue(long val) {
        try {
            return (Serializable) constructor.newInstance(val);
        } catch (InstantiationException e) {
            throw new HibernateException("Cannot create strongly typed id", e);
 
        } catch (IllegalAccessException e) {
            throw new HibernateException("Cannot create strongly typed id", e);
 
        } catch (InvocationTargetException e) {
            throw new HibernateException("Cannot create strongly typed id", e);
        }
    }
 
    @Override
    public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
        return delegate.sqlCreateStrings(dialect);
    }
 
    @Override
    public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
        return delegate.sqlDropStrings(dialect);
    }
 
    @Override
    public Object generatorKey() {
        return delegate.generatorKey();
    }
comments powered by Disqus