我有一个JPA层次结构,其中一个对象包含一组抽象类型,由不同数量的子类实现。问题是,当Hibernate尝试加载这些类时,它会生成一个通常获取主对象的查询,但(懒惰地)使用union(在子选择中)加载这些附加对象。有点像:
select ...
from (
select ...
from subtable1
union
select ...
from subtable2
)
where parent.id = ?
这是一个非常效率低下的查询(在父表中有10k行,每个子类中有1个用于获取父类中100个左右记录的整个层次结构的所有行需要几分钟,急切的查询最多需要50分钟。)
我不明白为什么他们不仅仅是从父表(select ... from parent p left join subtable1 ... left join subtable2 ...
)离开联接,因为这会在几分之一秒内带来所有必需的数据。有什么方法可以避免这种愚蠢的联合查询吗?
到目前为止,我见过的唯一可行的解决方案是将继承类型从TABLE_PER_CLASS更改为SINGLE_TABLE,但是对此的权衡不太理想(包括无法在列上使用“not null”,维护问题因为扩展类将属于不同的团队,甚至可能使用不同版本的软件等。)。
万一它可以帮助任何人,我会粘贴一个由Hibernate生成的真实查询与这种关系(JPQL在开始时作为注释),这个特定的查询花了34秒自己执行(0.6秒)当重写为左连接时):
/* select distinct p
from PersonJpa p left join fetch p.addressOfRecords a
left join fetch a.extensions e left join fetch a.commsType c
left join fetch p.credentials cr left join fetch cr.type t
left join fetch p.serviceProfiles s left join fetch s.extensions ex
where p.id in (?1) */
SELECT DISTINCT personjpa0_.person_id AS person1_902_0_,
addressofr1_.aor_id AS aor1_899_1_,
extensions2_.id AS id903_2_,
commstypej3_.comms_type AS comms1_908_3_,
credential4_.credentials_id AS credenti1_905_4_,
credential5_.credentials_type AS credenti1_904_5_,
servicepro6_.service_profile_id AS service1_901_6_,
extensions7_.id AS id907_7_,
personjpa0_.display_name AS display2_902_0_,
personjpa0_.first_name AS first3_902_0_,
personjpa0_.initials AS initials902_0_,
personjpa0_.last_name AS last5_902_0_,
personjpa0_.title AS title902_0_,
personjpa0_.user_name AS user7_902_0_,
addressofr1_.comms_type AS comms9_899_1_,
addressofr1_.display_form AS display2_899_1_,
addressofr1_.domain AS domain899_1_,
addressofr1_.label AS label899_1_,
addressofr1_.parameters AS parameters899_1_,
addressofr1_.person_id AS person10_899_1_,
addressofr1_.preference AS preference899_1_,
addressofr1_.send_html AS send7_899_1_,
addressofr1_.service_profile_id AS service11_899_1_,
addressofr1_.user_part AS user8_899_1_,
addressofr1_.person_id AS person10_0__,
addressofr1_.aor_id AS aor1_0__,
extensions2_.aor_id AS aor2_903_2_,
extensions2_.resolution_status AS resolution1_910_2_,
extensions2_.certificate_serial_number AS certific1_914_2_,
extensions2_.device_type_name AS device2_914_2_,
extensions2_.provisioned AS provisio3_914_2_,
extensions2_.provisioning_token AS provisio4_914_2_,
extensions2_.provisioning_unique_id AS provisio5_914_2_,
extensions2_.sip_password AS sip6_914_2_,
extensions2_.username AS username914_2_,
extensions2_.voicemail_long_timeout AS voicemail8_914_2_,
extensions2_.voicemail_short_timeout AS voicemail9_914_2_,
extensions2_.clazz_ AS clazz_2_,
extensions2_.aor_id AS aor2_1__,
extensions2_.id AS id1__,
credential4_.algorithm_name AS algorithm3_905_4_,
credential4_.password AS password905_4_,
credential4_.person_id AS person4_905_4_,
credential4_.realm_id AS realm5_905_4_,
credential4_.credentials_type AS credenti6_905_4_,
credential4_.person_id AS person4_2__,
credential4_.credentials_id AS credenti1_2__,
servicepro6_.name AS name901_6_,
servicepro6_.person_id AS person3_901_6_,
servicepro6_.person_id AS person3_3__,
servicepro6_.service_profile_id AS service1_3__,
extensions7_.service_profile_id AS service2_907_7_,
extensions7_.seqprofile_id AS seqprofile1_911_7_,
extensions7_.certificate_port AS certific1_915_7_,
extensions7_.email_aor_id AS email23_915_7_,
extensions7_.emergency_numbers AS emergency2_915_7_,
extensions7_.from_domain AS from3_915_7_,
extensions7_.outbound_domain AS outbound4_915_7_,
extensions7_.pbx_aor_id AS pbx24_915_7_,
extensions7_.provisioning_delivery AS provisio5_915_7_,
extensions7_.provisioning_host AS provisio6_915_7_,
extensions7_.provisioning_port AS provisio7_915_7_,
extensions7_.server_access_call_control_port AS server8_915_7_,
extensions7_.server_access_host AS server9_915_7_,
extensions7_.server_access_number AS server10_915_7_,
extensions7_.server_access_secure_mode AS server11_915_7_,
extensions7_.sip_domain AS sip12_915_7_,
extensions7_.sip_enabled AS sip13_915_7_,
extensions7_.sip_expires AS sip14_915_7_,
extensions7_.sip_proxy AS sip15_915_7_,
extensions7_.sip_realm AS sip16_915_7_,
extensions7_.sip_transport AS sip17_915_7_,
extensions7_.voicemail_long_timeout AS voicemail18_915_7_,
extensions7_.voicemail_message_server AS voicemail19_915_7_,
extensions7_.voicemail_number AS voicemail20_915_7_,
extensions7_.voicemail_short_timeout AS voicemail21_915_7_,
extensions7_.voicemail_userid AS voicemail22_915_7_,
extensions7_.clazz_ AS clazz_7_,
extensions7_.service_profile_id AS service2_4__,
extensions7_.id AS id4__
FROM cdm.cdm_person AS personjpa0_
LEFT OUTER JOIN
cdm.cdm_address_of_record AS addressofr1_
ON personjpa0_.person_id = addressofr1_.person_id
LEFT OUTER JOIN
(SELECT NULL AS device_type_name,
id,
NULL AS provisioning_unique_id,
aor_id,
NULL AS voicemail_long_timeout,
NULL AS username,
NULL AS certificate_serial_number,
NULL AS provisioning_token,
resolution_status,
NULL AS voicemail_short_timeout,
NULL AS provisioned,
NULL AS sip_password,
1 AS clazz_
FROM cdm.sb_aor_details
UNION
SELECT device_type_name,
id,
provisioning_unique_id,
aor_id,
voicemail_long_timeout,
username,
certificate_serial_number,
provisioning_token,
NULL AS resolution_status,
voicemail_short_timeout,
provisioned,
sip_password,
2 AS clazz_
FROM cdm.fmc_remote_number_address_of_record_extension) AS extensions2_
ON addressofr1_.aor_id = extensions2_.aor_id
LEFT OUTER JOIN
cdm.cdm_comms_type AS commstypej3_
ON addressofr1_.comms_type = commstypej3_.comms_type
LEFT OUTER JOIN
cdm.cdm_credentials AS credential4_
ON personjpa0_.person_id = credential4_.person_id
LEFT OUTER JOIN
cdm.cdm_credentials_type AS credential5_
ON credential4_.credentials_type = credential5_.credentials_type
LEFT OUTER JOIN
cdm.cdm_service_profile AS servicepro6_
ON personjpa0_.person_id = servicepro6_.person_id
LEFT OUTER JOIN
(SELECT NULL AS server_access_host,
NULL AS sip_domain,
NULL AS voicemail_message_server,
NULL AS outbound_domain,
NULL AS emergency_numbers,
NULL AS provisioning_delivery,
id,
NULL AS sip_expires,
NULL AS pbx_aor_id,
NULL AS server_access_call_control_port,
NULL AS provisioning_port,
NULL AS email_aor_id,
NULL AS voicemail_number,
NULL AS voicemail_long_timeout,
NULL AS certificate_port,
NULL AS sip_enabled,
NULL AS sip_transport,
NULL AS server_access_number,
service_profile_id,
NULL AS server_access_secure_mode,
NULL AS voicemail_userid,
seqprofile_id,
NULL AS sip_realm,
NULL AS voicemail_short_timeout,
NULL AS provisioning_host,
NULL AS from_domain,
NULL AS sip_proxy,
1 AS clazz_
FROM cdm.sb_service_profile_details
UNION
SELECT server_access_host,
sip_domain,
voicemail_message_server,
outbound_domain,
emergency_numbers,
provisioning_delivery,
id,
sip_expires,
pbx_aor_id,
server_access_call_control_port,
provisioning_port,
email_aor_id,
voicemail_number,
voicemail_long_timeout,
certificate_port,
sip_enabled,
sip_transport,
server_access_number,
service_profile_id,
server_access_secure_mode,
voicemail_userid,
NULL AS seqprofile_id,
sip_realm,
voicemail_short_timeout,
provisioning_host,
from_domain,
sip_proxy,
2 AS clazz_
FROM cdm.fmc_service_profile_extension) AS extensions7_
ON servicepro6_.service_profile_id = extensions7_.service_profile_id
WHERE personjpa0_.person_id IN (1, 2, [continuous ids], 199, 200);