升级Vaadin24/SpringBoot3后EclipseLink性能骤降问题求助
负责一款已投产17年的Web应用,当前技术栈:Vaadin23 + EclipseLink2.7.8。因Vaadin23停止支持,需升级至依赖SpringBoot3的Vaadin24,同步需将JPA从2.1(javax.persistence)迁移至3.x(jakarta.persistence)。
性能问题现象
- EclipseLink2.7.8:JUnit测试耗时约10分钟
- 升级至2.7.9:测试耗时增至20分钟
- 升级至2.7.14:测试耗时达30分钟
- 升级至3.0.4:测试至少需40分钟,应用整体变慢
核心问题:从2.7.8升级至2.7.9后,简单全表查询(如select o from YXZ o)耗时大幅上升,所有@OneToMany关联已显式标注fetch=FetchType.LAZY(默认配置)。
临时方案尝试
在SpringBoot3中保留EclipseLink2.7.8,编译与测试启动正常,persistence.xml配置可读取,但EntityManager无法识别@Entity类。
复现测试用例
编写JUnit测试对5个实体执行全表查询:
EntityManager em = Persistence.createEntityManagerFactory(Constants.PERSISTENCE_UNIT).createEntityManager(); String format = "%-20s: %6d %8d \n"; System.out.printf("%-20s: %6s %8s \n", "Entity", "Count", "Duration"); long start = System.currentTimeMillis(); String[] entities = {"Entity1", "Entity2", "Entity3", "Entity4", "Entity5"}; for (String entity: entities) { String query = String.format("select o from %s o", entity); long sStart = System.currentTimeMillis(); int size = em.createQuery(query).getResultList().size(); System.out.printf(format, entity, em.createQuery(query).getResultList().size(), System.currentTimeMillis() - sStart); } System.out.println("Duration summary:" + (System.currentTimeMillis() - start));
测试结果
EclipseLink2.7.8
| Entity | Count | Duration |
|---|---|---|
| Entity1 | 3448 | 328 |
| Entity2 | 1224 | 560 |
| Entity3 | 1427 | 533 |
| Entity4 | 9407 | 3172 |
| Entity5 | 23548 | 8970 |
总耗时:13565毫秒
EclipseLink2.7.9
| Entity | Count | Duration |
|---|---|---|
| Entity1 | 3448 | 465 |
| Entity2 | 1224 | 5187 |
| Entity3 | 1427 | 5834 |
| Entity4 | 9407 | 40513 |
| Entity5 | 23548 | 114871 |
总耗时:166872毫秒
方向一:修复EclipseLink 2.7.9+的全表查询性能问题
1. 检查并调整缓存策略
EclipseLink 2.7.9+可能调整了默认缓存行为,尝试:
- 给实体显式添加
@Cache(shared = false, type = CacheType.NONE)关闭二级缓存,验证是否是缓存加载导致的延迟 - 全局配置
eclipselink.cache.shared.default=false关闭二级缓存
2. 优化查询执行逻辑
- 启用SQL日志(配置
eclipselink.logging.level.sql=FINE),对比2.7.8和2.7.9生成的SQL,确认是否出现意外的关联查询或执行计划变化 - 对全表查询添加只读提示,减少实体管理开销:
em.createQuery(query).setHint("eclipselink.read-only", true).getResultList(); - 增大JDBC fetch size,减少数据库交互次数:配置
eclipselink.jdbc.fetch-size=1000(根据数据量调整)
3. 排查动态编织问题
EclipseLink的动态编织可能在新版本中引入性能损耗,尝试:
- 启用静态编织,在编译阶段生成增强的实体类,避免运行时编织开销
- 配置
eclipselink.weaving=false关闭动态编织,验证性能变化
4. 校验实体注解规范
2.7.9对JPA 2.2规范的兼容性更严格,检查:
@OneToMany的mappedBy属性是否正确指向关联实体的对应字段- 实体类是否存在未正确标注的关联关系,导致隐式的EAGER加载
方向二:让EclipseLink 2.7.8在Spring Boot 3中正常运行
Spring Boot 3基于Jakarta EE 9(使用jakarta.persistence),而EclipseLink 2.7.x仅支持javax.persistence,需解决包名兼容问题:
1. 依赖调整与包名桥接
- 排除Spring Boot自带的
jakarta.persistence-api,引入javax.persistence-api:<!-- Maven示例 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <exclusions> <exclusion> <groupId>jakarta.persistence</groupId> <artifactId>jakarta.persistence-api</artifactId> </exclusion> <exclusion> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>org.eclipse.persistence.jpa</artifactId> <version>2.7.8</version> </dependency> <dependency> <groupId>javax.persistence</groupId> <artifactId>javax.persistence-api</artifactId> <version>2.2</version> </dependency> - 通过IDE全局替换或编译工具,将代码中的
jakarta.persistence注解批量替换为javax.persistence
2. 手动配置EntityManagerFactory
关闭Spring Boot的JPA自动配置,手动创建EntityManagerFactory确保实体被扫描:
@SpringBootApplication(exclude = JpaAutoConfiguration.class) public class Application { @Bean public DataSource dataSource() { // 配置你的数据源(如HikariCP) return new HikariDataSource(); } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource); // 指定实体类所在包路径 em.setPackagesToScan("com.your.app.entities"); em.setPersistenceProviderClass(org.eclipse.persistence.jpa.PersistenceProvider.class); em.setPersistenceUnitName(Constants.PERSISTENCE_UNIT); Properties jpaProperties = new Properties(); jpaProperties.put("eclipselink.persistencexml", "META-INF/persistence.xml"); // 添加其他EclipseLink配置 jpaProperties.put("eclipselink.logging.level", "INFO"); em.setJpaProperties(jpaProperties); return em; } @Bean public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactory); return transactionManager; } }
3. 验证实体扫描
确保persistence.xml中正确配置了实体类,或通过setPackagesToScan指定扫描路径,避免EntityManager无法识别@Entity类的问题。
内容的提问来源于stack exchange,提问作者Matthias

