From 6db33002e0e4c7c5039f23deb50e77355f9c5ac3 Mon Sep 17 00:00:00 2001 From: dbetterton Date: Thu, 28 Apr 2016 10:05:45 -0500 Subject: [PATCH 1/4] Added BigDecimal, date and count support as fstn Added tests for above --- .../tennaito/rsql/builder/BuilderTools.java | 0 .../rsql/builder/SimpleBuilderTools.java | 0 .../tennaito/rsql/jpa/AbstractJpaVisitor.java | 4 + .../jpa/JpaCriteriaCountQueryVisitor.java | 130 ++++++++++++++++++ .../rsql/jpa/JpaCriteriaQueryVisitor.java | 2 +- .../tennaito/rsql/jpa/PredicateBuilder.java | 38 ++++- .../rsql/jpa/PredicateBuilderStrategy.java | 0 .../rsql/jpa/AbstractVisitorTest.java | 2 + .../rsql/jpa/DefaultArgumentParserTest.java | 8 +- .../tennaito/rsql/jpa/JpaVisitorTest.java | 108 +++++++++++---- .../tennaito/rsql/jpa/entity/Course.java | 22 +++ 11 files changed, 280 insertions(+), 34 deletions(-) mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/builder/BuilderTools.java mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/builder/SimpleBuilderTools.java mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/jpa/AbstractJpaVisitor.java create mode 100644 src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaCountQueryVisitor.java mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaQueryVisitor.java mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilderStrategy.java diff --git a/src/main/java/com/github/tennaito/rsql/builder/BuilderTools.java b/src/main/java/com/github/tennaito/rsql/builder/BuilderTools.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/github/tennaito/rsql/builder/SimpleBuilderTools.java b/src/main/java/com/github/tennaito/rsql/builder/SimpleBuilderTools.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/github/tennaito/rsql/jpa/AbstractJpaVisitor.java b/src/main/java/com/github/tennaito/rsql/jpa/AbstractJpaVisitor.java old mode 100644 new mode 100755 index 1eab91a..b85a30d --- a/src/main/java/com/github/tennaito/rsql/jpa/AbstractJpaVisitor.java +++ b/src/main/java/com/github/tennaito/rsql/jpa/AbstractJpaVisitor.java @@ -60,6 +60,10 @@ public AbstractJpaVisitor(E... e) { } } + public void setEntityClass(Class clazz) { + entityClass = clazz; + } + /** * Get builder tools. * diff --git a/src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaCountQueryVisitor.java b/src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaCountQueryVisitor.java new file mode 100644 index 0000000..69dad9b --- /dev/null +++ b/src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaCountQueryVisitor.java @@ -0,0 +1,130 @@ +/* + * The MIT License + * + * Copyright 2015 Antonio Rabelo. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.github.tennaito.rsql.jpa; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.EntityManager; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import cz.jirutka.rsql.parser.ast.AndNode; +import cz.jirutka.rsql.parser.ast.ComparisonNode; +import cz.jirutka.rsql.parser.ast.OrNode; +import cz.jirutka.rsql.parser.ast.RSQLVisitor; + +/** + * JpaCriteriaQueryVisitor + * + * Visitor class for Criteria Query count creation from RSQL AST Nodes. + * + * @author sza + * + * @param Entity type + */ +public class JpaCriteriaCountQueryVisitor extends AbstractJpaVisitor, T> implements RSQLVisitor, EntityManager> { + + private static final Logger LOG = Logger.getLogger(JpaCriteriaCountQueryVisitor.class.getName()); + + private final JpaPredicateVisitor predicateVisitor; + + private Root root; + + /** + * Construtor with template varargs for entityClass discovery. + * + * @param t not for usage + */ + @SafeVarargs + public JpaCriteriaCountQueryVisitor(T... t) { + super(t); + this.predicateVisitor = new JpaPredicateVisitor(t); + } + + /** + * Get the Predicate Visitor instance. + * + * @return Return the Predicate Visitor. + */ + protected JpaPredicateVisitor getPredicateVisitor() { + this.predicateVisitor.setBuilderTools(this.getBuilderTools()); + return this.predicateVisitor; + } + + /* (non-Javadoc) + * @see cz.jirutka.rsql.parser.ast.RSQLVisitor#visit(cz.jirutka.rsql.parser.ast.AndNode, java.lang.Object) + */ + public CriteriaQuery visit(AndNode node, EntityManager entityManager) { + LOG.log(Level.INFO, "Creating CriteriaQuery for AndNode: {0}", node); + + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + root = cq.from(entityClass); + cq.select(cb.countDistinct(root)); + cq.where(this.getPredicateVisitor().defineRoot(root).visit(node, entityManager)); + + return cq; + } + + /* (non-Javadoc) + * @see cz.jirutka.rsql.parser.ast.RSQLVisitor#visit(cz.jirutka.rsql.parser.ast.OrNode, java.lang.Object) + */ + public CriteriaQuery visit(OrNode node, EntityManager entityManager) { + LOG.log(Level.INFO, "Creating CriteriaQuery for OrNode: {0}", node); + + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + root = cq.from(entityClass); + cq.select(cb.countDistinct(root)); + root = cq.from(entityClass); + cq.where(this.getPredicateVisitor().defineRoot(root).visit(node, entityManager)); + return cq; + } + + /* (non-Javadoc) + * @see cz.jirutka.rsql.parser.ast.RSQLVisitor#visit(cz.jirutka.rsql.parser.ast.ComparisonNode, java.lang.Object) + */ + public CriteriaQuery visit(ComparisonNode node, EntityManager entityManager) { + LOG.log(Level.INFO, "Creating CriteriaQuery for ComparisonNode: {0}", node); + + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Long.class); + root = cq.from(entityClass); + cq.select(cb.countDistinct(root)); + cq.where(this.getPredicateVisitor().defineRoot(root).visit(node, entityManager)); + return cq; + } + + public Root getRoot() { + return root; + } + + public void setRoot(Root root) { + this.root = root; + } + + +} \ No newline at end of file diff --git a/src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaQueryVisitor.java b/src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaQueryVisitor.java old mode 100644 new mode 100755 index b62d39d..f9859c6 --- a/src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaQueryVisitor.java +++ b/src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaQueryVisitor.java @@ -59,7 +59,7 @@ public JpaCriteriaQueryVisitor(T... t) { super(t); this.predicateVisitor = new JpaPredicateVisitor(t); } - + /** * Get the Predicate Visitor instance. * diff --git a/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java b/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java old mode 100644 new mode 100755 index 1cec1e9..01e398c --- a/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java +++ b/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java @@ -43,6 +43,7 @@ import javax.persistence.metamodel.Metamodel; import javax.persistence.metamodel.PluralAttribute; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Set; import java.util.logging.Level; @@ -65,6 +66,9 @@ public final class PredicateBuilder { public static final Character LIKE_WILDCARD = '*'; + private static final Date START_DATE = new Date(0L) ; + private static final Date END_DATE = new Date(99999999999999999L) ; + /** * Private constructor. */ @@ -249,7 +253,14 @@ private static Predicate createPredicate(Expression propertyPath, ComparisonOper } case GREATER_THAN_OR_EQUAL : { Object argument = arguments.get(0); - return createGreaterEqual(propertyPath, (Number)argument, manager); + Predicate predicate; + if (argument instanceof Date){ + predicate = createBetweenThan(propertyPath, (Date)argument, END_DATE, manager); + }else{ + predicate = createGreaterEqual(propertyPath, (Number)argument, manager); + } + return predicate; + } case LESS_THAN : { Object argument = arguments.get(0); @@ -257,8 +268,15 @@ private static Predicate createPredicate(Expression propertyPath, ComparisonOper } case LESS_THAN_OR_EQUAL : { Object argument = arguments.get(0); - return createLessEqual(propertyPath, (Number)argument, manager); - } + + Predicate predicate; + if (argument instanceof Date){ + predicate = createBetweenThan(propertyPath,START_DATE, (Date)argument, manager); + }else{ + predicate = createLessEqual(propertyPath, (Number)argument, manager); + } + return predicate; + } case IN : return createIn(propertyPath, arguments, manager); case NOT_IN : return createNotIn(propertyPath, arguments, manager); } @@ -266,6 +284,20 @@ private static Predicate createPredicate(Expression propertyPath, ComparisonOper throw new IllegalArgumentException("Unknown operator: " + operator); } + /** + + * Creates the between than. + + * + + * @param propertyPath the property path + + * @param startDate the start date + + * @param argument the argument + + * @param manager the manager + + * @return the predicate + + */ + private static Predicate createBetweenThan(Expression propertyPath, Date start, Date end, EntityManager manager) { + CriteriaBuilder builder = manager.getCriteriaBuilder(); + return builder.between(propertyPath, start, end); + } + /** * Apply a case-insensitive "like" constraint to the property path. Value * should contains wildcards "*" (% in SQL) and "_". diff --git a/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilderStrategy.java b/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilderStrategy.java old mode 100644 new mode 100755 diff --git a/src/test/java/com/github/tennaito/rsql/jpa/AbstractVisitorTest.java b/src/test/java/com/github/tennaito/rsql/jpa/AbstractVisitorTest.java index 68627e8..f6d18f8 100644 --- a/src/test/java/com/github/tennaito/rsql/jpa/AbstractVisitorTest.java +++ b/src/test/java/com/github/tennaito/rsql/jpa/AbstractVisitorTest.java @@ -23,6 +23,7 @@ */ package com.github.tennaito.rsql.jpa; +import java.util.Date; import java.util.HashSet; import java.util.Set; @@ -89,6 +90,7 @@ public static void setUpBefore() throws Exception { c.setName("Testing Course"); c.setDepartment(department); c.setDetails(CourseDetails.of("test")); + c.setStartDate( new Date()); entityManager.persist(c); entityManager.getTransaction().commit(); diff --git a/src/test/java/com/github/tennaito/rsql/jpa/DefaultArgumentParserTest.java b/src/test/java/com/github/tennaito/rsql/jpa/DefaultArgumentParserTest.java index 901f213..4187b8b 100644 --- a/src/test/java/com/github/tennaito/rsql/jpa/DefaultArgumentParserTest.java +++ b/src/test/java/com/github/tennaito/rsql/jpa/DefaultArgumentParserTest.java @@ -26,6 +26,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.math.BigDecimal; import java.util.Date; import java.util.GregorianCalendar; @@ -142,7 +143,12 @@ public void testParseArgument() throws Exception { assertEquals(argument, e.getArgument()); assertEquals(Date.class, e.getPropertyType()); } - + + argument = "1235.2232"; + expected = new BigDecimal("1235.2232"); + actual = instance.parse(argument, BigDecimal.class); + assertEquals(expected, actual); + argument = "foo"; expected = new MockValueOfType(); actual = instance.parse(argument, MockValueOfType.class); diff --git a/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java b/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java index e167357..a86b42e 100644 --- a/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java +++ b/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java @@ -131,17 +131,27 @@ public void testGreaterThanSelection() throws Exception { assertEquals(0, courses.size()); } - @Test - public void testGreaterThanEqualSelection() throws Exception { - Node rootNode = new RSQLParser().parse("id=ge=1"); - RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); - CriteriaQuery query = rootNode.accept(visitor, entityManager); + @Test + public void testGreaterThanEqualSelection() throws Exception { + Node rootNode = new RSQLParser().parse("id=ge=1"); + RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); + CriteriaQuery query = rootNode.accept(visitor, entityManager); - List courses = entityManager.createQuery(query).getResultList(); - assertEquals("Testing Course", courses.get(0).getName()); - } + List courses = entityManager.createQuery(query).getResultList(); + assertEquals("Testing Course", courses.get(0).getName()); + } - @Test + @Test + public void testGreaterThanEqualSelectionForDate() throws Exception { + Node rootNode = new RSQLParser().parse("startDate=ge='2016-01-01'"); + RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); + CriteriaQuery query = rootNode.accept(visitor, entityManager); + + List courses = entityManager.createQuery(query).getResultList(); + assertEquals("Testing Course", courses.get(0).getName()); + } + + @Test public void testLessThanSelection() throws Exception { Node rootNode = new RSQLParser().parse("id=lt=1"); RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); @@ -161,7 +171,17 @@ public void testLessThanEqualSelection() throws Exception { assertEquals("Testing Course", courses.get(0).getName()); } - @Test + @Test + public void testLessThanEqualSelectionForDate() throws Exception { + Node rootNode = new RSQLParser().parse("startDate=le='2100-01-01'"); + RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); + CriteriaQuery query = rootNode.accept(visitor, entityManager); + + List courses = entityManager.createQuery(query).getResultList(); + assertEquals("Testing Course", courses.get(0).getName()); + } + + @Test public void testInSelection() throws Exception { Node rootNode = new RSQLParser().parse("id=in=(1,2,3,4)"); RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); @@ -223,22 +243,32 @@ public void testNotIsNullSelection() throws Exception { } @Test - public void testUndefinedComparisonOperator() { - try { - ComparisonOperator newOp = new ComparisonOperator("=def="); - Set set = new HashSet(); - set.add(newOp); - Node rootNode = new RSQLParser(set).parse("id=def=null"); - RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); - CriteriaQuery query = rootNode.accept(visitor, entityManager); - List courses = entityManager.createQuery(query).getResultList(); - fail(); - } catch(Exception e) { - assertEquals("Unknown operator: =def=", e.getMessage()); - } + public void testSetEntity() { + Node rootNode = new RSQLParser().parse("id==1"); + RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); + ((JpaCriteriaQueryVisitor)visitor).setEntityClass(Course.class); + CriteriaQuery query = rootNode.accept(visitor, entityManager); + List courses = (List)entityManager.createQuery(query).getResultList(); + assertEquals(1, courses.size()); } @Test + public void testUndefinedComparisonOperator() { + try { + ComparisonOperator newOp = new ComparisonOperator("=def="); + Set set = new HashSet(); + set.add(newOp); + Node rootNode = new RSQLParser(set).parse("id=def=null"); + RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); + CriteriaQuery query = rootNode.accept(visitor, entityManager); + List courses = entityManager.createQuery(query).getResultList(); + fail(); + } catch(Exception e) { + assertEquals("Unknown operator: =def=", e.getMessage()); + } + } + + @Test public void testDefinedComparisonOperator() { // define the new operator ComparisonOperator newOp = new ComparisonOperator("=def="); @@ -298,12 +328,22 @@ public void testAssociationAliasSelection() throws Exception { @Test public void testAndSelection() throws Exception { - Node rootNode = new RSQLParser().parse("department.id==1;id==2"); - RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); - CriteriaQuery query = rootNode.accept(visitor, entityManager); + Node rootNode = new RSQLParser().parse("department.id==1;id==2"); + RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); + CriteriaQuery query = rootNode.accept(visitor, entityManager); - List courses = entityManager.createQuery(query).getResultList(); - assertEquals(0, courses.size()); + List courses = entityManager.createQuery(query).getResultList(); + assertEquals(0, courses.size()); + } + + @Test + public void testAndSelectionCount() throws Exception { + Node rootNode = new RSQLParser().parse("department.id==1;id==2"); + RSQLVisitor, EntityManager> visitor = new JpaCriteriaCountQueryVisitor(); + CriteriaQuery query = rootNode.accept(visitor, entityManager); + + Long courseCount = entityManager.createQuery(query).getSingleResult(); + assertEquals((Long)0l, courseCount); } @Test @@ -315,7 +355,17 @@ public void testOrSelection() throws Exception { List courses = entityManager.createQuery(query).getResultList(); assertEquals("Testing Course", courses.get(0).getName()); } - + + @Test + public void testOrSelectionCount() throws Exception { + Node rootNode = new RSQLParser().parse("department.id==1,id==2"); + RSQLVisitor, EntityManager> visitor = new JpaCriteriaCountQueryVisitor(); + CriteriaQuery query = rootNode.accept(visitor, entityManager); + + Long courseCount = entityManager.createQuery(query).getSingleResult(); + assertEquals((Long)1l, courseCount); + } + @Test public void testVariousNodesSelection() throws Exception { Node rootNode = new RSQLParser().parse("((department.id==1;id==2),id<3);department.id=out=(3,4,5)"); diff --git a/src/test/java/com/github/tennaito/rsql/jpa/entity/Course.java b/src/test/java/com/github/tennaito/rsql/jpa/entity/Course.java index 1cf773d..50703be 100644 --- a/src/test/java/com/github/tennaito/rsql/jpa/entity/Course.java +++ b/src/test/java/com/github/tennaito/rsql/jpa/entity/Course.java @@ -30,6 +30,7 @@ import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; import javax.persistence.ManyToOne; +import java.util.Date; /** * @@ -48,6 +49,12 @@ public class Course extends AbstractTestEntity { @Column private Integer credits; + @Column + private Date startDate; + + @Column + private Date endDate; + @ManyToOne @JoinColumns({ @JoinColumn(name="id", referencedColumnName="id", insertable=false, updatable=false), @@ -98,4 +105,19 @@ public void setDetails(CourseDetails details) { this.details = details; } + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Date getEndDate() { + return endDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } } From 83a30253685e09d37c090126d1030d92dfab7c28 Mon Sep 17 00:00:00 2001 From: dbetterton Date: Thu, 5 May 2016 09:08:53 -0500 Subject: [PATCH 2/4] Fixed issue with embedded associations Fixed issue with mapping of associations --- .../java/com/github/tennaito/rsql/jpa/entity/Teacher.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/test/java/com/github/tennaito/rsql/jpa/entity/Teacher.java diff --git a/src/test/java/com/github/tennaito/rsql/jpa/entity/Teacher.java b/src/test/java/com/github/tennaito/rsql/jpa/entity/Teacher.java new file mode 100644 index 0000000..83bc4bf --- /dev/null +++ b/src/test/java/com/github/tennaito/rsql/jpa/entity/Teacher.java @@ -0,0 +1,7 @@ +package com.github.tennaito.rsql.jpa.entity; + +/** + * Created by dbetterton on 5/5/16. + */ +public class Teacher { +} From d3b0f7b13d1858d0588943e12ede1c4035e69e2b Mon Sep 17 00:00:00 2001 From: dbetterton Date: Thu, 5 May 2016 09:35:24 -0500 Subject: [PATCH 3/4] Fixes - this time with the actual files Please enter the commit message for your changes. Lines starting --- pom.xml | 2 +- .../tennaito/rsql/jpa/AbstractJpaVisitor.java | 7 +- .../rsql/jpa/JpaPredicateVisitor.java | 0 .../tennaito/rsql/jpa/PredicateBuilder.java | 90 ++++++++++++++----- .../rsql/misc/ArgumentFormatException.java | 0 .../tennaito/rsql/misc/ArgumentParser.java | 0 .../rsql/misc/DefaultArgumentParser.java | 2 + .../com/github/tennaito/rsql/misc/Mapper.java | 0 .../tennaito/rsql/misc/SimpleMapper.java | 2 +- .../parser/ast/ComparisonOperatorProxy.java | 0 .../rsql/jpa/AbstractVisitorTest.java | 7 ++ .../tennaito/rsql/jpa/JpaVisitorTest.java | 78 ++++++++++++---- .../rsql/jpa/entity/CourseDetails.java | 14 ++- .../tennaito/rsql/jpa/entity/Teacher.java | 24 +++-- src/test/resources/META-INF/persistence.xml | 1 + 15 files changed, 178 insertions(+), 49 deletions(-) mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/jpa/JpaPredicateVisitor.java mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/misc/ArgumentFormatException.java mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/misc/ArgumentParser.java mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/misc/DefaultArgumentParser.java mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/misc/Mapper.java mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/misc/SimpleMapper.java mode change 100644 => 100755 src/main/java/com/github/tennaito/rsql/parser/ast/ComparisonOperatorProxy.java diff --git a/pom.xml b/pom.xml index cb665ed..1a5e3b4 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.github.tennaito rsql-jpa - 2.0.2-SNAPSHOT + 2.0.6-SNAPSHOT jar diff --git a/src/main/java/com/github/tennaito/rsql/jpa/AbstractJpaVisitor.java b/src/main/java/com/github/tennaito/rsql/jpa/AbstractJpaVisitor.java index b85a30d..25065ea 100755 --- a/src/main/java/com/github/tennaito/rsql/jpa/AbstractJpaVisitor.java +++ b/src/main/java/com/github/tennaito/rsql/jpa/AbstractJpaVisitor.java @@ -49,7 +49,7 @@ public abstract class AbstractJpaVisitor implements RSQLVisitor clazz) { entityClass = clazz; } diff --git a/src/main/java/com/github/tennaito/rsql/jpa/JpaPredicateVisitor.java b/src/main/java/com/github/tennaito/rsql/jpa/JpaPredicateVisitor.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java b/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java index 01e398c..8bbf0de 100755 --- a/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java +++ b/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java @@ -35,6 +35,7 @@ import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.Expression; import javax.persistence.criteria.From; +import javax.persistence.criteria.Join; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.metamodel.Attribute; @@ -43,6 +44,7 @@ import javax.persistence.metamodel.Metamodel; import javax.persistence.metamodel.PluralAttribute; import java.util.ArrayList; +import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Set; @@ -177,7 +179,7 @@ public static Predicate createPredicate(ComparisonNode comparison, From star * @return The Path for the property path * @throws IllegalArgumentException if attribute of the given property name does not exist */ - public static Path findPropertyPath(String propertyPath, From startRoot, EntityManager entityManager, BuilderTools misc) { + public static Path findPropertyPath(String propertyPath, Path startRoot, EntityManager entityManager, BuilderTools misc) { String[] graph = propertyPath.split("\\."); Metamodel metaModel = entityManager.getMetamodel(); @@ -187,25 +189,34 @@ public static Path findPropertyPath(String propertyPath, From startRoot, for (String property : graph) { String mappedProperty = misc.getPropertiesMapper().translate(property, classMetadata.getJavaType()); - if (!hasPropertyName(mappedProperty, classMetadata)) { - throw new IllegalArgumentException("Unknown property: " + mappedProperty + " from entity " + classMetadata.getJavaType().getName()); - } - - if (isAssociationType(mappedProperty, classMetadata)) { - Class associationType = findPropertyType(mappedProperty, classMetadata); - String previousClass = classMetadata.getJavaType().getName(); - classMetadata = metaModel.managedType(associationType); - LOG.log(Level.INFO, "Create a join between {0} and {1}.", new Object[] {previousClass, classMetadata.getJavaType().getName()}); - root = ((From) root).join(mappedProperty); - } else { - LOG.log(Level.INFO, "Create property path for type {0} property {1}.", new Object[] {classMetadata.getJavaType().getName(), mappedProperty}); - root = root.get(mappedProperty); - - if (isEmbeddedType(mappedProperty, classMetadata)) { - Class embeddedType = findPropertyType(mappedProperty, classMetadata); - classMetadata = metaModel.managedType(embeddedType); - } - } + if( !mappedProperty.equals( property) ) { + root = findPropertyPath( mappedProperty, root, entityManager, misc ); + } else { + if (!hasPropertyName(mappedProperty, classMetadata)) { + throw new IllegalArgumentException("Unknown property: " + mappedProperty + " from entity " + classMetadata.getJavaType().getName()); + } + + if (isAssociationType(mappedProperty, classMetadata)) { + Class associationType = findPropertyType(mappedProperty, classMetadata); + String previousClass = classMetadata.getJavaType().getName(); + classMetadata = metaModel.managedType(associationType); + LOG.log(Level.INFO, "Create a join between {0} and {1}.", new Object[]{previousClass, classMetadata.getJavaType().getName()}); + + if (root instanceof Join) { + root = root.get(mappedProperty); + } else { + root = ((From) root).join(mappedProperty); + } + } else { + LOG.log(Level.INFO, "Create property path for type {0} property {1}.", new Object[]{classMetadata.getJavaType().getName(), mappedProperty}); + root = root.get(mappedProperty); + + if (isEmbeddedType(mappedProperty, classMetadata)) { + Class embeddedType = findPropertyType(mappedProperty, classMetadata); + classMetadata = metaModel.managedType(embeddedType); + } + } + } } return root; @@ -249,8 +260,15 @@ private static Predicate createPredicate(Expression propertyPath, ComparisonOper } case GREATER_THAN : { Object argument = arguments.get(0); - return createGreaterThan(propertyPath, (Number)argument, manager); - } + Predicate predicate; + if (argument instanceof Date) { + int days = 1; + predicate = createBetweenThan(propertyPath, modifyDate(argument, days), END_DATE, manager); + } else { + predicate = createGreaterThan(propertyPath, (Number) argument, manager); + } + return predicate; + } case GREATER_THAN_OR_EQUAL : { Object argument = arguments.get(0); Predicate predicate; @@ -264,8 +282,15 @@ private static Predicate createPredicate(Expression propertyPath, ComparisonOper } case LESS_THAN : { Object argument = arguments.get(0); - return createLessThan(propertyPath, (Number)argument, manager); - } + Predicate predicate; + if (argument instanceof Date) { + int days = -1; + predicate = createBetweenThan(propertyPath, START_DATE, modifyDate(argument, days), manager); + } else { + predicate = createLessThan(propertyPath, (Number) argument, manager); + } + return predicate; + } case LESS_THAN_OR_EQUAL : { Object argument = arguments.get(0); @@ -518,4 +543,21 @@ private static Class findPropertyType(String property, ManagedType cla private static boolean isNullArgument(Object argument) { return argument == null; } + + + /** + * Get date regarding the operation (less then or greater than) + * + * @param argument Date to be modified + * @param days Days to be added or removed form argument; + *@return Date modified date + */ + private static Date modifyDate(Object argument, int days) { + Date date = (Date) argument; + Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(Calendar.DATE, days); + date = c.getTime(); + return date; + } } diff --git a/src/main/java/com/github/tennaito/rsql/misc/ArgumentFormatException.java b/src/main/java/com/github/tennaito/rsql/misc/ArgumentFormatException.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/github/tennaito/rsql/misc/ArgumentParser.java b/src/main/java/com/github/tennaito/rsql/misc/ArgumentParser.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/github/tennaito/rsql/misc/DefaultArgumentParser.java b/src/main/java/com/github/tennaito/rsql/misc/DefaultArgumentParser.java old mode 100644 new mode 100755 index c48f27a..dbbba98 --- a/src/main/java/com/github/tennaito/rsql/misc/DefaultArgumentParser.java +++ b/src/main/java/com/github/tennaito/rsql/misc/DefaultArgumentParser.java @@ -26,6 +26,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -72,6 +73,7 @@ public T parse(String argument, Class type) if (type.equals(Float.class) || type.equals(float.class)) return (T) Float.valueOf(argument); if (type.equals(Double.class) || type.equals(double.class)) return (T) Double.valueOf(argument); if (type.equals(Long.class) || type.equals(long.class)) return (T) Long.valueOf(argument); + if (type.equals(BigDecimal.class) ) return (T) new BigDecimal(argument); } catch (IllegalArgumentException ex) { throw new ArgumentFormatException(argument, type); } diff --git a/src/main/java/com/github/tennaito/rsql/misc/Mapper.java b/src/main/java/com/github/tennaito/rsql/misc/Mapper.java old mode 100644 new mode 100755 diff --git a/src/main/java/com/github/tennaito/rsql/misc/SimpleMapper.java b/src/main/java/com/github/tennaito/rsql/misc/SimpleMapper.java old mode 100644 new mode 100755 index 0eda8dd..d8595cf --- a/src/main/java/com/github/tennaito/rsql/misc/SimpleMapper.java +++ b/src/main/java/com/github/tennaito/rsql/misc/SimpleMapper.java @@ -35,7 +35,7 @@ */ public class SimpleMapper implements Mapper { - private static final Logger LOG = Logger.getLogger(DefaultArgumentParser.class.getName()); + private static final Logger LOG = Logger.getLogger(SimpleMapper.class.getName()); private Map, Map> mapping; diff --git a/src/main/java/com/github/tennaito/rsql/parser/ast/ComparisonOperatorProxy.java b/src/main/java/com/github/tennaito/rsql/parser/ast/ComparisonOperatorProxy.java old mode 100644 new mode 100755 diff --git a/src/test/java/com/github/tennaito/rsql/jpa/AbstractVisitorTest.java b/src/test/java/com/github/tennaito/rsql/jpa/AbstractVisitorTest.java index f6d18f8..68916db 100644 --- a/src/test/java/com/github/tennaito/rsql/jpa/AbstractVisitorTest.java +++ b/src/test/java/com/github/tennaito/rsql/jpa/AbstractVisitorTest.java @@ -29,6 +29,7 @@ import javax.persistence.EntityManager; +import com.github.tennaito.rsql.jpa.entity.Teacher; import org.junit.BeforeClass; import com.github.tennaito.rsql.jpa.entity.Course; @@ -81,6 +82,11 @@ public static void setUpBefore() throws Exception { department.setCode("MI-MDW"); department.setHead(head); entityManager.persist(department); + + Teacher teacher = new Teacher(); + teacher.setId(23L); + teacher.setSpecialtyDescription("Maths"); + entityManager.persist(teacher); Course c = new Course(); c.setId(1L); @@ -90,6 +96,7 @@ public static void setUpBefore() throws Exception { c.setName("Testing Course"); c.setDepartment(department); c.setDetails(CourseDetails.of("test")); + c.getDetails().setTeacher(teacher); c.setStartDate( new Date()); entityManager.persist(c); diff --git a/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java b/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java index a86b42e..69acaa5 100644 --- a/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java +++ b/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java @@ -131,6 +131,16 @@ public void testGreaterThanSelection() throws Exception { assertEquals(0, courses.size()); } + @Test + public void testGreaterThanDate() throws Exception { + Node rootNode = new RSQLParser().parse("startDate=gt='2001-01-01'"); + RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); + CriteriaQuery query = rootNode.accept(visitor, entityManager); + + List courses = entityManager.createQuery(query).getResultList(); + assertEquals(1, courses.size()); + } + @Test public void testGreaterThanEqualSelection() throws Exception { Node rootNode = new RSQLParser().parse("id=ge=1"); @@ -171,6 +181,16 @@ public void testLessThanEqualSelection() throws Exception { assertEquals("Testing Course", courses.get(0).getName()); } + @Test + public void testLessThanDate() throws Exception { + Node rootNode = new RSQLParser().parse("startDate=lt='2222-02-02'"); + RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); + CriteriaQuery query = rootNode.accept(visitor, entityManager); + + List courses = entityManager.createQuery(query).getResultList(); + assertEquals(1, courses.size()); + } + @Test public void testLessThanEqualSelectionForDate() throws Exception { Node rootNode = new RSQLParser().parse("startDate=le='2100-01-01'"); @@ -309,24 +329,41 @@ public void testAssociationSelection() throws Exception { assertEquals("Testing Course", courses.get(0).getName()); } - @Test - public void testAssociationAliasSelection() throws Exception { - Node rootNode = new RSQLParser().parse("dept.id==1"); - JpaCriteriaQueryVisitor visitor = new JpaCriteriaQueryVisitor(); - // add to SimpleMapper - assertNotNull(((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).getMapping()); - ((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).addMapping(Course.class, new HashMap()); - ((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).addMapping(Course.class, "dept", "department"); + @Test + public void testAssociationAliasSelection() throws Exception { + Node rootNode = new RSQLParser().parse("dept.id==1"); + JpaCriteriaQueryVisitor visitor = new JpaCriteriaQueryVisitor(); + // add to SimpleMapper + assertNotNull(((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).getMapping()); + ((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).addMapping(Course.class, new HashMap()); + ((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).addMapping(Course.class, "dept", "department"); - CriteriaQuery query = rootNode.accept(visitor, entityManager); - List courses = entityManager.createQuery(query).getResultList(); - assertEquals("Testing Course", courses.get(0).getName()); - - ((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).setMapping(null); - assertNull(((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).getMapping()); - } + CriteriaQuery query = rootNode.accept(visitor, entityManager); + List courses = entityManager.createQuery(query).getResultList(); + assertEquals("Testing Course", courses.get(0).getName()); - @Test + ((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).setMapping(null); + assertNull(((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).getMapping()); + } + + @Test + public void testAssociationAliasSelectionWithAssociationAlias() throws Exception { + Node rootNode = new RSQLParser().parse("dept_id==1"); + JpaCriteriaQueryVisitor visitor = new JpaCriteriaQueryVisitor(); + // add to SimpleMapper + assertNotNull(((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).getMapping()); + ((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).addMapping(Course.class, new HashMap()); + ((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).addMapping(Course.class, "dept_id", "department.id"); + + CriteriaQuery query = rootNode.accept(visitor, entityManager); + List courses = entityManager.createQuery(query).getResultList(); + assertEquals("Testing Course", courses.get(0).getName()); + + ((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).setMapping(null); + assertNull(((SimpleMapper)visitor.getBuilderTools().getPropertiesMapper()).getMapping()); + } + + @Test public void testAndSelection() throws Exception { Node rootNode = new RSQLParser().parse("department.id==1;id==2"); RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); @@ -522,4 +559,13 @@ public void testSelectionUsingEmbeddedField() throws Exception { assertEquals("Testing Course", courses.get(0).getName()); } + @Test + public void testSelectionUsingEmbeddedAssociationField() throws Exception { + Node rootNode = new RSQLParser().parse("details.teacher.specialtyDescription==Maths"); + RSQLVisitor, EntityManager> visitor = new JpaCriteriaQueryVisitor(); + CriteriaQuery query = rootNode.accept(visitor, entityManager); + + List courses = entityManager.createQuery(query).getResultList(); + assertEquals("Testing Course", courses.get(0).getName()); + } } diff --git a/src/test/java/com/github/tennaito/rsql/jpa/entity/CourseDetails.java b/src/test/java/com/github/tennaito/rsql/jpa/entity/CourseDetails.java index 7b3c707..02bcf0d 100644 --- a/src/test/java/com/github/tennaito/rsql/jpa/entity/CourseDetails.java +++ b/src/test/java/com/github/tennaito/rsql/jpa/entity/CourseDetails.java @@ -1,12 +1,16 @@ package com.github.tennaito.rsql.jpa.entity; import javax.persistence.Embeddable; +import javax.persistence.OneToOne; @Embeddable public class CourseDetails { private String description; - + + @OneToOne + private Teacher teacher; + public String getDescription() { return description; } @@ -20,4 +24,12 @@ public static CourseDetails of(String description) { details.setDescription(description); return details; } + + public Teacher getTeacher() { + return teacher; + } + + public void setTeacher(Teacher teacher) { + this.teacher = teacher; + } } diff --git a/src/test/java/com/github/tennaito/rsql/jpa/entity/Teacher.java b/src/test/java/com/github/tennaito/rsql/jpa/entity/Teacher.java index 83bc4bf..b4a76b0 100644 --- a/src/test/java/com/github/tennaito/rsql/jpa/entity/Teacher.java +++ b/src/test/java/com/github/tennaito/rsql/jpa/entity/Teacher.java @@ -1,7 +1,21 @@ package com.github.tennaito.rsql.jpa.entity; -/** - * Created by dbetterton on 5/5/16. - */ -public class Teacher { -} +import javax.persistence.Column; +import javax.persistence.Entity; + +@Entity +public class Teacher extends AbstractTestEntity { + + @Column + private String specialtyDescription; + + public String getSpecialtyDescription() { + return specialtyDescription; + } + + public void setSpecialtyDescription(String specialtyDescription) { + this.specialtyDescription = specialtyDescription; + } + + +} \ No newline at end of file diff --git a/src/test/resources/META-INF/persistence.xml b/src/test/resources/META-INF/persistence.xml index 64745cb..215c469 100644 --- a/src/test/resources/META-INF/persistence.xml +++ b/src/test/resources/META-INF/persistence.xml @@ -10,6 +10,7 @@ com.github.tennaito.rsql.jpa.entity.Department com.github.tennaito.rsql.jpa.entity.Person com.github.tennaito.rsql.jpa.entity.Title + com.github.tennaito.rsql.jpa.entity.Teacher From a2348f9feaa1224e9f48bdc1b87ee8c85da63c37 Mon Sep 17 00:00:00 2001 From: dbetterton Date: Thu, 5 May 2016 09:50:18 -0500 Subject: [PATCH 4/4] Added tests to complete coverage --- .../github/tennaito/rsql/jpa/JpaVisitorTest.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java b/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java index 69acaa5..d56aea7 100644 --- a/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java +++ b/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java @@ -44,6 +44,7 @@ import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.From; import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; import org.junit.Before; import org.junit.Test; @@ -373,7 +374,20 @@ public void testAndSelection() throws Exception { assertEquals(0, courses.size()); } - @Test + @Test + public void testBasicSelectionCount() throws Exception { + Node rootNode = new RSQLParser().parse("department.id==1"); + RSQLVisitor, EntityManager> visitor = new JpaCriteriaCountQueryVisitor(); + CriteriaQuery query = rootNode.accept(visitor, entityManager); + + Long courseCount = entityManager.createQuery(query).getSingleResult(); + assertEquals((Long)1l, courseCount); + Root root = ((JpaCriteriaCountQueryVisitor)visitor).getRoot(); + assertNotNull(root); + ((JpaCriteriaCountQueryVisitor)visitor).setRoot(root); + } + + @Test public void testAndSelectionCount() throws Exception { Node rootNode = new RSQLParser().parse("department.id==1;id==2"); RSQLVisitor, EntityManager> visitor = new JpaCriteriaCountQueryVisitor();