EclipseLink 批量获取提示不适用于两个字段
EclipseLink Batch Fetch Hint Not Working For Two Fields
我在 Spring Data JPA 中使用 QueryHints 来使用 EclipseLink Batch Fetch 和 IN 类型。最终,我需要在大约 30 个字段中使用它,但它似乎不适用于 2 个字段。字段 A 具有多对一关系,字段 B 具有多对多关系。根据初始查询的结果,我希望批处理提示生成一个 IN 子句,其中字段 A 有 2 个 ID,字段 B 有 12 个 ID。当为 的一个字段打开提示时,这工作正常一次。当为两个字段都启用时,提示仅适用于 QueryHints 列表中 最后一个提示的字段。我试过 EAGER 和 LAZY fetch 在田野上作为黑暗中的一枪,但没有影响。
根据关系类型混合批量提取提示是否有限制?有什么不同的事情发生吗? EclipseLink 文档对此功能的描述不是很详细。
编辑:似乎我只启用哪些字段并不重要,它一次只对一个字段有效。这是两个实体的示例代码。 BaseEntity
定义 PK id 生成。
@Entity
@Table(name = "MainEntity")
public class MainEntity extends BaseEntity implements Cloneable {
...
@ManyToMany(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
@JoinTable(
name="EntityBMapping",
joinColumns={@JoinColumn(name="mainId", referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="bId", referencedColumnName="id")})
@JsonIgnore
private Set<EntityB> bSet = new HashSet<>();
@ManyToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
@JoinTable(
name="EntityAMapping",
joinColumns={@JoinColumn(name="mainId", referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="aId", referencedColumnName="id")})
@JsonIgnore
@OrderColumn(name="order_index", columnDefinition="SMALLINT")
private List<EntityA> aList = new ArrayList<>();
...
}
@Entity
@Cache(type=CacheType.FULL)
@Table(name = "EntityA")
public class EntityA extends BaseEntity {
@Column(name = "name", columnDefinition = "VARCHAR(100)")
private String name;
@ManyToMany(mappedBy = "entityASet", fetch=FetchType.LAZY)
@JsonIgnore
private Set<MainEntity> mainEntityList = new HashSet<>();
}
@Entity
@Cache(type=CacheType.FULL)
@Table(name = "EntityB")
public class EntityB extends BaseEntity {
@Column(name = "name", columnDefinition = "VARCHAR(100)")
private String name;
@ManyToMany(mappedBy = "entityBSet", cascade=CascadeType.ALL)
@JsonIgnore
private Set<MainEntity> mainEntityList = new HashSet<>();
}
存储库查询:
@QueryHints(value = {
@QueryHint(name = org.eclipse.persistence.config.QueryHints.BATCH_TYPE, value = "IN"),
@QueryHint(name = org.eclipse.persistence.config.QueryHints.BATCH_SIZE, value = "250"),
@QueryHint(name = org.eclipse.persistence.config.QueryHints.BATCH, value = "o.aList")},
@QueryHint(name = org.eclipse.persistence.config.QueryHints.BATCH, value = "o.bSet")},
forCounting = false)
List<MainEntity> findAll(Specification spec);
生成的查询:
SELECT id, STATUS, user_id FROM MainEntity WHERE ((STATUS = ?) OR ((STATUS = ?) AND (user_id = ?)))--bind => [ONESTAT, TWOSTAT, myuser]
..
SELECT t1.id, t1.name, t0.order_index FROM EntityAMapping t0, EntityA t1 WHERE ((t0.mainId = ?) AND (t1.id = t0.aId))--bind => [125e17d2-9327-4c6b-a65d-9d0bd8c040ac]
...
SELECT t1.id, t1.name, t0.mainId FROM EntityBMapping t0, EntityB t1 WHERE ((t1.id = t0.bId) AND (t0.mainId IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)))--bind => [125e17d2-9327-4c6b-a65d-9d0bd8c040ac, 1c07a3a9-7028-48ba-abe8-2296d58ebd57, 235bb4f2-d724-4237-b73b-725db2b9ca9f, 264f64b3-c355-4476-8530-11d2037b1f3c, 2d9a7044-73b3-491d-b5f1-d5b95cbb1fab, 31621c93-2b0b-4162-9e42-32705b7ba712, 39b33b19-c333-4523-a5a7-4ba0108fe9de, 40ba7706-4023-4b7e-9bd5-1641c5ed6498, 52eed760-9eaf-4f6a-a36f-076b3eae9297, 71797f0c-5528-4588-a82c-5e1d4d9c2a66, 89eda2ef-80ff-4f54-9e6a-cf69211dfa61, 930ba300-52fa-481c-a0ae-bd491e7dc631, 96dfadf9-2490-4584-b0d4-26757262266d, ae079d02-b0b5-4b85-8e6f-d3ff663afd6e, b2974160-33e8-4faf-ad06-902a8a0beb04, b86742d8-0368-4dde-8d17-231368796504, caeb79ce-2819-4295-948b-210514376f60, cafe838f-0993-4441-8b99-e012bbd4c5ee, da378482-27f9-40b7-990b-89778adc4a7e, e4d7d6b9-2b8f-40ab-95c1-33c6c98ec2ee, e557acf4-df01-4e66-9d5e-84742c99870d, ef55a83c-2f4c-47b9-99bb-6fa2f5c19a76, ef55a83c-2f4c-47b9-99bb-6fa2f5c19a77]
...
SELECT t1.id, t1.name, t0.order_index FROM EntityAMapping t0, EntityA t1 WHERE ((t0.mainId = ?) AND (t1.id = t0.aId))--bind => [1c07a3a9-7028-48ba-abe8-2296d58ebd57]
正如 Chris 所提到的,命名查询是解决此问题的最佳方法。另一种选择是使用自定义存储库并为每个指定的提示自己调用 EntityManager 上的 setHint(在 Spring Data JPA 中有大量用于创建自定义存储库的示例)。您可以尝试覆盖 SimpleJpaRepository
上的 findOne(...)
和 protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Sort sort)
以尝试创建一种通用方法来正确设置提示,但您可能需要检查您是否没有重复提示设置在 getQuery(...)
上,因为您仍然希望为此调用 super() ,然后在返回查询之前应用您的其他提示。如果您应用重复提示,我不确定行为会是什么。省去麻烦,使用命名查询是我的建议。
我在 Spring Data JPA 中使用 QueryHints 来使用 EclipseLink Batch Fetch 和 IN 类型。最终,我需要在大约 30 个字段中使用它,但它似乎不适用于 2 个字段。字段 A 具有多对一关系,字段 B 具有多对多关系。根据初始查询的结果,我希望批处理提示生成一个 IN 子句,其中字段 A 有 2 个 ID,字段 B 有 12 个 ID。当为 的一个字段打开提示时,这工作正常一次。当为两个字段都启用时,提示仅适用于 QueryHints 列表中 最后一个提示的字段。我试过 EAGER 和 LAZY fetch 在田野上作为黑暗中的一枪,但没有影响。
根据关系类型混合批量提取提示是否有限制?有什么不同的事情发生吗? EclipseLink 文档对此功能的描述不是很详细。
编辑:似乎我只启用哪些字段并不重要,它一次只对一个字段有效。这是两个实体的示例代码。 BaseEntity
定义 PK id 生成。
@Entity
@Table(name = "MainEntity")
public class MainEntity extends BaseEntity implements Cloneable {
...
@ManyToMany(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
@JoinTable(
name="EntityBMapping",
joinColumns={@JoinColumn(name="mainId", referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="bId", referencedColumnName="id")})
@JsonIgnore
private Set<EntityB> bSet = new HashSet<>();
@ManyToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
@JoinTable(
name="EntityAMapping",
joinColumns={@JoinColumn(name="mainId", referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="aId", referencedColumnName="id")})
@JsonIgnore
@OrderColumn(name="order_index", columnDefinition="SMALLINT")
private List<EntityA> aList = new ArrayList<>();
...
}
@Entity
@Cache(type=CacheType.FULL)
@Table(name = "EntityA")
public class EntityA extends BaseEntity {
@Column(name = "name", columnDefinition = "VARCHAR(100)")
private String name;
@ManyToMany(mappedBy = "entityASet", fetch=FetchType.LAZY)
@JsonIgnore
private Set<MainEntity> mainEntityList = new HashSet<>();
}
@Entity
@Cache(type=CacheType.FULL)
@Table(name = "EntityB")
public class EntityB extends BaseEntity {
@Column(name = "name", columnDefinition = "VARCHAR(100)")
private String name;
@ManyToMany(mappedBy = "entityBSet", cascade=CascadeType.ALL)
@JsonIgnore
private Set<MainEntity> mainEntityList = new HashSet<>();
}
存储库查询:
@QueryHints(value = {
@QueryHint(name = org.eclipse.persistence.config.QueryHints.BATCH_TYPE, value = "IN"),
@QueryHint(name = org.eclipse.persistence.config.QueryHints.BATCH_SIZE, value = "250"),
@QueryHint(name = org.eclipse.persistence.config.QueryHints.BATCH, value = "o.aList")},
@QueryHint(name = org.eclipse.persistence.config.QueryHints.BATCH, value = "o.bSet")},
forCounting = false)
List<MainEntity> findAll(Specification spec);
生成的查询:
SELECT id, STATUS, user_id FROM MainEntity WHERE ((STATUS = ?) OR ((STATUS = ?) AND (user_id = ?)))--bind => [ONESTAT, TWOSTAT, myuser]
..
SELECT t1.id, t1.name, t0.order_index FROM EntityAMapping t0, EntityA t1 WHERE ((t0.mainId = ?) AND (t1.id = t0.aId))--bind => [125e17d2-9327-4c6b-a65d-9d0bd8c040ac]
...
SELECT t1.id, t1.name, t0.mainId FROM EntityBMapping t0, EntityB t1 WHERE ((t1.id = t0.bId) AND (t0.mainId IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)))--bind => [125e17d2-9327-4c6b-a65d-9d0bd8c040ac, 1c07a3a9-7028-48ba-abe8-2296d58ebd57, 235bb4f2-d724-4237-b73b-725db2b9ca9f, 264f64b3-c355-4476-8530-11d2037b1f3c, 2d9a7044-73b3-491d-b5f1-d5b95cbb1fab, 31621c93-2b0b-4162-9e42-32705b7ba712, 39b33b19-c333-4523-a5a7-4ba0108fe9de, 40ba7706-4023-4b7e-9bd5-1641c5ed6498, 52eed760-9eaf-4f6a-a36f-076b3eae9297, 71797f0c-5528-4588-a82c-5e1d4d9c2a66, 89eda2ef-80ff-4f54-9e6a-cf69211dfa61, 930ba300-52fa-481c-a0ae-bd491e7dc631, 96dfadf9-2490-4584-b0d4-26757262266d, ae079d02-b0b5-4b85-8e6f-d3ff663afd6e, b2974160-33e8-4faf-ad06-902a8a0beb04, b86742d8-0368-4dde-8d17-231368796504, caeb79ce-2819-4295-948b-210514376f60, cafe838f-0993-4441-8b99-e012bbd4c5ee, da378482-27f9-40b7-990b-89778adc4a7e, e4d7d6b9-2b8f-40ab-95c1-33c6c98ec2ee, e557acf4-df01-4e66-9d5e-84742c99870d, ef55a83c-2f4c-47b9-99bb-6fa2f5c19a76, ef55a83c-2f4c-47b9-99bb-6fa2f5c19a77]
...
SELECT t1.id, t1.name, t0.order_index FROM EntityAMapping t0, EntityA t1 WHERE ((t0.mainId = ?) AND (t1.id = t0.aId))--bind => [1c07a3a9-7028-48ba-abe8-2296d58ebd57]
正如 Chris 所提到的,命名查询是解决此问题的最佳方法。另一种选择是使用自定义存储库并为每个指定的提示自己调用 EntityManager 上的 setHint(在 Spring Data JPA 中有大量用于创建自定义存储库的示例)。您可以尝试覆盖 SimpleJpaRepository
上的 findOne(...)
和 protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Sort sort)
以尝试创建一种通用方法来正确设置提示,但您可能需要检查您是否没有重复提示设置在 getQuery(...)
上,因为您仍然希望为此调用 super() ,然后在返回查询之前应用您的其他提示。如果您应用重复提示,我不确定行为会是什么。省去麻烦,使用命名查询是我的建议。