This is an internal documentation. There is a good chance you’re looking for something else. See Disclaimer.

Custom entity tuplizer

We use a custom EntityTuplizer (CustomEntityTuplizer) to support the following features:

Lazy initialization of proxy factories

Hibernate creates a ProxyFactory for each entity class. To improve the startup time of the application we create them lazily when they are needed for the first time. In addition this saves some memory as, most likely, proxies are not created for every entity class we use.

This is achieved by overriding buildProxyFactory() and returning a proxy instance that is initialized when it is used for the first time.

If the property spring.main.lazy-initialization is set to false the lazy proxy factory is initialized immediately. This leads to a longer startup time, but makes sure that everything is initialized when startup is completed, so that the first few requests aren’t slowed down by initialization.

Custom proxy factory

We use our own custom ByteBuddyProxyFactory (NiceByteBuddyProxyFactory) that uses the NiceByteBuddyInterceptor as LazyInitializer.

The lazy initializer contains the entity class and the identifier and by default initializes (i.e loads the data from the database) when any method is called on the proxy. However many methods of the Entity interface do not require the information from the database and would trigger an unnecessary proxy initialization.

Our custom lazy initializer prevents this and evaluates certain methods without initializing the proxy:

  • requireKey() / getKey(): the identifier is contained by the initializer and can be returned as PrimaryKey

  • hasKey(): always returns true because proxies always belong to persistent entities

  • getContext() / getManager() / getModel(): the DataModel and Context are injected into our custom initializer and can be returned directly or used in combination with the persistent class to evaluate these methods

Some additional methods are evaluated directly by the initializer only when the proxy is not initialized yet:

  • getState() is always CLEAN when the data was not loaded yet

  • isFieldChanged() / isFieldTouched() / getChangedFields() / getTouchedFields() / getTouchedRelations(): No fields are changed before the proxy is loaded

The custom initializer also plays a role when using the EntityManager#delete(Condition) method, that deletes a number of entities without initializing them (if possible). For that reason markDeleted() is implemented on the initializer as well so that getState() can correctly return PHANTOM.