Custom UserType is not really a singleton

When using a custom UserType, why does Hibernate create many of them per class/field, instead of having a singleton?

For example:

public class A {
    @Type("MyType") private String a1;
    @Type("MyType") private String a2;
}
public class B {
    @Type("MyType") private String b1;
    @Type("MyType") private String b2;
}

I would expect Hibernate to generate 4 MyType singletons, but on startup, logging shows that setParameterValues method is called many times on unique MyType instances.

If I get the EntityPersister.getPropertyType().getUserType(), I get one of the MyType instances created, but it’s not the only one. When nullSafeGet/Set are called it can be from one of several instances associated with the specific Java field.

Why is MyType a singleton for each field? Is it possible to find all the instances of MyType for each field?

I’m on Hibernate 5.4.19.Final.

I guess this is because a UserType instance can be parameterized by the type use.

That doesn’t quite explain it; each class/entity field needs to definitely get its own singleton, so in my example there should be at least 4 MyType singletons: a1, a2, b1, and b2. That allows parameterizing each one separately.

But Hibernate instead creates several instances each. This is counter-intuitive to the Hibernate docs which imply that these are singletons (e.g. the examples register types using an INSTANCE singleton).

Is the amount of instances increasing or does it stay constant? If you can post stacktraces of the constructor calls I can give it a deeper look. I understand that it would be an issue if the amount is increasing, but if not, why is it so important to you that the instances are singletons?

The issue is that I want to inject different Spring beans into each MyType instance: a1 gets one value, but a2/b1/b2 all get different values. These are all injected after the EntityManagerFactory is complete and Spring is initializing the rest of the application beans, by crawling the EMF/EntityPersister.

If MyTypes are singletons per field, I can easily find them all and inject whatever I want. But EntityPersister.getPropertyType(“a1”).getUserType() returns only one of the instances created for a given field. I don’t see a way to find all of them, or to set the configuration only once for the MyType associated with a given field.

Apparently the problem is that SimpleValue.getType always re-creates the type; it’s own type variable isn’t being assigned, so every call to getType() ends up calling TypeResolver.heuristicType.

Here are the first few stack traces on the MyType constructor; these are all for the same field:

	at java.lang.Class.newInstance(Class.java:442)
	at org.hibernate.type.TypeFactory.custom(TypeFactory.java:177)
	at org.hibernate.type.TypeFactory.byClass(TypeFactory.java:78)
	at org.hibernate.type.TypeResolver.heuristicType(TypeResolver.java:126)
	at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:484)
	at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:466)
	at org.hibernate.mapping.Property.isValid(Property.java:227)
	at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:624)
	at org.hibernate.mapping.RootClass.validate(RootClass.java:267)
	at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:354)
	at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:465)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1259)

Then again:

	at java.lang.Class.newInstance(Class.java:442)
	at org.hibernate.type.TypeFactory.custom(TypeFactory.java:177)
	at org.hibernate.type.TypeFactory.byClass(TypeFactory.java:78)
	at org.hibernate.type.TypeResolver.heuristicType(TypeResolver.java:126)
	at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:484)
	at org.hibernate.mapping.Property.getType(Property.java:70)
	at org.hibernate.event.service.internal.EventListenerRegistryImpl.prepare(EventListenerRegistryImpl.java:152)
	at org.hibernate.boot.internal.MetadataImpl.initSessionFactory(MetadataImpl.java:379)
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:213)
	at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:469)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1259)

Again:

	at java.lang.Class.newInstance(Class.java:442)
	at org.hibernate.type.TypeFactory.custom(TypeFactory.java:177)
	at org.hibernate.type.TypeFactory.byClass(TypeFactory.java:78)
	at org.hibernate.type.TypeResolver.heuristicType(TypeResolver.java:126)
	at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:484)
	at org.hibernate.tuple.PropertyFactory.buildEntityBasedAttribute(PropertyFactory.java:158)
	at org.hibernate.tuple.entity.EntityMetamodel.<init>(EntityMetamodel.java:224)
	at org.hibernate.persister.entity.AbstractEntityPersister.<init>(AbstractEntityPersister.java:604)
	at org.hibernate.persister.entity.SingleTableEntityPersister.<init>(SingleTableEntityPersister.java:125)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:96)
	at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:77)
	at org.hibernate.metamodel.internal.MetamodelImpl.initialize(MetamodelImpl.java:181)
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:301)
	at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:469)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1259)

There’s a few more but you get the idea.

Thanks for the report, I’ll see what I can do. I created the following issue for this: https://hibernate.atlassian.net/browse/HHH-14335

Thank you! I do have a workaround for now; I implement DynamicParameterizedType and the entity class and field name are in the parameters, so I centralize the configuration using a static map. Not ideal, but it gets me through.

I found another issue that seems related to this; I don’t know if this is fixed is 5.4.25 like HHH-14335, but I thought I’d bring it up.

Does an update Query create yet another new MyType instance? I’m having an issue where the Query is using a MyType instance configured only via the parameters declared in @Type, but not the enhanced configuration that I apply to MyType during application startup.

If MyType was a true singleton per field, this shouldn’t happen, as I would be unambiguously modifying the parameters for the only possible MyType instance.

Does this make sense? Will a Query still use the singleton MyType with this fix in HHH-14335?

MyType in this case is a CompositeUserType, and I’m dynamically setting the result of getPropertyTypes during startup to reflect the actual database column types, since MyType is used in various applications that have different schemas and column types per field. This works for everything except an update Query, which behaves as if MyType was only configured with the parameters in @Type.

Sorry, but I have no idea what your problem is. Please create a test case that reproduces the issue so that I can take a deeper look.