Repository: olingo-odata2 Updated Branches: refs/heads/master bb2517adb -> 6c7a67425
[ODATAJAVA-1147]Entity read is not working due to normalization in JPAQueryBuilder Project: http://git-wip-us.apache.org/repos/asf/olingo-odata2/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata2/commit/6c7a6742 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata2/tree/6c7a6742 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata2/diff/6c7a6742 Branch: refs/heads/master Commit: 6c7a67425af35bbaac7c4e9a7c0066d9a9463cc2 Parents: bb2517a Author: Archana Rai <[email protected]> Authored: Wed Jul 26 14:40:53 2017 +0530 Committer: Archana Rai <[email protected]> Committed: Wed Jul 26 14:40:53 2017 +0530 ---------------------------------------------------------------------- .../core/access/data/JPAQueryBuilder.java | 60 ++++++++++---- .../core/access/data/JPAQueryBuilderTest.java | 82 ++++++++++++++++++++ .../core/mock/ODataJPAContextMock.java | 18 ++++- 3 files changed, 140 insertions(+), 20 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/6c7a6742/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 6852a17..6f66af9 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,17 @@ ******************************************************************************/ package org.apache.olingo.odata2.jpa.processor.core.access.data; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.persistence.EntityManager; +import javax.persistence.Query; +import javax.persistence.metamodel.Attribute; +import javax.persistence.metamodel.EntityType; + import org.apache.olingo.odata2.api.edm.EdmException; import org.apache.olingo.odata2.api.uri.UriInfo; import org.apache.olingo.odata2.api.uri.info.DeleteUriInfo; @@ -36,16 +47,6 @@ import org.apache.olingo.odata2.jpa.processor.api.jpql.JPQLContextType; import org.apache.olingo.odata2.jpa.processor.api.jpql.JPQLStatement; import org.apache.olingo.odata2.jpa.processor.api.model.JPAEdmMapping; -import javax.persistence.EntityManager; -import javax.persistence.Query; -import javax.persistence.metamodel.Attribute; -import javax.persistence.metamodel.EntityType; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - public class JPAQueryBuilder { enum UriInfoType { @@ -238,12 +239,17 @@ public class JPAQueryBuilder { } private static final Pattern NORMALIZATION_NEEDED_PATTERN = Pattern.compile(".*[\\s(](\\S+\\.\\S+\\.\\S+).*"); + private static final Pattern VALUE_NORM_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(EntityManager em, String jpqlQuery) { + private static String normalizeMembers(EntityManager em, String jpqlQuery) { + + //check if clause values are string with x.y.z format + //starting with quotes; + String query = checkConditionValues(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); + Matcher normalizationNeededMatcher = NORMALIZATION_NEEDED_PATTERN.matcher(query); if (!normalizationNeededMatcher.find()) { return jpqlQuery; } @@ -251,7 +257,7 @@ public class JPAQueryBuilder { if (containsEmbeddedAttributes(em, jpqlQuery)) { return jpqlQuery; } - + String normalizedJpqlQuery = jpqlQuery; Map<String, String> joinAliases = new HashMap<String, String>(); @@ -263,7 +269,6 @@ public class JPAQueryBuilder { joinAliases.put(joinAlias[0], joinAlias[1]); } } - // normalize query boolean normalizationNeeded = true; while (normalizationNeeded) { @@ -295,12 +300,13 @@ public class JPAQueryBuilder { // use alias normalizedJpqlQuery = normalizedJpqlQuery.replaceAll(memberInfo + "\\" + JPQLStatement.DELIMITER.PERIOD, alias + JPQLStatement.DELIMITER.PERIOD); - + //check for values like "x.y.z" + query = checkConditionValues(normalizedJpqlQuery); // check if further normalization is needed - normalizationNeededMatcher = NORMALIZATION_NEEDED_PATTERN.matcher(normalizedJpqlQuery); + normalizationNeededMatcher = NORMALIZATION_NEEDED_PATTERN.matcher(query); normalizationNeeded = normalizationNeededMatcher.find(); } - + // add distinct to avoid duplicates in result set return normalizedJpqlQuery.replaceFirst( JPQLStatement.KEYWORD.SELECT + JPQLStatement.DELIMITER.SPACE, @@ -308,6 +314,26 @@ public class JPAQueryBuilder { } /** + * Check if the statement contains string values having x.y.z kind of format + * It will replace those values with parameters before checking for normalization + * and later added back + * */ + private static String checkConditionValues(String jpqlQuery) { + int i=0; + StringBuffer query= new StringBuffer(); + query.append(jpqlQuery); + Matcher valueMatcher = VALUE_NORM_PATTERN.matcher(query); + while (valueMatcher.find()) { + String val = valueMatcher.group(); + int index = query.indexOf(val); + String var = "["+ ++i +"] "; + query.replace(index, index + val.length(), var); + valueMatcher = VALUE_NORM_PATTERN.matcher(query); + } + return query.toString(); + } + + /** * Verify via {@link EntityManager} if one of the attributes of the selected entity * contains a embedded attribute. * Return true if at least one embedded attribute is found or false if non embedded http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/6c7a6742/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAQueryBuilderTest.java ---------------------------------------------------------------------- diff --git a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAQueryBuilderTest.java b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAQueryBuilderTest.java index 4c9fbaa..7d35ed6 100644 --- a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAQueryBuilderTest.java +++ b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/access/data/JPAQueryBuilderTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import javax.persistence.EntityManager; @@ -32,8 +33,10 @@ import org.apache.olingo.odata2.api.edm.EdmEntitySet; import org.apache.olingo.odata2.api.edm.EdmEntityType; import org.apache.olingo.odata2.api.edm.EdmException; import org.apache.olingo.odata2.api.edm.EdmMapping; +import org.apache.olingo.odata2.api.edm.EdmProperty; import org.apache.olingo.odata2.api.exception.ODataException; import org.apache.olingo.odata2.api.processor.ODataContext; +import org.apache.olingo.odata2.api.uri.KeyPredicate; import org.apache.olingo.odata2.api.uri.NavigationSegment; import org.apache.olingo.odata2.api.uri.UriInfo; import org.apache.olingo.odata2.api.uri.info.DeleteUriInfo; @@ -188,6 +191,36 @@ public class JPAQueryBuilderTest { } } + @Test + public void buildQueryGetEntityTest() { + EdmMapping mapping = (EdmMapping) mockMapping(); + try { + assertNotNull(builder.build((GetEntityUriInfo) mockURIInfo(mapping))); + } catch (ODataException e) { + fail(ODataJPATestConstants.EXCEPTION_MSG_PART_1 + e.getMessage() + ODataJPATestConstants.EXCEPTION_MSG_PART_2); + } + } + + @Test + public void buildQueryValueNormalizeTest() { + EdmMapping mapping = (EdmMapping) mockNormalizedValueMapping(); + try { + assertNotNull(builder.build((GetEntityUriInfo) mockURIInfo(mapping))); + } catch (ODataException e) { + fail(ODataJPATestConstants.EXCEPTION_MSG_PART_1 + e.getMessage() + ODataJPATestConstants.EXCEPTION_MSG_PART_2); + } + } + + @Test + public void buildQueryNormalizeTest() { + EdmMapping mapping = (EdmMapping) mockNormalizedMapping(); + try { + assertNotNull(builder.build((GetEntityUriInfo) mockURIInfo(mapping))); + } catch (ODataException e) { + fail(ODataJPATestConstants.EXCEPTION_MSG_PART_1 + e.getMessage() + ODataJPATestConstants.EXCEPTION_MSG_PART_2); + } + } + private UriInfo mockURIInfoWithListener(boolean isNavigationEnabled) throws EdmException { UriInfo uriInfo = EasyMock.createMock(UriInfo.class); if (isNavigationEnabled) { @@ -206,13 +239,62 @@ public class JPAQueryBuilderTest { return uriInfo; } + + @SuppressWarnings("unchecked") + private UriInfo mockURIInfo(EdmMapping mapping) throws EdmException { + + UriInfo uriInfo = EasyMock.createMock(UriInfo.class); + List<NavigationSegment> navSegments = new ArrayList<NavigationSegment>(); + EasyMock.expect(uriInfo.getNavigationSegments()).andStubReturn(navSegments); + EdmEntityType edmEntityType = EasyMock.createMock(EdmEntityType.class); + EasyMock.expect(edmEntityType.getMapping()).andStubReturn(mapping); + EdmEntitySet edmEntitySet = EasyMock.createMock(EdmEntitySet.class); + EasyMock.expect(edmEntitySet.getEntityType()).andStubReturn(edmEntityType); + EasyMock.expect(uriInfo.getTargetEntitySet()).andStubReturn(edmEntitySet); + List<KeyPredicate> keyPreds =EasyMock.createMock(ArrayList.class); + EdmProperty edmProperty = EasyMock.createMock(EdmProperty.class); + EasyMock.expect(edmProperty.getMapping()).andStubReturn(mapping); + KeyPredicate keyPredicate = EasyMock.createMock(KeyPredicate.class); + EasyMock.expect(keyPredicate.getLiteral()).andReturn("Id").anyTimes(); + EasyMock.expect(keyPredicate.getProperty()).andReturn(edmProperty).anyTimes(); + EasyMock.expect(keyPreds.size()).andStubReturn(1); + Iterator<KeyPredicate> keyPredicateitr = EasyMock.createMock(Iterator.class); + EasyMock.expect(keyPredicateitr.next()).andStubReturn(keyPredicate); + EasyMock.expect(keyPredicateitr.hasNext()).andStubReturn(true); + EasyMock.expect(keyPreds.iterator()).andStubReturn(keyPredicateitr); + EasyMock.expect(keyPreds.isEmpty()).andStubReturn(false); + EasyMock.expect(keyPreds.get(0)).andStubReturn(keyPredicate); + EasyMock.expect(uriInfo.getKeyPredicates()).andStubReturn(keyPreds); + EasyMock.replay(edmEntityType, edmEntitySet, uriInfo, keyPredicate, keyPreds, edmProperty); + return uriInfo; + + } private JPAEdmMapping mockEdmMapping() { JPAEdmMappingImpl mockedEdmMapping = new JPAEdmMappingImpl(); mockedEdmMapping.setODataJPATombstoneEntityListener(JPAQueryExtensionMock.class); return mockedEdmMapping; } + + private JPAEdmMapping mockMapping() { + JPAEdmMappingImpl mockedEdmMapping = new JPAEdmMappingImpl(); + mockedEdmMapping.setInternalName("Customer"); + return mockedEdmMapping; + } + + private JPAEdmMapping mockNormalizedMapping() { + JPAEdmMappingImpl mockedEdmMapping = new JPAEdmMappingImpl(); + mockedEdmMapping.setInternalName("C1.Customer.Name"); + return mockedEdmMapping; + } + + private JPAEdmMapping mockNormalizedValueMapping() { + JPAEdmMappingImpl mockedEdmMapping = new JPAEdmMappingImpl(); + mockedEdmMapping.setInternalName("'C1.Customer.Name'"); + return mockedEdmMapping; + } + public static final class JPAQueryExtensionMock extends ODataJPAQueryExtensionEntityListener { Query query = EasyMock.createMock(Query.class); http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/6c7a6742/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/ODataJPAContextMock.java ---------------------------------------------------------------------- diff --git a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/ODataJPAContextMock.java b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/ODataJPAContextMock.java index 83cb0d3..cb59b6f 100644 --- a/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/ODataJPAContextMock.java +++ b/odata2-jpa-processor/jpa-core/src/test/java/org/apache/olingo/odata2/jpa/processor/core/mock/ODataJPAContextMock.java @@ -18,12 +18,18 @@ ******************************************************************************/ package org.apache.olingo.odata2.jpa.processor.core.mock; +import java.util.HashSet; +import java.util.Set; + import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; +import javax.persistence.Query; +import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.Metamodel; import org.apache.olingo.odata2.api.processor.ODataContext; import org.apache.olingo.odata2.jpa.processor.api.ODataJPAContext; +import org.easymock.Capture; import org.easymock.EasyMock; public abstract class ODataJPAContextMock { @@ -49,7 +55,7 @@ public abstract class ODataJPAContextMock { ODataJPAContext odataJPAContext = EasyMock.createMock(ODataJPAContext.class); EasyMock.expect(odataJPAContext.getPersistenceUnitName()).andStubReturn(NAMESPACE); EasyMock.expect(odataJPAContext.getEntityManagerFactory()).andReturn(mockEntityManagerFactory()); - EasyMock.expect(odataJPAContext.getEntityManager()).andReturn(mockEntityManager()); + EasyMock.expect(odataJPAContext.getEntityManager()).andStubReturn(mockEntityManager()); EasyMock.expect(odataJPAContext.getJPAEdmMappingModel()).andReturn(MAPPING_MODEL); EasyMock.expect(odataJPAContext.getJPAEdmExtension()).andReturn(null); EasyMock.expect(odataJPAContext.getDefaultNaming()).andReturn(true); @@ -62,9 +68,15 @@ public abstract class ODataJPAContextMock { private static EntityManager mockEntityManager() { EntityManager em = EasyMock.createMock(EntityManager.class); - EasyMock.expect(em.getMetamodel()).andReturn(null); + Metamodel mm = EasyMock.createMock(Metamodel.class); + EasyMock.expect(em.getMetamodel()).andReturn(mm).anyTimes(); + Set<EntityType<?>> et = new HashSet<EntityType<?>>(); + EasyMock.expect(mm.getEntities()).andReturn(et).anyTimes(); EasyMock.expect(em.isOpen()).andReturn(true).anyTimes(); - EasyMock.replay(em); + Query jpqlquery = EasyMock.createMock(Query.class); + Capture<String> capturedArgument = new Capture<String>(); + EasyMock.expect(em.createQuery(EasyMock.capture(capturedArgument))).andReturn(jpqlquery); + EasyMock.replay(em,mm); return em; }
