DATAJPA-476 - Mitigate spec violations in Hibernate for query creation.

Hibernate invalidly returns null for getModel() on its PluralAttribute implementation which causes the necessity for joins not having been detected previously.

We now fall back to joining in case we don't find a Model and deal with a PluralAttribute.
This commit is contained in:
Oliver Gierke
2014-03-04 11:20:48 +01:00
parent bc9ee616ef
commit 8a1b4365a2
3 changed files with 70 additions and 7 deletions

View File

@@ -50,6 +50,7 @@ import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.Attribute.PersistentAttributeType;
import javax.persistence.metamodel.Bindable;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.PluralAttribute;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.domain.Sort;
@@ -439,22 +440,25 @@ public abstract class QueryUtils {
static <T> Expression<T> toExpressionRecursively(From<?, ?> from, PropertyPath property) {
Bindable<?> propertyPathModel = null;
if (from.getModel() instanceof ManagedType) {
Bindable<?> model = from.getModel();
String segment = property.getSegment();
if (model instanceof ManagedType) {
/*
* Required to keep support for EclipseLink 2.4.x. TODO: Remove once we drop that (probably Dijkstra M1)
* See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=413892
*/
propertyPathModel = (Bindable<?>) ((ManagedType<?>) from.getModel()).getAttribute(property.getSegment());
propertyPathModel = (Bindable<?>) ((ManagedType<?>) model).getAttribute(segment);
} else {
propertyPathModel = from.get(property.getSegment()).getModel();
propertyPathModel = from.get(segment).getModel();
}
if (requiresJoin(propertyPathModel)) {
Join<?, ?> join = getOrCreateJoin(from, property.getSegment());
if (requiresJoin(propertyPathModel, model instanceof PluralAttribute)) {
Join<?, ?> join = getOrCreateJoin(from, segment);
return (Expression<T>) (property.hasNext() ? toExpressionRecursively(join, property.next()) : join);
} else {
Path<Object> path = from.get(property.getSegment());
Path<Object> path = from.get(segment);
return (Expression<T>) (property.hasNext() ? toExpressionRecursively(path, property.next()) : path);
}
}
@@ -464,9 +468,14 @@ public abstract class QueryUtils {
* non-optional association.
*
* @param propertyPathModel must not be {@literal null}.
* @param for
* @return
*/
private static boolean requiresJoin(Bindable<?> propertyPathModel) {
private static boolean requiresJoin(Bindable<?> propertyPathModel, boolean forPluralAttribute) {
if (propertyPathModel == null && forPluralAttribute) {
return true;
}
if (!(propertyPathModel instanceof Attribute)) {
return false;

View File

@@ -18,7 +18,14 @@ package org.springframework.data.jpa.repository.query;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Persistence;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
@@ -105,7 +112,42 @@ public class QueryUtilsIntegrationTests {
assertThat(root.getJoins(), hasSize(1));
}
/**
* @see DATAJPA-476
*/
@Test
public void traversesPluralAttributeCorrectly() {
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("merchant");
CriteriaBuilder builder = entityManagerFactory.createEntityManager().getCriteriaBuilder();
CriteriaQuery<Merchant> query = builder.createQuery(Merchant.class);
Root<Merchant> root = query.from(Merchant.class);
QueryUtils.toExpressionRecursively(root, PropertyPath.from("employeesCredentialsUid", Merchant.class));
}
protected void assertNoJoinRequestedForOptionalAssociation(Root<Order> root) {
assertThat(root.getJoins(), is(empty()));
}
@Entity
static class Merchant {
@Id String id;
@OneToMany Set<Employee> employees;
}
@Entity
static class Employee {
@Id String id;
@OneToMany Set<Credential> credentials;
}
@Entity
static class Credential {
@Id String id;
String uid;
}
}

View File

@@ -55,6 +55,18 @@
</properties>
</persistence-unit>
<!-- DATAJPA-476 -->
<persistence-unit name="merchant">
<class>org.springframework.data.jpa.domain.sample.User</class>
<class>org.springframework.data.jpa.repository.query.QueryUtilsIntegrationTests$Merchant</class>
<class>org.springframework.data.jpa.repository.query.QueryUtilsIntegrationTests$Employee</class>
<class>org.springframework.data.jpa.repository.query.QueryUtilsIntegrationTests$Credential</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
</properties>
</persistence-unit>
<!-- Custom PUs for metadata tests -->
<persistence-unit name="metadata">