You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2016/08/24 20:41:26 UTC

olingo-odata2 git commit: [OLINGO-414] Merged fix for ManyTo with DISTINCT generation

Repository: olingo-odata2
Updated Branches:
  refs/heads/OLINGO-JPA-Fixes a1f6d1f5f -> 3ed58899d


[OLINGO-414] Merged fix for ManyTo with DISTINCT generation


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata2/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata2/commit/3ed58899
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata2/tree/3ed58899
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata2/diff/3ed58899

Branch: refs/heads/OLINGO-JPA-Fixes
Commit: 3ed58899d76c57ef8a26a3a02098c736f4740856
Parents: a1f6d1f
Author: mibo <mi...@apache.org>
Authored: Wed Aug 24 22:40:03 2016 +0200
Committer: mibo <mi...@apache.org>
Committed: Wed Aug 24 22:40:03 2016 +0200

----------------------------------------------------------------------
 .../jpa/processor/api/jpql/JPQLStatement.java   |   1 +
 .../core/access/data/JPAQueryBuilder.java       | 106 ++++++++++++++++---
 2 files changed, 93 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/3ed58899/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/jpql/JPQLStatement.java
----------------------------------------------------------------------
diff --git a/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/jpql/JPQLStatement.java b/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/jpql/JPQLStatement.java
index 56ea231..57660b0 100644
--- a/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/jpql/JPQLStatement.java
+++ b/odata2-jpa-processor/jpa-api/src/main/java/org/apache/olingo/odata2/jpa/processor/api/jpql/JPQLStatement.java
@@ -120,6 +120,7 @@ public class JPQLStatement {
 
   public static final class KEYWORD {
     public static final String SELECT = "SELECT";
+    public static final String SELECT_DISTINCT = "SELECT DISTINCT";
     public static final String FROM = "FROM";
     public static final String WHERE = "WHERE";
     public static final String LEFT_OUTER_JOIN = "LEFT OUTER JOIN";

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/3ed58899/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAQueryBuilder.java
----------------------------------------------------------------------
diff --git a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAQueryBuilder.java b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAQueryBuilder.java
index c5e61a8..bf20832 100644
--- a/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAQueryBuilder.java
+++ b/odata2-jpa-processor/jpa-core/src/main/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAQueryBuilder.java
@@ -18,6 +18,11 @@
  ******************************************************************************/
 package org.apache.olingo.odata2.jpa.processor.core.access.data;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 import javax.persistence.EntityManager;
 import javax.persistence.Query;
 
@@ -41,14 +46,14 @@ import org.apache.olingo.odata2.jpa.processor.api.model.JPAEdmMapping;
 
 public class JPAQueryBuilder {
 
-  public enum UriInfoType {
+  enum UriInfoType {
     GetEntitySet,
     GetEntity,
     GetEntitySetCount,
     GetEntityCount,
     PutMergePatch,
     Delete
-  };
+  }
 
   private EntityManager em = null;
   private int pageSize = 0;
@@ -82,7 +87,7 @@ public class JPAQueryBuilder {
   public Query build(GetEntityUriInfo uriInfo) throws ODataJPARuntimeException {
     Query query = null;
     try {
-      ODataJPAQueryExtensionEntityListener listener = getODataJPAQuertEntityListener((UriInfo) uriInfo);
+      ODataJPAQueryExtensionEntityListener listener = getODataJPAQueryEntityListener((UriInfo) uriInfo);
       if (listener != null) {
         query = listener.getQuery(uriInfo, em);
       }
@@ -99,7 +104,7 @@ public class JPAQueryBuilder {
   public Query build(GetEntitySetCountUriInfo uriInfo) throws ODataJPARuntimeException {
     Query query = null;
     try {
-      ODataJPAQueryExtensionEntityListener listener = getODataJPAQuertEntityListener((UriInfo) uriInfo);
+      ODataJPAQueryExtensionEntityListener listener = getODataJPAQueryEntityListener((UriInfo) uriInfo);
       if (listener != null) {
         query = listener.getQuery(uriInfo, em);
       }
@@ -116,7 +121,7 @@ public class JPAQueryBuilder {
   public Query build(GetEntityCountUriInfo uriInfo) throws ODataJPARuntimeException {
     Query query = null;
     try {
-      ODataJPAQueryExtensionEntityListener listener = getODataJPAQuertEntityListener((UriInfo) uriInfo);
+      ODataJPAQueryExtensionEntityListener listener = getODataJPAQueryEntityListener((UriInfo) uriInfo);
       if (listener != null) {
         query = listener.getQuery(uriInfo, em);
       }
@@ -133,7 +138,7 @@ public class JPAQueryBuilder {
   public Query build(DeleteUriInfo uriInfo) throws ODataJPARuntimeException {
     Query query = null;
     try {
-      ODataJPAQueryExtensionEntityListener listener = getODataJPAQuertEntityListener((UriInfo) uriInfo);
+      ODataJPAQueryExtensionEntityListener listener = getODataJPAQueryEntityListener((UriInfo) uriInfo);
       if (listener != null) {
         query = listener.getQuery(uriInfo, em);
       }
@@ -150,7 +155,7 @@ public class JPAQueryBuilder {
   public Query build(PutMergePatchUriInfo uriInfo) throws ODataJPARuntimeException {
     Query query = null;
     try {
-      ODataJPAQueryExtensionEntityListener listener = getODataJPAQuertEntityListener((UriInfo) uriInfo);
+      ODataJPAQueryExtensionEntityListener listener = getODataJPAQueryEntityListener((UriInfo) uriInfo);
       if (listener != null) {
         query = listener.getQuery(uriInfo, em);
       }
@@ -168,17 +173,16 @@ public class JPAQueryBuilder {
       throws EdmException,
       ODataJPAModelException, ODataJPARuntimeException {
 
-    Query query = null;
     JPQLContextType contextType = determineJPQLContextType(uriParserResultView, type);
     JPQLContext jpqlContext = buildJPQLContext(contextType, uriParserResultView);
-    JPQLStatement jpqlStatement = JPQLStatement.createBuilder(jpqlContext)
-        .build();
-    query = em.createQuery(jpqlStatement.toString());
+    JPQLStatement jpqlStatement = JPQLStatement.createBuilder(jpqlContext).build();
 
-    return query;
+    return em.createQuery(normalizeMembers(jpqlStatement.toString()));
   }
 
-  public ODataJPAQueryExtensionEntityListener getODataJPAQuertEntityListener(UriInfo uriInfo) throws EdmException,
+  
+
+  public ODataJPAQueryExtensionEntityListener getODataJPAQueryEntityListener(UriInfo uriInfo) throws EdmException,
       InstantiationException, IllegalAccessException {
     ODataJPAQueryExtensionEntityListener queryListener = null;
     ODataJPATombstoneEntityListener listener = getODataJPATombstoneEntityListener(uriInfo);
@@ -236,7 +240,81 @@ public class JPAQueryBuilder {
     return contextType;
   }
 
-  public final class JPAQueryInfo {
+  private static final Pattern NORMALIZATION_NEEDED_PATTERN = Pattern.compile(".*[\\s\\(](\\S+\\.\\S+\\.\\S+).*");
+  private static final Pattern JOIN_ALIAS_PATTERN = Pattern.compile(".*\\sJOIN\\s(\\S*\\s\\S*).*");
+
+  private static String normalizeMembers(String jpqlQuery) {
+    // check if normalization is needed (if query contains "x.y.z" elements
+    // starting with space or parenthesis)
+    Matcher normalizationNeededMatcher = NORMALIZATION_NEEDED_PATTERN.matcher(jpqlQuery);
+    if (!normalizationNeededMatcher.find()) {
+      return jpqlQuery;
+    }
+
+    String normalizedJpqlQuery = jpqlQuery;
+    Map<String, String> joinAliases = new HashMap<String, String>();
+
+    // collect information about existing joins/aliases
+    Matcher joinAliasMatcher = JOIN_ALIAS_PATTERN.matcher(normalizedJpqlQuery);
+    if (joinAliasMatcher.find()) {
+      for (int i = 1; i <= joinAliasMatcher.groupCount(); i++) {
+        String[] joinAlias = joinAliasMatcher.group(i).split(String.valueOf(JPQLStatement.DELIMITER.SPACE));
+        joinAliases.put(joinAlias[0], joinAlias[1]);
+      }
+    }
+
+    // normalize query
+    boolean normalizationNeeded = true;
+    while (normalizationNeeded) {
+      String membershipToNormalize = normalizationNeededMatcher.group(1);
+
+      // get member info
+      String memberInfo = membershipToNormalize.substring(0,
+          ordinalIndexOf(membershipToNormalize, JPQLStatement.DELIMITER.PERIOD, 1));
+
+      String alias;
+      if (joinAliases.containsKey(memberInfo)) {
+        // use existing alias
+        alias = joinAliases.get(memberInfo);
+      } else {
+        // create new join/alias
+        alias = "R" + (joinAliases.size() + 1);
+
+        int joinInsertPosition = normalizedJpqlQuery.indexOf(JPQLStatement.KEYWORD.WHERE);
+        if (joinInsertPosition == -1) {
+          joinInsertPosition = normalizedJpqlQuery.indexOf(JPQLStatement.KEYWORD.ORDERBY);
+        }
+        normalizedJpqlQuery = normalizedJpqlQuery.substring(0, joinInsertPosition) + JPQLStatement.KEYWORD.JOIN
+            + JPQLStatement.DELIMITER.SPACE + memberInfo + JPQLStatement.DELIMITER.SPACE + alias
+            + JPQLStatement.DELIMITER.SPACE + normalizedJpqlQuery.substring(joinInsertPosition);
+
+        joinAliases.put(memberInfo, alias);
+      }
+
+      // use alias
+      normalizedJpqlQuery = normalizedJpqlQuery.replaceAll(memberInfo + "\\" + JPQLStatement.DELIMITER.PERIOD,
+          alias + JPQLStatement.DELIMITER.PERIOD);
+
+      // check if further normalization is needed
+      normalizationNeededMatcher = NORMALIZATION_NEEDED_PATTERN.matcher(normalizedJpqlQuery);
+      normalizationNeeded = normalizationNeededMatcher.find();
+    }
+
+    // add distinct to avoid duplicates in result set
+    return normalizedJpqlQuery.replaceFirst(
+        JPQLStatement.KEYWORD.SELECT + JPQLStatement.DELIMITER.SPACE,
+        JPQLStatement.KEYWORD.SELECT_DISTINCT + JPQLStatement.DELIMITER.SPACE);
+  }
+
+  private static int ordinalIndexOf(String str, char s, int n) {
+    int pos = str.indexOf(s, 0);
+    while (n-- > 0 && pos != -1) {
+      pos = str.indexOf(s, pos + 1);
+    }
+    return pos;
+  }
+
+  final class JPAQueryInfo {
     private Query query = null;
     private boolean isTombstoneQuery = false;