eclipselink вложенная пакетная выборка
Я создал очень простую модель с 3 объектами:
Department
который имеет несколько (отношениеemployees
)Employee
который имеет несколько (отношениеaddresses
)Address
Я хотел бы получить обе партии Employee
а также Address
от получения Department
Итак, у меня есть следующие JPL:
select d from Department d
и я добавил следующую подсказку:
query.setHint(QueryHints.BATCH, "d.employees.addresses");
Тем не менее, я постоянно получаю следующую ошибку:
Exception [EclipseLink-6089] (Eclipse Persistence Services - 2.4.2.v20130514-5956486): org.eclipse.persistence.exceptions.QueryException
Exception Description: The expression has not been initialized correctly. Only a single ExpressionBuilder should be used for a query.
For parallel expressions, the query class must be provided to the ExpressionBuilder constructor, and the query's ExpressionBuilder must
always be on the left side of the expression.
Expression: [
Base my.jpa.test.Department]
Query: ReadAllQuery(referenceClass=Department sql="SELECT ID, NAME FROM DEPARTMENT")
at org.eclipse.persistence.exceptions.QueryException.noExpressionBuilderFound(QueryException.java:904)
at org.eclipse.persistence.expressions.ExpressionBuilder.getDescriptor(ExpressionBuilder.java:195)
at org.eclipse.persistence.internal.expressions.DataExpression.getContainingDescriptor(DataExpression.java:214)
at org.eclipse.persistence.internal.expressions.DataExpression.getMapping(DataExpression.java:221)
at org.eclipse.persistence.internal.expressions.QueryKeyExpression.getMapping(QueryKeyExpression.java:426)
at org.eclipse.persistence.mappings.DatabaseMapping.extractNestedExpressions(DatabaseMapping.java:464)
at org.eclipse.persistence.mappings.ForeignReferenceMapping.prepareNestedBatchQuery(ForeignReferenceMapping.java:882)
at org.eclipse.persistence.mappings.ForeignReferenceMapping.batchedValueFromRow(ForeignReferenceMapping.java:220)
at org.eclipse.persistence.mappings.ForeignReferenceMapping.valueFromRow(ForeignReferenceMapping.java:2046)
at org.eclipse.persistence.mappings.ForeignReferenceMapping.buildCloneFromRow(ForeignReferenceMapping.java:289)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildAttributesIntoWorkingCopyClone(ObjectBuilder.java:1617)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildWorkingCopyCloneFromRow(ObjectBuilder.java:1769)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObjectInUnitOfWork(ObjectBuilder.java:672)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:609)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:564)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:777)
at org.eclipse.persistence.queries.ReadAllQuery.registerResultInUnitOfWork(ReadAllQuery.java:797)
at org.eclipse.persistence.queries.ReadAllQuery.executeObjectLevelReadQuery(ReadAllQuery.java:434)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1150)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:852)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1109)
at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:393)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1197)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2879)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1607)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1589)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1554)
at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:231)
at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:411)
at my.jpa.test.Department.main(Department.java:124)
Если я использую непосредственно @BatchFetch
аннотация на @OneToMany
отношения, то запрос работает отлично.
Как я могу исправить эту проблему, не используя аннотации (поскольку аннотации вызовут пакетный запрос для всех моих запросов)?
Если вам нужна дополнительная информация или полный код, я с радостью ее предоставлю.
2 ответа
Недавно я боролся с той же проблемой, и, к сожалению, не смог ее решить. В качестве обходного пути я определил SELECT с аннотацией @NamedQuery в родительской сущности:
@Entity
@NamedQueries
(
{
@NamedQuery(name = "Department.batchFetch", query = "Select d from Department d",
hints =
{
(...)
// works with JOIN/EXISTS too
@QueryHint(name = QueryHints.BATCH_TYPE, value = "IN"),
@QueryHint(name = QueryHints.BATCH, value = "d.employees.addresses")
}),
}
)
public class Person
{
(...)
}
Метод, вызывающий NamedQuery, будет иметь:
Query query = em.createNamedQuery(queryName); // "Department.batchFetch"
List<Department> result = query.getResultList();
Это не совсем то, что я хотел (я не могу настроить сущности, которые я хочу загрузить во время выполнения), но это лучше, чем если бы ВСЕ запросы использовали пакетную выборку с @BatchQuery для меня.
Я решил проблему, используя настройщик и создав класс, который расширяет OneToOneMapping (но я думаю, что он работает и для ManyToOneMapping), переопределив метод prepareNestedBatchQuery.
Заменить:
if (expressionBuilder.getQueryClass() == null) {
expressionBuilder.setSession(query.getSession().getRootSession(null));
expressionBuilder.setQueryClass(query.getReferenceClass());
}
с:
if (expressionBuilder.getQueryClass() == null) {
expressionBuilder.setSession(query.getSession().getRootSession(null));
expressionBuilder.setQueryClass(query.getReferenceClass());
} else if (expressionBuilder.getSession() == null) {
expressionBuilder.setSession(query.getSession().getRootSession(null));
}
Ваш DescriptorCustomizer должен вызвать descriptor.setMappings с новым вектором сопоставлений.