Support for Query By Example.

Original pull request #1195
Closes #1192
This commit is contained in:
Diego Krupitza
2022-03-11 16:16:56 +01:00
committed by Jens Schauder
parent ee6c2c89b5
commit a6fb4df590
17 changed files with 1447 additions and 12 deletions

View File

@@ -46,10 +46,13 @@ import org.springframework.data.relational.core.mapping.PersistentPropertyPathEx
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.Criteria;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.sql.Aliased;
import org.springframework.data.relational.core.sql.LockMode;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.data.relational.core.sql.Table;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.lang.Nullable;
/**
@@ -65,6 +68,7 @@ import org.springframework.lang.Nullable;
* @author Myeonghyeon Lee
* @author Mikhail Polivakha
* @author Chirag Tailor
* @author Diego Krupitza
*/
@SuppressWarnings("Convert2MethodRef")
class SqlGeneratorUnitTests {
@@ -739,6 +743,92 @@ class SqlGeneratorUnitTests {
}
@Nullable
@Test
void selectByQueryValidTest() {
final SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class);
DummyEntity probe = new DummyEntity();
probe.name = "Diego";
Criteria criteria = Criteria.where("name").is(probe.name);
Query query = Query.query(criteria);
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
String generatedSQL = sqlGenerator.selectByQuery(query, parameterSource);
assertThat(generatedSQL).isNotNull().contains(":x_name");
assertThat(parameterSource.getValues()) //
.containsOnly(entry("x_name", probe.name));
}
@Test
void existsByQuerySimpleValidTest() {
final SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class);
DummyEntity probe = new DummyEntity();
probe.name = "Diego";
Criteria criteria = Criteria.where("name").is(probe.name);
Query query = Query.query(criteria);
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
String generatedSQL = sqlGenerator.existsByQuery(query, parameterSource);
assertThat(generatedSQL).isNotNull().contains(":x_name");
assertThat(parameterSource.getValues()) //
.containsOnly(entry("x_name", probe.name));
}
@Test
void countByQuerySimpleValidTest() {
final SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class);
DummyEntity probe = new DummyEntity();
probe.name = "Diego";
Criteria criteria = Criteria.where("name").is(probe.name);
Query query = Query.query(criteria);
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
String generatedSQL = sqlGenerator.countByQuery(query, parameterSource);
assertThat(generatedSQL) //
.isNotNull() //
.containsIgnoringCase("COUNT(1)") //
.contains(":x_name");
assertThat(parameterSource.getValues()) //
.containsOnly(entry("x_name", probe.name));
}
@Test
void selectByQueryPaginationValidTest() {
final SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class);
DummyEntity probe = new DummyEntity();
probe.name = "Diego";
Criteria criteria = Criteria.where("name").is(probe.name);
Query query = Query.query(criteria);
PageRequest pageRequest = PageRequest.of(2, 1, Sort.by(Sort.Order.asc("name")));
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
String generatedSQL = sqlGenerator.selectByQuery(query, parameterSource, pageRequest);
assertThat(generatedSQL) //
.isNotNull() //
.contains(":x_name") //
.containsIgnoringCase("ORDER BY dummy_entity.x_name ASC") //
.containsIgnoringCase("LIMIT 1") //
.containsIgnoringCase("OFFSET 2 LIMIT 1");
assertThat(parameterSource.getValues()) //
.containsOnly(entry("x_name", probe.name));
}
private SqlIdentifier getAlias(Object maybeAliased) {
if (maybeAliased instanceof Aliased) {
@@ -761,7 +851,8 @@ class SqlGeneratorUnitTests {
@SuppressWarnings("unused")
static class DummyEntity {
@Column("id1") @Id Long id;
@Column("id1")
@Id Long id;
String name;
ReferencedEntity ref;
Set<Element> elements;
@@ -838,7 +929,8 @@ class SqlGeneratorUnitTests {
// these column names behave like single double quote in the name since the get quoted and then doubling the double
// quote escapes it.
@Id @Column("test\"\"_@id") Long id;
@Id
@Column("test\"\"_@id") Long id;
@Column("test\"\"_@123") String name;
}

View File

@@ -33,12 +33,18 @@ import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationListener;
@@ -46,11 +52,14 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.jdbc.repository.query.Modifying;
import org.springframework.data.jdbc.repository.query.Query;
@@ -68,7 +77,9 @@ import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.ListCrudRepository;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.repository.query.Param;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
@@ -84,6 +95,7 @@ import org.springframework.transaction.annotation.Transactional;
* @author Jens Schauder
* @author Mark Paluch
* @author Chirag Tailor
* @author Diego Krupitza
*/
@Transactional
@TestExecutionListeners(value = AssumeFeatureTestExecutionListener.class, mergeMode = MERGE_WITH_DEFAULTS)
@@ -707,6 +719,557 @@ public class JdbcRepositoryIntegrationTests {
.isEqualTo(root1.intermediates.get(0).leaves.get(0).name);
}
@Test
void findOneByExampleShouldGetOne() {
DummyEntity dummyEntity1 = createDummyEntity();
dummyEntity1.setFlag(true);
repository.save(dummyEntity1);
DummyEntity dummyEntity2 = createDummyEntity();
dummyEntity2.setName("Diego");
repository.save(dummyEntity2);
Example<DummyEntity> diegoExample = Example.of(new DummyEntity("Diego"));
Optional<DummyEntity> foundExampleDiego = repository.findOne(diegoExample);
assertThat(foundExampleDiego).isPresent();
assertThat(foundExampleDiego.get()).isNotNull();
assertThat(foundExampleDiego.get().getName()).isEqualTo("Diego");
}
@Test
void findOneByExampleMultipleMatchShouldGetOne() {
DummyEntity dummyEntity1 = createDummyEntity();
repository.save(dummyEntity1);
DummyEntity dummyEntity2 = createDummyEntity();
repository.save(dummyEntity2);
Example<DummyEntity> example = Example.of(createDummyEntity());
assertThatThrownBy(() -> repository.findOne(example)).isInstanceOf(IncorrectResultSizeDataAccessException.class)
.hasMessageContaining("expected 1, actual 2");
}
@Test
void findOneByExampleShouldGetNone() {
DummyEntity dummyEntity1 = createDummyEntity();
dummyEntity1.setFlag(true);
repository.save(dummyEntity1);
Example<DummyEntity> diegoExample = Example.of(new DummyEntity("NotExisting"));
Optional<DummyEntity> foundExampleDiego = repository.findOne(diegoExample);
assertThat(foundExampleDiego).isNotPresent();
}
@Test
void findAllByExampleShouldGetOne() {
DummyEntity dummyEntity1 = createDummyEntity();
dummyEntity1.setFlag(true);
repository.save(dummyEntity1);
DummyEntity dummyEntity2 = createDummyEntity();
dummyEntity2.setName("Diego");
repository.save(dummyEntity2);
Example<DummyEntity> example = Example.of(new DummyEntity("Diego"));
Iterable<DummyEntity> allFound = repository.findAll(example);
assertThat(allFound) //
.isNotNull() //
.hasSize(1) //
.extracting(DummyEntity::getName) //
.containsExactly(example.getProbe().getName());
}
@Test
void findAllByExampleMultipleMatchShouldGetOne() {
DummyEntity dummyEntity1 = createDummyEntity();
repository.save(dummyEntity1);
DummyEntity dummyEntity2 = createDummyEntity();
repository.save(dummyEntity2);
Example<DummyEntity> example = Example.of(createDummyEntity());
Iterable<DummyEntity> allFound = repository.findAll(example);
assertThat(allFound) //
.isNotNull() //
.hasSize(2) //
.extracting(DummyEntity::getName) //
.containsOnly(example.getProbe().getName());
}
@Test
void findAllByExampleShouldGetNone() {
DummyEntity dummyEntity1 = createDummyEntity();
dummyEntity1.setFlag(true);
repository.save(dummyEntity1);
Example<DummyEntity> example = Example.of(new DummyEntity("NotExisting"));
Iterable<DummyEntity> allFound = repository.findAll(example);
assertThat(allFound) //
.isNotNull() //
.isEmpty();
}
@Test
void findAllByExamplePageableShouldGetOne() {
DummyEntity dummyEntity1 = createDummyEntity();
dummyEntity1.setFlag(true);
repository.save(dummyEntity1);
DummyEntity dummyEntity2 = createDummyEntity();
dummyEntity2.setName("Diego");
repository.save(dummyEntity2);
Example<DummyEntity> example = Example.of(new DummyEntity("Diego"));
Pageable pageRequest = PageRequest.of(0, 10);
Iterable<DummyEntity> allFound = repository.findAll(example, pageRequest);
assertThat(allFound) //
.isNotNull() //
.hasSize(1) //
.extracting(DummyEntity::getName) //
.containsExactly(example.getProbe().getName());
}
@Test
void findAllByExamplePageableMultipleMatchShouldGetOne() {
DummyEntity dummyEntity1 = createDummyEntity();
repository.save(dummyEntity1);
DummyEntity dummyEntity2 = createDummyEntity();
repository.save(dummyEntity2);
Example<DummyEntity> example = Example.of(createDummyEntity());
Pageable pageRequest = PageRequest.of(0, 10);
Iterable<DummyEntity> allFound = repository.findAll(example, pageRequest);
assertThat(allFound) //
.isNotNull() //
.hasSize(2) //
.extracting(DummyEntity::getName) //
.containsOnly(example.getProbe().getName());
}
@Test
void findAllByExamplePageableShouldGetNone() {
DummyEntity dummyEntity1 = createDummyEntity();
dummyEntity1.setFlag(true);
repository.save(dummyEntity1);
Example<DummyEntity> example = Example.of(new DummyEntity("NotExisting"));
Pageable pageRequest = PageRequest.of(0, 10);
Iterable<DummyEntity> allFound = repository.findAll(example, pageRequest);
assertThat(allFound) //
.isNotNull() //
.isEmpty();
}
@Test
void findAllByExamplePageableOutsidePageShouldGetNone() {
DummyEntity dummyEntity1 = createDummyEntity();
repository.save(dummyEntity1);
DummyEntity dummyEntity2 = createDummyEntity();
repository.save(dummyEntity2);
Example<DummyEntity> example = Example.of(createDummyEntity());
Pageable pageRequest = PageRequest.of(10, 10);
Iterable<DummyEntity> allFound = repository.findAll(example, pageRequest);
assertThat(allFound) //
.isNotNull() //
.isEmpty();
}
@ParameterizedTest
@MethodSource("findAllByExamplePageableSource")
void findAllByExamplePageable(Pageable pageRequest, int size, int totalPages, List<String> notContains) {
for (int i = 0; i < 100; i++) {
DummyEntity dummyEntity = createDummyEntity();
dummyEntity.setFlag(true);
dummyEntity.setName("" + i);
repository.save(dummyEntity);
}
DummyEntity dummyEntityExample = createDummyEntity();
dummyEntityExample.setName(null);
dummyEntityExample.setFlag(true);
Example<DummyEntity> example = Example.of(dummyEntityExample);
Page<DummyEntity> allFound = repository.findAll(example, pageRequest);
// page has correct size
assertThat(allFound) //
.isNotNull() //
.hasSize(size);
// correct number of total
assertThat(allFound.getTotalElements()).isEqualTo(100);
assertThat(allFound.getTotalPages()).isEqualTo(totalPages);
if (!notContains.isEmpty()) {
assertThat(allFound) //
.extracting(DummyEntity::getName) //
.doesNotContain(notContains.toArray(new String[0]));
}
}
public static Stream<Arguments> findAllByExamplePageableSource() {
return Stream.of( //
Arguments.of(PageRequest.of(0, 3), 3, 34, Arrays.asList("3", "4", "100")), //
Arguments.of(PageRequest.of(1, 10), 10, 10, Arrays.asList("9", "20", "30")), //
Arguments.of(PageRequest.of(2, 10), 10, 10, Arrays.asList("1", "2", "3")), //
Arguments.of(PageRequest.of(33, 3), 1, 34, Collections.emptyList()), //
Arguments.of(PageRequest.of(36, 3), 0, 34, Collections.emptyList()), //
Arguments.of(PageRequest.of(0, 10000), 100, 1, Collections.emptyList()), //
Arguments.of(PageRequest.of(100, 10000), 0, 1, Collections.emptyList()) //
);
}
@Test
void existsByExampleShouldGetOne() {
DummyEntity dummyEntity1 = createDummyEntity();
dummyEntity1.setFlag(true);
repository.save(dummyEntity1);
DummyEntity dummyEntity2 = createDummyEntity();
dummyEntity2.setName("Diego");
repository.save(dummyEntity2);
Example<DummyEntity> example = Example.of(new DummyEntity("Diego"));
boolean exists = repository.exists(example);
assertThat(exists).isTrue();
}
@Test
void existsByExampleMultipleMatchShouldGetOne() {
DummyEntity dummyEntity1 = createDummyEntity();
repository.save(dummyEntity1);
DummyEntity dummyEntity2 = createDummyEntity();
repository.save(dummyEntity2);
Example<DummyEntity> example = Example.of(createDummyEntity());
boolean exists = repository.exists(example);
assertThat(exists).isTrue();
}
@Test
void existsByExampleShouldGetNone() {
DummyEntity dummyEntity1 = createDummyEntity();
dummyEntity1.setFlag(true);
repository.save(dummyEntity1);
Example<DummyEntity> example = Example.of(new DummyEntity("NotExisting"));
boolean exists = repository.exists(example);
assertThat(exists).isFalse();
}
@Test
void existsByExampleComplex() {
final Instant pointInTime = Instant.now().minusSeconds(10000);
final DummyEntity one = repository.save(createDummyEntity());
DummyEntity two = createDummyEntity();
two.setName("Diego");
two.setPointInTime(pointInTime);
two = repository.save(two);
DummyEntity exampleEntitiy = createDummyEntity();
exampleEntitiy.setName("Diego");
exampleEntitiy.setPointInTime(pointInTime);
Example<DummyEntity> example = Example.of(exampleEntitiy);
boolean exists = repository.exists(example);
assertThat(exists).isTrue();
}
@Test
void countByExampleShouldGetOne() {
DummyEntity dummyEntity1 = createDummyEntity();
dummyEntity1.setFlag(true);
repository.save(dummyEntity1);
DummyEntity dummyEntity2 = createDummyEntity();
dummyEntity2.setName("Diego");
repository.save(dummyEntity2);
Example<DummyEntity> example = Example.of(new DummyEntity("Diego"));
long count = repository.count(example);
assertThat(count).isOne();
}
@Test
void countByExampleMultipleMatchShouldGetOne() {
DummyEntity dummyEntity1 = createDummyEntity();
repository.save(dummyEntity1);
DummyEntity dummyEntity2 = createDummyEntity();
repository.save(dummyEntity2);
Example<DummyEntity> example = Example.of(createDummyEntity());
long count = repository.count(example);
assertThat(count).isEqualTo(2);
}
@Test
void countByExampleShouldGetNone() {
DummyEntity dummyEntity1 = createDummyEntity();
dummyEntity1.setFlag(true);
repository.save(dummyEntity1);
Example<DummyEntity> example = Example.of(new DummyEntity("NotExisting"));
long count = repository.count(example);
assertThat(count).isNotNull().isZero();
}
@Test
void countByExampleComplex() {
final Instant pointInTime = Instant.now().minusSeconds(10000);
final DummyEntity one = repository.save(createDummyEntity());
DummyEntity two = createDummyEntity();
two.setName("Diego");
two.setPointInTime(pointInTime);
two = repository.save(two);
DummyEntity exampleEntitiy = createDummyEntity();
exampleEntitiy.setName("Diego");
exampleEntitiy.setPointInTime(pointInTime);
Example<DummyEntity> example = Example.of(exampleEntitiy);
long count = repository.count(example);
assertThat(count).isOne();
}
@Test
void fetchByExampleFluentAllSimple() {
String searchName = "Diego";
Instant now = Instant.now();
final DummyEntity one = repository.save(createDummyEntity());
DummyEntity two = createDummyEntity();
two.setName(searchName);
two.setPointInTime(now.minusSeconds(10000));
two = repository.save(two);
DummyEntity third = createDummyEntity();
third.setName(searchName);
third.setPointInTime(now.minusSeconds(200000));
third = repository.save(third);
DummyEntity exampleEntitiy = createDummyEntity();
exampleEntitiy.setName(searchName);
Example<DummyEntity> example = Example.of(exampleEntitiy);
List<DummyEntity> matches = repository.findBy(example, p -> p.sortBy(Sort.by("pointInTime").descending()).all());
assertThat(matches).hasSize(2).contains(two, third);
assertThat(matches.get(0)).isEqualTo(two);
}
@Test
void fetchByExampleFluentCountSimple() {
String searchName = "Diego";
Instant now = Instant.now();
final DummyEntity one = repository.save(createDummyEntity());
DummyEntity two = createDummyEntity();
two.setName(searchName);
two.setPointInTime(now.minusSeconds(10000));
two = repository.save(two);
DummyEntity third = createDummyEntity();
third.setName(searchName);
third.setPointInTime(now.minusSeconds(200000));
third = repository.save(third);
DummyEntity exampleEntitiy = createDummyEntity();
exampleEntitiy.setName(searchName);
Example<DummyEntity> example = Example.of(exampleEntitiy);
Long matches = repository.findBy(example, FluentQuery.FetchableFluentQuery::count);
assertThat(matches).isEqualTo(2);
}
@Test
void fetchByExampleFluentOnlyInstantFirstSimple() {
String searchName = "Diego";
Instant now = Instant.now();
final DummyEntity one = repository.save(createDummyEntity());
DummyEntity two = createDummyEntity();
two.setName(searchName);
two.setPointInTime(now.minusSeconds(10000));
two = repository.save(two);
DummyEntity third = createDummyEntity();
third.setName(searchName);
third.setPointInTime(now.minusSeconds(200000));
third = repository.save(third);
DummyEntity exampleEntitiy = createDummyEntity();
exampleEntitiy.setName(searchName);
Example<DummyEntity> example = Example.of(exampleEntitiy);
Optional<DummyEntity> matches = repository.findBy(example,
p -> p.sortBy(Sort.by("pointInTime").descending()).first());
assertThat(matches).contains(two);
}
@Test
void fetchByExampleFluentOnlyInstantOneValueError() {
String searchName = "Diego";
Instant now = Instant.now();
final DummyEntity one = repository.save(createDummyEntity());
DummyEntity two = createDummyEntity();
two.setName(searchName);
two.setPointInTime(now.minusSeconds(10000));
two = repository.save(two);
DummyEntity third = createDummyEntity();
third.setName(searchName);
third.setPointInTime(now.minusSeconds(200000));
third = repository.save(third);
DummyEntity exampleEntitiy = createDummyEntity();
exampleEntitiy.setName(searchName);
Example<DummyEntity> example = Example.of(exampleEntitiy);
assertThatThrownBy(() -> repository.findBy(example, p -> p.sortBy(Sort.by("pointInTime").descending()).one()))
.isInstanceOf(IncorrectResultSizeDataAccessException.class).hasMessageContaining("expected 1, actual 2");
}
@Test
void fetchByExampleFluentOnlyInstantOneValueSimple() {
String searchName = "Diego";
Instant now = Instant.now();
final DummyEntity one = repository.save(createDummyEntity());
DummyEntity two = createDummyEntity();
two.setName(searchName);
two.setPointInTime(now.minusSeconds(10000));
two = repository.save(two);
DummyEntity exampleEntitiy = createDummyEntity();
exampleEntitiy.setName(searchName);
Example<DummyEntity> example = Example.of(exampleEntitiy);
Optional<DummyEntity> match = repository.findBy(example, p -> p.sortBy(Sort.by("pointInTime").descending()).one());
assertThat(match).contains(two);
}
@Test
void fetchByExampleFluentOnlyInstantOneValueAsSimple() {
String searchName = "Diego";
Instant now = Instant.now();
final DummyEntity one = repository.save(createDummyEntity());
DummyEntity two = createDummyEntity();
two.setName(searchName);
two.setPointInTime(now.minusSeconds(10000));
two = repository.save(two);
DummyEntity exampleEntitiy = createDummyEntity();
exampleEntitiy.setName(searchName);
Example<DummyEntity> example = Example.of(exampleEntitiy);
Optional<DummyProjectExample> match = repository.findBy(example, p -> p.as(DummyProjectExample.class).one());
assertThat(match.get().getName()).contains(two.getName());
}
private Instant createDummyBeforeAndAfterNow() {
Instant now = Instant.now();
@@ -730,7 +1293,11 @@ public class JdbcRepositoryIntegrationTests {
return now;
}
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
interface DummyProjectExample {
String getName();
}
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long>, QueryByExampleExecutor<DummyEntity> {
@Lock(LockMode.PESSIMISTIC_WRITE)
List<DummyEntity> findAllByName(String name);

View File

@@ -64,6 +64,7 @@ import org.springframework.util.ReflectionUtils;
* @author Evgeni Dimitrov
* @author Fei Dong
* @author Chirag Tailor
* @author Diego Krupitza
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = TestConfiguration.class)
@@ -175,7 +176,8 @@ public class EnableJdbcRepositoriesIntegrationTests {
private static class DummyRepositoryBaseClass<T, ID> implements CrudRepository<T, ID> {
DummyRepositoryBaseClass(JdbcAggregateTemplate template, PersistentEntity<?, ?> persistentEntity) {
DummyRepositoryBaseClass(JdbcAggregateTemplate template, PersistentEntity<?, ?> persistentEntity,
JdbcConverter converter) {
}

View File

@@ -24,6 +24,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
/**
@@ -36,11 +37,12 @@ public class SimpleJdbcRepositoryUnitTests {
@Mock JdbcAggregateOperations operations;
@Mock RelationalPersistentEntity<Sample> entity;
@Mock JdbcConverter converter;
@Test // DATAJDBC-252
public void saveReturnsEntityProducedByOperations() {
SimpleJdbcRepository<Sample, Object> repository = new SimpleJdbcRepository<>(operations, entity);
SimpleJdbcRepository<Sample, Object> repository = new SimpleJdbcRepository<>(operations, entity,converter);
Sample expected = new Sample();
doReturn(expected).when(operations).save(any());