Skip to content

1192 Query by Example (6/7) #1195

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
c5df8f3
Prepare 2.4 M1 (2021.2.0).
christophstrobl Jan 14, 2022
38a8089
Release version 2.4 M1 (2021.2.0).
christophstrobl Jan 14, 2022
51ffa1c
Prepare next development iteration.
christophstrobl Jan 14, 2022
cb5e327
After release cleanups.
christophstrobl Jan 14, 2022
d879f21
Prepare 2.4 M2 (2021.2.0).
christophstrobl Jan 18, 2022
76ece50
Release version 2.4 M2 (2021.2.0).
christophstrobl Jan 18, 2022
dce0e36
Prepare next development iteration.
christophstrobl Jan 18, 2022
eee773e
After release cleanups.
christophstrobl Jan 18, 2022
c4933c0
Support element conversion of array results.
ctailor2 Jan 25, 2022
45e15b1
Replaces broken test with a working one.
schauder Jan 31, 2022
cef041f
Polishing.
schauder Jan 31, 2022
0dcf12c
Externalize build properties.
gregturn Jan 31, 2022
a132d43
Render windowing functions without arguments correctly.
schauder Feb 2, 2022
97331f7
Replaces java.sql.Types constants with java.sql.SQLType values.
schauder Nov 24, 2021
44b7b8f
Polishing.
mp911de Feb 4, 2022
f48bbab
Null precedence is now supported if the underlying database supports it.
ctailor2 Feb 3, 2022
4989872
Polishing.
schauder Feb 8, 2022
dbf5dd5
Fix a typo in the docs for query methods.
janmax Feb 8, 2022
e68c355
Introduced pessimistic locks for derived queries.
DiegoKrupitza Feb 4, 2022
cd3d0b1
Polishing.
schauder Feb 9, 2022
e8c933d
Polishing.
schauder Feb 11, 2022
e84a34a
Fixes NPE in PersistentPropertyPathExtension.equals.
schauder Feb 11, 2022
f7887f8
Added reference documentation for Lock on derived queries.
DiegoKrupitza Feb 12, 2022
1456788
Polishing.
schauder Feb 15, 2022
d4c223d
Adapt to changes in entity creation metadata APIs in Spring Data Comm…
odrotbohm Feb 15, 2022
4d21e56
Update CI properties.
gregturn Feb 15, 2022
4a8b185
Update copyright year to 2022.
christophstrobl Feb 17, 2022
2937cd0
Prepare 2.4 M3 (2021.2.0).
mp911de Feb 18, 2022
cd84c8d
Release version 2.4 M3 (2021.2.0).
mp911de Feb 18, 2022
b36e809
Prepare next development iteration.
mp911de Feb 18, 2022
0240b64
After release cleanups.
mp911de Feb 18, 2022
b7afc38
`StringBuilder` can be replaced with String concatenation.
DiegoKrupitza Feb 17, 2022
9554fdf
Remove unnecessary `toString()` call.
DiegoKrupitza Feb 17, 2022
63f2331
Avoid conversion when return value is null.
schauder Feb 21, 2022
aa8c8a1
Use Java 17 to build snapshots for Artifactory.
mp911de Feb 22, 2022
23983bb
Upgrade to Maven Wrapper 3.8.4.
mp911de Feb 22, 2022
98d2b22
Update CI properties.
mp911de Feb 22, 2022
c8eafe7
Move Lock to relational module.
schauder Feb 22, 2022
5352fe3
Associate value with isTrue/isFalse criteria operators.
mp911de Feb 28, 2022
bd0be77
Polishing.
mp911de Feb 28, 2022
3682711
Introduced `queryLookupStrategy` to `EnableJdbcRepositories`.
DiegoKrupitza Feb 24, 2022
b4d3838
Polishing.
schauder Mar 7, 2022
bfba422
Fix toString for negated conditions.
Mengzuozhu Mar 8, 2022
676cae2
Support of `QueryByExampleExecutor#findOne(Example example)`.
DiegoKrupitza Mar 11, 2022
1156e70
Support of `QueryByExampleExecutor#findAll(...)`.
DiegoKrupitza Mar 11, 2022
03088ef
Support of `QueryByExampleExecutor#exists(...)`.
DiegoKrupitza Mar 11, 2022
a9f653b
Support of `QueryByExampleExecutor#count(...)`.
DiegoKrupitza Mar 12, 2022
239a720
Support of `QueryByExampleExecutor#findAll(Example<S> example, Pageab…
DiegoKrupitza Mar 12, 2022
b878bba
Marked missing implementations with `UnsupportedOperationException`.
DiegoKrupitza Mar 12, 2022
edca55e
Merge https://github.com/spring-projects/spring-data-relational into …
DiegoKrupitza Apr 9, 2022
62559f6
Removed `Example` abstraction from `JdbcAggregateTemplate`.
DiegoKrupitza Apr 9, 2022
ef49e54
Support of QueryByExample `FetchableFluentQuery`.
DiegoKrupitza Apr 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#Mon Oct 11 14:30:22 CEST 2021
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip
#Tue Feb 22 13:59:08 CET 2022
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@
*/
package org.springframework.data.jdbc.core;

import java.util.Optional;

import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.relational.core.query.Query;
import org.springframework.lang.Nullable;

/**
Expand All @@ -26,6 +30,7 @@
* @author Jens Schauder
* @author Thomas Lang
* @author Milan Milanov
* @author Diego Krupitza
*/
public interface JdbcAggregateOperations {

Expand Down Expand Up @@ -154,4 +159,54 @@ public interface JdbcAggregateOperations {
* @since 2.0
*/
<T> Page<T> findAll(Class<T> domainType, Pageable pageable);

/**
* Execute a {@code SELECT} query and convert the resulting item to an entity ensuring exactly one result.
*
* @param query must not be {@literal null}.
* @param entityClass the entity type must not be {@literal null}.
* @return exactly one result or {@link Optional#empty()} if no match found.
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
*/
<T> Optional<T> selectOne(Query query, Class<T> entityClass);

/**
* Execute a {@code SELECT} query and convert the resulting items to a {@link Iterable} that is sorted.
*
* @param query must not be {@literal null}.
* @param entityClass the entity type must not be {@literal null}.
* @param sort the sorting that should be used on the result.
* @return a non-null sorted list with all the matching results.
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
*/
<T> Iterable<T> select(Query query, Class<T> entityClass, Sort sort);

/**
* Determine whether there are aggregates that match the {@link Query}
*
* @param query must not be {@literal null}.
* @param entityClass the entity type must not be {@literal null}.
* @return {@literal true} if the object exists.
*/
<T> boolean exists(Query query, Class<T> entityClass);

/**
* Counts the number of aggregates of a given type that match the given <code>query</code>.
*
* @param query must not be {@literal null}.
* @param entityClass the entity type must not be {@literal null}.
* @return the number of instances stored in the database. Guaranteed to be not {@code null}.
*/
<T> long count(Query query, Class<T> entityClass);

/**
* Returns a {@link Page} of entities matching the given {@link Query}. In case no match could be found, an empty
* {@link Page} is returned.
*
* @param query must not be {@literal null}.
* @param entityClass the entity type must not be {@literal null}.
* @param pageable can be null.
* @return a {@link Page} of entities matching the given {@link Example}.
*/
<T> Page<T> select(Query query, Class<T> entityClass, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
Expand All @@ -41,6 +42,7 @@
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.mapping.event.*;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
Expand All @@ -54,6 +56,7 @@
* @author Christoph Strobl
* @author Milan Milanov
* @author Myeonghyeon Lee
* @author Diego Krupitza
* @author Chirag Tailor
*/
public class JdbcAggregateTemplate implements JdbcAggregateOperations {
Expand All @@ -65,6 +68,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {

private final DataAccessStrategy accessStrategy;
private final AggregateChangeExecutor executor;

private final JdbcConverter converter;

private EntityCallbacks entityCallbacks = EntityCallbacks.create();
Expand Down Expand Up @@ -232,6 +236,38 @@ public <T> Page<T> findAll(Class<T> domainType, Pageable pageable) {
return PageableExecutionUtils.getPage(content, pageable, () -> accessStrategy.count(domainType));
}

@Override
public <T> Optional<T> selectOne(Query query, Class<T> entityClass) {
return accessStrategy.selectOne(query, entityClass);
}

@Override
public <T> Iterable<T> select(Query query, Class<T> entityClass, Sort sort) {
return accessStrategy.select(query, entityClass);
}

@Override
public <T> boolean exists(Query query, Class<T> entityClass) {
return accessStrategy.exists(query, entityClass);
}

@Override
public <T> long count(Query query, Class<T> entityClass) {
return accessStrategy.count(query, entityClass);
}

@Override
public <T> Page<T> select(Query query, Class<T> entityClass, Pageable pageable) {
Iterable<T> items = triggerAfterConvert(accessStrategy.select(query, entityClass, pageable));
List<T> content = StreamSupport.stream(items.spliterator(), false).collect(Collectors.toList());

return PageableExecutionUtils.getPage(content, pageable, () -> accessStrategy.count(query, entityClass));
}

/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.JdbcAggregateOperations#findAll(java.lang.Class)
*/
@Override
public <T> Iterable<T> findAll(Class<T> domainType) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;

Expand All @@ -25,6 +26,7 @@
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.conversion.IdValueSource;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.sql.LockMode;

/**
Expand All @@ -36,6 +38,7 @@
* @author Tyler Van Gorder
* @author Milan Milanov
* @author Myeonghyeon Lee
* @author Diego Krupitza
* @author Chirag Tailor
* @since 1.1
*/
Expand Down Expand Up @@ -148,6 +151,31 @@ public <T> Iterable<T> findAll(Class<T> domainType, Pageable pageable) {
return collect(das -> das.findAll(domainType, pageable));
}

@Override
public <T> Optional<T> selectOne(Query query, Class<T> probeType) {
return collect(das -> das.selectOne(query, probeType));
}

@Override
public <T> Iterable<T> select(Query query, Class<T> probeType) {
return collect(das -> das.select(query, probeType));
}

@Override
public <T> Iterable<T> select(Query query, Class<T> probeType, Pageable pageable) {
return collect(das -> das.select(query, probeType, pageable));
}

@Override
public <T> boolean exists(Query query, Class<T> probeType) {
return collect(das -> das.exists(query, probeType));
}

@Override
public <T> long count(Query query, Class<T> probeType) {
return collect(das -> das.count(query, probeType));
}

private <T> T collect(Function<DataAccessStrategy, T> function) {

// Keep <T> as Eclipse fails to compile if <> is used.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.domain.Pageable;
Expand All @@ -25,6 +26,7 @@
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.conversion.IdValueSource;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.sql.LockMode;
import org.springframework.lang.Nullable;

Expand All @@ -37,6 +39,7 @@
* @author Tyler Van Gorder
* @author Milan Milanov
* @author Myeonghyeon Lee
* @author Diego Krupitza
* @author Chirag Tailor
*/
public interface DataAccessStrategy extends RelationResolver {
Expand Down Expand Up @@ -258,4 +261,54 @@ Iterable<Object> findAllByPath(Identifier identifier,
* @since 2.0
*/
<T> Iterable<T> findAll(Class<T> domainType, Pageable pageable);

/**
* Execute a {@code SELECT} query and convert the resulting item to an entity ensuring exactly one result.
*
* @param query must not be {@literal null}.
* @param probeType the type of entities. Must not be {@code null}.
* @return exactly one result or {@link Optional#empty()} if no match found.
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
*/
<T> Optional<T> selectOne(Query query, Class<T> probeType);

/**
* Execute a {@code SELECT} query and convert the resulting items to a {@link Iterable}.
*
* @param query must not be {@literal null}.
* @param probeType the type of entities. Must not be {@code null}.
* @return a non-null list with all the matching results.
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
*/
<T> Iterable<T> select(Query query, Class<T> probeType);

/**
* Execute a {@code SELECT} query and convert the resulting items to a {@link Iterable}. Applies the {@link Pageable}
* to the result.
*
* @param query must not be {@literal null}.
* @param probeType the type of entities. Must not be {@literal null}.
* @param pageable the pagination that should be applied. Must not be {@literal null}.
* @return a non-null list with all the matching results.
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
*/
<T> Iterable<T> select(Query query, Class<T> probeType, Pageable pageable);

/**
* Determine whether there is an aggregate of type <code>probeType</code> that matches the provided {@link Query}.
*
* @param query must not be {@literal null}.
* @param probeType the type of entities. Must not be {@code null}.
* @return {@literal true} if the object exists.
*/
<T> boolean exists(Query query, Class<T> probeType);

/**
* Counts the rows in the table representing the given probe type, that match the given <code>query</code>.
*
* @param probeType the probe type for which to count the elements. Must not be {@code null}.
* @param query the query which elements have to match.
* @return the count. Guaranteed to be not {@code null}.
*/
<T> long count(Query query, Class<T> probeType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
import org.springframework.data.relational.core.sql.LockMode;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.lang.Nullable;
Expand All @@ -55,6 +57,7 @@
* @author Myeonghyeon Lee
* @author Yunyoung LEE
* @author Radim Tlusty
* @author Diego Krupitza
* @author Chirag Tailor
* @since 1.1
*/
Expand Down Expand Up @@ -314,6 +317,61 @@ public <T> Iterable<T> findAll(Class<T> domainType, Pageable pageable) {
return operations.query(sql(domainType).getFindAll(pageable), (RowMapper<T>) getEntityRowMapper(domainType));
}

@Override
public <T> Optional<T> selectOne(Query query, Class<T> probeType) {
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
String sqlQuery = sql(probeType).selectByQuery(query, parameterSource);

T foundObject;
try {
foundObject = operations.queryForObject(sqlQuery, parameterSource, (RowMapper<T>) getEntityRowMapper(probeType));
} catch (EmptyResultDataAccessException e) {
foundObject = null;
}

return Optional.ofNullable(foundObject);
}

@Override
public <T> Iterable<T> select(Query query, Class<T> probeType) {
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
String sqlQuery = sql(probeType).selectByQuery(query, parameterSource);

return operations.query(sqlQuery, parameterSource, (RowMapper<T>) getEntityRowMapper(probeType));
}

@Override
public <T> Iterable<T> select(Query query, Class<T> probeType, Pageable pageable) {
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
String sqlQuery = sql(probeType).selectByQuery(query, parameterSource, pageable);

return operations.query(sqlQuery, parameterSource, (RowMapper<T>) getEntityRowMapper(probeType));
}

@Override
public <T> boolean exists(Query query, Class<T> probeType) {
MapSqlParameterSource parameterSource = new MapSqlParameterSource();

String sqlQuery = sql(probeType).existsByQuery(query, parameterSource);

Boolean result = operations.queryForObject(sqlQuery, parameterSource, Boolean.class);
Assert.notNull(result, "The result of an exists query must not be null");

return result;
}

@Override
public <T> long count(Query query, Class<T> probeType) {
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
String sqlQuery = sql(probeType).countByQuery(query, parameterSource);

Long result = operations.queryForObject(sqlQuery, parameterSource, Long.class);

Assert.notNull(result, "The result of a count query must not be null.");

return result;
}

private EntityRowMapper<?> getEntityRowMapper(Class<?> domainType) {
return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), converter);
}
Expand Down Expand Up @@ -349,4 +407,4 @@ private <T> SqlIdentifier getIdColumn(Class<T> domainType) {
return Optional.ofNullable(context.getRequiredPersistentEntity(domainType).getIdProperty())
.map(RelationalPersistentProperty::getColumnName).orElse(null);
}
}
}
Loading