Scenario:
I'm supporting an Enterprise application that runs in Wildfly10. The application (.war) uses J2EE technologies (EJBs, JPA, JAX-RS) and SpringBoot features (like, SpringMVC, SpringRest, SpringData, SpringRestData) ... Both stacks co-exists "happily" because they don't interact between them; however, they do share common classes, like utility or Entity Classes (the stacks map to the same database model). Why the application uses those stacks is out the scope of the question.
Currently, I'm trying to improve the performance of a @RestController
that pulls some data from the database using a JPA Spring Repository. I found that we're suffering the N + 1 queries
problem when calling the @RestController. In past projects (where there were only J2EE technologies), I have used the @BatchSize
hibernate annotation to mitigate this problem with total success.
But, in this project, Spring seems to be skipping such annotation. How do I know that? Because I turned on the hibernate SQL logging (hibernate.show_sql
) and I can see the N + 1 queries
is still happening ...
Key Points:
Here are some insights about the application that you must know before providing (or trying to guess) any answer:
- The application has many sub-modules encapsulated as libraries inside WAR file (/WEB-INF/lib) ... Some of these libraries are the jars that encapsulate the entity classes; others are the jars that encapsulate the REST Services (that could be JAX-RS services or Spring Controllers).
- The Spring configuration is done in the classes defined in the WAR artifact: in there, we have a class (that extends from
SpringBootServletInitializer
) annotated with@SpringBootApplication
and another class (that extends fromRepositoryRestConfigurerAdapter
) annotated with@Configuration
. Spring customization is done is such class. - The application works with multiple datasources, which are defined in the Wildly server. Spring DATA JPA must address any query pointing to the right datasource. To accomplish this requirement, the application (Spring) was configured like this:
@Bean(destroyMethod="")
@ConfigurationProperties(prefix="app.datasource")
public DataSource dataSource() {
// the following class extends from AbstractRoutingDataSource
// and resolve datasources using JNDI names (the wildfly mode!)
return new DataSourceRouter();
}
@Bean("entityManagerFactory")
public LocalContainerEntityManagerFactoryBean getEntityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean lemfb;
lemfb = new LocalContainerEntityManagerFactoryBean();
lemfb.setPersistenceUnitName("abcd-pu");
lemfb.setDataSource(dataSource());
return lemfb;
}
The last @Bean
declaration favors the use of a persistence.xml
file, which we do have in the route /WEB-INF/classes/META-INF/
(i.e. Spring does find this file!) ... In such file, we define our domain classes, so that Spring JPA can see such entities. Also, we can define special JPA properties like: hibernate.show_sql
and hibernate.use_sql_comments
without issues (this is how I detected the N + 1 queries
problem in the first place) ...
What I have done so far?
I tried to add the
@BatchSize
annotation to the problematic collection. No luck!I created a new JAX-RS Service whose purpose was to mimic the behavior of the @RestController. I confirmed that the
@BatchSize
annotation does work in the application's deployment, at least, in JAX-RS Services! (NOTE: the service uses it own persistence.xml) ...
Test details (Updated 2020/07/30): What I did here was to create a new JAX-RS Service and deployed it inside the WAR application, next to the @RestController that presents the problem (I mean, it is the same WAR and the same physical JVM). Both services pull from database the same entity (same class - same classloader), which has a lazy Collection annotated with @BatchSize
! ... If I invoke both services, the JAX-RS honors the @BatchSize and pulls the collection using the expected strategy, the @RestController does not ... So, what it is happening here? The only thing different between the services is that each one has a different persistence.xml
: the persistence.xml for the JAX-RS is picked by Wildfly directly, the other one is picked by Spring and delegated to Wildfly (I guess) ...
I tried to add the properties:
hibernate.batch_fetch_style
(=dynamic) andhibernate.default_batch_fetch_size
(=10), to thepersistence.xml
read by Spring ... No luck. I debug the Spring startup process and I saw that such properties are passed to the Spring Engine, but Spring does not care about them. The weird thing here is that properties like:hibernate.show_sql
, Spring does honor them ... For those who are asking: "What does these properties do?" Well, they are global equivalent to apply@BatchSize
to any JPA lazy collection or proxy without declaring such annotation in any entity.I setup a small SpringBoot Project using the same Spring version as enterprise application (which is 1.5.8.RELEASE, by the way) and both the annotation and properties approach worked as supposed to.
I've been stuck with this issue for two days, any help to fix this will be appreciated ... thanks!