diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Criteria.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Criteria.java
new file mode 100644
index 00000000..a6b3d3ab
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Criteria.java
@@ -0,0 +1,695 @@
+/*
+ * Copyright 2019-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.relational.core.query;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.springframework.dao.InvalidDataAccessApiUsageException;
+import org.springframework.data.relational.core.sql.SqlIdentifier;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Central class for creating queries. It follows a fluent API style so that you can easily chain together multiple
+ * criteria. Static import of the {@code Criteria.property(…)} method will improve readability as in
+ * {@code where(property(…).is(…)}.
+ *
+ * The Criteria API supports composition with a {@link #empty() NULL object} and a {@link #from(List) static factory
+ * method}. Example usage:
+ *
+ *
+ * Criteria.from(Criteria.where("name").is("Foo"), Criteria.from(Criteria.where("age").greaterThan(42)));
+ *
+ *
+ * rendering:
+ *
+ *
+ * WHERE name = 'Foo' AND age > 42
+ *
+ *
+ * @author Mark Paluch
+ * @author Oliver Drotbohm
+ * @author Roman Chigvintsev
+ * @since 2.0
+ */
+public class Criteria implements CriteriaDefinition {
+
+ static final Criteria EMPTY = new Criteria(SqlIdentifier.EMPTY, Comparator.INITIAL, null);
+
+ private final @Nullable Criteria previous;
+ private final Combinator combinator;
+ private final List group;
+
+ private final @Nullable SqlIdentifier column;
+ private final @Nullable Comparator comparator;
+ private final @Nullable Object value;
+ private final boolean ignoreCase;
+
+ private Criteria(SqlIdentifier column, Comparator comparator, @Nullable Object value) {
+ this(null, Combinator.INITIAL, Collections.emptyList(), column, comparator, value, false);
+ }
+
+ private Criteria(@Nullable Criteria previous, Combinator combinator, List group,
+ @Nullable SqlIdentifier column, @Nullable Comparator comparator, @Nullable Object value) {
+ this(previous, combinator, group, column, comparator, value, false);
+ }
+
+ private Criteria(@Nullable Criteria previous, Combinator combinator, List group,
+ @Nullable SqlIdentifier column, @Nullable Comparator comparator, @Nullable Object value, boolean ignoreCase) {
+
+ this.previous = previous;
+ this.combinator = previous != null && previous.isEmpty() ? Combinator.INITIAL : combinator;
+ this.group = group;
+ this.column = column;
+ this.comparator = comparator;
+ this.value = value;
+ this.ignoreCase = ignoreCase;
+ }
+
+ private Criteria(@Nullable Criteria previous, Combinator combinator, List group) {
+
+ this.previous = previous;
+ this.combinator = previous != null && previous.isEmpty() ? Combinator.INITIAL : combinator;
+ this.group = group;
+ this.column = null;
+ this.comparator = null;
+ this.value = null;
+ this.ignoreCase = false;
+ }
+
+
+ /**
+ * Static factory method to create an empty Criteria.
+ *
+ * @return an empty {@link Criteria}.
+ */
+ public static Criteria empty() {
+ return EMPTY;
+ }
+
+ /**
+ * Create a new {@link Criteria} and combine it as group with {@code AND} using the provided {@link List Criterias}.
+ *
+ * @return new {@link Criteria}.
+ */
+ public static Criteria from(Criteria... criteria) {
+
+ Assert.notNull(criteria, "Criteria must not be null");
+ Assert.noNullElements(criteria, "Criteria must not contain null elements");
+
+ return from(Arrays.asList(criteria));
+ }
+
+ /**
+ * Create a new {@link Criteria} and combine it as group with {@code AND} using the provided {@link List Criterias}.
+ *
+ * @return new {@link Criteria}.
+ */
+ public static Criteria from(List criteria) {
+
+ Assert.notNull(criteria, "Criteria must not be null");
+ Assert.noNullElements(criteria, "Criteria must not contain null elements");
+
+ if (criteria.isEmpty()) {
+ return EMPTY;
+ }
+
+ if (criteria.size() == 1) {
+ return criteria.get(0);
+ }
+
+ return EMPTY.and(criteria);
+ }
+
+ /**
+ * Static factory method to create a Criteria using the provided {@code column} name.
+ *
+ * @param column Must not be {@literal null} or empty.
+ * @return a new {@link CriteriaStep} object to complete the first {@link Criteria}.
+ */
+ public static CriteriaStep where(String column) {
+
+ Assert.hasText(column, "Column name must not be null or empty!");
+
+ return new DefaultCriteriaStep(SqlIdentifier.unquoted(column));
+ }
+
+ /**
+ * Create a new {@link Criteria} and combine it with {@code AND} using the provided {@code column} name.
+ *
+ * @param column Must not be {@literal null} or empty.
+ * @return a new {@link CriteriaStep} object to complete the next {@link Criteria}.
+ */
+ public CriteriaStep and(String column) {
+
+ Assert.hasText(column, "Column name must not be null or empty!");
+
+ SqlIdentifier identifier = SqlIdentifier.unquoted(column);
+ return new DefaultCriteriaStep(identifier) {
+ @Override
+ protected Criteria createCriteria(Comparator comparator, Object value) {
+ return new Criteria(Criteria.this, Combinator.AND, Collections.emptyList(), identifier, comparator, value);
+ }
+ };
+ }
+
+ /**
+ * Create a new {@link Criteria} and combine it as group with {@code AND} using the provided {@link Criteria} group.
+ *
+ * @param criteria criteria object.
+ * @return a new {@link Criteria} object.
+ * @since 1.1
+ */
+ public Criteria and(Criteria criteria) {
+
+ Assert.notNull(criteria, "Criteria must not be null!");
+
+ return and(Collections.singletonList(criteria));
+ }
+
+ /**
+ * Create a new {@link Criteria} and combine it as group with {@code AND} using the provided {@link Criteria} group.
+ *
+ * @param criteria criteria objects.
+ * @return a new {@link Criteria} object.
+ * @since 1.1
+ */
+ public Criteria and(List criteria) {
+
+ Assert.notNull(criteria, "Criteria must not be null!");
+
+ return new Criteria(Criteria.this, Combinator.AND, criteria);
+ }
+
+ /**
+ * Create a new {@link Criteria} and combine it with {@code OR} using the provided {@code column} name.
+ *
+ * @param column Must not be {@literal null} or empty.
+ * @return a new {@link CriteriaStep} object to complete the next {@link Criteria}.
+ */
+ public CriteriaStep or(String column) {
+
+ Assert.hasText(column, "Column name must not be null or empty!");
+
+ SqlIdentifier identifier = SqlIdentifier.unquoted(column);
+ return new DefaultCriteriaStep(identifier) {
+ @Override
+ protected Criteria createCriteria(Comparator comparator, Object value) {
+ return new Criteria(Criteria.this, Combinator.OR, Collections.emptyList(), identifier, comparator, value);
+ }
+ };
+ }
+
+ /**
+ * Create a new {@link Criteria} and combine it as group with {@code OR} using the provided {@link Criteria} group.
+ *
+ * @param criteria criteria object.
+ * @return a new {@link Criteria} object.
+ * @since 1.1
+ */
+ public Criteria or(Criteria criteria) {
+
+ Assert.notNull(criteria, "Criteria must not be null!");
+
+ return or(Collections.singletonList(criteria));
+ }
+
+ /**
+ * Create a new {@link Criteria} and combine it as group with {@code OR} using the provided {@link Criteria} group.
+ *
+ * @param criteria criteria object.
+ * @return a new {@link Criteria} object.
+ * @since 1.1
+ */
+ public Criteria or(List criteria) {
+
+ Assert.notNull(criteria, "Criteria must not be null!");
+
+ return new Criteria(Criteria.this, Combinator.OR, criteria);
+ }
+
+ /**
+ * Creates a new {@link Criteria} with the given "ignore case" flag.
+ *
+ * @param ignoreCase {@literal true} if comparison should be done in case-insensitive way
+ * @return a new {@link Criteria} object
+ */
+ public Criteria ignoreCase(boolean ignoreCase) {
+ if (this.ignoreCase != ignoreCase) {
+ return new Criteria(previous, combinator, group, column, comparator, value, ignoreCase);
+ }
+ return this;
+ }
+
+ /**
+ * @return the previous {@link Criteria} object. Can be {@literal null} if there is no previous {@link Criteria}.
+ * @see #hasPrevious()
+ */
+ @Nullable
+ public Criteria getPrevious() {
+ return previous;
+ }
+
+ /**
+ * @return {@literal true} if this {@link Criteria} has a previous one.
+ */
+ public boolean hasPrevious() {
+ return previous != null;
+ }
+
+ /**
+ * @return {@literal true} if this {@link Criteria} is empty.
+ * @since 1.1
+ */
+ @Override
+ public boolean isEmpty() {
+
+ if (!doIsEmpty()) {
+ return false;
+ }
+
+ Criteria parent = this.previous;
+
+ while (parent != null) {
+
+ if (!parent.doIsEmpty()) {
+ return false;
+ }
+
+ parent = parent.previous;
+ }
+
+ return true;
+ }
+
+ private boolean doIsEmpty() {
+
+ if (this.comparator == Comparator.INITIAL) {
+ return true;
+ }
+
+ if (this.column != null) {
+ return false;
+ }
+
+ for (Criteria criteria : group) {
+
+ if (!criteria.isEmpty()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return {@literal true} if this {@link Criteria} is empty.
+ */
+ public boolean isGroup() {
+ return !this.group.isEmpty();
+ }
+
+ /**
+ * @return {@link Combinator} to combine this criteria with a previous one.
+ */
+ public Combinator getCombinator() {
+ return combinator;
+ }
+
+ public List getGroup() {
+ return group;
+ }
+
+ /**
+ * @return the column/property name.
+ */
+ @Nullable
+ public SqlIdentifier getColumn() {
+ return column;
+ }
+
+ /**
+ * @return {@link Comparator}.
+ */
+ @Nullable
+ public Comparator getComparator() {
+ return comparator;
+ }
+
+ /**
+ * @return the comparison value. Can be {@literal null}.
+ */
+ @Nullable
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Checks whether comparison should be done in case-insensitive way.
+ *
+ * @return {@literal true} if comparison should be done in case-insensitive way
+ */
+ @Override
+ public boolean isIgnoreCase() {
+ return ignoreCase;
+ }
+
+ /**
+ * Interface declaring terminal builder methods to build a {@link Criteria}.
+ */
+ public interface CriteriaStep {
+
+ /**
+ * Creates a {@link Criteria} using equality.
+ *
+ * @param value must not be {@literal null}.
+ */
+ Criteria is(Object value);
+
+ /**
+ * Creates a {@link Criteria} using equality (is not).
+ *
+ * @param value must not be {@literal null}.
+ */
+ Criteria not(Object value);
+
+ /**
+ * Creates a {@link Criteria} using {@code IN}.
+ *
+ * @param values must not be {@literal null}.
+ */
+ Criteria in(Object... values);
+
+ /**
+ * Creates a {@link Criteria} using {@code IN}.
+ *
+ * @param values must not be {@literal null}.
+ */
+ Criteria in(Collection> values);
+
+ /**
+ * Creates a {@link Criteria} using {@code NOT IN}.
+ *
+ * @param values must not be {@literal null}.
+ */
+ Criteria notIn(Object... values);
+
+ /**
+ * Creates a {@link Criteria} using {@code NOT IN}.
+ *
+ * @param values must not be {@literal null}.
+ */
+ Criteria notIn(Collection> values);
+
+ /**
+ * Creates a {@link Criteria} using less-than ({@literal <}).
+ *
+ * @param value must not be {@literal null}.
+ */
+ Criteria lessThan(Object value);
+
+ /**
+ * Creates a {@link Criteria} using less-than or equal to ({@literal <=}).
+ *
+ * @param value must not be {@literal null}.
+ */
+ Criteria lessThanOrEquals(Object value);
+
+ /**
+ * Creates a {@link Criteria} using greater-than({@literal >}).
+ *
+ * @param value must not be {@literal null}.
+ */
+ Criteria greaterThan(Object value);
+
+ /**
+ * Creates a {@link Criteria} using greater-than or equal to ({@literal >=}).
+ *
+ * @param value must not be {@literal null}.
+ */
+ Criteria greaterThanOrEquals(Object value);
+
+ /**
+ * Creates a {@link Criteria} using {@code LIKE}.
+ *
+ * @param value must not be {@literal null}.
+ */
+ Criteria like(Object value);
+
+ /**
+ * Creates a {@link Criteria} using {@code NOT LIKE}.
+ *
+ * @param value must not be {@literal null}
+ * @return a new {@link Criteria} object
+ */
+ Criteria notLike(Object value);
+
+ /**
+ * Creates a {@link Criteria} using {@code IS NULL}.
+ */
+ Criteria isNull();
+
+ /**
+ * Creates a {@link Criteria} using {@code IS NOT NULL}.
+ */
+ Criteria isNotNull();
+
+ /**
+ * Creates a {@link Criteria} using {@code IS TRUE}.
+ *
+ * @return a new {@link Criteria} object
+ */
+ Criteria isTrue();
+
+ /**
+ * Creates a {@link Criteria} using {@code IS FALSE}.
+ *
+ * @return a new {@link Criteria} object
+ */
+ Criteria isFalse();
+ }
+
+ /**
+ * Default {@link CriteriaStep} implementation.
+ */
+ static class DefaultCriteriaStep implements CriteriaStep {
+
+ private final SqlIdentifier property;
+
+ DefaultCriteriaStep(SqlIdentifier property) {
+ this.property = property;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#is(java.lang.Object)
+ */
+ @Override
+ public Criteria is(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+
+ return createCriteria(Comparator.EQ, value);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#not(java.lang.Object)
+ */
+ @Override
+ public Criteria not(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+
+ return createCriteria(Comparator.NEQ, value);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#in(java.lang.Object[])
+ */
+ @Override
+ public Criteria in(Object... values) {
+
+ Assert.notNull(values, "Values must not be null!");
+ Assert.noNullElements(values, "Values must not contain a null value!");
+
+ if (values.length > 1 && values[1] instanceof Collection) {
+ throw new InvalidDataAccessApiUsageException(
+ "You can only pass in one argument of type " + values[1].getClass().getName());
+ }
+
+ return createCriteria(Comparator.IN, Arrays.asList(values));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#in(java.util.Collection)
+ */
+ @Override
+ public Criteria in(Collection> values) {
+
+ Assert.notNull(values, "Values must not be null!");
+ Assert.noNullElements(values.toArray(), "Values must not contain a null value!");
+
+ return createCriteria(Comparator.IN, values);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#notIn(java.lang.Object[])
+ */
+ @Override
+ public Criteria notIn(Object... values) {
+
+ Assert.notNull(values, "Values must not be null!");
+ Assert.noNullElements(values, "Values must not contain a null value!");
+
+ if (values.length > 1 && values[1] instanceof Collection) {
+ throw new InvalidDataAccessApiUsageException(
+ "You can only pass in one argument of type " + values[1].getClass().getName());
+ }
+
+ return createCriteria(Comparator.NOT_IN, Arrays.asList(values));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#notIn(java.util.Collection)
+ */
+ @Override
+ public Criteria notIn(Collection> values) {
+
+ Assert.notNull(values, "Values must not be null!");
+ Assert.noNullElements(values.toArray(), "Values must not contain a null value!");
+
+ return createCriteria(Comparator.NOT_IN, values);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#lessThan(java.lang.Object)
+ */
+ @Override
+ public Criteria lessThan(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+
+ return createCriteria(Comparator.LT, value);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#lessThanOrEquals(java.lang.Object)
+ */
+ @Override
+ public Criteria lessThanOrEquals(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+
+ return createCriteria(Comparator.LTE, value);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#greaterThan(java.lang.Object)
+ */
+ @Override
+ public Criteria greaterThan(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+
+ return createCriteria(Comparator.GT, value);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#greaterThanOrEquals(java.lang.Object)
+ */
+ @Override
+ public Criteria greaterThanOrEquals(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+
+ return createCriteria(Comparator.GTE, value);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#like(java.lang.Object)
+ */
+ @Override
+ public Criteria like(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+
+ return createCriteria(Comparator.LIKE, value);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#notLike(java.lang.Object)
+ */
+ @Override
+ public Criteria notLike(Object value) {
+ Assert.notNull(value, "Value must not be null!");
+ return createCriteria(Comparator.NOT_LIKE, value);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#isNull()
+ */
+ @Override
+ public Criteria isNull() {
+ return createCriteria(Comparator.IS_NULL, null);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#isNotNull()
+ */
+ @Override
+ public Criteria isNotNull() {
+ return createCriteria(Comparator.IS_NOT_NULL, null);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#isTrue()
+ */
+ @Override
+ public Criteria isTrue() {
+ return createCriteria(Comparator.IS_TRUE, null);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#isFalse()
+ */
+ @Override
+ public Criteria isFalse() {
+ return createCriteria(Comparator.IS_FALSE, null);
+ }
+
+ protected Criteria createCriteria(Comparator comparator, Object value) {
+ return new Criteria(this.property, comparator, value);
+ }
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/CriteriaDefinition.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/CriteriaDefinition.java
new file mode 100644
index 00000000..8fbe7ae3
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/CriteriaDefinition.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.relational.core.query;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.springframework.data.relational.core.sql.SqlIdentifier;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Interface defining a criteria definition object. A criteria definition may chain multiple predicates and may also
+ * represent a group of nested criteria objects.
+ *
+ * @author Mark Paluch
+ * @since 2.0
+ */
+public interface CriteriaDefinition {
+
+ /**
+ * Static factory method to create an empty Criteria.
+ *
+ * @return an empty {@link Criteria}.
+ */
+ static CriteriaDefinition empty() {
+ return Criteria.EMPTY;
+ }
+
+ /**
+ * Create a new {@link Criteria} and combine it as group with {@code AND} using the provided {@link List Criterias}.
+ *
+ * @return new {@link Criteria}.
+ */
+ static CriteriaDefinition from(Criteria... criteria) {
+
+ Assert.notNull(criteria, "Criteria must not be null");
+ Assert.noNullElements(criteria, "Criteria must not contain null elements");
+
+ return from(Arrays.asList(criteria));
+ }
+
+ /**
+ * Create a new {@link Criteria} and combine it as group with {@code AND} using the provided {@link List Criterias}.
+ *
+ * @return new {@link Criteria}.
+ * @since 1.1
+ */
+ static CriteriaDefinition from(List criteria) {
+
+ Assert.notNull(criteria, "Criteria must not be null");
+ Assert.noNullElements(criteria, "Criteria must not contain null elements");
+
+ if (criteria.isEmpty()) {
+ return Criteria.EMPTY;
+ }
+
+ if (criteria.size() == 1) {
+ return criteria.get(0);
+ }
+
+ return Criteria.EMPTY.and(criteria);
+ }
+
+ /**
+ * @return {@literal true} if this {@link Criteria} is empty.
+ */
+ boolean isGroup();
+
+ List extends CriteriaDefinition> getGroup();
+
+ /**
+ * @return the column/property name.
+ */
+ @Nullable
+ SqlIdentifier getColumn();
+
+ /**
+ * @return {@link Criteria.Comparator}.
+ */
+ @Nullable
+ Comparator getComparator();
+
+ /**
+ * @return the comparison value. Can be {@literal null}.
+ */
+ @Nullable
+ Object getValue();
+
+ /**
+ * Checks whether comparison should be done in case-insensitive way.
+ *
+ * @return {@literal true} if comparison should be done in case-insensitive way
+ */
+ boolean isIgnoreCase();
+
+ /**
+ * @return the previous {@link CriteriaDefinition} object. Can be {@literal null} if there is no previous
+ * {@link CriteriaDefinition}.
+ * @see #hasPrevious()
+ */
+ @Nullable
+ CriteriaDefinition getPrevious();
+
+ /**
+ * @return {@literal true} if this {@link Criteria} has a previous one.
+ */
+ boolean hasPrevious();
+
+ /**
+ * @return {@literal true} if this {@link Criteria} is empty.
+ */
+ boolean isEmpty();
+
+ /**
+ * @return {@link Combinator} to combine this criteria with a previous one.
+ */
+ Combinator getCombinator();
+
+ enum Combinator {
+ INITIAL, AND, OR;
+ }
+
+ enum Comparator {
+ INITIAL, EQ, NEQ, LT, LTE, GT, GTE, IS_NULL, IS_NOT_NULL, LIKE, NOT_LIKE, NOT_IN, IN, IS_TRUE, IS_FALSE
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Query.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Query.java
new file mode 100644
index 00000000..313dece0
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Query.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.relational.core.query;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.relational.core.sql.SqlIdentifier;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Query object representing {@link Criteria}, columns, {@link Sort}, and limit/offset for a SQL query. {@link Query} is
+ * created with a fluent API creating immutable objects.
+ *
+ * @author Mark Paluch
+ * @since 2.0
+ * @see Criteria
+ * @see Sort
+ * @see Pageable
+ */
+public class Query {
+
+ private final @Nullable CriteriaDefinition criteria;
+
+ private final List columns;
+ private final Sort sort;
+ private final int limit;
+ private final long offset;
+
+ /**
+ * Static factory method to create a {@link Query} using the provided {@link CriteriaDefinition}.
+ *
+ * @param criteria must not be {@literal null}.
+ * @return a new {@link Query} for the given {@link Criteria}.
+ */
+ public static Query query(CriteriaDefinition criteria) {
+ return new Query(criteria);
+ }
+
+ /**
+ * Creates a new {@link Query} using the given {@link Criteria}.
+ *
+ * @param criteria must not be {@literal null}.
+ */
+ private Query(@Nullable CriteriaDefinition criteria) {
+ this(criteria, Collections.emptyList(), Sort.unsorted(), -1, -1);
+ }
+
+ private Query(@Nullable CriteriaDefinition criteria, List columns, Sort sort, int limit, long offset) {
+
+ this.criteria = criteria;
+ this.columns = columns;
+ this.sort = sort;
+ this.limit = limit;
+ this.offset = offset;
+ }
+
+ /**
+ * Create a new empty {@link Query}.
+ *
+ * @return
+ */
+ public static Query empty() {
+ return new Query(null);
+ }
+
+ /**
+ * Add columns to the query.
+ *
+ * @param columns
+ * @return a new {@link Query} object containing the former settings with {@code columns} applied.
+ */
+ public Query columns(String... columns) {
+
+ Assert.notNull(columns, "Columns must not be null");
+
+ return withColumns(Arrays.stream(columns).map(SqlIdentifier::unquoted).collect(Collectors.toList()));
+ }
+
+ /**
+ * Add columns to the query.
+ *
+ * @param columns
+ * @return a new {@link Query} object containing the former settings with {@code columns} applied.
+ */
+ public Query columns(Collection columns) {
+
+ Assert.notNull(columns, "Columns must not be null");
+
+ return withColumns(columns.stream().map(SqlIdentifier::unquoted).collect(Collectors.toList()));
+ }
+
+ /**
+ * Add columns to the query.
+ *
+ * @param columns
+ * @return a new {@link Query} object containing the former settings with {@code columns} applied.
+ * @since 1.1
+ */
+ public Query columns(SqlIdentifier... columns) {
+
+ Assert.notNull(columns, "Columns must not be null");
+
+ return withColumns(Arrays.asList(columns));
+ }
+
+ /**
+ * Add columns to the query.
+ *
+ * @param columns
+ * @return a new {@link Query} object containing the former settings with {@code columns} applied.
+ */
+ private Query withColumns(Collection columns) {
+
+ Assert.notNull(columns, "Columns must not be null");
+
+ List newColumns = new ArrayList<>(this.columns);
+ newColumns.addAll(columns);
+ return new Query(this.criteria, newColumns, this.sort, this.limit, offset);
+ }
+
+ /**
+ * Set number of rows to skip before returning results.
+ *
+ * @param offset
+ * @return a new {@link Query} object containing the former settings with {@code offset} applied.
+ */
+ public Query offset(long offset) {
+ return new Query(this.criteria, this.columns, this.sort, this.limit, offset);
+ }
+
+ /**
+ * Limit the number of returned documents to {@code limit}.
+ *
+ * @param limit
+ * @return a new {@link Query} object containing the former settings with {@code limit} applied.
+ */
+ public Query limit(int limit) {
+ return new Query(this.criteria, this.columns, this.sort, limit, this.offset);
+ }
+
+ /**
+ * Set the given pagination information on the {@link Query} instance. Will transparently set {@code offset} and
+ * {@code limit} as well as applying the {@link Sort} instance defined with the {@link Pageable}.
+ *
+ * @param pageable
+ * @return a new {@link Query} object containing the former settings with {@link Pageable} applied.
+ */
+ public Query with(Pageable pageable) {
+
+ if (pageable.isUnpaged()) {
+ return this;
+ }
+
+ assertNoCaseSort(pageable.getSort());
+
+ return new Query(this.criteria, this.columns, this.sort.and(sort), pageable.getPageSize(), pageable.getOffset());
+ }
+
+ /**
+ * Add a {@link Sort} to the {@link Query} instance.
+ *
+ * @param sort
+ * @return a new {@link Query} object containing the former settings with {@link Sort} applied.
+ */
+ public Query sort(Sort sort) {
+
+ Assert.notNull(sort, "Sort must not be null!");
+
+ if (sort.isUnsorted()) {
+ return this;
+ }
+
+ assertNoCaseSort(sort);
+
+ return new Query(this.criteria, this.columns, this.sort.and(sort), this.limit, this.offset);
+ }
+
+ /**
+ * Return the {@link Criteria} to be applied.
+ *
+ * @return
+ */
+ public Optional getCriteria() {
+ return Optional.ofNullable(this.criteria);
+ }
+
+ /**
+ * Return the columns that this query should project.
+ *
+ * @return
+ */
+ public List getColumns() {
+ return columns;
+ }
+
+ /**
+ * Return {@literal true} if the {@link Query} has a sort parameter.
+ *
+ * @return {@literal true} if sorted.
+ * @see Sort#isSorted()
+ */
+ public boolean isSorted() {
+ return sort.isSorted();
+ }
+
+ public Sort getSort() {
+ return sort;
+ }
+
+ /**
+ * Return the number of rows to skip.
+ *
+ * @return
+ */
+ public long getOffset() {
+ return this.offset;
+ }
+
+ /**
+ * Return the maximum number of rows to be return.
+ *
+ * @return
+ */
+ public int getLimit() {
+ return this.limit;
+ }
+
+ private static void assertNoCaseSort(Sort sort) {
+
+ for (Sort.Order order : sort) {
+ if (order.isIgnoreCase()) {
+ throw new IllegalArgumentException(String.format(
+ "Given sort contained an Order for %s with ignore case;" + "Ignore case sorting is not supported",
+ order.getProperty()));
+ }
+ }
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Update.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Update.java
new file mode 100644
index 00000000..aac9cb2a
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Update.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2019-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.relational.core.query;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.springframework.data.relational.core.sql.SqlIdentifier;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Class to easily construct SQL update assignments.
+ *
+ * @author Mark Paluch
+ * @author Oliver Drotbohm
+ * @since 2.0
+ */
+public class Update {
+
+ private static final Update EMPTY = new Update(Collections.emptyMap());
+
+ private final Map columnsToUpdate;
+
+ private Update(Map columnsToUpdate) {
+ this.columnsToUpdate = columnsToUpdate;
+ }
+
+ /**
+ * Static factory method to create an {@link Update} from {@code assignments}.
+ *
+ * @param assignments must not be {@literal null}.
+ * @return
+ */
+ public static Update from(Map assignments) {
+ return new Update(new LinkedHashMap<>(assignments));
+ }
+
+ /**
+ * Static factory method to create an {@link Update} using the provided column.
+ *
+ * @param column must not be {@literal null}.
+ * @param value can be {@literal null}.
+ * @return
+ */
+ public static Update update(String column, @Nullable Object value) {
+ return EMPTY.set(column, value);
+ }
+
+ /**
+ * Update a column by assigning a value.
+ *
+ * @param column must not be {@literal null}.
+ * @param value can be {@literal null}.
+ * @return
+ */
+ public Update set(String column, @Nullable Object value) {
+
+ Assert.hasText(column, "Column for update must not be null or blank");
+
+ return addMultiFieldOperation(SqlIdentifier.unquoted(column), value);
+ }
+
+ /**
+ * Update a column by assigning a value.
+ *
+ * @param column must not be {@literal null}.
+ * @param value can be {@literal null}.
+ * @return
+ * @since 1.1
+ */
+ public Update set(SqlIdentifier column, @Nullable Object value) {
+ return addMultiFieldOperation(column, value);
+ }
+
+ /**
+ * Returns all assignments.
+ *
+ * @return
+ */
+ public Map getAssignments() {
+ return Collections.unmodifiableMap(this.columnsToUpdate);
+ }
+
+ private Update addMultiFieldOperation(SqlIdentifier key, @Nullable Object value) {
+
+ Assert.notNull(key, "Column for update must not be null");
+
+ Map updates = new LinkedHashMap<>(this.columnsToUpdate);
+ updates.put(key, value);
+
+ return new Update(updates);
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/package-info.java
new file mode 100644
index 00000000..34fedea5
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Query and update support.
+ */
+@org.springframework.lang.NonNullApi
+@org.springframework.lang.NonNullFields
+package org.springframework.data.relational.core.query;
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/query/CriteriaTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/query/CriteriaTests.java
new file mode 100644
index 00000000..5a2de5cb
--- /dev/null
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/query/CriteriaTests.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.relational.core.query;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Mark Paluch
+ */
+public class CriteriaTests {
+
+}
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/query/CriteriaUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/query/CriteriaUnitTests.java
new file mode 100644
index 00000000..3fd4fe05
--- /dev/null
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/query/CriteriaUnitTests.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2019-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.relational.core.query;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.assertj.core.api.SoftAssertions.*;
+import static org.springframework.data.relational.core.query.Criteria.*;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import org.springframework.data.relational.core.sql.SqlIdentifier;
+
+/**
+ * Unit tests for {@link Criteria}.
+ *
+ * @author Mark Paluch
+ * @author Jens Schauder
+ * @author Roman Chigvintsev
+ */
+public class CriteriaUnitTests {
+
+ @Test // DATAJDBC-513
+ public void fromCriteria() {
+
+ Criteria nested1 = where("foo").isNotNull();
+ Criteria nested2 = where("foo").isNull();
+ Criteria criteria = Criteria.from(nested1, nested2);
+
+ assertThat(criteria.isGroup()).isTrue();
+ assertThat(criteria.getGroup()).containsExactly(nested1, nested2);
+ assertThat(criteria.getPrevious()).isEqualTo(Criteria.empty());
+ }
+
+ @Test // DATAJDBC-513
+ public void fromCriteriaOptimized() {
+
+ Criteria nested = where("foo").is("bar").and("baz").isNotNull();
+ Criteria criteria = Criteria.from(nested);
+
+ assertThat(criteria).isSameAs(nested);
+ }
+
+ @Test // DATAJDBC-513
+ public void isEmpty() {
+
+ assertSoftly(softly -> {
+
+ Criteria empty = empty();
+ Criteria notEmpty = where("foo").is("bar");
+
+ assertThat(empty.isEmpty()).isTrue();
+ assertThat(notEmpty.isEmpty()).isFalse();
+
+ assertThat(Criteria.from(notEmpty).isEmpty()).isFalse();
+ assertThat(Criteria.from(notEmpty, notEmpty).isEmpty()).isFalse();
+
+ assertThat(Criteria.from(empty).isEmpty()).isTrue();
+ assertThat(Criteria.from(empty, empty).isEmpty()).isTrue();
+
+ assertThat(Criteria.from(empty, notEmpty).isEmpty()).isFalse();
+ assertThat(Criteria.from(notEmpty, empty).isEmpty()).isFalse();
+ });
+ }
+
+ @Test // DATAJDBC-513
+ public void andChainedCriteria() {
+
+ Criteria criteria = where("foo").is("bar").and("baz").isNotNull();
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("baz"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.IS_NOT_NULL);
+ assertThat(criteria.getValue()).isNull();
+ assertThat(criteria.getPrevious()).isNotNull();
+ assertThat(criteria.getCombinator()).isEqualTo(Criteria.Combinator.AND);
+
+ criteria = criteria.getPrevious();
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.EQ);
+ assertThat(criteria.getValue()).isEqualTo("bar");
+ }
+
+ @Test // DATAJDBC-513
+ public void andGroupedCriteria() {
+
+ Criteria criteria = where("foo").is("bar").and(where("foo").is("baz"));
+
+ assertThat(criteria.isGroup()).isTrue();
+ assertThat(criteria.getGroup()).hasSize(1);
+ assertThat(criteria.getGroup().get(0).getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getCombinator()).isEqualTo(Criteria.Combinator.AND);
+
+ criteria = criteria.getPrevious();
+
+ assertThat(criteria).isNotNull();
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.EQ);
+ assertThat(criteria.getValue()).isEqualTo("bar");
+ }
+
+ @Test // DATAJDBC-513
+ public void orChainedCriteria() {
+
+ Criteria criteria = where("foo").is("bar").or("baz").isNotNull();
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("baz"));
+ assertThat(criteria.getCombinator()).isEqualTo(Criteria.Combinator.OR);
+
+ criteria = criteria.getPrevious();
+
+ assertThat(criteria).isNotNull();
+ assertThat(criteria.getPrevious()).isNull();
+ assertThat(criteria.getValue()).isEqualTo("bar");
+ }
+
+ @Test // DATAJDBC-513
+ public void orGroupedCriteria() {
+
+ Criteria criteria = where("foo").is("bar").or(where("foo").is("baz"));
+
+ assertThat(criteria.isGroup()).isTrue();
+ assertThat(criteria.getGroup()).hasSize(1);
+ assertThat(criteria.getGroup().get(0).getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getCombinator()).isEqualTo(Criteria.Combinator.OR);
+
+ criteria = criteria.getPrevious();
+
+ assertThat(criteria).isNotNull();
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.EQ);
+ assertThat(criteria.getValue()).isEqualTo("bar");
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildEqualsCriteria() {
+
+ Criteria criteria = where("foo").is("bar");
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.EQ);
+ assertThat(criteria.getValue()).isEqualTo("bar");
+ }
+
+ @Test
+ public void shouldBuildEqualsIgnoreCaseCriteria() {
+ Criteria criteria = where("foo").is("bar").ignoreCase(true);
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.EQ);
+ assertThat(criteria.getValue()).isEqualTo("bar");
+ assertThat(criteria.isIgnoreCase()).isTrue();
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildNotEqualsCriteria() {
+
+ Criteria criteria = where("foo").not("bar");
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.NEQ);
+ assertThat(criteria.getValue()).isEqualTo("bar");
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildInCriteria() {
+
+ Criteria criteria = where("foo").in("bar", "baz");
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.IN);
+ assertThat(criteria.getValue()).isEqualTo(Arrays.asList("bar", "baz"));
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildNotInCriteria() {
+
+ Criteria criteria = where("foo").notIn("bar", "baz");
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.NOT_IN);
+ assertThat(criteria.getValue()).isEqualTo(Arrays.asList("bar", "baz"));
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildGtCriteria() {
+
+ Criteria criteria = where("foo").greaterThan(1);
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.GT);
+ assertThat(criteria.getValue()).isEqualTo(1);
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildGteCriteria() {
+
+ Criteria criteria = where("foo").greaterThanOrEquals(1);
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.GTE);
+ assertThat(criteria.getValue()).isEqualTo(1);
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildLtCriteria() {
+
+ Criteria criteria = where("foo").lessThan(1);
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.LT);
+ assertThat(criteria.getValue()).isEqualTo(1);
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildLteCriteria() {
+
+ Criteria criteria = where("foo").lessThanOrEquals(1);
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.LTE);
+ assertThat(criteria.getValue()).isEqualTo(1);
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildLikeCriteria() {
+
+ Criteria criteria = where("foo").like("hello%");
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.LIKE);
+ assertThat(criteria.getValue()).isEqualTo("hello%");
+ }
+
+ @Test
+ public void shouldBuildNotLikeCriteria() {
+ Criteria criteria = where("foo").notLike("hello%");
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.NOT_LIKE);
+ assertThat(criteria.getValue()).isEqualTo("hello%");
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildIsNullCriteria() {
+
+ Criteria criteria = where("foo").isNull();
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.IS_NULL);
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildIsNotNullCriteria() {
+
+ Criteria criteria = where("foo").isNotNull();
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.IS_NOT_NULL);
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildIsTrueCriteria() {
+
+ Criteria criteria = where("foo").isTrue();
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.IS_TRUE);
+ }
+
+ @Test // DATAJDBC-513
+ public void shouldBuildIsFalseCriteria() {
+
+ Criteria criteria = where("foo").isFalse();
+
+ assertThat(criteria.getColumn()).isEqualTo(SqlIdentifier.unquoted("foo"));
+ assertThat(criteria.getComparator()).isEqualTo(CriteriaDefinition.Comparator.IS_FALSE);
+ }
+}