Search code examples
javahibernatehibernate-mapping

How to programmatically bind Hibernate Type for selected entity fields?


I'm looking a way to bind Type for specific entity fields during entity manager configuration phase. I need it to be able to apply extra "rules" to target entity field using external source without entity class changes.

So basically I'm trying to avoid hardcode @Type annotation way as below:

@Type(type = foo.package.MyType, parameters = {
    @Parameter(name = "fooProperty", value = "fooValue")
})
private String someField;

Instead I would like to set Type for someField while building model programmatically.


Solution

  • Here's one way I've seen before. It is a little low-level, so I suspect there is a cleaner way to do this.

    This uses a custom Persister in Hibernate to allow us to substitute the type while the SessionFactory ( EntityManagerFactory ) is being created.

    First, the @Persister annotation is used to declare the custom Persister :

    @Entity
    @Persister(impl = MyPersister.class)
    public class EntityWithPersister {
    
        private String someField;
    

    Then normally the custom persister should extend SingleTableEntityPersister in Hibernate. If the entity is using a different @Inheritance(strategy), then it may need to extend JoinedSubclassEntityPersister or UnionSubclassEntityPersister instead.

    This offers the chance to change a type at the point of construction, for example:

    public class MyPersister extends SingleTableEntityPersister {
    
        public MyPersister(PersistentClass persistentClass,
                EntityDataAccess cacheAccessStrategy,
                NaturalIdDataAccess naturalIdRegionAccessStrategy,
                PersisterCreationContext creationContext)
                throws HibernateException {
            super(modify(persistentClass), cacheAccessStrategy,
                    naturalIdRegionAccessStrategy, creationContext);
        }
    
        private static PersistentClass modify(PersistentClass persistentClass) {
            SimpleValue value = (SimpleValue) persistentClass
                    .getProperty("someField").getValue();
            value.setTypeName(MyType.class.getName());
            return persistentClass;
        }
    }
    

    If you need to access more of the context you are in, creationContext.getSessionFactory() is probably a good starting point.