Why is method DefaultLoadEventListener.loadFromDatasource now private?

Hi there,

I’m currently working on the Hibernate migration to 5.3.x. I’ve just discovered that org.hibernate.event.internal.DefaultLoadEventListener#loadFromDatasource is now private instead of protected as I’ve hooked in here to solve some inheritance joining issues that come with our core design. Is there a specific reason why the visibility has been restricted?

Best regards,
Niko

It most likely is a simple refactor based on the fact that DefaultLoadEventListener is an internal class and we don’t expect users to override such a class in the way that you described typically.

Can you elaborate on your inheritance join issue so we can determine if there is a better approach?

Hi there,
sorry for the delay, I was sick the last week.

Basically we have a big “Base” table that a large number of classes inherit from, using joined inheritance. The Base table includes the database identifier and the qualified class name). When I simply want to query a Base entity by using the ID, I cannot do this on MySQL/MariaDB due to the 61 table join issue. This means that I always have to prequery the Base table to get the correct class by ID, then I can query the proper ID/class combination.

For a better understanding, I have attached the existing implementation I used in Hibernate 4.3, skipping some helper methods:

	@Override
	protected Object loadFromDatasource(LoadEvent event, EntityPersister persister, EntityKey keyToLoad, LoadType options) {

		String classToCheck = event.getEntityClassName();
		if (preloadProperClassToPreventTableLimit(classToCheck)) {
			// bypassing 61 table limit
			String newClassName = lookupClass(event);
			LOGGER.debug("Change {} to {} for ID {}", classToCheck, newClassName, event.getEntityId());
			EventSource source = event.getSession();
			// regenerate entity key with proper class
			persister = source.getFactory().getEntityPersister(newClassName);
			keyToLoad = source.generateEntityKey(event.getEntityId(), persister);
			event.setEntityClassName(newClassName);
		}
		return super.loadFromDatasource(event, persister, keyToLoad, options);
	}

If you can help me with better approach (yes, I know that the design approach with inheritance was wrong in the first place, but we have to stick to this), I’d be very grateful.

Regards,
Niko

You can also use a custom EntityPersister and override the load method to use your custom load logic:

public Object load(
		Serializable id,
		Object optionalObject,
		LockOptions lockOptions,
		SharedSessionContractImplementor session
	) throws HibernateException {
		return load(id, optionalObject, lockOptions.getLockMode(), session);
	}

Then, at the base class entity level, you declare the custom persister:

@Entity(name = "BaseEntity")
@Persister( impl = MySQLCustomEntityPersister.class )
public class BaseEntity {
	....
}

Thank very much for this suggestion, this looks like a more fine-grained and suitable approach which will supersede my solution.

One small question remains open: is @Persister effectively inherited to subclasses?

I don’t recall this. You should test it and see if it works. If it does not, just add @Persister to subclasses too.

All right, I will post the results when I can spare some time to implement the changes. Thanks for the help!

I tried to implement an EntityPersister based approach, but effectively I don’t see where I have a mutable handle on the class being loaded as only id and session are effectively available and the EntityPersister itself already supplies the class.

public Object load(Serializable id, Object optionalObject, LockOptions lockOptions, SharedSessionContractImplementor session) throws HibernateException {

So to me it seems that I have to fiddle with the loader which seems quite complex for this task.

What if you just extend the default Hibernate entity Persister and override just the methods that you need?

This is what I tried in the first place (I decided to extend from JoinedSubclassEntityPersister). But I didn’t find any appropriate method to override without digging really deep in. For from my current point of view, overriding the load listener is the most straight-forward approach that requires the least amount of additional code:

@Override
	public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException {
		String classToCheck = event.getEntityClassName();
		if (preloadProperClassToPreventTableLimit(classToCheck)) {
			// bypassing 61 table limit
			String newClassName = lookupClass(event);
			LOGGER.debug("Change {} to {} for ID {}", classToCheck, newClassName, event.getEntityId());
			event.setEntityClassName(newClassName);
		}

		super.onLoad(event, loadType);
	}

This works fine for me.

OK, let’s proceed here.

The previously mentioned approach has the serious performance drawback that the additional preload logic (that is triggering one lightweight DB query) is always executed, no matter if the entity is already on the cache or if it needs to be loaded from the datasource. This decision cannot be made in the onLoad method.

So basically the only solution I currently see to keep existing behaviour is

  • to use a custom implementation that is copied from DefaultLoadEventListener
  • and to change the persister based on the preloaded class directly before using loadFromDataSource

To avoid code duplication, it is critical that both loadFromDataSource is protected (and, even better, also getPersister().

Btw, it seems that DefaultLoadEventListener#getEvenListenerGroup and DefaultLoadEventListener#assembleCacheEntry is dead code.

Hi @nikowitt ,

Could you prepare a PR with what you exactly want? It will be easier for us to review and be sure the right thing is done.

We will release 5.3.1 very soon (it’s a matter of hours now) so if you can provide it quickly, it might help to get it in.

Keep in mind that we should definitely find a better solution for this issue as extending an internal class is not recommended to say the least but we can keep that for another day.

Thanks!

Okay, I do my best :slight_smile:

No need for tests or whatever, just try to make your changes on ORM, build it and make it work with your code.

./gradlew publishToMavenLocal

might help :slight_smile:

https://hibernate.atlassian.net/browse/HHH-12629 with
https://github.com/hibernate/hibernate-orm/pull/2307

it’s merged. Let’s try to come up with a better solution when Steve Eversole returns though.

Thanks all!