Class ManagedTypeHelper
To avoid polluting the secondary super-type cache, the important aspect is to not switch types repeatedly for the same concrete object; using a Java agent which was developed for this purpose (https://github.com/franz1981/type-pollution-agent) we identified a strong case with Hibernate ORM is triggered when the entities are using bytecode enhancement, as they are being frequently checked for compatibility with the following interfaces:
Some additional interfaces are involved in bytecode enhancement (such as ManagedMappedSuperclass),
but some might not be managed here as there was no evidence of them triggering the problem;
this might change after further testing.
The approach we pursue is to have all these internal interfaces extend a single
interface PrimeAmongSecondarySupertypes which then exposes a type widening
contract; this allows to consistently cast to PrimeAmongSecondarySupertypes exclusively
and avoid any further type checks; since the cast consistently happens on this interface
we avoid polluting the secondary super type cache described in JDK-8180450.
This presents two known drawbacks:
1# we do assume such user entities aren't being used via interfaces in hot user code; this is typically not the case based on our experience of Hibernate usage, but it can't be ruled out.
2# we're introducing virtual dispatch calls which are likely going to be megamorphic; this is not great but we assume it's far better to avoid the scalability issue.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfacestatic interfaceThis interface has been introduced to mitigate JDK-8180450.
Sadly, usingBiConsumerwill trigger a type pollution issue because of generics type-erasure:BiConsumer's actual parameters types on the lambda implemention'sBiConsumer.accept(T, U)are stealthy enforced viacheckcast, messing up with type check cached data.static interfacestatic interface -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic CompositeOwnerasCompositeOwner(Object entity) Cast the object to CompositeOwner (using this is highly preferrable over a direct cast)static CompositeTrackerasCompositeTracker(Object entity) Cast the object to CompositeTracker (using this is highly preferrable over a direct cast)static HibernateProxyasHibernateProxy(Object entity) Cast the object to HibernateProxy (using this is highly preferrable over a direct cast)static HibernateProxyasHibernateProxyOrNull(Object entity) Cast the object to an HibernateProxy, or return null in case it is not an instance of HibernateProxystatic ManagedEntityasManagedEntity(Object entity) Cast the object to ManagedEntity (using this is highly preferrable over a direct cast)Cast the object to PersistentAttributeInterceptable (using this is highly preferrable over a direct cast)static SelfDirtinessTrackerasSelfDirtinessTracker(Object entity) Cast the object to SelfDirtinessTracker (using this is highly preferrable over a direct cast)static booleanisCompositeOwner(Object entity) static booleanisCompositeTracker(@Nullable Object entity) static booleanisHibernateProxy(Object entity) static booleanisManagedEntity(Object entity) static booleanisManagedType(Class<?> type) static booleanstatic booleanstatic booleanisSelfDirtinessTracker(Object entity) static booleanisSelfDirtinessTrackerType(Class<?> type) static voidprocessIfManagedEntity(Object entity, ManagedTypeHelper.ManagedEntityConsumer action) static <T> voidprocessIfPersistentAttributeInterceptable(Object entity, ManagedTypeHelper.PersistentAttributeInterceptableAction<T> action, T optionalParam) Helper to execute an action on an entity, but exclusively if it's implementing the interface.static <T> voidprocessIfSelfDirtinessTracker(Object entity, ManagedTypeHelper.SelfDirtinessTrackerAction<T> action, T optionalParam) If the entity is implementing SelfDirtinessTracker, apply some action to it; this action should take a parameter of type T.static voidIf the entity is implementing SelfDirtinessTracker, apply some action to it.
-
Constructor Details
-
ManagedTypeHelper
public ManagedTypeHelper()
-
-
Method Details
-
isManagedType
- Returns:
- true if and only if the type is assignable to a type.
-
isSelfDirtinessTrackerType
- Returns:
- true if and only if the type is assignable to a type.
-
isPersistentAttributeInterceptableType
- Returns:
- true if and only if the type is assignable to a type.
-
isManagedEntity
- Returns:
- true if and only if the entity implements
ManagedEntity
-
isHibernateProxy
- Returns:
- true if and only if the entity implements
HibernateProxy
-
isPersistentAttributeInterceptable
- Returns:
- true if and only if the entity implements
-
isSelfDirtinessTracker
- Returns:
- true if and only if the entity implements
-
isCompositeOwner
- Returns:
- true if and only if the entity implements
-
isCompositeTracker
- Returns:
- true if and only if the entity implements
-
processIfPersistentAttributeInterceptable
public static <T> void processIfPersistentAttributeInterceptable(Object entity, ManagedTypeHelper.PersistentAttributeInterceptableAction<T> action, T optionalParam) Helper to execute an action on an entity, but exclusively if it's implementing the interface. Otherwise no action is performed.- Type Parameters:
T- the type of the additional parameter.- Parameters:
action- The action to be performed; it should take the entity as first parameter, and an additional parameter T as second parameter.optionalParam- a parameter which can be passed to the action
-
processIfSelfDirtinessTracker
public static void processIfSelfDirtinessTracker(Object entity, ManagedTypeHelper.SelfDirtinessTrackerConsumer action) If the entity is implementing SelfDirtinessTracker, apply some action to it. It is first cast to SelfDirtinessTracker using an optimal strategy. If the entity does not implement SelfDirtinessTracker, no operation is performed. -
processIfManagedEntity
public static void processIfManagedEntity(Object entity, ManagedTypeHelper.ManagedEntityConsumer action) -
processIfSelfDirtinessTracker
public static <T> void processIfSelfDirtinessTracker(Object entity, ManagedTypeHelper.SelfDirtinessTrackerAction<T> action, T optionalParam) If the entity is implementing SelfDirtinessTracker, apply some action to it; this action should take a parameter of type T. It is first cast to SelfDirtinessTracker using an optimal strategy. If the entity does not implement SelfDirtinessTracker, no operation is performed.- Type Parameters:
T- the type of the additional parameter.- Parameters:
optionalParam- a parameter which can be passed to the action
-
asPersistentAttributeInterceptable
Cast the object to PersistentAttributeInterceptable (using this is highly preferrable over a direct cast)- Parameters:
entity- the entity to cast- Returns:
- the same instance after casting
- Throws:
ClassCastException- if it's not of the right type
-
asPersistentAttributeInterceptableOrNull
public static PersistentAttributeInterceptable asPersistentAttributeInterceptableOrNull(Object entity) -
asHibernateProxy
Cast the object to HibernateProxy (using this is highly preferrable over a direct cast)- Parameters:
entity- the entity to cast- Returns:
- the same instance after casting
- Throws:
ClassCastException- if it's not of the right type
-
asManagedEntity
Cast the object to ManagedEntity (using this is highly preferrable over a direct cast)- Parameters:
entity- the entity to cast- Returns:
- the same instance after casting
- Throws:
ClassCastException- if it's not of the right type
-
asCompositeTracker
Cast the object to CompositeTracker (using this is highly preferrable over a direct cast)- Parameters:
entity- the entity to cast- Returns:
- the same instance after casting
- Throws:
ClassCastException- if it's not of the right type
-
asCompositeOwner
Cast the object to CompositeOwner (using this is highly preferrable over a direct cast)- Parameters:
entity- the entity to cast- Returns:
- the same instance after casting
- Throws:
ClassCastException- if it's not of the right type
-
asSelfDirtinessTracker
Cast the object to SelfDirtinessTracker (using this is highly preferrable over a direct cast)- Parameters:
entity- the entity to cast- Returns:
- the same instance after casting
- Throws:
ClassCastException- if it's not of the right type
-
asHibernateProxyOrNull
Cast the object to an HibernateProxy, or return null in case it is not an instance of HibernateProxy- Parameters:
entity- the entity to cast- Returns:
- the same instance after casting or null if it is not an instance of HibernateProxy
-