Jee*_*87c 5 java postgresql hibernate jpa spring-data-jpa
我有以下 Spring Data JPA 存储库
public interface FooRepository extends JpaRepository<Foo, String> {
@QueryHints(
value = {
@QueryHint(name = HINT_FETCH_SIZE, value = "1000"),
@QueryHint(name = HINT_CACHEABLE, value = "false"),
@QueryHint(name = HINT_FLUSH_MODE, value = "ALWAYS"),
@QueryHint(name = HINT_CACHE_MODE, value = "IGNORE"),
@QueryHint(name = HINT_READONLY, value = "true")
})
Stream<Foo> findAll();
}
Run Code Online (Sandbox Code Playgroud)
在下面的方法中调用如下
@Transactional
public void doSomething() {
AtomicInteger counter = new AtomicInteger();
try(Stream<Foo> stream = fooRepository.findAll()) {
stream.forEach(foo -> {
int i = counter.incrementAndGet();
logger.info(() -> "" + i);
});
}
}
Run Code Online (Sandbox Code Playgroud)
当运行具有数百万个实体的代码时Foo,这个确切的代码会抛出一个OutOfMemoryError. 查看崩溃后的堆转储,我发现有大量的MutableEntityEntry,Foo和EntityEntryContext$ManagedEntityImpl。所有三个都有完全相同的计数。最重要的是, 的数量正好是该数量的两倍EntityKey。EntityKey例如,我在堆转储中前 3 个和 80k 各有 40k 。
为了完成这项工作,我尝试手动刷新、清除和垃圾收集,但没有成功,如下所示
@Transactional // org.springframework.transaction.annotation.Transactional
public void doSomething() {
entityManager.joinTransaction(); // properly injected through Spring DI
AtomicInteger counter = new AtomicInteger();
try(Stream<Foo> stream = fooRepository.findAll()) {
stream.forEach(foo -> {
int i = counter.incrementAndGet();
if (i % 100 == 0) {
fooRepository.flush();
entityManager.clear();
System.gc();
logger.info(() -> "flush, clear, gc");
}
logger.info(() -> "" + i);
});
}
Run Code Online (Sandbox Code Playgroud)
由于我的代码中没有保留任何foo对流式传输的实体的引用,并且在抛出错误后不会查找堆转储中的对象,因此我怀疑问题出在 Hibernate 的 L1 会话缓存中,即使存在停用缓存QueryHint(从我的理解)。感觉就像只HINT_FETCH_SIZE在我的方法给定的情况下工作QueryHints,我不知道为什么。
仅供参考,我在我的项目中根本没有使用 Spring Boot。所以我有以下 bean 来SpringConfiguration配置 Spring Data JPA:
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory()
throws MalformedURLException {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.POSTGRESQL);
vendorAdapter.setGenerateDdl(false);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan(getClass().getPackage().getName());
factory.setDataSource(dataSource());
Properties jpaProperties = new Properties();
jpaProperties.setProperty(
"hibernate.physical_naming_strategy",
"my.domain.hibernate.SnakeCasePhysicalNamingStrategy");
jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQL10Dialect");
factory.setJpaProperties(jpaProperties);
return factory;
}
@Bean
public EntityManager entityManager() throws MalformedURLException {
return entityManagerFactory().getObject().createEntityManager();
}
@Bean
public PlatformTransactionManager transactionManager() throws MalformedURLException {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory().getObject());
return txManager;
}
Run Code Online (Sandbox Code Playgroud)
这是每个的版本
终于发现了问题,那就是entityManager在我的班级中注入的方式。您必须在类中的字段声明上使用,而不是在 my 中为其添加 beanSpringConfiguration并通过构造函数注入它。@PersistenceContext
这是工作代码:
@PersistenceContext
private EntityManager entityManager;
[...]
@Transactional // org.springframework.transaction.annotation.Transactional
public void doSomething() {
entityManager.joinTransaction();
AtomicInteger counter = new AtomicInteger();
try(Stream<Foo> stream = fooRepository.findAll()) {
stream.forEach(foo -> {
int i = counter.incrementAndGet();
if (i % 100 == 0) {
entityManager.flush();
entityManager.clear();
logger.info(() -> "flush then clear);
}
logger.info(() -> "" + i);
});
}
Run Code Online (Sandbox Code Playgroud)
这样做entityManager.clear()将正确清除 L1 会话缓存,如此处所述
| 归档时间: |
|
| 查看次数: |
7348 次 |
| 最近记录: |