【问题标题】:Why Spring does not recognize @BatchSize annotation为什么 Spring 不识别 @BatchSize 注释
【发布时间】: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 仍在发生...


要点:

在提供(或尝试猜测)任何答案之前,您必须了解以下有关应用程序的一些见解:

  1. 应用程序有许多子模块被封装为WAR文件(/WEB-INF/lib)中的库......其中一些库是封装实体类的jar;其他是封装 REST 服务的 jars(可能是 JAX-RS 服务或 Spring 控制器)。
  2. Spring 配置在 WAR 工件中定义的类中完成:在那里,我们有一个用 @SpringBootApplication 注释的类(从 SpringBootServletInitializer 扩展)和另一个用 RepositoryRestConfigurerAdapter 注释的类(从 RepositoryRestConfigurerAdapter 扩展) @Configuration。 Spring的定制就是这样的类。
  3. 该应用程序使用 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_sqlhibernate.use_sql_comments(这就是我首先检测到 N + 1 queries 问题的方式)...


到目前为止我做了什么?

  1. 我尝试将@BatchSize 注释添加到有问题的集合中。运气不好!

  2. 我创建了一个新的 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(我猜).. .

  1. 我尝试将属性:hibernate.batch_fetch_style (=dynamic) 和 hibernate.default_batch_fetch_size (=10) 添加到 Spring 读取的 persistence.xml ... 不走运。我调试了 Spring 启动过程,我看到这些属性被传递给 Spring Engine,但 Spring 并不关心它们。这里的奇怪之处在于:hibernate.show_sql 之类的属性,Spring 确实尊重它们……对于那些问:“这些属性有什么作用?”的人。好吧,它们是全局等效的,可以将 @BatchSize 应用于任何 JPA 惰性集合或代理,而无需在任何实体中声明此类注释。

  2. 我使用与企业应用程序相同的 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


【解决方案1】:

我可以考虑 2-3 个可能的问题。

  1. 出于某种原因,您修改的任何内容都不会被 Wildfly 接收 - Wildfly 类路径解析是一个单独的主题,某些缺少的配置可能会导致您做噩梦。这可以确定您是否有权调试查询,并且如果您在 Entity 类的构造函数中放置断点,您将有机会评估正在使用的实体配置,在执行 conetxt 的某处。
  2. BatchSize 不适用于 OneToOne,它仅适用于 OneToMany 关系。
  3. 定义BatchSize 的典型方法是与示例here 中提到的延迟加载一起进行。如果您没有使用 Lazy fetch,hibernate 会假定您愿意进行急切加载并进行另一个选择查询以获取所有详细信息。请确认您使用的语法与上面示例中给出的相同。

新增功能:

  1. PropertyBinder#setLazy()函数中放置条件断点,并且可以回溯它并在CollectionBinderAnnotationBinder中放置相关断点。然后重新启动/重新部署服务器并查看您为相关属性获取的数据。这会让你清楚地知道它在哪里失败了..

    • 为什么是条件断点?这是因为您将拥有数千个属性,如果您不向断点添加条件,您将需要 1 小时才能到达您的实际断点
    • 条件应该是什么 - 如果它的属性绑定器,条件应该类似于 `this.name == 。对于其他类,您也可以使用相同的方法。

    抱歉,条件断点的描述过于详细,您可能会发现它是多余的。

看来调试问题的唯一方法是从服务器启动调试休眠框架,那么只有我们才能找出根本原因

【讨论】:

  • 谢谢,@Anand Vaidya;也许我不够明确,但是当我进行问题中描述的测试 #2 时,我跨越了所有列出的问题......我将更新测试描述以解释原因......
  • 您的代码受版权保护吗?是否可以通过 gitlab repo 等方式共享它。这个问题似乎是一个孤立的环境问题,只有调试才有帮助,IMO。
  • 您是否尝试在构造函数中放置一个断点并从那里进行调试以找出根本原因?如果您有兴趣,我们可以进行屏幕共享会话,这个问题对我来说看起来很有趣..
  • 具体是哪个构造函数?
  • 添加到原始答案中。
猜你喜欢
  • 2021-08-14
  • 1970-01-01
  • 2013-09-20
  • 1970-01-01
  • 2021-06-03
  • 1970-01-01
  • 1970-01-01
  • 2022-12-19
  • 2019-07-03
相关资源
最近更新 更多