与 SQL 相比,Hibernate JPQL 查询非常慢
Posted
技术标签:
【中文标题】与 SQL 相比,Hibernate JPQL 查询非常慢【英文标题】:Hibernate JPQL query is very slow compared to SQL 【发布时间】:2021-01-12 15:46:09 【问题描述】:在我的项目中,我遇到了一个需要大约 1.5 秒的 JPQL 查询的问题。当我直接在 PostgreSQL 数据库上执行从调试日志(执行 Hibernates 的同一日志)复制的 SQL 查询时,大约需要 15 毫秒。
@Service
@Transactional
@Slf4j
public class PersonSyncServiceImpl
extends HotelApiCommunicationService implements PersonSyncService
[...]
private PersonLinked getLinkedPerson(CNPerson cnPerson, Obiekt obiekt)
PersonLinked person = cnPerson.getPersonLinked();
if (person == null)
var o = cnPerson;
List<PersonLinked> personList = personLinkedDao.findPersonLinkedByRecordData(
o.getPersonImiona(), o.getPersonNazwisko(), o.getPersonPesel(), o.getPersonEmail(), o.getPersonTelefon1());
person = personList.stream()
.findFirst().orElse(null);
if (person == null)
person = createPersonLinkedFromCnPerson(cnPerson);
personLinkedDao.save(person);
cnPerson.setPersonLinked(person);
return person;
[...]
问题出在这一行:
List<PersonLinked> personList = personLinkedDao.findPersonLinkedByRecordData(
o.getPersonImiona(), o.getPersonNazwisko(), o.getPersonPesel(), o.getPersonEmail(), o.getPersonTelefon1());
带有已定义查询的道:
@Repository
@Transactional
public interface PersonLinkedDao extends JpaRepository<PersonLinked, Long>
@Query("select o from PersonLinked o \n" +
"where o.personImiona = :imie and o.personNazwisko = :nazwisko \n" +
" and (o.personPesel = :pesel or o.personEmail = :email or o.personTelefon1 = :telefon)")
List<PersonLinked> findPersonLinkedByRecordData(
@Param("imie") String personImiona,
@Param("nazwisko") String personNazwisko,
@Param("pesel") String personPesel,
@Param("email") String personEmail,
@Param("telefon") String personTelefon);
来自 Hibernate 调试日志的 SQL:
select [..]
from
person personlinke0_
where
personlinke0_.person_imiona=?
and personlinke0_.person_nazwisko=?
and (
personlinke0_.person_pesel=?
or personlinke0_.person_email=?
or personlinke0_.person_telefon1=?
)
当我在数据库上执行这个查询大约需要 15 毫秒,从代码执行大约需要 1.5 秒。我在代码中注释掉了这一行并且滞后消失了,所以肯定问题是这个 jpql 选择。
数据库连接配置:
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
spring.datasource.url=jdbc:postgresql://192.168.1.200:5433/XXXXXXX
spring.datasource.username=XXXXX
spring.datasource.password=XXXXX
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.generate_statistics=true
更新 1:
调试日志:
26-09-2020 16:06:36.130 [http-nio-8091-exec-2] DEBUG org.hibernate.SQL.logStatement -
select [...]
from
person personlinke0_
where
personlinke0_.person_imiona=?
and personlinke0_.person_nazwisko=?
and (
personlinke0_.person_pesel=?
or personlinke0_.person_email=?
or personlinke0_.person_telefon1=?
)
26-09-2020 16:06:36.130 [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.doGetTransaction - Found thread-bound EntityManager [SessionImpl(1971671100<open>)] for JPA transaction
26-09-2020 16:06:36.130 [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.handleExistingTransaction - Participating in existing transaction
26-09-2020 16:06:36.146 [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.doGetTransaction - Found thread-bound EntityManager [SessionImpl(1971671100<open>)] for JPA transaction
26-09-2020 16:06:36.146 [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.handleExistingTransaction - Participating in existing transaction
26-09-2020 16:06:36.146 [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.doGetTransaction - Found thread-bound EntityManager [SessionImpl(1971671100<open>)] for JPA transaction
26-09-2020 16:06:36.146 [http-nio-8091-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager.handleExistingTransaction - Participating in existing transaction
26-09-2020 16:06:37.521 [http-nio-8091-exec-2] DEBUG org.hibernate.SQL.logStatement -
更新 2:
PersonLinked 实体类:
@Entity
@Table(name = "person")
@Getter
@Setter
@SuperBuilder
@EqualsAndHashCode(of = "personId")
public class PersonLinked extends SCPerson
@Id
@GeneratedValue(generator = "seq_person", strategy = GenerationType.SEQUENCE)
@SequenceGenerator(name = "seq_person", sequenceName = "seq_person", allocationSize = 30)
@Column(name = "OSOBA_ID", nullable = false)
private Long personId;
@OneToMany(mappedBy = "personLinked", fetch = FetchType.LAZY)
private List<CNPerson> cnPersonList;
@Tolerate
public PersonLinked()
super();
@PrePersist
@Override
protected void preInsert()
super.preInsert();
SCPerson 类:
@MappedSuperclass
@Getter
@Setter
@SuperBuilder
public class SCPerson
[...]
【问题讨论】:
你怎么能确定这个查询只需要时间或其他操作?如果你只想要顶部元素,你只能获取它。 @Eklavya 我在我的配置中启用了“logging.level.org.hibernate.SQL=DEBUG”,我在日志中看到了它。否则,当我将此行更改为“List@Transactional
。我认为这与其他一些问题有关
该表有多少行?您可以使用 pageable 仅获取前一个元素,而不是从表 Ex 中获取所有数据。将所有数据映射到实体中需要时间。
@Eklavya 表人为空。
【参考方案1】:
最后我找到了解决方案,问题出在代码的另一部分。 在调用 getLinkedPerson() 方法之前,我有这行代码:
List<CNPerson> cnPersonList = cnPersonDao.findCnPersonNotLinkedWithPerson(obiekt.getLoid());
cnPersonList 在这里包含大约 70 000 个对象。
我改成:
List<Integer> ids = cnPersonDao.findCnPersonIdsNotLinkedWithPerson(obiekt.getLoid());
问题描述在这里:https://***.com/a/46258045/9678458
在休眠上下文刷新期间减速。万一你更新 太多的对象 ORM 引擎(比如说 Hibernate)必须下沉它们并且 将它们留在记忆中。从字面上看,Hibernate 必须具有所有旧状态和 更新对象的所有新状态。有时它会这样做 不理想。
您可以使用调试来表明这一点。尝试找到最慢的地方,然后 检查那里到底调用了什么。我可能猜它会变慢 当休眠更新缓存状态时关闭。
我认为这是因为实体 CNPerson 和 PersonLinked 是链接的,但我不确定:
@ManyToOne(fetch = FetchType.LAZY,
cascade = CascadeType.MERGE, CascadeType.PERSIST)
@JoinTable(name = "cnperson_links",
joinColumns = @JoinColumn(name = "cnperson_loid"),
inverseJoinColumns = @JoinColumn(name = "person_id"))
private PersonLinked personLinked;
【讨论】:
以上是关于与 SQL 相比,Hibernate JPQL 查询非常慢的主要内容,如果未能解决你的问题,请参考以下文章
JPA 2.1 @ConstructorResult 和 @SqlResultSetMapping 与 JPQL SELECT NEW SomeConstructor(...) 相比有啥优势?