gpt4 book ai didi

java - JPA/Hibernate在获取数据时非常慢

转载 作者:行者123 更新时间:2023-12-04 15:29:11 29 4
gpt4 key购买 nike

我有一个使用 JPA/Hibernate 从 SQL Server 2008 R2 获取数据的 DAO。在我的特定用例中,我正在执行一个简单的 SELECT 查询,该查询返回大约 100000 条记录,但需要超过 35 分钟才能完成。

我通过手动加载 sql server 驱动程序创建了一个基本的 JDBC 连接,并且相同的查询在 15 秒内返回了 100k 条记录。所以我的配置出了点问题。

这是我的 springDataContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="persistenceXmlLocation" value="classpath:persistence.xml"/>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServer2008Dialect</prop>
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">none</prop>
<prop key="hibernate.use_sql_comments">false</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql:false}</prop>
<prop key="jadira.usertype.autoRegisterUserTypes">true</prop>
<prop key="jadira.usertype.javaZone">America/Chicago</prop>
<prop key="jadira.usertype.databaseZone">America/Chicago</prop>
<prop key="jadira.usertype.useJdbc42Apis">false</prop>
</props>
</property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<tx:annotation-driven/>

<jpa:repositories base-package="com.mycompany.foo"/>

<context:component-scan base-package="com.mycompany.foo.impl" />
</beans>

bean myDataSource由使用 DAO 的任何应用程序提供,其定义如下:
<bean id="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="url" value="jdbc:sqlserver://mysqlhost.mycompany.com:1433;database=MYDB"/>
<property name="username" value="username"/>
<property name="password" value="chucknorris"/>
</bean>

我有一个复杂的查询,我在其中设置 fetchSize :
package com.mycompany.foo.impl;

import com.mycompany.foo.RecurringLoanPaymentAccountsDao;
import org.hibernate.Query;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.joda.time.DateTime;

import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Named
public class FooDaoImpl implements FooDao {
@PersistenceContext
private EntityManager entityManager;

public ScrollableResults getData(int chunkSize, DateTime tomorrow, DateTime anotherDate) {
Session session = entityManager.unwrap(Session.class);

Query query = session.createQuery(
"from MyAccountTable as account " +
// bunch of left joins (I am using @JoinColumns in my models)
"where account.some_date >= :tomorrow " +
"and account.some_other_date < :anotherDate"
// <snip snip>
);

query.setParameter("tomorrow", tomorrow)
.setParameter("anotherDate", anotherDate)
.setFetchSize(chunkSize);

return query.scroll(ScrollMode.FORWARD_ONLY);
}
}

我也切换到 Vanilla JPA 并做了 jpaQuery.getResultList() ,但这同样慢。

如果需要,我可以提供其他相关代码。关于我在这里做错了什么的任何线索?

更新 1:添加架构详细信息

不幸的是,我在一家银行工作,所以我不能分享确切的代码。但让我试着代表相关的位。

这是我要查询的主表:
@Entity
@Table(name = "MY_TABLE")
public class MyTable {
@EmbeddedId
private Id id = new Id();

@Column(name = "SOME_COL")
private String someColumn;

// other columns

@OneToMany(fetch = FetchType.LAZY)
@JoinColumns({
@JoinColumn(name = "ACCT_NBR", referencedColumnName = "ACCT_NBR", insertable = false, updatable = false),
@JoinColumn(name = "CUST_NBR", referencedColumnName = "CUST_NBR", insertable = false, updatable = false)
})
private List<ForeignTable> foreignTable;

// getter and setter for properties

public static class Id implements Serializable {
@Column(name = "ACCT_NBR")
private short accountNumber;

@Column(name = "CUST_NBR")
private short customerNumber;
}
}

基本上,每张 table 都有 ACCT_NBRCUST_NBR聚集在一起时唯一的列(包括外部表)。所以我的加入条件包括这两个。

外部表的模型看起来完全一样(带有和上面的主表一样的嵌入 ID),当然除了 ACCT_NBR 之外还有自己的一组列。和 CUST_NBR .

现在我只关心外部表中除了上面提到的 ID 列之外的其他 2 列: TYPE_IDACCT_BALANCE .
TYPE_ID是我希望在我的 LEFT JOIN 中使用的内容条件,使最终的 SQL 看起来像这样:
LEFT JOIN FOREIGN_TABLE FRN
ON MAIN.ACCT_NBR = FRN.ACCT_NBR
AND MAIN.CUST_NBR = FRN.CUST_NBR
AND FRN.TYPE_ID = <some_static_id>

当此特定 TYPE_ID 不匹配时,我希望 LEFT JOIN 产生 NULL 数据.

在我的 SELECT克劳斯,我选择 FOREIGN_TABLE.ACCT_BALANCE ,如果上面的左连接没有匹配的行,这当然是空的。

这个方法是从一个spring批处理应用程序的Tasklet调用的,我有一种感觉,一旦tasklet完成处理,它就会给出一个空指针异常,因此只读事务被关闭。

最佳答案

在评论中,您指出您的 @Joincolumn是或FetchType.EAGER .这是非常激进的和你的问题。
Eager表示 Hibernate 将 JOIN FETCH您查询中的所有列,解析并加载所有记录以在您的实体的新实例中分派(dispatch)数据。您可能知道,如果您将表 A 和 B 连接起来,结果将是巨大的 A.*, B.*A x B记录和 A重复了很多次。这就是 Eager 的情况.这可能会迅速升级,尤其是对于许多 Eager列。

切换到 Lazy告诉 Hibernate 不加载数据。它只是准备了一个Proxy将调用单独的 SELECT 的对象仅在需要时(如果您的交易仍处于打开状态)。

您可以随时强制使用 FETCH使用 JOIN FETCH table 手动在您的 HQL 中.这是优选的方式。

关于java - JPA/Hibernate在获取数据时非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34522685/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com