【发布时间】:2020-11-19 21:06:54
【问题描述】:
场景:
我支持在 Wildfly10 中运行的企业应用程序。应用程序 (.war) 使用 J2EE 技术(EJB、JPA、JAX-RS)和 SpringBoot 功能(如 SpringMVC、SpringRest、SpringData、SpringRestData)......两个堆栈“愉快地”共存,因为它们之间不交互他们;但是,它们确实共享公共类,例如实用程序或实体类(堆栈映射到相同的数据库模型)。 为什么应用程序使用这些堆栈超出了问题的范围。
目前,我正在尝试提高@RestController 的性能,该@RestController 使用JPA Spring 存储库从数据库中提取一些数据。我发现我们在调用@RestController 时遇到了N + 1 queries 问题。在过去的项目中(只有 J2EE 技术),我使用了@BatchSize hibernate 注释来缓解这个问题并取得了圆满成功。
但是,在这个项目中,Spring 似乎跳过了这样的注释。 我怎么知道?因为我打开了休眠 SQL 日志记录 (hibernate.show_sql),我可以看到 N + 1 queries 仍在发生...
要点:
在提供(或尝试猜测)任何答案之前,您必须了解以下有关应用程序的一些见解:
- 应用程序有许多子模块被封装为WAR文件(/WEB-INF/lib)中的库......其中一些库是封装实体类的jar;其他是封装 REST 服务的 jars(可能是 JAX-RS 服务或 Spring 控制器)。
- Spring 配置在 WAR 工件中定义的类中完成:在那里,我们有一个用
@SpringBootApplication注释的类(从SpringBootServletInitializer扩展)和另一个用RepositoryRestConfigurerAdapter注释的类(从RepositoryRestConfigurerAdapter扩展)@Configuration。 Spring的定制就是这样的类。 - 该应用程序使用 Wildly 服务器中定义的多个数据源。 Spring DATA JPA 必须解决指向正确数据源的任何查询。为了满足这个要求,应用程序 (Spring) 的配置如下:
@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;
}
最后的@Bean 声明倾向于使用persistence.xml 文件,我们在/WEB-INF/classes/META-INF/ 路径中确实有它(即Spring 确实找到了这个文件!) ... 在这样的文件中,我们定义我们的域类,以便 Spring JPA 可以看到这样的实体。此外,我们可以毫无问题地定义特殊的 JPA 属性,例如:hibernate.show_sql 和 hibernate.use_sql_comments(这就是我首先检测到 N + 1 queries 问题的方式)...
到目前为止我做了什么?
-
我尝试将
@BatchSize注释添加到有问题的集合中。运气不好! -
我创建了一个新的 JAX-RS 服务,其目的是模仿 @RestController 的行为。我确认
@BatchSize注释在应用程序的部署中确实有效,至少在 JAX-RS 服务中有效! (注意:服务使用它自己的 persistence.xml)...
测试详情(2020 年 7 月 30 日更新):我在这里所做的是创建一个新的 JAX-RS 服务并将其部署在 WAR 应用程序中,在 @RestController 旁边显示问题(我的意思是,它是相同的 WAR 和相同的物理 JVM)。两个服务都从数据库中提取相同的实体(相同的类 - 相同的类加载器),它有一个带有 @BatchSize 注释的 lazy Collection! ...如果我调用这两个服务,JAX-RS 尊重@BatchSize 并使用预期的策略拉取集合,@RestController 不会...那么,这里发生了什么?服务之间唯一不同的是每个服务都有不同的persistence.xml:JAX-RS 的 persistence.xml 由 Wildfly 直接选取,另一个由 Spring 选取并委托给 Wildfly(我猜).. .
-
我尝试将属性:
hibernate.batch_fetch_style(=dynamic) 和hibernate.default_batch_fetch_size(=10) 添加到 Spring 读取的persistence.xml... 不走运。我调试了 Spring 启动过程,我看到这些属性被传递给 Spring Engine,但 Spring 并不关心它们。这里的奇怪之处在于:hibernate.show_sql之类的属性,Spring 确实尊重它们……对于那些问:“这些属性有什么作用?”的人。好吧,它们是全局等效的,可以将@BatchSize应用于任何 JPA 惰性集合或代理,而无需在任何实体中声明此类注释。 -
我使用与企业应用程序相同的 Spring 版本(顺便说一下,1.5.8.RELEASE)以及注释和属性设置了一个小型 SpringBoot 项目方法按预期工作。
我已经被这个问题困扰了两天,任何解决这个问题的帮助将不胜感激......谢谢!
【问题讨论】:
-
与数据源配置无关,IMO。你能分享你的实体代码吗?我们也许可以提供更好的帮助。
-
Spring 对
@BatchSize没有任何作用,也就是说,hibernate 对它有作用。 Spring不读取任何hibernate内容。 Spring 只是这些东西之上的一层,使配置更容易,Spring 不会读取或解释任何东西。 -
我不能说够了 Spring 没有加载任何东西,它正在委托给 JPA。现在,如果您在应用程序和 WildFly 中发布 JPA,事情就会变得有趣。 Spring 再次与此没有任何关系,这完全取决于您如何使用 hibernate。
-
什么是“N+1 查询问题”?
-
@maksimov,您可以检查一下以了解什么是“N+1”查询问题:stackoverflow.com/a/49789933
标签: java spring jpa spring-data-jpa wildfly-10