From 6c9fe4a2474c62e5b1381d8848cee202efa65e1e Mon Sep 17 00:00:00 2001 From: sza Date: Fri, 20 Nov 2015 12:36:18 +0100 Subject: [PATCH 1/5] rename SimpleMapper name class for log --- src/main/java/com/github/tennaito/rsql/misc/SimpleMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/github/tennaito/rsql/misc/SimpleMapper.java b/src/main/java/com/github/tennaito/rsql/misc/SimpleMapper.java index 0eda8dd..d8595cf 100644 --- 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; From ba9be90ee82f72c9226f3bac8a168b7c1a54870b Mon Sep 17 00:00:00 2001 From: sza Date: Fri, 20 Nov 2015 12:42:17 +0100 Subject: [PATCH 2/5] adding BigDecimal support to DefaultArgumentParser --- .../github/tennaito/rsql/misc/DefaultArgumentParser.java | 2 ++ .../github/tennaito/rsql/jpa/DefaultArgumentParserTest.java | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/main/java/com/github/tennaito/rsql/misc/DefaultArgumentParser.java b/src/main/java/com/github/tennaito/rsql/misc/DefaultArgumentParser.java index c48f27a..e991242 100644 --- 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/test/java/com/github/tennaito/rsql/jpa/DefaultArgumentParserTest.java b/src/test/java/com/github/tennaito/rsql/jpa/DefaultArgumentParserTest.java index 901f213..4673adc 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; @@ -143,6 +144,11 @@ public void testParseArgument() throws Exception { 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); From 4d33003330ad09cd9496101545ca090343f372fe Mon Sep 17 00:00:00 2001 From: sza Date: Fri, 20 Nov 2015 17:01:44 +0100 Subject: [PATCH 3/5] add date support to le and ge operator --- pom.xml | 2 +- .../tennaito/rsql/jpa/PredicateBuilder.java | 38 ++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index cb665ed..8ef4adf 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.github.tennaito rsql-jpa - 2.0.2-SNAPSHOT + 2.0.3-SNAPSHOT jar 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 1cec1e9..7cad762 100644 --- a/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java +++ b/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java @@ -26,6 +26,7 @@ import com.github.tennaito.rsql.builder.BuilderTools; import com.github.tennaito.rsql.parser.ast.ComparisonOperatorProxy; + import cz.jirutka.rsql.parser.ast.ComparisonNode; import cz.jirutka.rsql.parser.ast.ComparisonOperator; import cz.jirutka.rsql.parser.ast.LogicalNode; @@ -42,7 +43,9 @@ import javax.persistence.metamodel.ManagedType; 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 +68,10 @@ 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 +256,13 @@ 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,7 +270,14 @@ 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); @@ -267,6 +287,20 @@ private static Predicate createPredicate(Expression propertyPath, ComparisonOper } /** + * 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 "_". * From 236daf94cdb08060a2bf6869395ec76226a93eba Mon Sep 17 00:00:00 2001 From: sza Date: Fri, 20 Nov 2015 17:11:51 +0100 Subject: [PATCH 4/5] add date support TUs --- .../rsql/jpa/AbstractVisitorTest.java | 2 ++ .../tennaito/rsql/jpa/JpaVisitorTest.java | 27 +++++++++++++++++++ .../tennaito/rsql/jpa/entity/Course.java | 17 ++++++++++++ 3 files changed, 46 insertions(+) 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..8ed59c9 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.Calendar; import java.util.HashSet; import java.util.Set; @@ -88,6 +89,7 @@ public static void setUpBefore() throws Exception { c.setCredits(10); c.setName("Testing Course"); c.setDepartment(department); + c.setDate(Calendar.getInstance().getTime()); c.setDetails(CourseDetails.of("test")); 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 e167357..ec71818 100644 --- a/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java +++ b/src/test/java/com/github/tennaito/rsql/jpa/JpaVisitorTest.java @@ -33,8 +33,11 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -68,6 +71,8 @@ public class JpaVisitorTest extends AbstractVisitorTest { final static XorNode xorNode = new XorNode(new ArrayList()); + private static final DateFormat DATE_TIME_FORMATTER = new SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss"); @Before public void setUp() throws Exception { @@ -141,6 +146,17 @@ public void testGreaterThanEqualSelection() throws Exception { assertEquals("Testing Course", courses.get(0).getName()); } + @Test + public void testGreaterThanEqualDate() throws Exception { + Date date = new Date(0L); + Node rootNode = new RSQLParser().parse("date=ge='"+DATE_TIME_FORMATTER.format(date)+"'"); + 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"); @@ -150,6 +166,17 @@ public void testLessThanSelection() throws Exception { List courses = entityManager.createQuery(query).getResultList(); assertEquals(0, courses.size()); } + + @Test + public void testLessThanEqualDate() throws Exception { + Date date = new Date(999999999999999999L); + Node rootNode = new RSQLParser().parse("date=le='"+DATE_TIME_FORMATTER.format(date)+"'"); + 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 testLessThanEqualSelection() throws Exception { 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..5695f06 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 @@ -24,12 +24,16 @@ */ package com.github.tennaito.rsql.jpa.entity; +import java.util.Date; + import javax.persistence.Column; import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; import javax.persistence.ManyToOne; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; /** * @@ -48,6 +52,11 @@ public class Course extends AbstractTestEntity { @Column private Integer credits; + @Column + @Temporal(TemporalType.TIMESTAMP) + private Date date; + + @ManyToOne @JoinColumns({ @JoinColumn(name="id", referencedColumnName="id", insertable=false, updatable=false), @@ -98,4 +107,12 @@ public void setDetails(CourseDetails details) { this.details = details; } + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + } From fde7425ba3600ed1af7841e8a6494366c36dd964 Mon Sep 17 00:00:00 2001 From: sza Date: Fri, 4 Mar 2016 11:49:53 +0100 Subject: [PATCH 5/5] count query visito --- rsql-jpa.iml | 33 +++++ .../jpa/JpaCriteriaCountQueryVisitor.java | 130 ++++++++++++++++++ .../rsql/jpa/JpaCriteriaQueryVisitor.java | 21 ++- .../tennaito/rsql/jpa/PredicateBuilder.java | 10 +- .../rsql/misc/ArgumentFormatException.java | 2 +- .../tennaito/rsql/misc/ArgumentParser.java | 2 +- 6 files changed, 190 insertions(+), 8 deletions(-) create mode 100644 rsql-jpa.iml create mode 100644 src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaCountQueryVisitor.java diff --git a/rsql-jpa.iml b/rsql-jpa.iml new file mode 100644 index 0000000..baaefbd --- /dev/null +++ b/rsql-jpa.iml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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..0ac0383 --- /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; + } + + +} diff --git a/src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaQueryVisitor.java b/src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaQueryVisitor.java index b62d39d..b334500 100644 --- a/src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaQueryVisitor.java +++ b/src/main/java/com/github/tennaito/rsql/jpa/JpaCriteriaQueryVisitor.java @@ -28,7 +28,7 @@ import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.From; +import javax.persistence.criteria.Root; import cz.jirutka.rsql.parser.ast.AndNode; import cz.jirutka.rsql.parser.ast.ComparisonNode; @@ -49,12 +49,15 @@ public class JpaCriteriaQueryVisitor extends AbstractJpaVisitor predicateVisitor; + + private Root root; /** * Construtor with template varargs for entityClass discovery. * * @param t not for usage */ + @SafeVarargs public JpaCriteriaQueryVisitor(T... t) { super(t); this.predicateVisitor = new JpaPredicateVisitor(t); @@ -76,7 +79,7 @@ protected JpaPredicateVisitor getPredicateVisitor() { public CriteriaQuery visit(AndNode node, EntityManager entityManager) { LOG.log(Level.INFO, "Creating CriteriaQuery for AndNode: {0}", node); CriteriaQuery criteria = entityManager.getCriteriaBuilder().createQuery(entityClass); - From root = criteria.from(entityClass); + root = criteria.from(entityClass); return criteria.where(this.getPredicateVisitor().defineRoot(root).visit(node, entityManager)); } @@ -86,7 +89,7 @@ public CriteriaQuery visit(AndNode node, EntityManager entityManager) { public CriteriaQuery visit(OrNode node, EntityManager entityManager) { LOG.log(Level.INFO, "Creating CriteriaQuery for OrNode: {0}", node); CriteriaQuery criteria = entityManager.getCriteriaBuilder().createQuery(entityClass); - From root = criteria.from(entityClass); + root = criteria.from(entityClass); return criteria.where(this.getPredicateVisitor().defineRoot(root).visit(node, entityManager)); } @@ -96,7 +99,17 @@ public CriteriaQuery visit(OrNode node, EntityManager entityManager) { public CriteriaQuery visit(ComparisonNode node, EntityManager entityManager) { LOG.log(Level.INFO, "Creating CriteriaQuery for ComparisonNode: {0}", node); CriteriaQuery criteria = entityManager.getCriteriaBuilder().createQuery(entityClass); - From root = criteria.from(entityClass); + root = criteria.from(entityClass); return criteria.where(this.getPredicateVisitor().defineRoot(root).visit(node, entityManager)); } + + public Root getRoot() { + return root; + } + + public void setRoot(Root root) { + this.root = root; + } + + } 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 7cad762..ec4f614 100644 --- a/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java +++ b/src/main/java/com/github/tennaito/rsql/jpa/PredicateBuilder.java @@ -45,6 +45,7 @@ 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; @@ -70,13 +71,18 @@ public final class PredicateBuilder { private static final Date START_DATE = new Date(0L) ; - private static final Date END_DATE = new Date(99999999999999999L); + private static final Date END_DATE; + static { + Calendar endCalendar = Calendar.getInstance(); + endCalendar.set(9000, 01, 01); + END_DATE = endCalendar.getTime(); + } /** * Private constructor. */ private PredicateBuilder(){ - super(); + super(); } /** diff --git a/src/main/java/com/github/tennaito/rsql/misc/ArgumentFormatException.java b/src/main/java/com/github/tennaito/rsql/misc/ArgumentFormatException.java index 8f35683..d232648 100644 --- a/src/main/java/com/github/tennaito/rsql/misc/ArgumentFormatException.java +++ b/src/main/java/com/github/tennaito/rsql/misc/ArgumentFormatException.java @@ -28,7 +28,7 @@ * Indicate that argument is not in suitable format required by entity's * property, i.e. is not parseable to the specified type. * - * @author Jakub Jirutka + * @author Jakub Jirutka jakub@jirutka.cz * @author AntonioRabelo */ public class ArgumentFormatException extends RuntimeException { diff --git a/src/main/java/com/github/tennaito/rsql/misc/ArgumentParser.java b/src/main/java/com/github/tennaito/rsql/misc/ArgumentParser.java index b9a15dc..4b7907b 100644 --- a/src/main/java/com/github/tennaito/rsql/misc/ArgumentParser.java +++ b/src/main/java/com/github/tennaito/rsql/misc/ArgumentParser.java @@ -30,7 +30,7 @@ * Interface for Argument Parser that is used for parsing given string argument * from RSQL query according to type of the target property. * - * @author Jakub Jirutka + * @author Jakub Jirutka jakub@jirutka.cz * @author AntonioRabelo */ public interface ArgumentParser {