diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..06a9297
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,7 @@
+language: java
+jdk:
+ - oraclejdk8
+before_install:
+ - curl -o $HOME/.m2/settings.xml https://raw.githubusercontent.com/Cosium/spring-data-jpa-entity-graph/$TRAVIS_BRANCH/src/site/resources/settings.xml
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
\ No newline at end of file
diff --git a/README.md b/README.md
index 0978adb..8afe82e 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,37 @@
-### Why?
+[](https://travis-ci.org/Cosium/spring-data-jpa-entity-graph)
+[](https://codecov.io/gh/Cosium/spring-data-jpa-entity-graph)
+[](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.cosium.spring.data%22%20AND%20a%3A%22spring-data-jpa-entity-graph%22)
-Today, Spring Data JPA supports EntityGraph exlusively through annotations.
+# Spring Data JPA EntityGraph
+
+Today, [Spring Data JPA](https://github.com/spring-projects/spring-data-jpa) supports EntityGraph exlusively through annotations.
Thus, for a method, the choice of EntityGraph must be made before compilation.
-This library gives the ability to pass EntityGraph on any Spring Data JPA repository method as an argument, making the EntityGraph choice fully dynamic.
+This extension gives the ability to pass EntityGraph on any Spring Data JPA repository method as an argument, making the EntityGraph choice fully dynamic.
+
+Example:
+```java
+productRepository.findByName("foo", EntityGraphUtils.fromName("Product.brand"));
+```
+
+## Compatibility
+
+This library follows the Spring Data JPA versionning semantic.
+
+spring-data-jpa-entity-graph | spring-data-jpa
+---------------------------- | ---------------
+1.11.x | 1.11.y
+1.10.x | 1.10.y
-### Quick start in 3 steps
+## Quick start
-1. Add the dependency :
+1. In addition to spring-data-jpa, add the library dependency :
```xml
com.cosium.spring.dataspring-data-jpa-entity-graph
- 1.11.00-SNAPSHOT
+ 1.10.13
```
2. In your Spring configuration, set the repository factory bean class to `JpaEntityGraphRepositoryFactoryBean` :
@@ -27,7 +45,7 @@ This library gives the ability to pass EntityGraph on any Spring Data JPA reposi
```
3. Make sure your repositories extend `JpaEntityGraphRepository`, `JpaEntityGraphSpecificationExecutor` and/or `JpaEntityGraphQueryDslPredicateExecutor`
-### Usage
+## Basic Usage
Let's consider the following entities and repository :
```java
@@ -63,14 +81,44 @@ public interface ProductRepository extends JpaEntityGraphRepository
-
+4.0.0
+ Spring Data JPA EntityGraph
+ A Spring Data JPA extension allowing full usage of JPA EntityGraph on repositories
+ https://github.com/Cosium/spring-data-jpa-entity-graph
+
com.cosium.spring.dataspring-data-jpa-entity-graph
- 1.11.00-SNAPSHOT
+ 1.10.14-SNAPSHOT1.6
- 1.11.0.BUILD-SNAPSHOT
+ 1.10.5.RELEASE2.6.24.1.32.5.31.3.0
- 1.4.1.RELEASE
+ 1.3.8.RELEASE
1.4.193
@@ -86,6 +88,12 @@
${dbunit}test
+
+ org.assertj
+ assertj-core
+ 3.3.0
+ test
+
@@ -146,7 +154,7 @@
falsetruetrue
- false
+ truereleasedeploy
@@ -168,15 +176,20 @@
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.6
+ org.jacoco
+ jacoco-maven-plugin
+ 0.7.7.201606060606
- sign-artifacts
- deploy
- sign
+ prepare-agent
+
+
+
+ report
+ test
+
+ report
@@ -184,6 +197,54 @@
+
+
+ release-sign-artifacts
+
+
+ performRelease
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.6
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+
+
+
+
+
+
+ ossrh
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
+
+ scm:git:https://github.com/Cosium/spring-data-jpa-entity-graph
+ scm:git:https://github.com/Cosium/spring-data-jpa-entity-graph
+ https://github.com/Cosium/spring-data-jpa-entity-graph
+ HEAD
+
+
Cosiumhttp://www.cosium.com
@@ -198,15 +259,12 @@
-
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
+
+
+ MIT License
+ http://www.opensource.org/licenses/mit-license.php
+ repo
+
+
\ No newline at end of file
diff --git a/release.sh b/release.sh
new file mode 100755
index 0000000..03c6696
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+mvn --batch-mode clean release:prepare release:perform && git push && git push --tags
\ No newline at end of file
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/AbstractEntityGraph.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/AbstractEntityGraph.java
index 5470609..80c07a2 100644
--- a/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/AbstractEntityGraph.java
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/AbstractEntityGraph.java
@@ -1,5 +1,6 @@
package com.cosium.spring.data.jpa.entity.graph.domain;
+import com.google.common.base.MoreObjects;
import org.springframework.util.Assert;
/**
@@ -9,22 +10,52 @@
*/
public abstract class AbstractEntityGraph implements EntityGraph{
- private EntityGraphType type = EntityGraphType.FETCH;
+ protected static final EntityGraphType DEFAULT_ENTITY_GRAPH_TYPE = EntityGraphType.FETCH;
+ private EntityGraphType entityGraphType = DEFAULT_ENTITY_GRAPH_TYPE;
+ private boolean optional;
public AbstractEntityGraph(){}
- public AbstractEntityGraph(EntityGraphType type){
- Assert.notNull(type);
- this.type = type;
+ public AbstractEntityGraph(EntityGraphType entityGraphType){
+ this(entityGraphType, false);
+ }
+
+ public AbstractEntityGraph(EntityGraphType entityGraphType, boolean optional){
+ Assert.notNull(entityGraphType);
+ this.entityGraphType = entityGraphType;
+ this.optional = optional;
}
@Override
- public EntityGraphType getType() {
- return type;
+ public EntityGraphType getEntityGraphType() {
+ return entityGraphType;
+ }
+
+ public void setEntityGraphType(EntityGraphType entityGraphType) {
+ Assert.notNull(entityGraphType);
+ this.entityGraphType = entityGraphType;
}
- public void setType(EntityGraphType type) {
- Assert.notNull(type);
- this.type = type;
+ /**
+ * False by default
+ * @return True if the EntityGraph is optional.
+ * Passing an optional EntityGraph to an unsupported method will not trigger {@link com.cosium.spring.data.jpa.entity.graph.repository.exception.InapplicableEntityGraphException}.
+ */
+ @Override
+ public boolean isOptional() {
+ return optional;
}
+
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("entityGraphType", entityGraphType)
+ .add("optional", optional)
+ .toString();
+ }
+
}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/DynamicEntityGraph.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/DynamicEntityGraph.java
index 999c96b..737f49c 100644
--- a/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/DynamicEntityGraph.java
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/DynamicEntityGraph.java
@@ -3,6 +3,8 @@
import java.util.Collections;
import java.util.List;
+import com.google.common.base.MoreObjects;
+
/**
* Created on 22/11/16.
*
@@ -22,12 +24,20 @@ public DynamicEntityGraph(EntityGraphType type, List attributePaths){
}
@Override
- public List getAttributePaths() {
+ public List getEntityGraphAttributePaths() {
return attributePaths;
}
@Override
- public final String getName() {
+ public final String getEntityGraphName() {
return null;
}
+
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("attributePaths", attributePaths)
+ .toString();
+ }
}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/EntityGraph.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/EntityGraph.java
index c2c7ae8..3931c3a 100644
--- a/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/EntityGraph.java
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/EntityGraph.java
@@ -10,17 +10,23 @@
public interface EntityGraph {
/**
- * @return The type of the entity graph. Can't be null.
+ * @return The type of the entity graph. May be null.
*/
- EntityGraphType getType();
+ EntityGraphType getEntityGraphType();
/**
- * @return The name to use to retrieve the EntityGraph. May be null or empty
+ * @return The name to use to retrieve the EntityGraph. May be null.
*/
- String getName();
+ String getEntityGraphName();
/**
* @return The attribute paths. May be null.
*/
- List getAttributePaths();
+ List getEntityGraphAttributePaths();
+
+ /**
+ * @return True if the EntityGraph is optional.
+ * Passing an optional EntityGraph to an unsupported method will not trigger {@link com.cosium.spring.data.jpa.entity.graph.repository.exception.InapplicableEntityGraphException}.
+ */
+ boolean isOptional();
}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/EntityGraphUtils.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/EntityGraphUtils.java
index 660a956..c49bbba 100644
--- a/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/EntityGraphUtils.java
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/EntityGraphUtils.java
@@ -1,5 +1,7 @@
package com.cosium.spring.data.jpa.entity.graph.domain;
+import java.util.List;
+
/**
* Created on 22/11/16.
*
@@ -7,8 +9,55 @@
*/
public class EntityGraphUtils {
- public static EntityGraph fromName(String name){
+ private static final EntityGraph EMPTY_ENTITY_GRAPH = new EmptyEntityGraph();
+
+ /**
+ * @return An empty EntityGraph
+ */
+ public static EntityGraph empty() {
+ return EMPTY_ENTITY_GRAPH;
+ }
+
+ /**
+ * @param name The name of the targeted EntityGraph
+ * @return An EntityGraph referenced by name
+ */
+ public static EntityGraph fromName(String name) {
return new NamedEntityGraph(name);
}
+ /**
+ *
+ * @param name The name of the targeted EntityGraph
+ * @param optional Is the EntityGraph usage optional?
+ * @return An EntityGraph referenced by name
+ */
+ public static EntityGraph fromName(String name, boolean optional){
+ NamedEntityGraph namedEntityGraph = new NamedEntityGraph(name);
+ namedEntityGraph.setOptional(optional);
+ return namedEntityGraph;
+ }
+
+ private static final class EmptyEntityGraph implements EntityGraph {
+
+ @Override
+ public EntityGraphType getEntityGraphType() {
+ return null;
+ }
+
+ @Override
+ public String getEntityGraphName() {
+ return null;
+ }
+
+ @Override
+ public List getEntityGraphAttributePaths() {
+ return null;
+ }
+
+ @Override
+ public boolean isOptional() {
+ return false;
+ }
+ }
}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/NamedEntityGraph.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/NamedEntityGraph.java
index 7e8f790..7b42357 100644
--- a/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/NamedEntityGraph.java
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/domain/NamedEntityGraph.java
@@ -2,6 +2,7 @@
import java.util.List;
+import com.google.common.base.MoreObjects;
import org.springframework.util.Assert;
/**
@@ -13,25 +14,34 @@ public class NamedEntityGraph extends AbstractEntityGraph {
private final String name;
- public NamedEntityGraph(EntityGraphType type, String name) {
- super(type);
+ public NamedEntityGraph(EntityGraphType type, boolean optional, String name) {
+ super(type, optional);
Assert.hasLength(name);
this.name = name;
}
+ public NamedEntityGraph(EntityGraphType type, String name) {
+ this(type, false, name);
+ }
+
public NamedEntityGraph(String name){
- Assert.hasLength(name);
- this.name = name;
+ this(DEFAULT_ENTITY_GRAPH_TYPE, name);
}
@Override
- public String getName() {
+ public String getEntityGraphName() {
return name;
}
@Override
- public final List getAttributePaths() {
+ public final List getEntityGraphAttributePaths() {
return null;
}
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("name", name)
+ .toString();
+ }
}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/exception/InapplicableEntityGraphException.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/exception/InapplicableEntityGraphException.java
new file mode 100644
index 0000000..315601f
--- /dev/null
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/exception/InapplicableEntityGraphException.java
@@ -0,0 +1,13 @@
+package com.cosium.spring.data.jpa.entity.graph.repository.exception;
+
+/**
+ * Created on 27/11/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+public class InapplicableEntityGraphException extends RepositoryEntityGraphException {
+
+ public InapplicableEntityGraphException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/exception/MultipleDefaultEntityGraphException.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/exception/MultipleDefaultEntityGraphException.java
new file mode 100644
index 0000000..b426f3c
--- /dev/null
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/exception/MultipleDefaultEntityGraphException.java
@@ -0,0 +1,14 @@
+package com.cosium.spring.data.jpa.entity.graph.repository.exception;
+
+
+/**
+ * Created on 27/11/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+public class MultipleDefaultEntityGraphException extends RepositoryEntityGraphException {
+
+ public MultipleDefaultEntityGraphException(String entityGraph1Name, String entityGraph2Name) {
+ super("Multiple default entity graphs detected : " + entityGraph1Name + " and " + entityGraph2Name);
+ }
+}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/exception/MultipleEntityGraphException.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/exception/MultipleEntityGraphException.java
new file mode 100644
index 0000000..61d3bb6
--- /dev/null
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/exception/MultipleEntityGraphException.java
@@ -0,0 +1,13 @@
+package com.cosium.spring.data.jpa.entity.graph.repository.exception;
+
+/**
+ * Created on 27/11/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+public class MultipleEntityGraphException extends RuntimeException {
+
+ public MultipleEntityGraphException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/exception/RepositoryEntityGraphException.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/exception/RepositoryEntityGraphException.java
new file mode 100644
index 0000000..39cea7a
--- /dev/null
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/exception/RepositoryEntityGraphException.java
@@ -0,0 +1,14 @@
+package com.cosium.spring.data.jpa.entity.graph.repository.exception;
+
+/**
+ * Created on 27/11/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+public abstract class RepositoryEntityGraphException extends RuntimeException {
+
+ public RepositoryEntityGraphException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/CountQueryDetector.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/CountQueryDetector.java
new file mode 100644
index 0000000..187fbf6
--- /dev/null
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/CountQueryDetector.java
@@ -0,0 +1,48 @@
+package com.cosium.spring.data.jpa.entity.graph.repository.support;
+
+
+import com.querydsl.jpa.JPQLQuery;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.core.NamedThreadLocal;
+
+/**
+ * Created on 05/12/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+class CountQueryDetector implements MethodInterceptor {
+
+ private static final CountQueryDetector INSTANCE = new CountQueryDetector();
+ private static final String FETCH_METHOD_NAME_PREFIX = "fetch";
+
+ private static final NamedThreadLocal IS_COUNT_QUERY = new NamedThreadLocal("A thread local holding a boolean describing " +
+ "the fact that the current query is count query");
+
+ private CountQueryDetector(){
+
+ }
+
+ static JPQLQuery> proxy(JPQLQuery> countQuery){
+ ProxyFactory proxyFactory = new ProxyFactory(countQuery);
+ proxyFactory.addAdvice(INSTANCE);
+ return (JPQLQuery>) proxyFactory.getProxy();
+ }
+
+ static boolean isCountQuery(){
+ return IS_COUNT_QUERY.get() == null? false: IS_COUNT_QUERY.get();
+ }
+
+ @Override
+ public Object invoke(MethodInvocation invocation) throws Throwable {
+ if(invocation.getMethod().getName().startsWith(FETCH_METHOD_NAME_PREFIX)){
+ IS_COUNT_QUERY.set(true);
+ }
+ try{
+ return invocation.proceed();
+ } finally {
+ IS_COUNT_QUERY.set(false);
+ }
+ }
+}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/EntityGraphAwareQueryDslJpaRepository.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/EntityGraphAwareQueryDslJpaRepository.java
new file mode 100644
index 0000000..9653396
--- /dev/null
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/EntityGraphAwareQueryDslJpaRepository.java
@@ -0,0 +1,31 @@
+package com.cosium.spring.data.jpa.entity.graph.repository.support;
+
+import javax.persistence.EntityManager;
+import java.io.Serializable;
+
+import com.querydsl.core.types.Predicate;
+import com.querydsl.jpa.JPQLQuery;
+import org.springframework.data.jpa.repository.support.JpaEntityInformation;
+import org.springframework.data.jpa.repository.support.QueryDslJpaRepository;
+import org.springframework.data.querydsl.EntityPathResolver;
+
+/**
+ * Created on 05/12/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+class EntityGraphAwareQueryDslJpaRepository extends QueryDslJpaRepository {
+
+ public EntityGraphAwareQueryDslJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager) {
+ super(entityInformation, entityManager);
+ }
+
+ public EntityGraphAwareQueryDslJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager, EntityPathResolver resolver) {
+ super(entityInformation, entityManager, resolver);
+ }
+
+ @Override
+ protected JPQLQuery> createCountQuery(Predicate predicate) {
+ return CountQueryDetector.proxy(super.createCountQuery(predicate));
+ }
+}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/EntityGraphBean.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/EntityGraphBean.java
index 4a5cbb0..bc1f0cf 100644
--- a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/EntityGraphBean.java
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/EntityGraphBean.java
@@ -1,25 +1,49 @@
package com.cosium.spring.data.jpa.entity.graph.repository.support;
+import com.google.common.base.MoreObjects;
+import org.springframework.core.ResolvableType;
import org.springframework.data.jpa.repository.query.JpaEntityGraph;
import org.springframework.util.Assert;
/**
* Wrapper class allowing to hold a {@link JpaEntityGraph} with its associated domain class.
- *
* Created on 23/11/16.
*
* @author Reda.Housni-Alaoui
*/
class EntityGraphBean {
+
private final JpaEntityGraph jpaEntityGraph;
private final Class> domainClass;
+ private final ResolvableType repositoryMethodReturnType;
+ private final boolean optional;
+ private final boolean primary;
+ private final boolean valid;
- public EntityGraphBean(JpaEntityGraph jpaEntityGraph, Class> domainClass) {
+ public EntityGraphBean(JpaEntityGraph jpaEntityGraph, Class> domainClass, ResolvableType repositoryMethodReturnType, boolean optional, boolean primary) {
Assert.notNull(jpaEntityGraph);
Assert.notNull(domainClass);
+ Assert.notNull(repositoryMethodReturnType);
this.jpaEntityGraph = jpaEntityGraph;
this.domainClass = domainClass;
+ this.repositoryMethodReturnType = repositoryMethodReturnType;
+ this.optional = optional;
+ this.primary = primary;
+ this.valid = computeValidity();
+ }
+
+ private boolean computeValidity() {
+ Class> resolvedReturnType = repositoryMethodReturnType.resolve();
+ if (Void.TYPE.equals(resolvedReturnType) || domainClass.isAssignableFrom(resolvedReturnType)) {
+ return true;
+ }
+ for (Class genericType : repositoryMethodReturnType.resolveGenerics()) {
+ if (domainClass.isAssignableFrom(genericType)) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -35,4 +59,35 @@ public JpaEntityGraph getJpaEntityGraph() {
public Class> getDomainClass() {
return domainClass;
}
+
+ /**
+ * @return True if this entity graph is not mandatory
+ */
+ public boolean isOptional() {
+ return optional;
+ }
+
+ /**
+ * @return True if this EntityGraph seems valid
+ */
+ public boolean isValid() {
+ return valid;
+ }
+
+ /**
+ * @return True if this EntityGraph is a primary one. Default EntityGraph is an example of non primary EntityGraph.
+ */
+ public boolean isPrimary() {
+ return primary;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("jpaEntityGraph", jpaEntityGraph)
+ .add("domainClass", domainClass)
+ .add("repositoryMethodReturnType", repositoryMethodReturnType)
+ .add("optional", optional)
+ .toString();
+ }
}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/JpaEntityGraphRepositoryFactory.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/JpaEntityGraphRepositoryFactory.java
index f835915..b93f684 100644
--- a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/JpaEntityGraphRepositoryFactory.java
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/JpaEntityGraphRepositoryFactory.java
@@ -9,7 +9,6 @@
import java.util.List;
import com.cosium.spring.data.jpa.entity.graph.domain.EntityGraph;
-import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.repository.core.RepositoryMetadata;
@@ -62,7 +61,7 @@ private static void addEntityGraphToSpecialTypes(Class> clazz, String fieldNam
*/
public JpaEntityGraphRepositoryFactory(EntityManager entityManager) {
super(entityManager);
- addRepositoryProxyPostProcessor(new RepositoryMethodEntityGraphExtractor());
+ addRepositoryProxyPostProcessor(new RepositoryMethodEntityGraphExtractor(entityManager));
}
@Override
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/QueryDslJpaEntityGraphRepository.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/QueryDslJpaEntityGraphRepository.java
index 6105f1a..ff98722 100644
--- a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/QueryDslJpaEntityGraphRepository.java
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/QueryDslJpaEntityGraphRepository.java
@@ -28,13 +28,13 @@ public class QueryDslJpaEntityGraphRepository
public QueryDslJpaEntityGraphRepository(JpaEntityInformation entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
- this.queryDslJpaRepositoryDelegate = new QueryDslJpaRepository((JpaEntityInformation) entityInformation, entityManager);
+ this.queryDslJpaRepositoryDelegate = new EntityGraphAwareQueryDslJpaRepository((JpaEntityInformation) entityInformation, entityManager);
}
public QueryDslJpaEntityGraphRepository(JpaEntityInformation entityInformation, EntityManager entityManager,
EntityPathResolver resolver) {
super(entityInformation, entityManager);
- this.queryDslJpaRepositoryDelegate = new QueryDslJpaRepository(entityInformation, entityManager, resolver);
+ this.queryDslJpaRepositoryDelegate = new EntityGraphAwareQueryDslJpaRepository(entityInformation, entityManager, resolver);
}
@Override
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/QueryHintsUtils.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/QueryHintsUtils.java
new file mode 100644
index 0000000..44a00c8
--- /dev/null
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/QueryHintsUtils.java
@@ -0,0 +1,45 @@
+package com.cosium.spring.data.jpa.entity.graph.repository.support;
+
+import javax.persistence.EntityManager;
+import java.util.Map;
+
+import org.springframework.data.jpa.repository.EntityGraph;
+import org.springframework.data.jpa.repository.query.Jpa21Utils;
+
+/**
+ * Created on 24/11/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+class QueryHintsUtils {
+ /**
+ * @param queryHints
+ * @return True if the QueryHints already hold an EntityGraph
+ */
+ static boolean containsEntityGraph(Map queryHints){
+ return queryHints != null
+ && (queryHints.containsKey(EntityGraph.EntityGraphType.FETCH.getKey())
+ || queryHints.containsKey(EntityGraph.EntityGraphType.LOAD.getKey()));
+ }
+
+ /**
+ * Remove all EntityGraph pre existing in the QueryHints
+ * @param queryHints
+ */
+ static void removeEntityGraphs(Map queryHints){
+ if(queryHints == null){
+ return;
+ }
+ queryHints.remove(EntityGraph.EntityGraphType.FETCH.getKey());
+ queryHints.remove(EntityGraph.EntityGraphType.LOAD.getKey());
+ }
+
+
+ static Map buildQueryHints(EntityManager entityManager, EntityGraphBean entityGraph) {
+ return Jpa21Utils.tryGetFetchGraphHints(
+ entityManager,
+ entityGraph.getJpaEntityGraph(),
+ entityGraph.getDomainClass()
+ );
+ }
+}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/RepositoryEntityManagerEntityGraphInjector.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/RepositoryEntityManagerEntityGraphInjector.java
index 3c3507c..852dde0 100644
--- a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/RepositoryEntityManagerEntityGraphInjector.java
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/RepositoryEntityManagerEntityGraphInjector.java
@@ -2,12 +2,14 @@
import javax.persistence.EntityManager;
import javax.persistence.Query;
+import javax.persistence.criteria.CriteriaQuery;
import java.util.*;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.ProxyFactory;
-import org.springframework.data.jpa.repository.query.Jpa21Utils;
/**
* Injects captured {@link org.springframework.data.jpa.repository.query.JpaEntityGraph} into query hints.
@@ -19,6 +21,8 @@
*/
class RepositoryEntityManagerEntityGraphInjector implements MethodInterceptor {
+ private static final Logger LOG = LoggerFactory.getLogger(RepositoryEntityManagerEntityGraphInjector.class);
+
/**
* The list of methods that can take a map of query hints as an argument
*/
@@ -28,6 +32,9 @@ class RepositoryEntityManagerEntityGraphInjector implements MethodInterceptor {
*/
private static final List CREATE_QUERY_METHODS = Arrays.asList("createQuery", "createNamedQuery");
+ private RepositoryEntityManagerEntityGraphInjector() {
+ }
+
/**
* Builds a proxy on entity manager which is aware of methods that can make use of query hints.
*
@@ -42,48 +49,47 @@ static EntityManager proxy(EntityManager entityManager) {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
+ EntityGraphBean entityGraphCandidate = RepositoryMethodEntityGraphExtractor.getCurrentJpaEntityGraph();
String methodName = invocation.getMethod().getName();
- if (FIND_METHODS.contains(methodName)) {
- addEntityGraph(invocation);
+ boolean hasEntityGraphCandidate = entityGraphCandidate != null;
+ if (hasEntityGraphCandidate && FIND_METHODS.contains(methodName)) {
+ addEntityGraphToFindMethodQueryHints(entityGraphCandidate, invocation);
}
+
Object result = invocation.proceed();
- if (CREATE_QUERY_METHODS.contains(methodName)) {
- addEntityGraph(invocation, (Query) result);
+
+ if (hasEntityGraphCandidate
+ && CREATE_QUERY_METHODS.contains(methodName)
+ && isQueryCreationEligible(entityGraphCandidate, invocation)) {
+ result = RepositoryQueryEntityGraphInjector.proxy((Query) result, (EntityManager) invocation.getThis(), entityGraphCandidate);
}
return result;
}
- private Map getCurrentQueryHints(EntityManager entityManager) {
- EntityGraphBean entityGraphBean = RepositoryMethodEntityGraphExtractor.getCurrentJpaEntityGraph();
- if (entityGraphBean == null) {
- return new HashMap();
+ private boolean isQueryCreationEligible(EntityGraphBean entityGraphCandidate, MethodInvocation invocation) {
+ Class> resultType = null;
+ for (Object argument : invocation.getArguments()) {
+ if (argument instanceof Class) {
+ resultType = (Class>) argument;
+ break;
+ } else if (argument instanceof CriteriaQuery) {
+ resultType = ((CriteriaQuery) argument).getResultType();
+ break;
+ }
}
- return Jpa21Utils.tryGetFetchGraphHints(
- entityManager,
- entityGraphBean.getJpaEntityGraph(),
- entityGraphBean.getDomainClass()
- );
+ return resultType == null || resultType.equals(entityGraphCandidate.getDomainClass());
}
- /**
- * Push the current entity graph to the created query
- *
- * @param invocation The method invocation
- * @param query The query to populate
- */
- private void addEntityGraph(MethodInvocation invocation, Query query) {
- Map hints = getCurrentQueryHints((EntityManager) invocation.getThis());
- for (Map.Entry hint : hints.entrySet()) {
- query.setHint(hint.getKey(), hint.getValue());
- }
- }
/**
* Push the current entity graph to the find method query hints.
*
+ * @param entityGraphCandidate The EntityGraph to set
* @param invocation The invocation of the find method
*/
- private void addEntityGraph(MethodInvocation invocation) {
+ private void addEntityGraphToFindMethodQueryHints(EntityGraphBean entityGraphCandidate, MethodInvocation invocation) {
+ LOG.trace("Trying to push the EntityGraph candidate to the query hints find method");
+
Map queryProperties = null;
int index = 0;
for (Object argument : invocation.getArguments()) {
@@ -94,11 +100,17 @@ private void addEntityGraph(MethodInvocation invocation) {
index++;
}
if (queryProperties == null) {
+ LOG.trace("No query hints passed to the find method.");
+ return;
+ }
+ if (!entityGraphCandidate.isPrimary() && QueryHintsUtils.containsEntityGraph(queryProperties)) {
+ LOG.trace("The query hints passed with the find method already hold an entity graph. Overriding aborted because the candidate EntityGraph is optional.");
return;
}
queryProperties = new HashMap(queryProperties);
- queryProperties.putAll(getCurrentQueryHints((EntityManager) invocation.getThis()));
+ QueryHintsUtils.removeEntityGraphs(queryProperties);
+ queryProperties.putAll(QueryHintsUtils.buildQueryHints((EntityManager) invocation.getThis(), entityGraphCandidate));
invocation.getArguments()[index] = queryProperties;
}
}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/RepositoryMethodEntityGraphExtractor.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/RepositoryMethodEntityGraphExtractor.java
index d15b864..ed48895 100644
--- a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/RepositoryMethodEntityGraphExtractor.java
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/RepositoryMethodEntityGraphExtractor.java
@@ -1,12 +1,21 @@
package com.cosium.spring.data.jpa.entity.graph.repository.support;
+import javax.persistence.EntityManager;
import java.util.List;
import com.cosium.spring.data.jpa.entity.graph.domain.EntityGraph;
+import com.cosium.spring.data.jpa.entity.graph.domain.EntityGraphUtils;
+import com.cosium.spring.data.jpa.entity.graph.repository.exception.InapplicableEntityGraphException;
+import com.cosium.spring.data.jpa.entity.graph.repository.exception.MultipleDefaultEntityGraphException;
+import com.cosium.spring.data.jpa.entity.graph.repository.exception.MultipleEntityGraphException;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.core.NamedThreadLocal;
+import org.springframework.core.ResolvableType;
import org.springframework.data.jpa.repository.query.JpaEntityGraph;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor;
@@ -21,35 +30,152 @@
*/
class RepositoryMethodEntityGraphExtractor implements RepositoryProxyPostProcessor {
- private static final ThreadLocal CURRENT_ENTITY_GRAPH =
- new NamedThreadLocal("Thread local holding the current spring data jpa repository entity graph");
+ private static final Logger LOG = LoggerFactory.getLogger(RepositoryMethodEntityGraphExtractor.class);
+
+ private static final ThreadLocal CURRENT_REPOSITORY =
+ new NamedThreadLocal("Thread local holding the current repository");
+
+ private final EntityManager entityManager;
+
+ RepositoryMethodEntityGraphExtractor(EntityManager entityManager) {
+ this.entityManager = entityManager;
+ }
@Override
public void postProcess(ProxyFactory factory, RepositoryInformation repositoryInformation) {
- factory.addAdvice(new JpaEntityGraphMethodInterceptor(repositoryInformation.getDomainType()));
+ factory.addAdvice(new JpaEntityGraphMethodInterceptor(entityManager, repositoryInformation.getDomainType()));
}
static EntityGraphBean getCurrentJpaEntityGraph() {
- return CURRENT_ENTITY_GRAPH.get();
+ JpaEntityGraphMethodInterceptor currentRepository = CURRENT_REPOSITORY.get();
+ if (currentRepository == null) {
+ return null;
+ }
+ return currentRepository.getCurrentJpaEntityGraph();
+ }
+
+ /**
+ * @param entityGraph
+ * @return True if the provided EntityGraph is empty
+ */
+ private static boolean isEmpty(EntityGraph entityGraph) {
+ return entityGraph == null ||
+ (
+ entityGraph.getEntityGraphAttributePaths() == null
+ && entityGraph.getEntityGraphName() == null
+ && entityGraph.getEntityGraphType() == null
+ );
}
private static class JpaEntityGraphMethodInterceptor implements MethodInterceptor {
+ private static final String DEFAULT_ENTITYGRAPH_NAME_SUFFIX = ".default";
private final Class> domainClass;
+ private final EntityGraph defaultEntityGraph;
+ private final ThreadLocal currentEntityGraph = new NamedThreadLocal("Thread local holding the current spring data jpa repository entity graph");
- JpaEntityGraphMethodInterceptor(Class> domainClass) {
+ JpaEntityGraphMethodInterceptor(EntityManager entityManager, Class domainClass) {
this.domainClass = domainClass;
+ this.defaultEntityGraph = findDefaultEntityGraph(entityManager);
+ }
+
+ /**
+ * @param entityManager
+ * @return The default entity graph if it exists. Null otherwise.
+ */
+ private EntityGraph findDefaultEntityGraph(EntityManager entityManager) {
+ EntityGraph defaultEntityGraph = null;
+ List> entityGraphs = (List>) entityManager.getEntityGraphs(domainClass);
+ for (javax.persistence.EntityGraph entityGraph : entityGraphs) {
+ if (entityGraph.getName().endsWith(DEFAULT_ENTITYGRAPH_NAME_SUFFIX)) {
+ if (defaultEntityGraph != null) {
+ throw new MultipleDefaultEntityGraphException(entityGraph.getName(), defaultEntityGraph.getEntityGraphName());
+ }
+ defaultEntityGraph = EntityGraphUtils.fromName(entityGraph.getName(), true);
+ }
+ }
+ return defaultEntityGraph;
+ }
+
+ EntityGraphBean getCurrentJpaEntityGraph() {
+ return currentEntityGraph.get();
+ }
+
+ @Override
+ public Object invoke(MethodInvocation invocation) throws Throwable {
+ JpaEntityGraphMethodInterceptor oldRepo = CURRENT_REPOSITORY.get();
+ CURRENT_REPOSITORY.set(this);
+ try {
+ return doInvoke(invocation);
+ } finally {
+ CURRENT_REPOSITORY.set(oldRepo);
+ }
+ }
+
+ private Object doInvoke(MethodInvocation invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ EntityGraph providedEntityGraph = null;
+ for (Object argument : arguments) {
+ if (!(argument instanceof EntityGraph)) {
+ continue;
+ }
+ EntityGraph newEntityGraph = (EntityGraph) argument;
+ if (providedEntityGraph != null) {
+ throw new MultipleEntityGraphException("Duplicate EntityGraphs detected. '"
+ + providedEntityGraph + "' and '" + newEntityGraph + "' were passed to method " + invocation.getMethod());
+ }
+ providedEntityGraph = newEntityGraph;
+ }
+
+ Class> implementationClass;
+ if (invocation instanceof ReflectiveMethodInvocation) {
+ implementationClass = ((ReflectiveMethodInvocation) invocation).getProxy().getClass();
+ } else {
+ implementationClass = invocation.getThis().getClass();
+ }
+
+ EntityGraphBean entityGraphCandidate = buildEntityGraphCandidate(
+ providedEntityGraph,
+ ResolvableType.forMethodReturnType(invocation.getMethod(), implementationClass)
+ );
+
+ if (entityGraphCandidate != null && !entityGraphCandidate.isValid()) {
+ if (entityGraphCandidate.isOptional()) {
+ LOG.trace("Cannot apply EntityGraph {}", entityGraphCandidate);
+ entityGraphCandidate = null;
+ } else {
+ throw new InapplicableEntityGraphException("Cannot apply EntityGraph " + entityGraphCandidate + " to the the current query");
+ }
+ }
+
+ EntityGraphBean oldEntityGraphCandidate = currentEntityGraph.get();
+ boolean newEntityGraphCandidatePreValidated = entityGraphCandidate != null && (oldEntityGraphCandidate == null || !oldEntityGraphCandidate.isPrimary());
+ if (newEntityGraphCandidatePreValidated) {
+ currentEntityGraph.set(entityGraphCandidate);
+ }
+ try {
+ return invocation.proceed();
+ } finally {
+ if (newEntityGraphCandidatePreValidated) {
+ currentEntityGraph.set(oldEntityGraphCandidate);
+ }
+ }
}
- private EntityGraphBean buildEntityGraphBean(EntityGraph entityGraph) {
- if (entityGraph == null) {
+ private EntityGraphBean buildEntityGraphCandidate(EntityGraph providedEntityGraph, ResolvableType returnType) {
+ boolean isPrimary = true;
+ if (isEmpty(providedEntityGraph)) {
+ providedEntityGraph = defaultEntityGraph;
+ isPrimary = false;
+ }
+ if (providedEntityGraph == null) {
return null;
}
- Assert.notNull(entityGraph.getType());
+ Assert.notNull(providedEntityGraph.getEntityGraphType());
org.springframework.data.jpa.repository.EntityGraph.EntityGraphType type;
- switch (entityGraph.getType()) {
+ switch (providedEntityGraph.getEntityGraphType()) {
case FETCH:
type = org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.FETCH;
break;
@@ -57,41 +183,17 @@ private EntityGraphBean buildEntityGraphBean(EntityGraph entityGraph) {
type = org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.LOAD;
break;
default:
- throw new RuntimeException("Unknown entity graph type");
+ throw new RuntimeException("Unexpected entity graph type '" + providedEntityGraph.getEntityGraphType() + "'");
}
- List attributePaths = entityGraph.getAttributePaths();
+ List attributePaths = providedEntityGraph.getEntityGraphAttributePaths();
JpaEntityGraph jpaEntityGraph = new JpaEntityGraph(
- StringUtils.hasText(entityGraph.getName()) ? entityGraph.getName() : domainClass.getName() + "-_-_-_-_-_-",
+ StringUtils.hasText(providedEntityGraph.getEntityGraphName()) ? providedEntityGraph.getEntityGraphName() : domainClass.getName() + "-_-_-_-_-_-",
type,
attributePaths != null ? attributePaths.toArray(new String[attributePaths.size()]) : null
);
- return new EntityGraphBean(jpaEntityGraph, domainClass);
- }
-
- @Override
- public Object invoke(MethodInvocation invocation) throws Throwable {
- Object[] arguments = invocation.getArguments();
- EntityGraph entityGraph = null;
- for (Object argument : arguments) {
- if (!(argument instanceof EntityGraph)) {
- continue;
- }
- entityGraph = (EntityGraph) argument;
- break;
- }
- EntityGraphBean entityGraphBean = buildEntityGraphBean(entityGraph);
- if (entityGraphBean != null) {
- CURRENT_ENTITY_GRAPH.set(entityGraphBean);
- }
- try {
- return invocation.proceed();
- } finally {
- if (entityGraphBean != null) {
- CURRENT_ENTITY_GRAPH.remove();
- }
- }
+ return new EntityGraphBean(jpaEntityGraph, domainClass, returnType, providedEntityGraph.isOptional(), isPrimary);
}
}
}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/RepositoryQueryEntityGraphInjector.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/RepositoryQueryEntityGraphInjector.java
new file mode 100644
index 0000000..0645b1a
--- /dev/null
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/RepositoryQueryEntityGraphInjector.java
@@ -0,0 +1,67 @@
+package com.cosium.spring.data.jpa.entity.graph.repository.support;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.util.Assert;
+
+/**
+ * Created on 24/11/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+class RepositoryQueryEntityGraphInjector implements MethodInterceptor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RepositoryQueryEntityGraphInjector.class);
+
+ private static final List EXECUTE_QUERY_METHODS = Arrays.asList("getResultList", "getSingleResult");
+
+ private final EntityManager entityManager;
+ private final EntityGraphBean entityGraphCandidate;
+
+ private RepositoryQueryEntityGraphInjector(EntityManager entityManager, EntityGraphBean entityGraphCandidate) {
+ Assert.notNull(entityManager);
+ Assert.notNull(entityGraphCandidate);
+ this.entityManager = entityManager;
+ this.entityGraphCandidate = entityGraphCandidate;
+ }
+
+ static Query proxy(Query query, EntityManager entityManager, EntityGraphBean entityGraphCandidate) {
+ ProxyFactory proxyFactory = new ProxyFactory(query);
+ proxyFactory.addAdvice(new RepositoryQueryEntityGraphInjector(entityManager, entityGraphCandidate));
+ return (Query) proxyFactory.getProxy();
+ }
+
+ @Override
+ public Object invoke(MethodInvocation invocation) throws Throwable {
+ if (EXECUTE_QUERY_METHODS.contains(invocation.getMethod().getName())) {
+ addEntityGraphToQuery((Query) invocation.getThis());
+ }
+ return invocation.proceed();
+ }
+
+ private void addEntityGraphToQuery(Query query) {
+ if (CountQueryDetector.isCountQuery()) {
+ LOG.trace("CountQuery detected.");
+ return;
+ }
+ if (!entityGraphCandidate.isPrimary() && QueryHintsUtils.containsEntityGraph(query.getHints())) {
+ LOG.trace("The query hints passed with the find method already hold an entity graph. Overriding aborted because the candidate EntityGraph is optional.");
+ return;
+ }
+
+ QueryHintsUtils.removeEntityGraphs(query.getHints());
+ Map hints = QueryHintsUtils.buildQueryHints(entityManager, entityGraphCandidate);
+ for (Map.Entry hint : hints.entrySet()) {
+ query.setHint(hint.getKey(), hint.getValue());
+ }
+ }
+}
diff --git a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/SimpleJpaEntityGraphRepository.java b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/SimpleJpaEntityGraphRepository.java
index 1cc430c..098ebeb 100644
--- a/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/SimpleJpaEntityGraphRepository.java
+++ b/src/main/java/com/cosium/spring/data/jpa/entity/graph/repository/support/SimpleJpaEntityGraphRepository.java
@@ -2,9 +2,7 @@
import javax.persistence.EntityManager;
import java.io.Serializable;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import com.cosium.spring.data.jpa.entity.graph.domain.EntityGraph;
import com.cosium.spring.data.jpa.entity.graph.repository.JpaEntityGraphRepository;
@@ -14,8 +12,6 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.data.jpa.repository.query.Jpa21Utils;
-import org.springframework.data.jpa.repository.query.JpaEntityGraph;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
diff --git a/src/site/resources/settings.xml b/src/site/resources/settings.xml
new file mode 100644
index 0000000..3756550
--- /dev/null
+++ b/src/site/resources/settings.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+ default
+
+
+ central
+ central
+ http://repo1.maven.org/maven2
+
+
+ spring-snapshot-local
+ spring-snapshot-local
+ https://repo.spring.io/libs-snapshot-local
+
+
+
+
+
+
+ default
+
+
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/BaseTest.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/BaseTest.java
index ab538bd..86ae4f0 100644
--- a/src/test/java/com/cosium/spring/data/jpa/entity/graph/BaseTest.java
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/BaseTest.java
@@ -1,14 +1,11 @@
package com.cosium.spring.data.jpa.entity.graph;
import com.github.springtestdbunit.DbUnitTestExecutionListener;
-import com.github.springtestdbunit.annotation.DatabaseSetup;
-import com.github.springtestdbunit.annotation.DatabaseTearDown;
import org.junit.runner.RunWith;
-import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureDataJpa;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
-import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
@@ -20,9 +17,8 @@
*/
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class, DbUnitTestExecutionListener.class})
-@RunWith(SpringRunner.class)
+@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DataRepositoryConfiguration.class})
-@AutoConfigureDataJpa
@DirtiesContext
public abstract class BaseTest {
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/DataRepositoryConfiguration.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/DataRepositoryConfiguration.java
index ff5108f..f6bf6d4 100644
--- a/src/test/java/com/cosium/spring/data/jpa/entity/graph/DataRepositoryConfiguration.java
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/DataRepositoryConfiguration.java
@@ -1,6 +1,8 @@
package com.cosium.spring.data.jpa.entity.graph;
import com.cosium.spring.data.jpa.entity.graph.repository.support.JpaEntityGraphRepositoryFactoryBean;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@@ -11,6 +13,7 @@
*/
@Configuration
@EnableJpaRepositories(repositoryFactoryBeanClass = JpaEntityGraphRepositoryFactoryBean.class)
+@EnableAutoConfiguration
public class DataRepositoryConfiguration {
}
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphCustomRepositoryTest.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphCustomRepositoryTest.java
new file mode 100644
index 0000000..301e9d9
--- /dev/null
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphCustomRepositoryTest.java
@@ -0,0 +1,48 @@
+package com.cosium.spring.data.jpa.entity.graph.repository;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.inject.Inject;
+
+import java.util.List;
+
+import com.cosium.spring.data.jpa.entity.graph.BaseTest;
+import com.cosium.spring.data.jpa.entity.graph.domain.EntityGraphUtils;
+import com.cosium.spring.data.jpa.entity.graph.repository.sample.Product;
+import com.cosium.spring.data.jpa.entity.graph.repository.sample.ProductRepository;
+import com.github.springtestdbunit.annotation.DatabaseSetup;
+import com.github.springtestdbunit.annotation.DatabaseTearDown;
+import org.hibernate.Hibernate;
+import org.junit.Test;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Created on 28/11/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+@DatabaseSetup(BaseTest.DATASET)
+@DatabaseTearDown
+public class JpaEntityGraphCustomRepositoryTest extends BaseTest {
+
+ @Inject
+ private ProductRepository productRepository;
+
+ @Test
+ @Transactional
+ public void given_products_when_calling_customvoidmethod_with_eg_then_it_should_work(){
+ productRepository.customMethod(EntityGraphUtils.fromName(Product.BRAND_EG));
+ }
+
+ @Test
+ @Transactional
+ public void given_products_when_calling_customMethodCallingAnotherRepository_with_eg_then_brand_should_be_loaded(){
+ List products = productRepository.customMethodCallingAnotherRepository(EntityGraphUtils.fromName(Product.BRAND_EG));
+ assertThat(products).isNotEmpty();
+ for(Product product: products){
+ assertThat(Hibernate.isInitialized(product.getBrand())).isTrue();
+ }
+ }
+
+
+}
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphQueryDslPredicateExecutorTest.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphQueryDslPredicateExecutorTest.java
index 17b968d..2bdaa1a 100644
--- a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphQueryDslPredicateExecutorTest.java
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphQueryDslPredicateExecutorTest.java
@@ -1,6 +1,6 @@
package com.cosium.spring.data.jpa.entity.graph.repository;
-import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.*;
import javax.inject.Inject;
@@ -12,8 +12,11 @@
import com.cosium.spring.data.jpa.entity.graph.repository.sample.QProduct;
import com.github.springtestdbunit.annotation.DatabaseSetup;
import com.github.springtestdbunit.annotation.DatabaseTearDown;
+import com.querydsl.core.types.Predicate;
import org.hibernate.Hibernate;
import org.junit.Test;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
import org.springframework.transaction.annotation.Transactional;
/**
@@ -30,7 +33,7 @@ public class JpaEntityGraphQueryDslPredicateExecutorTest extends BaseTest {
@Transactional
@Test
- public void given_null_eg_when_findone_then_then_brand_should_not_be_loaded(){
+ public void given_null_eg_when_findone_then_brand_should_not_be_loaded(){
Product product = productRepository.findOne(QProduct.product.name.eq("Product 1"), (EntityGraph) null);
assertThat(product).isNotNull();
assertThat(Hibernate.isInitialized(product.getBrand())).isFalse();
@@ -38,9 +41,20 @@ public void given_null_eg_when_findone_then_then_brand_should_not_be_loaded(){
@Transactional
@Test
- public void given_brand_eg_when_findone_then_then_brand_should_not_be_loaded(){
- Product product = productRepository.findOne(QProduct.product.name.eq("Product 1"), EntityGraphUtils.fromName(Product.PRODUCT_BRAND_EG));
+ public void given_brand_eg_when_findone_then_brand_should_be_loaded(){
+ Product product = productRepository.findOne(QProduct.product.name.eq("Product 1"), EntityGraphUtils.fromName(Product.BRAND_EG));
assertThat(product).isNotNull();
assertThat(Hibernate.isInitialized(product.getBrand())).isTrue();
}
+
+ @Transactional
+ @Test
+ public void given_brand_eg_when_findpage_then_brand_should_be_loaded(){
+ Page productPage = productRepository.findAll((Predicate) null, new PageRequest(0, 10),EntityGraphUtils.fromName(Product.BRAND_EG));
+ assertThat(productPage.getContent()).isNotEmpty();
+ for(Product product: productPage.getContent()){
+ assertThat(Hibernate.isInitialized(product.getBrand())).isTrue();
+ }
+ }
+
}
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphRepositoryTest.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphRepositoryTest.java
index d03b7e3..3514f73 100644
--- a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphRepositoryTest.java
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphRepositoryTest.java
@@ -1,6 +1,6 @@
package com.cosium.spring.data.jpa.entity.graph.repository;
-import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.*;
import javax.inject.Inject;
import javax.persistence.criteria.CriteriaBuilder;
@@ -12,14 +12,14 @@
import com.cosium.spring.data.jpa.entity.graph.BaseTest;
import com.cosium.spring.data.jpa.entity.graph.domain.EntityGraphUtils;
-import com.cosium.spring.data.jpa.entity.graph.repository.sample.EntityGraphSpecification;
-import com.cosium.spring.data.jpa.entity.graph.repository.sample.Product;
-import com.cosium.spring.data.jpa.entity.graph.repository.sample.ProductRepository;
+import com.cosium.spring.data.jpa.entity.graph.repository.exception.InapplicableEntityGraphException;
+import com.cosium.spring.data.jpa.entity.graph.repository.sample.*;
import com.github.springtestdbunit.annotation.DatabaseSetup;
import com.github.springtestdbunit.annotation.DatabaseTearDown;
import org.hibernate.Hibernate;
import org.junit.Test;
-import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
import org.springframework.transaction.annotation.Transactional;
/**
@@ -33,6 +33,8 @@ public class JpaEntityGraphRepositoryTest extends BaseTest {
@Inject
private ProductRepository productRepository;
+ @Inject
+ private BrandRepository brandRepository;
@Transactional
@Test
@@ -42,10 +44,26 @@ public void given_null_eg_when_findone_then_then_brand_should_not_be_loaded(){
assertThat(Hibernate.isInitialized(product.getBrand())).isFalse();
}
+ @Transactional
+ @Test
+ public void given_empty_eg_when_findone_then_then_brand_should_not_be_loaded(){
+ Product product = productRepository.findOne(1L, EntityGraphUtils.empty());
+ assertThat(product).isNotNull();
+ assertThat(Hibernate.isInitialized(product.getBrand())).isFalse();
+ }
+
@Transactional
@Test
public void given_brand_eg_when_findone_then_brand_should_be_loaded(){
- Product product = productRepository.findOne(1L, EntityGraphUtils.fromName(Product.PRODUCT_BRAND_EG));
+ Product product = productRepository.findOne(1L, EntityGraphUtils.fromName(Product.BRAND_EG));
+ assertThat(product).isNotNull();
+ assertThat(Hibernate.isInitialized(product.getBrand())).isTrue();
+ }
+
+ @Transactional
+ @Test
+ public void given_optional_brand_eg_when_findone_then_brand_should_be_loaded(){
+ Product product = productRepository.findOne(1L, EntityGraphUtils.fromName(Product.BRAND_EG, true));
assertThat(product).isNotNull();
assertThat(Hibernate.isInitialized(product.getBrand())).isTrue();
}
@@ -53,11 +71,90 @@ public void given_brand_eg_when_findone_then_brand_should_be_loaded(){
@Transactional
@Test
public void given_brand_eg_when_findByName_then_brand_should_be_loaded(){
- List products = productRepository.findByName("Product 1", EntityGraphUtils.fromName(Product.PRODUCT_BRAND_EG));
+ List products = productRepository.findByName("Product 1", EntityGraphUtils.fromName(Product.BRAND_EG));
assertThat(products).hasSize(1);
for(Product product: products){
assertThat(Hibernate.isInitialized(product.getBrand())).isTrue();
}
}
+ @Transactional
+ @Test
+ public void given_brand_eg_when_findAll_paginated_then_brand_should_be_loaded(){
+ Page productPage = productRepository.findAll(new PageRequest(0, 10), EntityGraphUtils.fromName(Product.BRAND_EG));
+ assertThat(productPage.getContent()).isNotEmpty();
+ for(Product product: productPage.getContent()){
+ assertThat(Hibernate.isInitialized(product.getBrand())).isTrue();
+ }
+ }
+
+ @Transactional
+ @Test
+ public void given_brand_eg_when_findAll_paginated_with_spec_then_brand_should_be_loaded(){
+ Page productPage = productRepository.findAll(new EntityGraphSpecification(Product.BRAND_EG) {
+ @Override
+ public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) {
+ return null;
+ }
+ }, new PageRequest(0, 10));
+
+ assertThat(productPage.getContent()).isNotEmpty();
+ for(Product product: productPage.getContent()){
+ assertThat(Hibernate.isInitialized(product.getBrand())).isTrue();
+ }
+ }
+
+ @Transactional
+ @Test
+ public void given_default_eg_when_findone_without_eg_then_supplier_should_be_loaded(){
+ Product product = productRepository.findOne(1L);
+ assertThat(product).isNotNull();
+ assertThat(Hibernate.isInitialized(product.getMaker())).isTrue();
+ }
+
+ @Transactional
+ @Test
+ public void given_default_eg_when_findByBarcode_with_eg_annotation_on_brand_eg_then_brand_should_be_loaded(){
+ Product product = productRepository.findByBarcode("1111");
+ assertThat(product).isNotNull();
+ assertThat(Hibernate.isInitialized(product.getBrand())).isTrue();
+ }
+
+ @Transactional
+ @Test
+ public void given_products_when_countproductsbyname_then_it_should_work(){
+ assertThat(productRepository.countByName("Product 1")).isEqualTo(1);
+ }
+
+ @Transactional
+ @Test
+ public void given_products_when_findAllRaw_then_it_should_work(){
+ assertThat(productRepository.findAllRaw()).isNotEmpty();
+ }
+
+ @Transactional
+ @Test
+ public void given_entity_without_default_eg_when_findall_then_it_should_work(){
+ assertThat(brandRepository.findAll()).isNotEmpty();
+ }
+
+ @Transactional
+ @Test(expected = InapplicableEntityGraphException.class)
+ public void given_products_and_ProductName_projection_when_findProductNameByName_with_mandatory_eg_then_it_should_fail(){
+ productRepository.findProductNameByName("Product 1", EntityGraphUtils.fromName(Product.BRAND_EG));
+ }
+
+ @Transactional
+ @Test
+ public void given_products_and_ProductName_projection_when_findProductNameByName_with_optional_eg_then_it_should_not_fail(){
+ productRepository.findProductNameByName("Product 1", EntityGraphUtils.fromName(Product.BRAND_EG, true));
+ }
+
+ @Transactional
+ @Test
+ public void given_products_and_ProductName_projection_when_findProductNameByName_without_eg_then_it_should_work(){
+ ProductName productName = productRepository.findProductNameByName("Product 1", EntityGraphUtils.empty());
+ assertThat(productName).isNotNull();
+ }
+
}
\ No newline at end of file
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphSpecificationExecutorTest.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphSpecificationExecutorTest.java
index 78d0b89..57d7034 100644
--- a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphSpecificationExecutorTest.java
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/JpaEntityGraphSpecificationExecutorTest.java
@@ -10,6 +10,9 @@
import java.util.List;
import com.cosium.spring.data.jpa.entity.graph.BaseTest;
+import com.cosium.spring.data.jpa.entity.graph.domain.EntityGraphUtils;
+import com.cosium.spring.data.jpa.entity.graph.repository.exception.MultipleEntityGraphException;
+import com.cosium.spring.data.jpa.entity.graph.repository.sample.EmptyEntityGraphSpecification;
import com.cosium.spring.data.jpa.entity.graph.repository.sample.EntityGraphSpecification;
import com.cosium.spring.data.jpa.entity.graph.repository.sample.Product;
import com.cosium.spring.data.jpa.entity.graph.repository.sample.ProductRepository;
@@ -35,7 +38,7 @@ public class JpaEntityGraphSpecificationExecutorTest extends BaseTest {
@Transactional
@Test
public void given_brand_eg_when_findbyspecification_implementing_eg_then_brand_should_be_loaded() {
- List products = productRepository.findAll((Specification) new EntityGraphSpecification(Product.PRODUCT_BRAND_EG) {
+ List products = productRepository.findAll((Specification) new EntityGraphSpecification(Product.BRAND_EG) {
@Override
public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) {
return cb.equal(root.get("name"), "Product 2");
@@ -46,4 +49,63 @@ public Predicate toPredicate(Root root, CriteriaQuery> query, Criteri
assertThat(Hibernate.isInitialized(product.getBrand())).isTrue();
}
}
+
+ @Transactional
+ @Test
+ public void given_brand_optional_eg_when_findbyspecification_implementing_eg_then_brand_should_be_loaded() {
+ List products = productRepository.findAll((Specification) new EntityGraphSpecification(Product.BRAND_EG, true) {
+ @Override
+ public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) {
+ return cb.equal(root.get("name"), "Product 2");
+ }
+ });
+ assertThat(products).hasSize(1);
+ for (Product product : products) {
+ assertThat(Hibernate.isInitialized(product.getBrand())).isTrue();
+ }
+ }
+
+ @Transactional
+ @Test(expected = MultipleEntityGraphException.class)
+ public void given_products_when_findAllBySpec_with_two_non_empty_egs_then_it_should_fail() {
+ productRepository.findAll(new EntityGraphSpecification(Product.BRAND_EG) {
+ @Override
+ public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) {
+ return null;
+ }
+ }, EntityGraphUtils.fromName(Product.BRAND_EG));
+ }
+
+ @Transactional
+ @Test(expected = MultipleEntityGraphException.class)
+ public void given_products_when_findAllBySpec_with_an_empty_eg_and_a_non_empty_one_then_it_should_fail() {
+ productRepository.findAll(new EntityGraphSpecification(Product.BRAND_EG) {
+ @Override
+ public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) {
+ return null;
+ }
+ }, EntityGraphUtils.empty());
+ }
+
+ @Transactional
+ @Test(expected = MultipleEntityGraphException.class)
+ public void given_products_when_findAllBySpec_with_two_empty_eg_then_it_should_fail() {
+ productRepository.findAll(new EmptyEntityGraphSpecification() {
+ @Override
+ public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) {
+ return null;
+ }
+ }, EntityGraphUtils.empty());
+ }
+
+ @Transactional
+ @Test(expected = MultipleEntityGraphException.class)
+ public void given_products_when_findAllBySpec_with_a_non_empty_eg_and_an_empty_one_then_it_should_fail() {
+ productRepository.findAll(new EmptyEntityGraphSpecification() {
+ @Override
+ public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) {
+ return null;
+ }
+ }, EntityGraphUtils.fromName(Product.BRAND_EG));
+ }
}
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/Brand.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/Brand.java
index eee7acf..4855a1d 100644
--- a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/Brand.java
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/Brand.java
@@ -7,9 +7,14 @@
*
* @author Reda.Housni-Alaoui
*/
+@NamedEntityGraphs(value = {
+ @NamedEntityGraph(name = Brand.EMPTY_EG, attributeNodes = {})
+})
@Entity
public class Brand {
+ public static final String EMPTY_EG = "Brand.empty";
+
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Access(value = AccessType.PROPERTY)
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/BrandRepository.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/BrandRepository.java
new file mode 100644
index 0000000..c785f0a
--- /dev/null
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/BrandRepository.java
@@ -0,0 +1,12 @@
+package com.cosium.spring.data.jpa.entity.graph.repository.sample;
+
+import com.cosium.spring.data.jpa.entity.graph.repository.JpaEntityGraphRepository;
+
+/**
+ * Created on 24/11/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+public interface BrandRepository extends JpaEntityGraphRepository {
+
+}
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/EmptyEntityGraphSpecification.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/EmptyEntityGraphSpecification.java
new file mode 100644
index 0000000..f0bb1cd
--- /dev/null
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/EmptyEntityGraphSpecification.java
@@ -0,0 +1,38 @@
+package com.cosium.spring.data.jpa.entity.graph.repository.sample;
+
+import java.util.List;
+
+import com.cosium.spring.data.jpa.entity.graph.domain.EntityGraph;
+import com.cosium.spring.data.jpa.entity.graph.domain.EntityGraphType;
+import com.cosium.spring.data.jpa.entity.graph.domain.EntityGraphUtils;
+import org.springframework.data.jpa.domain.Specification;
+
+/**
+ * Created on 27/11/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+public abstract class EmptyEntityGraphSpecification implements Specification, EntityGraph {
+
+ private final EntityGraph entityGraph = EntityGraphUtils.empty();
+
+ @Override
+ public EntityGraphType getEntityGraphType() {
+ return entityGraph.getEntityGraphType();
+ }
+
+ @Override
+ public String getEntityGraphName() {
+ return entityGraph.getEntityGraphName();
+ }
+
+ @Override
+ public List getEntityGraphAttributePaths() {
+ return entityGraph.getEntityGraphAttributePaths();
+ }
+
+ @Override
+ public boolean isOptional() {
+ return false;
+ }
+}
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/EntityGraphSpecification.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/EntityGraphSpecification.java
index 4b19361..8eaf9bc 100644
--- a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/EntityGraphSpecification.java
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/EntityGraphSpecification.java
@@ -14,23 +14,34 @@
public abstract class EntityGraphSpecification implements Specification, EntityGraph {
private final String entityGraphName;
+ private final boolean optional;
- public EntityGraphSpecification(String entityGraphName) {
+ public EntityGraphSpecification(String entityGraphName, boolean optional) {
this.entityGraphName = entityGraphName;
+ this.optional = optional;
+ }
+
+ public EntityGraphSpecification(String entityGraphName) {
+ this(entityGraphName, false);
}
@Override
- public String getName() {
+ public String getEntityGraphName() {
return entityGraphName;
}
@Override
- public EntityGraphType getType() {
+ public EntityGraphType getEntityGraphType() {
return EntityGraphType.FETCH;
}
@Override
- public List getAttributePaths() {
+ public List getEntityGraphAttributePaths() {
return null;
}
+
+ @Override
+ public boolean isOptional() {
+ return optional;
+ }
}
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/Maker.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/Maker.java
new file mode 100644
index 0000000..3cf877a
--- /dev/null
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/Maker.java
@@ -0,0 +1,34 @@
+package com.cosium.spring.data.jpa.entity.graph.repository.sample;
+
+import javax.persistence.*;
+
+/**
+ * Created on 24/11/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+@Entity
+public class Maker {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Access(value = AccessType.PROPERTY)
+ private long id = 0;
+
+ private String name;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/Product.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/Product.java
index 7d66d23..6e8f856 100644
--- a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/Product.java
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/Product.java
@@ -8,14 +8,18 @@
* @author Reda.Housni-Alaoui
*/
@NamedEntityGraphs(value = {
- @NamedEntityGraph(name = Product.PRODUCT_BRAND_EG, attributeNodes = {
+ @NamedEntityGraph(name = Product.DEFAULT_EG, attributeNodes = {
+ @NamedAttributeNode("maker")
+ }),
+ @NamedEntityGraph(name = Product.BRAND_EG, attributeNodes = {
@NamedAttributeNode("brand")
})
})
@Entity
public class Product {
- public static final String PRODUCT_BRAND_EG = "Product.brand";
+ public static final String DEFAULT_EG = "Product.default";
+ public static final String BRAND_EG = "Product.brand";
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -24,9 +28,14 @@ public class Product {
private String name;
+ private String barcode;
+
@ManyToOne(fetch = FetchType.LAZY)
private Brand brand;
+ @ManyToOne(fetch = FetchType.LAZY)
+ private Maker maker;
+
public long getId() {
return id;
}
@@ -50,4 +59,20 @@ public Brand getBrand() {
public void setBrand(Brand brand) {
this.brand = brand;
}
+
+ public Maker getMaker() {
+ return maker;
+ }
+
+ public void setMaker(Maker maker) {
+ this.maker = maker;
+ }
+
+ public String getBarcode() {
+ return barcode;
+ }
+
+ public void setBarcode(String barcode) {
+ this.barcode = barcode;
+ }
}
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/ProductName.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/ProductName.java
new file mode 100644
index 0000000..d6b88e1
--- /dev/null
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/ProductName.java
@@ -0,0 +1,10 @@
+package com.cosium.spring.data.jpa.entity.graph.repository.sample;
+
+/**
+ * Created on 27/11/16.
+ *
+ * @author Reda.Housni-Alaoui
+ */
+public interface ProductName {
+ String getName();
+}
diff --git a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/ProductRepository.java b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/ProductRepository.java
index 02ec554..721bb06 100644
--- a/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/ProductRepository.java
+++ b/src/test/java/com/cosium/spring/data/jpa/entity/graph/repository/sample/ProductRepository.java
@@ -6,6 +6,7 @@
import com.cosium.spring.data.jpa.entity.graph.repository.JpaEntityGraphQueryDslPredicateExecutor;
import com.cosium.spring.data.jpa.entity.graph.repository.JpaEntityGraphRepository;
import com.cosium.spring.data.jpa.entity.graph.repository.JpaEntityGraphSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
/**
* Created on 22/11/16.
@@ -15,8 +16,20 @@
public interface ProductRepository
extends JpaEntityGraphRepository,
JpaEntityGraphSpecificationExecutor,
- JpaEntityGraphQueryDslPredicateExecutor {
+ JpaEntityGraphQueryDslPredicateExecutor,
+ ProductRepositoryCustom {
List findByName(String name, EntityGraph entityGraph);
+ List findByBrand(Brand brand);
+
+ ProductName findProductNameByName(String name, EntityGraph entityGraph);
+
+ @org.springframework.data.jpa.repository.EntityGraph(value = Product.BRAND_EG)
+ Product findByBarcode(String barcode);
+
+ long countByName(String name);
+
+ @Query("select p.name from Product p")
+ List