Using JPA with a Hibernate (5.4.16) implentation and LocalContainerEntityManagerFactoryBean with JpaTransactionManager (5.2.6) I'm trying to use a Hibernate Filter, which implies access to the newly created EntityManager, to enable it.

I kept looking through the code but I can't seem to find a way to customize the EntityManager. I think something similar to AbstractJpaVendorAdapter#postProcessEntityManagerFactory perhaps would be nice, if possible.

Comment From: jhoeller

When exactly would you like to enable this filter? Globally for the entire application configuration? Or per transaction but decoupled from actual data access? Within a transaction boundary, you could also set it programmatically on an injected EntityManager before performing data access operations.

Comment From: andrei-ivanov

Hmm, The workaround that I currently have is a load time woven aspect:

@Aspect
public class EntityManagerAspect {
    private boolean enabled = false;

    @EventListener(ContextRefreshedEvent.class)
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public void enable() {
        enabled = true;
    }

    @AfterReturning(pointcut = "execution(* javax.persistence.EntityManagerFactory+.createEntityManager(..))", returning = "entityManager")
    public void enableMultiTenantsFilter(EntityManager entityManager) {
        if (enabled) {
            // custom class obtained from a JAX-RS SecurityContext ThreadLocal
            UserPrincipal principal = getUserPrincipal();
            Filter filter = entityManager.unwrap(Session.class).enableFilter(TenantEntity.TENANTS_FILTER);
            filter.setParameter(TenantEntity.TENANTS_FILTER_PARAM, tenant);
        }
    }
}

So, each time a new EntityManager instance gets created, the filter gets enabled and queries get it applied (except native ones).

Comment From: jhoeller

For pre-/post-processing a transactional EntityManager, you could use JpaDialect.begin/cleanupTransaction... but admittedly that's not really meant for application-level concerns.

Based on your case case, a general JpaVendorAdapter.postProcessEntityManager method would indeed make sense. I'll see what we can do for 5.2.7 there.

Comment From: jhoeller

It turns out that this is not totally straightforward in terms of capturing all relevant createEntityManager calls, including our differentiation between transactional resources and application-level EntityManager handles, requiring not only an addition to JpaVendorAdapter but also a revision of the JpaTransactionManager-EntityManagerFactoryInfo interaction. I'll rather schedule this for 5.3 M1 instead, due in June as well.

Comment From: jhoeller

For post-processing transactional EntityManager instances, you could also override the existing JpaTransactionManager.createEntityManagerForTransaction() method, calling super and then applying the filter to it. This would cover common access within a Spring-managed transaction.

Comment From: andrei-ivanov

I guess that's a possibility (that I didn't see). Now it would also be nice to play nice with Spring Boot, which I'm currently using 😀

@Bean
@ConditionalOnMissingBean
public PlatformTransactionManager transactionManager(
        ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
    return transactionManager;
}

In this case, it doesn't seem that I would be losing that much by creating a sub-class though, I don't use any PlatformTransactionManagerCustomizer

Comment From: jhoeller

Good point about the convenience of customizing a pre-built instance there...

In addition to the JpaVendorAdapter callback, I'm tempted to add a setEntityManagerCustomizer method to LocalContainerEntityManagerFactoryBean, typically to be used with a lambda expression against a given EntityManager, applied whenever JpaVendorAdapter.postProcessEntityManager is being called as well. Would that help for your scenario? We could also add such a lambda-based customizer to JpaTransactionManager, locally applied to transactional EntityManagers only, allowing for differentiation between all EntityManagers and just transactional EntityManagers.

Comment From: andrei-ivanov

Yes, seems like a good idea, I just have to be able to inject them somehow when they are instantiated by Boot. Not sure for my specific case if I would need both of them, but I assume others might.

Comment From: jhoeller

I went with a convenient setEntityManagerInitializer(Consumer<EntityManager>) method on JpaTransactionManager as well as LocalContainerEntityManagerFactoryBean (based on the newly introduced JpaVendorAdapter.postProcessEntityManager and EntityManagerFactoryInfo.createNativeEntityManager SPI underneath), and also a corresponding setSessionInitializer(Consumer<Session>) on HibernateTransactionManager (here just on the transaction manager since we have no common hook for all Session opening calls).

Comment From: andrei-ivanov

Thank you for the incredible fast turnaround 🙂

Now, to get this used with Spring Boot, I guess I should create a ticket for that project, right?

Comment From: jhoeller

This should work fine with Boot's existing TransactionManagerCustomizers, or with overridden entity manager factory or transaction manager definitions, or even with @Autowired LocalContainerEntityManagerFactoryBean / @Autowired JpaTransactionManager injection and a call to setEntityManagerInitializer (before any actual JPA interactions come in). So I don't think it needs explicit support in Boot; that said, there is always room for additional convenience in Boot if you have something specific in mind there.

Comment From: andrei-ivanov

That's good news, but then will this be backported to 5.2? I see it sill has an assigned milestone of 5.2.7.

Comment From: jhoeller

I changed the milestone to 5.3 M1 a few days ago... I'm afraid this is not the kind of change we do in a patch release, due to the revision of the SPI contracts. For the time being, the best you can do in 5.2.x is to override JpaTransactionManager.createEntityManagerForTransaction, as discussed above.

Comment From: bugy

After reading the discussion here, I came up with this solution for now:

    @Bean
    @ConditionalOnMissingBean
    public PlatformTransactionManager transactionManager(
            ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
        JpaTransactionManager transactionManager = new JpaTransactionManager() {
            @Override
            protected EntityManager createEntityManagerForTransaction() {
                final EntityManager entityManager = super.createEntityManagerForTransaction();
                Session session = entityManager.unwrap(Session.class);
                session.enableFilter(MY_FILTER).setParameter(MY_PARAM, myValue);
                return entityManager;
            }
        };
        transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));

        return transactionManager;
    }

Is it the best one to do with spring 5.2 (actually 5.1 for us)?

PS thanks @andrei-ivanov for pointing me to this issue!

Comment From: oloaP

Thank you for that new way of customizing entity manager @jhoeller, it is very useful indeed to enable hibernate filters by default. I'm trying to do the same thing in a JTA context. I'm was not able to find out how Hibernate handles entity manager initialization in this case. Do you have any idea if some sort of equivalent mecanism could be implemented in Spring JtaTransactionManager ?