You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltaspike.apache.org by hw...@apache.org on 2015/12/29 20:25:11 UTC

deltaspike git commit: DELTASPIKE-1052 support entity graphs built from attribute paths

Repository: deltaspike
Updated Branches:
  refs/heads/master 8df71b676 -> 6d75f5ea9


DELTASPIKE-1052 support entity graphs built from attribute paths


Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/6d75f5ea
Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/6d75f5ea
Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/6d75f5ea

Branch: refs/heads/master
Commit: 6d75f5ea95416183964be57275c340f41ab7f82d
Parents: 8df71b6
Author: Harald Wellmann <hw...@apache.org>
Authored: Tue Dec 29 20:25:03 2015 +0100
Committer: Harald Wellmann <hw...@apache.org>
Committed: Tue Dec 29 20:25:03 2015 +0100

----------------------------------------------------------------------
 .../data/impl/graph/EntityGraphHelper.java      | 139 ++++++++++++++++++-
 .../impl/handler/CdiQueryInvocationContext.java |  10 +-
 .../deltaspike/data/test/ee7/domain/Flat.java   |  21 +++
 .../deltaspike/data/test/ee7/domain/House.java  |   3 +
 .../test/ee7/graph/HouseRepositoryTest.java     |  53 ++++++-
 .../data/test/ee7/service/HouseRepository.java  |   8 ++
 6 files changed, 225 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d75f5ea/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/graph/EntityGraphHelper.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/graph/EntityGraphHelper.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/graph/EntityGraphHelper.java
index 72bad91..eb3b296 100644
--- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/graph/EntityGraphHelper.java
+++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/graph/EntityGraphHelper.java
@@ -20,6 +20,10 @@ package org.apache.deltaspike.data.impl.graph;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 import javax.persistence.EntityManager;
 
@@ -29,13 +33,42 @@ public final class EntityGraphHelper
 {
 
     private static final Class<?> ENTITY_GRAPH_CLASS;
+    private static final Class<?> SUBGRAPH_CLASS;
+    private static final Method ADD_ATTRIBUTE_NODES;
+    private static final Method ADD_SUBGRAPH;
+    private static final Method SUBGRAPH_ADD_ATTRIBUTE_NODES;
 
     static
     {
         ENTITY_GRAPH_CLASS = ClassUtils.tryToLoadClassForName("javax.persistence.EntityGraph");
+        SUBGRAPH_CLASS = ClassUtils.tryToLoadClassForName("javax.persistence.Subgraph");
+        if (ENTITY_GRAPH_CLASS == null)
+        {
+            ADD_ATTRIBUTE_NODES = null;
+            ADD_SUBGRAPH = null;
+            SUBGRAPH_ADD_ATTRIBUTE_NODES = null;
+        }
+        else
+        {
+            try
+            {
+                ADD_ATTRIBUTE_NODES = ENTITY_GRAPH_CLASS.getMethod("addAttributeNodes",
+                    String[].class);
+                ADD_SUBGRAPH = ENTITY_GRAPH_CLASS.getMethod("addSubgraph", String.class);
+                SUBGRAPH_ADD_ATTRIBUTE_NODES = SUBGRAPH_CLASS.getMethod("addAttributeNodes",
+                    String[].class);
+            }
+            catch (NoSuchMethodException e)
+            {
+                throw new EntityGraphException(e.getMessage(), e.getCause());
+            }
+            catch (SecurityException e)
+            {
+                throw new EntityGraphException(e.getMessage(), e.getCause());
+            }
+        }
     }
-    
-    
+
     private EntityGraphHelper()
     {
         // hidden constructor
@@ -72,13 +105,111 @@ public final class EntityGraphHelper
         }
     }
 
+    public static Object createEntityGraph(EntityManager em, Class<?> entityClass)
+    {
+        ensureAvailable();
+        try
+        {
+            Method method = EntityManager.class.getMethod("createEntityGraph", Class.class);
+            return method.invoke(em, entityClass);
+        }
+        catch (NoSuchMethodException e)
+        {
+            throw new EntityGraphException("no method EntityManager.createEntityGraph()", e);
+        }
+        catch (SecurityException e)
+        {
+            throw new EntityGraphException("no access to method EntityManager.createEntityGraph()",
+                e);
+        }
+        catch (IllegalAccessException e)
+        {
+            throw new EntityGraphException("no access to method EntityManager.createEntityGraph()",
+                e);
+        }
+        catch (InvocationTargetException e)
+        {
+            throw new EntityGraphException(e.getCause().getMessage(), e.getCause());
+        }
+    }
+
     private static void ensureAvailable()
     {
         if (!isAvailable())
         {
-            throw new EntityGraphException(
-                "Class java.persistence.EntityGraph is not available. "
+            throw new EntityGraphException("Class java.persistence.EntityGraph is not available. "
                 + "Does your PersistenceProvider support JPA 2.1?");
         }
     }
+
+    public static Object addSubgraph(Object entityGraph, String attributeName)
+    {
+        try
+        {
+            return ADD_SUBGRAPH.invoke(entityGraph, attributeName);
+        }
+        catch (IllegalAccessException e)
+        {
+            throw new EntityGraphException("no access to method EntityGraph.addSubgraph()", e);
+        }
+        catch (InvocationTargetException e)
+        {
+            throw new EntityGraphException(e.getCause().getMessage(), e.getCause());
+        }
+    }
+
+    public static void addAttributeNodes(Object graph, String attributeName)
+    {
+        try
+        {
+            if (ENTITY_GRAPH_CLASS.isInstance(graph))
+            {
+                ADD_ATTRIBUTE_NODES.invoke(graph, new Object[] { new String[] { attributeName } });
+            }
+            else if (SUBGRAPH_CLASS.isInstance(graph))
+            {
+                SUBGRAPH_ADD_ATTRIBUTE_NODES.invoke(graph,
+                    new Object[] { new String[] { attributeName } });
+            }
+        }
+        catch (IllegalAccessException e)
+        {
+            throw new EntityGraphException("no access to method addAttributeNodes()", e);
+        }
+        catch (InvocationTargetException e)
+        {
+            throw new EntityGraphException(e.getCause().getMessage(), e.getCause());
+        }
+    }
+
+    public static Object buildEntityGraph(EntityManager em, Class<?> entityClass,
+        String[] attributePaths)
+    {
+        Object graph = createEntityGraph(em, entityClass);
+        List<String> paths = new ArrayList<String>(Arrays.asList(attributePaths));
+
+        Collections.sort(paths);
+        Collections.reverse(paths);
+
+        for (String path : attributePaths)
+        {
+            if (path.contains("."))
+            {
+                String[] segments = path.split("\\.");
+                Object parent = addSubgraph(graph, segments[0]);
+
+                for (int i = 1; i < segments.length - 1; i++)
+                {
+                    addSubgraph(parent, segments[i]);
+                }
+
+                addAttributeNodes(parent, segments[segments.length - 1]);
+            }
+            else
+            {
+                addAttributeNodes(graph, path);
+            }
+        }
+        return graph;
+    }
 }

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d75f5ea/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java
index f156741..40686f0 100644
--- a/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java
+++ b/deltaspike/modules/data/impl/src/main/java/org/apache/deltaspike/data/impl/handler/CdiQueryInvocationContext.java
@@ -323,7 +323,15 @@ public class CdiQueryInvocationContext implements QueryInvocationContext
         }
         
         String graphName = entityGraphAnn.value();
-        Object graph = EntityGraphHelper.getEntityGraph(getEntityManager(), graphName);
+        Object graph;
+        if (graphName.isEmpty())
+        {
+            graph = EntityGraphHelper.buildEntityGraph(getEntityManager(), entityClass, entityGraphAnn.paths());
+        }
+        else
+        {
+            graph = EntityGraphHelper.getEntityGraph(getEntityManager(), graphName);
+        }
         query.setHint(entityGraphAnn.type().getHintName(), graph);
     }
 

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d75f5ea/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/domain/Flat.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/domain/Flat.java b/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/domain/Flat.java
index c961067..e021be3 100644
--- a/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/domain/Flat.java
+++ b/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/domain/Flat.java
@@ -19,11 +19,16 @@
 package org.apache.deltaspike.data.test.ee7.domain;
 
 import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
 
+import javax.persistence.CascadeType;
 import javax.persistence.Entity;
 import javax.persistence.GeneratedValue;
 import javax.persistence.Id;
 import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.OrderColumn;
 import javax.persistence.Table;
 
 @Entity
@@ -36,6 +41,10 @@ public class Flat implements Serializable
     @Id
     @GeneratedValue
     private Long id;
+    
+    @OneToMany(mappedBy = "flat", cascade = CascadeType.ALL)
+    @OrderColumn
+    private List<Tenant> tenants = new ArrayList<Tenant>();
 
     @ManyToOne
     private House house;
@@ -71,4 +80,16 @@ public class Flat implements Serializable
     {
         this.house = house;
     }
+
+    
+    public List<Tenant> getTenants()
+    {
+        return tenants;
+    }
+
+    
+    public void setTenants(List<Tenant> tenants)
+    {
+        this.tenants = tenants;
+    }
 }

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d75f5ea/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/domain/House.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/domain/House.java b/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/domain/House.java
index c8afcfd..a0f7c61 100644
--- a/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/domain/House.java
+++ b/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/domain/House.java
@@ -30,6 +30,7 @@ import javax.persistence.NamedAttributeNode;
 import javax.persistence.NamedEntityGraph;
 import javax.persistence.NamedEntityGraphs;
 import javax.persistence.OneToMany;
+import javax.persistence.OrderColumn;
 import javax.persistence.Table;
 
 @NamedEntityGraphs({
@@ -50,9 +51,11 @@ public class House implements Serializable
     private String name;
 
     @OneToMany(mappedBy = "house", cascade = CascadeType.ALL)
+    @OrderColumn
     private List<Flat> flats = new ArrayList<Flat>();
 
     @OneToMany(mappedBy = "house", cascade = CascadeType.ALL)
+    @OrderColumn
     private List<Garage> garages = new ArrayList<Garage>();
 
     public Long getId()

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d75f5ea/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/graph/HouseRepositoryTest.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/graph/HouseRepositoryTest.java b/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/graph/HouseRepositoryTest.java
index 8803300..37d619f 100644
--- a/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/graph/HouseRepositoryTest.java
+++ b/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/graph/HouseRepositoryTest.java
@@ -102,8 +102,6 @@ public class HouseRepositoryTest
     {
         House house = repository.findOptionalByName("Bellevue");
         assertNotNull(house);
-        assertNotNull(house.getId());
-        assertEquals("Bellevue", house.getName());
 
         PersistenceUnitUtil puu = entityManager.getEntityManagerFactory().getPersistenceUnitUtil();
 
@@ -117,8 +115,6 @@ public class HouseRepositoryTest
     {
         House house = repository.fetchByName("Bellevue");
         assertNotNull(house);
-        assertNotNull(house.getId());
-        assertEquals("Bellevue", house.getName());
 
         assertTrue(puu.isLoaded(house, "flats"));
         assertFalse(puu.isLoaded(house, "garages"));
@@ -139,6 +135,41 @@ public class HouseRepositoryTest
         }
     }
 
+    @Test
+    @InSequence(5)
+    public void should_build_dynamic_graph_from_paths() throws Exception
+    {
+        House house = repository.fetchByNameWithDynamicGraph("Bellevue");
+        assertNotNull(house);
+
+        assertTrue(puu.isLoaded(house, "flats"));
+        assertTrue(puu.isLoaded(house, "garages"));
+        
+        assertEquals(2, house.getFlats().size());
+        assertEquals(2, house.getGarages().size());
+
+        Flat flat = house.getFlats().get(0);
+        assertFalse(puu.isLoaded(flat, "tenants"));
+    }
+
+    @Test
+    @InSequence(6)
+    public void should_build_dynamic_graph_from_composite_paths() throws Exception
+    {
+        House house = repository.fetchByNameWithFlatTenants("Bellevue");
+        assertNotNull(house);
+
+        assertTrue(puu.isLoaded(house, "flats"));
+        assertTrue(puu.isLoaded(house, "garages"));
+        
+        assertEquals(2, house.getFlats().size());
+        assertEquals(2, house.getGarages().size());
+
+        Flat flat = house.getFlats().get(0);
+        assertTrue(puu.isLoaded(flat, "tenants"));
+        assertEquals(3, flat.getTenants().size());
+    }
+
     @Before
     public void init() throws Exception
     {
@@ -150,6 +181,20 @@ public class HouseRepositoryTest
             Flat flat1 = new Flat();
             flat1.setName("Flat 1");
             flat1.setHouse(house);
+            
+            Tenant alice = new Tenant();
+            alice.setName("Alice");
+            alice.setFlat(flat1);
+
+            Tenant bob = new Tenant();
+            bob.setName("Bob");
+            bob.setFlat(flat1);
+            
+            Tenant charlie = new Tenant();
+            charlie.setName("Charlie");
+            charlie.setFlat(flat1);
+            
+            flat1.setTenants(Arrays.asList(alice, bob, charlie));
 
             Flat flat2 = new Flat();
             flat2.setName("Flat 2");

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/6d75f5ea/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/service/HouseRepository.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/service/HouseRepository.java b/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/service/HouseRepository.java
index f9fa797..e0c6121 100644
--- a/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/service/HouseRepository.java
+++ b/deltaspike/modules/data/test-ee7/src/test/java/org/apache/deltaspike/data/test/ee7/service/HouseRepository.java
@@ -42,6 +42,14 @@ public interface HouseRepository extends FullEntityRepository<House, Long>
     @EntityGraph("none")
     House fetchByNameWithInvalidGraph(String name);
 
+    @Query("select h from House h where h.name = ?1")
+    @EntityGraph(paths = {"flats", "garages"})
+    House fetchByNameWithDynamicGraph(String name);
+
+    @Query("select h from House h where h.name = ?1")
+    @EntityGraph(paths = {"flats.tenants", "garages"})
+    House fetchByNameWithFlatTenants(String name);
+
     @Modifying @Query("delete from House")
     int deleteAll();