Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.6.0.BUILD-SNAPSHOT</version>
<version>1.6.0.DATAJPA-466-SNAPSHOT</version>

<name>Spring Data JPA</name>
<description>Spring Data module for JPA repositories.</description>
Expand Down Expand Up @@ -138,12 +138,6 @@
</dependency>

<!-- Persistence providers -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.eclipse.persistence</groupId>
Expand All @@ -152,6 +146,13 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa-persistence-jdbc</artifactId>
Expand Down
118 changes: 118 additions & 0 deletions src/main/java/org/springframework/data/jpa/domain/JpaEntityGraph.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.domain;

import javax.persistence.EntityGraph;

import org.springframework.util.Assert;

/**
* EntityGraph configuration for JPA 2.1 {@link EntityGraph}s.
*
* @author Thomas Darimont
* @since 1.6
*/
public class JpaEntityGraph {

private final String name;
private final EntityGraphType type;

/**
* Creates an {@link JpaEntityGraph}.
*
* @param name must not be {@null}.
* @param type must not be {@null}.
*/
public JpaEntityGraph(String name, EntityGraphType type) {

Assert.notNull(name, "Name must not be null!");
Assert.notNull(type, "FetchGraphType must not be null!");
Assert.hasText(name, "The name of an EntityGraph must not be empty!");

this.name = name;
this.type = type;
}

/**
* Returns the name of the {@link EntityGraph} configuration to use.
*
* @return
*/
public String getName() {
return name;
}

/**
* Returns the {@link EntityGraphType} of the {@link EntityGraph} to use.
*
* @return
*/
public EntityGraphType getType() {
return type;
}

/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "JpaEntityGraph [name=" + name + ", type=" + type + "]";
}

/**
* Enum for JPA 2.1 {@link javax.persistence.EntityGraph} types.
*
* @author Thomas Darimont
* @since 1.6
*/
public enum EntityGraphType {

/**
* Fetch Graph Semantics
* <p>
* When the javax.persistence.loadgraph property is used to specify an entity graph, attributes that are specified
* by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are
* treated according to their specified or default FetchType.
* <p>
*
* @see JPA 2.1 Specification: 3.7.4.1 Fetch Graph Semantics
*/
LOAD("javax.persistence.loadgraph"),

/**
* Fetch Graph Semantics:
* <p>
* When the javax.persistence.fetchgraph property is used to specify an entity graph, attributes that are specified
* by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are
* treated as FetchType.LAZY
* <p>
*
* @see JPA 2.1 Specification: 3.7.4.2 Load Graph Semantics
*/
FETCH("javax.persistence.fetchgraph");

private final String key;

private EntityGraphType(String value) {
this.key = value;
}

public String getKey() {
return key;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.repository;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.data.annotation.QueryAnnotation;
import org.springframework.data.jpa.domain.JpaEntityGraph.EntityGraphType;

/**
* Annotation to configure the JPA 2.1 {@link javax.persistence.EntityGraph}s that should be used on repository methods.
*
* @author Thomas Darimont
* @since 1.6
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@QueryAnnotation
@Documented
public @interface EntityGraph {

/**
* The name of the EntityGraph to use.
*
* @return
*/
String value();

/**
* The {@link Type} of the EntityGraph to use, defaults to {@link Type#FETCH}.
*
* @return
*/
EntityGraphType type() default EntityGraphType.FETCH;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import javax.persistence.QueryHint;
import javax.persistence.TypedQuery;

import org.springframework.data.jpa.domain.JpaEntityGraph;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.CollectionExecution;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.PagedExecution;
Expand Down Expand Up @@ -121,12 +123,26 @@ protected JpaQueryExecution getExecution() {
private <T extends Query> T applyHints(T query, JpaQueryMethod method) {

for (QueryHint hint : method.getHints()) {
query.setHint(hint.name(), hint.value());
applyQueryHint(query, hint);
}

return query;
}

/**
* Protected to be able to customize in sub-classes.
*
* @param query must not be {@literal null}
* @param hint must not be {@literal null}
*/
protected <T extends Query> void applyQueryHint(T query, QueryHint hint) {

Assert.notNull(query, "Query must not be null!");
Assert.notNull(hint, "QueryHint must not be null!");

query.setHint(hint.name(), hint.value());
}

/**
* Applies the {@link LockModeType} provided by the {@link JpaQueryMethod} to the given {@link Query}.
*
Expand All @@ -145,7 +161,28 @@ protected ParameterBinder createBinder(Object[] values) {
}

protected Query createQuery(Object[] values) {
return applyLockMode(applyHints(doCreateQuery(values), method), method);
return applyLockMode(applyEntityGraphConfiguration(applyHints(doCreateQuery(values), method), method), method);
}

/**
* Configures the {@link javax.persistence.EntityGraph} to use for the given {@link JpaQueryMethod} if the
* {@link EntityGraph} annotation is present.
*
* @param query must not be {@literal null}.
* @param method must not be {@literal null}.
* @return
*/
private Query applyEntityGraphConfiguration(Query query, JpaQueryMethod method) {

Assert.notNull(query, "Query must not be null!");
Assert.notNull(method, "JpaQueryMethod must not be null!");

JpaEntityGraph entityGraph = method.getEntityGraph();
if (entityGraph != null) {
Jpa21QueryCustomizer.INSTANCE.tryConfigureFetchGraph(em, query, entityGraph);
}

return query;
}

protected TypedQuery<Long> createCountQuery(Object[] values) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.repository.query;

import java.lang.reflect.Method;

import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.springframework.data.jpa.domain.JpaEntityGraph;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

/**
* Customizes a given JPA query with JPA 2.1 features.
*
* @author Thomas Darimont
* @since 1.6
*/
enum Jpa21QueryCustomizer {

INSTANCE;

private static final boolean JPA21_AVAILABLE = ClassUtils.isPresent("javax.persistence.NamedEntityGraph",
Jpa21QueryCustomizer.class.getClassLoader());

private static final Method GET_ENTITY_GRAPH_METHOD;

static {

if (JPA21_AVAILABLE) {
GET_ENTITY_GRAPH_METHOD = ReflectionUtils.findMethod(EntityManager.class, "getEntityGraph", String.class);
} else {
GET_ENTITY_GRAPH_METHOD = null;
}
}

/**
* Adds a JPA 2.1 fetch-graph or load-graph hint to the given {@link Query} if running under JPA 2.1.
*
* @see JPA 2.1 Specfication 3.7.4 - Use of Entity Graphs in find and query operations P.117
* @param em must not be {@literal null}
* @param query must not be {@literal null}
* @param entityGraph must not be {@literal null}
*/
public void tryConfigureFetchGraph(EntityManager em, Query query, JpaEntityGraph entityGraph) {

Assert.notNull(em, "EntityManager must not be null!");
Assert.notNull(query, "Query must not be null!");
Assert.notNull(entityGraph, "EntityGraph must not be null!");
Assert.isTrue(JPA21_AVAILABLE, "The EntityGraph-Feature requires at least a JPA 2.1 persistence provider!");
Assert.isTrue(GET_ENTITY_GRAPH_METHOD != null,
"It seems that you have the JPA 2.1 API but a JPA 2.0 implementation on the classpath!");

EntityGraph<?> graph = em.getEntityGraph(entityGraph.getName());

if (graph == null) {
return;
}

query.setHint(entityGraph.getType().getKey(), graph);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import javax.persistence.QueryHint;

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.jpa.domain.JpaEntityGraph;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
Expand Down Expand Up @@ -143,6 +145,23 @@ LockModeType getLockModeType() {
return (LockModeType) AnnotationUtils.getValue(annotation);
}

/**
* Returns the {@link EntityGraph} to be used for the query.
*
* @return
* @since 1.6
*/
JpaEntityGraph getEntityGraph() {

EntityGraph annotation = findAnnotation(method, EntityGraph.class);

if (annotation == null) {
return null;
}

return new JpaEntityGraph(annotation.value(), annotation.type());
}

/**
* Returns whether the potentially configured {@link QueryHint}s shall be applied when triggering the count query for
* pagination.
Expand Down
Loading