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 interface
static interface
This interface has been introduced to mitigate JDK-8180450.
Sadly, usingBiConsumer
will 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 interface
static interface
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic CompositeOwner
asCompositeOwner
(Object entity) Cast the object to CompositeOwner (using this is highly preferrable over a direct cast)static CompositeTracker
asCompositeTracker
(Object entity) Cast the object to CompositeTracker (using this is highly preferrable over a direct cast)static HibernateProxy
asHibernateProxy
(Object entity) Cast the object to HibernateProxy (using this is highly preferrable over a direct cast)static HibernateProxy
asHibernateProxyOrNull
(Object entity) Cast the object to an HibernateProxy, or return null in case it is not an instance of HibernateProxystatic ManagedEntity
asManagedEntity
(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 SelfDirtinessTracker
asSelfDirtinessTracker
(Object entity) Cast the object to SelfDirtinessTracker (using this is highly preferrable over a direct cast)static boolean
isCompositeOwner
(Object entity) static boolean
isCompositeTracker
(@Nullable Object entity) static boolean
isHibernateProxy
(Object entity) static boolean
isManagedEntity
(Object entity) static boolean
isManagedType
(Class type) static boolean
static boolean
static boolean
isSelfDirtinessTracker
(Object entity) static boolean
static void
processIfManagedEntity
(Object entity, ManagedTypeHelper.ManagedEntityConsumer action) 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.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.static void
If the entity is implementing SelfDirtinessTracker, apply some action to it.
-
Constructor Details
-
ManagedTypeHelper
public ManagedTypeHelper()
-
-
Method Details
-
isManagedType
- Parameters:
type
-- Returns:
- true if and only if the type is assignable to a type.
-
isSelfDirtinessTrackerType
- Parameters:
type
-- Returns:
- true if and only if the type is assignable to a type.
-
isPersistentAttributeInterceptableType
- Parameters:
type
-- 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
- Parameters:
entity
-- Returns:
- true if and only if the entity implements
-
isSelfDirtinessTracker
- Parameters:
entity
-- Returns:
- true if and only if the entity implements
-
isCompositeOwner
- Parameters:
entity
-- Returns:
- true if and only if the entity implements
-
isCompositeTracker
- Parameters:
entity
-- 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:
entity
-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.- Parameters:
entity
-action
-
-
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:
entity
-action
-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
-