why Spring ORM JpaTransactionManager using native entity manger factory?

Viewed 166

Is there any specific reason while creating entity manager jps transaction using the native object?

   EntityManagerFactory emf = this.getEntityManagerFactory();
   if (emf instanceof EntityManagerFactoryInfo) {
            emf = 
           ((EntityManagerFactoryInfo)emf).getNativeEntityManagerFactory();
   }

Our requirement is to use the Proxy (Created ) instead of the native object, we have created aspect around the getSession method to add tenant id(Discriminator Column) dynamically for query

Thanks, Vishnu

1 Answers

To-the-point explanation:

The reason why the native EntityManagerFactory is unwrapped in JpaTransactionManager's createEntityManagerForTransaction() method is because still using the proxy would later create an extended EntityManager instead of a transaction-scoped one. The latter is the one we want in the case of regular transactional sessions.

What would happen if we still used the proxy:

Because the proxy delegates all calls to ManagedEntityManagerFactoryInvocationHandler, the call to createEntityManager(), from which we hope to obtain a transaction-scoped entity manager, will also be delegated.
If we look at the implementation of the above-mentioned handler's invoke() method, we observe that it calls invokeProxyMethod() from AbstractEntityManagerFactoryBean where we see that it specifically triggers the creation of an extended transaction manager, instead of a transaction-scoped one. This is probably just a design choice of Spring developers.

Object invokeProxyMethod(Method method, @Nullable Object[] args) throws Throwable {
    // unrelated code here
    if (method.getName().equals("createEntityManager") && args != null && args.length > 0 &&
            args[0] == SynchronizationType.SYNCHRONIZED) {
        EntityManager rawEntityManager = (args.length > 1 ?
                getNativeEntityManagerFactory().createEntityManager((Map<?, ?>) args[1]) :
                getNativeEntityManagerFactory().createEntityManager());
        // we get an extended entity manager
        return ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, true);
    }
    // more unrelated code here
    Object retVal = method.invoke(getNativeEntityManagerFactory(), args);
    if (retVal instanceof EntityManager) {
        EntityManager rawEntityManager = (EntityManager) retVal;
        retVal = ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, false);
    }
    // in the final path, we also get an extended entity manager
    return retVal;
}

Some paths I would look into to circumvent your problem:

You may try to subclass JpaTransactionManager and override createEntityManagerForTransaction() with the minimum changes that would accomodate your mentioned use of aspects later on. However, simply not unwrapping the EntityManagerFactory proxy would probably not be what you intend, due to above explanation related to extended entity managers.

Another thing you may want to look into is changing the aspects you have mentioned to not require the use of Spring's entityManagerFactory proxy and play fine with the native object. As a last resort, you could try to subclass and override multiple Spring ORM classes' methods mentioned throughout this solution so that you accomodate both Spring's behaviour and your aspects.

Related