#346 - Demonstrating the use of @Query with and without @Modifying.
Added assertions to existing tests to verify they work as intended. Changed Model to a value object because we can.
This commit is contained in:
committed by
Oliver Gierke
parent
2571c89af0
commit
68a15b0f5a
@@ -1,6 +1,5 @@
|
||||
== Spring Data JDBC basics
|
||||
|
||||
|
||||
=== SimpleEntityTests
|
||||
|
||||
This example demonstrate basic usage of JDBC based repositories.
|
||||
@@ -26,3 +25,6 @@ This is achieved by providing a custom `NamingStrategy` which maps both to the s
|
||||
|
||||
* When the database returns a data type for query which Spring Data JDBC doesn't map out of the box a custom conversion can be registered using a `ConversionCustomizer` as demonstrated in `AggregateContext.conversionCustomizer()`.
|
||||
|
||||
* `LegoSetRepository` has methods that utilize `@Query` annotations.
|
||||
|
||||
* Note that `Model` is a value class, i.e. it is immutable, and doesn't have an ID.
|
||||
@@ -38,33 +38,32 @@ import org.springframework.lang.Nullable;
|
||||
@Configuration
|
||||
@EnableJdbcRepositories
|
||||
public class AggregateConfiguration {
|
||||
final AtomicInteger id = new AtomicInteger(0);
|
||||
|
||||
@Bean
|
||||
public ApplicationListener<?> idSetting() {
|
||||
|
||||
final AtomicInteger id = new AtomicInteger(0);
|
||||
|
||||
return (ApplicationListener<BeforeSaveEvent>) event -> {
|
||||
|
||||
Object entity = event.getEntity();
|
||||
|
||||
if (entity instanceof LegoSet) {
|
||||
|
||||
LegoSet legoSet = (LegoSet) entity;
|
||||
|
||||
if (legoSet.getId() == 0) {
|
||||
legoSet.setId(id.incrementAndGet());
|
||||
}
|
||||
|
||||
Manual manual = legoSet.getManual();
|
||||
|
||||
if (manual != null) {
|
||||
manual.setId((long) legoSet.getId());
|
||||
}
|
||||
if (event.getEntity() instanceof LegoSet) {
|
||||
setIds((LegoSet) event.getEntity());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void setIds(LegoSet legoSet) {
|
||||
|
||||
if (legoSet.getId() == 0) {
|
||||
legoSet.setId(id.incrementAndGet());
|
||||
}
|
||||
|
||||
Manual manual = legoSet.getManual();
|
||||
|
||||
if (manual != null) {
|
||||
manual.setId((long) legoSet.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NamingStrategy namingStrategy() {
|
||||
|
||||
|
||||
@@ -77,10 +77,7 @@ public class LegoSet {
|
||||
|
||||
public void addModel(String name, String description) {
|
||||
|
||||
Model model = new Model();
|
||||
model.name = name;
|
||||
model.description = description;
|
||||
|
||||
Model model = new Model(name, description);
|
||||
models.put(name, model);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,28 @@
|
||||
*/
|
||||
package example.springdata.jdbc.basics.aggregate;
|
||||
|
||||
import org.springframework.data.jdbc.repository.query.Modifying;
|
||||
import org.springframework.data.jdbc.repository.query.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A repository for {@link LegoSet}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
interface LegoSetRepository extends CrudRepository<LegoSet, Integer> {}
|
||||
interface LegoSetRepository extends CrudRepository<LegoSet, Integer> {
|
||||
|
||||
@Query("SELECT m.name modelName, m.description, l.name setName" +
|
||||
" FROM model m" +
|
||||
" JOIN LegoSet l" +
|
||||
" ON m.legoset = l.id" +
|
||||
" WHERE :age BETWEEN l.minAge and l.maxAge")
|
||||
List<ModelReport> reportModelForAge(@Param("age") int age);
|
||||
|
||||
@Modifying
|
||||
@Query("UPDATE model set name = lower(name) WHERE name <> lower(name)")
|
||||
int lowerCaseMapKeys();
|
||||
}
|
||||
|
||||
@@ -15,18 +15,16 @@
|
||||
*/
|
||||
package example.springdata.jdbc.basics.aggregate;
|
||||
|
||||
import lombok.ToString;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* One of potentially multiple models that can be build from a single {@link LegoSet}. No getters or setters needed.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@ToString
|
||||
@Value
|
||||
public class Model {
|
||||
|
||||
@Id String name;
|
||||
String name;
|
||||
String description;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 example.springdata.jdbc.basics.aggregate;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@Value
|
||||
public class ModelReport {
|
||||
|
||||
private final String modelName;
|
||||
private final String description;
|
||||
private final String setName;
|
||||
}
|
||||
@@ -15,15 +15,21 @@
|
||||
*/
|
||||
package example.springdata.jdbc.basics.aggregate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import example.springdata.jdbc.basics.Output;
|
||||
|
||||
import java.time.Period;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.assertj.core.groups.Tuple;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureJdbc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
@@ -34,6 +40,7 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = AggregateConfiguration.class)
|
||||
@AutoConfigureJdbc
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
|
||||
public class AggregateTests {
|
||||
|
||||
@Autowired LegoSetRepository repository;
|
||||
@@ -41,34 +48,88 @@ public class AggregateTests {
|
||||
@Test
|
||||
public void exerciseSomewhatComplexEntity() {
|
||||
|
||||
LegoSet smallCar = createLegoSet();
|
||||
LegoSet smallCar = createLegoSet("Small Car 01", 5, 12);
|
||||
smallCar.setManual(new Manual("Just put all the pieces together in the right order", "Jens Schauder"));
|
||||
smallCar.addModel("suv", "SUV with sliding doors.");
|
||||
smallCar.addModel("roadster", "Slick red roadster.");
|
||||
|
||||
repository.save(smallCar);
|
||||
Output.list(repository.findAll(), "Original LegoSet");
|
||||
Iterable<LegoSet> legoSets = repository.findAll();
|
||||
Output.list(legoSets, "Original LegoSet");
|
||||
checkLegoSets(legoSets, "Just put all the pieces together in the right order", 2);
|
||||
|
||||
smallCar.getManual().setText("Just make it so it looks like a car.");
|
||||
smallCar.addModel("pickup", "A pickup truck with some tools in the back.");
|
||||
|
||||
repository.save(smallCar);
|
||||
Output.list(repository.findAll(), "Updated");
|
||||
legoSets = repository.findAll();
|
||||
Output.list(legoSets, "Updated");
|
||||
checkLegoSets(legoSets, "Just make it so it looks like a car.", 3);
|
||||
|
||||
smallCar.setManual(new Manual("One last attempt: Just build a car! Ok?", "Jens Schauder"));
|
||||
|
||||
repository.save(smallCar);
|
||||
Output.list(repository.findAll(), "Manual replaced");
|
||||
legoSets = repository.findAll();
|
||||
Output.list(legoSets, "Manual replaced");
|
||||
checkLegoSets(legoSets, "One last attempt: Just build a car! Ok?", 3);
|
||||
|
||||
}
|
||||
|
||||
private LegoSet createLegoSet() {
|
||||
@Test
|
||||
public void customQueries() {
|
||||
|
||||
LegoSet smallCars = createLegoSet("Small Car - 01", 5, 10);
|
||||
smallCars.setManual(new Manual("Just put all the pieces together in the right order", "Jens Schauder"));
|
||||
|
||||
smallCars.addModel("SUV", "SUV with sliding doors.");
|
||||
smallCars.addModel("roadster", "Slick red roadster.");
|
||||
|
||||
LegoSet f1Racer = createLegoSet("F1 Racer", 6, 15);
|
||||
f1Racer.setManual(new Manual("Build a helicopter or a plane", "M. Shoemaker"));
|
||||
|
||||
f1Racer.addModel("F1 Ferrari 2018", "A very fast red car.");
|
||||
|
||||
LegoSet constructionVehicles = createLegoSet("Construction Vehicles", 3, 6);
|
||||
constructionVehicles.setManual(
|
||||
new Manual("Build a Road Roler, a Mobile Crane, a Tracked Dumper, or a Backhoe Loader ", "Bob the Builder"));
|
||||
|
||||
constructionVehicles.addModel("scoop", "A backhoe loader");
|
||||
constructionVehicles.addModel("Muck", "Muck is a continuous tracked dump truck with an added bulldozer blade");
|
||||
constructionVehicles.addModel("lofty", "A mobile crane");
|
||||
constructionVehicles.addModel("roley",
|
||||
"A road roller that loves to make up songs and frequently spins his eyes when he is excited.");
|
||||
|
||||
repository.saveAll(Arrays.asList(smallCars, f1Racer, constructionVehicles));
|
||||
|
||||
List<ModelReport> report = repository.reportModelForAge(6);
|
||||
Output.list(report, "Model Report");
|
||||
|
||||
assertThat(report).hasSize(7)
|
||||
.allMatch(m -> m.getDescription() != null && m.getModelName() != null && m.getSetName() != null);
|
||||
|
||||
int updated = repository.lowerCaseMapKeys();
|
||||
// SUV, F1 Ferrari 2018 and Muck get updated
|
||||
assertThat(updated).isEqualTo(3);
|
||||
|
||||
}
|
||||
|
||||
private LegoSet createLegoSet(String name, int minimumAge, int maximumAge) {
|
||||
|
||||
LegoSet smallCar = new LegoSet();
|
||||
|
||||
smallCar.setName("Small Car 01");
|
||||
smallCar.setMinimumAge(Period.ofYears(5));
|
||||
smallCar.setMaximumAge(Period.ofYears(12));
|
||||
smallCar.setName(name);
|
||||
smallCar.setMinimumAge(Period.ofYears(minimumAge));
|
||||
smallCar.setMaximumAge(Period.ofYears(maximumAge));
|
||||
|
||||
return smallCar;
|
||||
}
|
||||
|
||||
private void checkLegoSets(Iterable<LegoSet> legoSets, String manualText, int numberOfModels) {
|
||||
|
||||
assertThat(legoSets) //
|
||||
.extracting( //
|
||||
ls -> ls.getManual().getText(), //
|
||||
ls -> ls.getModels().size()) //
|
||||
.containsExactly(new Tuple(manualText, numberOfModels));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user