From 5d556d6d45c6a28dda5e963add6c7d1f11fb5f66 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Wed, 12 Apr 2017 16:06:24 +0200 Subject: [PATCH] #313 - Added examples showing basic usage of Spring Data JDBC. Original pull request: #324. --- README.md | 4 + jdbc/basics/README.adoc | 28 ++++ jdbc/basics/pom.xml | 16 ++ .../springdata/jdbc/basics/Output.java | 43 +++++ .../jdbc/basics/aggregate/AgeGroup.java | 29 ++++ .../aggregate/AggregateConfiguration.java | 155 ++++++++++++++++++ .../jdbc/basics/aggregate/LegoSet.java | 89 ++++++++++ .../basics/aggregate/LegoSetRepository.java | 26 +++ .../jdbc/basics/aggregate/Manual.java | 43 +++++ .../jdbc/basics/aggregate/Model.java | 35 ++++ .../jdbc/basics/simpleentity/Category.java | 62 +++++++ .../simpleentity/CategoryConfiguration.java | 88 ++++++++++ .../simpleentity/CategoryRepository.java | 27 +++ .../src/main/resources/application.properties | 2 + jdbc/basics/src/main/resources/schema.sql | 5 + .../jdbc/basics/aggregate/AggregateTests.java | 71 ++++++++ .../simpleentity/SimpleEntityTests.java | 67 ++++++++ jdbc/pom.xml | 43 +++++ pom.xml | 6 + 19 files changed, 839 insertions(+) create mode 100644 jdbc/basics/README.adoc create mode 100644 jdbc/basics/pom.xml create mode 100644 jdbc/basics/src/main/java/example/springdata/jdbc/basics/Output.java create mode 100644 jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/AgeGroup.java create mode 100644 jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/AggregateConfiguration.java create mode 100644 jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/LegoSet.java create mode 100644 jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/LegoSetRepository.java create mode 100644 jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/Manual.java create mode 100644 jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/Model.java create mode 100644 jdbc/basics/src/main/java/example/springdata/jdbc/basics/simpleentity/Category.java create mode 100644 jdbc/basics/src/main/java/example/springdata/jdbc/basics/simpleentity/CategoryConfiguration.java create mode 100644 jdbc/basics/src/main/java/example/springdata/jdbc/basics/simpleentity/CategoryRepository.java create mode 100644 jdbc/basics/src/main/resources/application.properties create mode 100644 jdbc/basics/src/main/resources/schema.sql create mode 100644 jdbc/basics/src/test/java/example/springdata/jdbc/basics/aggregate/AggregateTests.java create mode 100644 jdbc/basics/src/test/java/example/springdata/jdbc/basics/simpleentity/SimpleEntityTests.java create mode 100644 jdbc/pom.xml diff --git a/README.md b/README.md index a9e2434c..651be259 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,10 @@ We have separate folders for the samples of individual modules: * `example` - Sample for Spring Data repositories to access an LDAP store. +## Spring Data JDBC + +* `basic` - Basic usage of Spring Data JDBC. + ## Miscellaneous * `bom` - Example project how to use the Spring Data release train bom in non-Spring-Boot scenarios. diff --git a/jdbc/basics/README.adoc b/jdbc/basics/README.adoc new file mode 100644 index 00000000..e3ebb5a1 --- /dev/null +++ b/jdbc/basics/README.adoc @@ -0,0 +1,28 @@ +== Spring Data JDBC basics + + +=== SimpleEntityTests + +This example demonstrate basic usage of JDBC based repositories. + +* The `SimpleEntityTests` demonstrate CRUD operations for an entity without references, just simple properties of various types. + +* The `CategoryContext` shows how to configure an application context so that Spring Data JDBC can create repositories. + +* The `ApplicationListener` registered in `CategoryContext` demonstrate how to react to events published by Spring Data JDBC and how entities can get manipulated in such event listeners. + +=== AggregateTests + +This example demonstrates various ways to bend what the standard mapping of Spring Data JDBC can do. + +* `AggregateContext.idSetting()` registers an `ApplicationListener` to implement a custom id generation strategy for `LegoSet` and `Manual`. + +* `AggregateContext.namingStrategy()` registers a custom `NamingStrategy` in order to map property and class names to database columns and tables. + +* The `minimumAge` and `maximumAge` properties show a way to use types which don't have direct mapping to a SQL type, by marking them with `@Transient` and having converted properties for Spring Data JDBC to use. + +* The `models` attribute demonstrate mapping of a `Map`. It does work out of the box, but in this case the the key of the map should be one attribute of the entity. +This is achieved by providing a custom `NamingStrategy` which maps both to the same database column. + +* 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()`. + diff --git a/jdbc/basics/pom.xml b/jdbc/basics/pom.xml new file mode 100644 index 00000000..823367a0 --- /dev/null +++ b/jdbc/basics/pom.xml @@ -0,0 +1,16 @@ + + 4.0.0 + + spring-data-jdbc-basics + + + org.springframework.data.examples + spring-data-jdbc-examples + 2.0.0.BUILD-SNAPSHOT + ../pom.xml + + + Spring Data JDBC - Basic usage examples + Sample project demonstrating Spring Data JDBC features + \ No newline at end of file diff --git a/jdbc/basics/src/main/java/example/springdata/jdbc/basics/Output.java b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/Output.java new file mode 100644 index 00000000..a86d166a --- /dev/null +++ b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/Output.java @@ -0,0 +1,43 @@ +/* + * Copyright 2017 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; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import lombok.experimental.UtilityClass; + +/** + * Trivial class to print domain objects to the console in a somewhat readable format. + * + * @author Jens Schauder + */ +@UtilityClass +public class Output { + + private final Logger LOG = LoggerFactory.getLogger(Output.class); + + public static void list(Iterable categories, String title) { + + StringBuilder message = new StringBuilder(String.format("==== %s ====\n", title)); + + categories.forEach(category -> { + message.append(category.toString().replace(", ", ",\n\t")); + }); + + LOG.info(message.toString()); + } +} diff --git a/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/AgeGroup.java b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/AgeGroup.java new file mode 100644 index 00000000..68b3cd96 --- /dev/null +++ b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/AgeGroup.java @@ -0,0 +1,29 @@ +/* + * Copyright 2017 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; + +/** + * Age group for which a {@link LegoSet} is intended. + * + * @author Jens Schauder + */ +public enum AgeGroup { + + _0to3, + _3to8, + _8to12, + _12andOlder +} diff --git a/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/AggregateConfiguration.java b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/AggregateConfiguration.java new file mode 100644 index 00000000..c4f6f997 --- /dev/null +++ b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/AggregateConfiguration.java @@ -0,0 +1,155 @@ +/* + * Copyright 2017 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 org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.jdbc.core.DataAccessStrategy; +import org.springframework.data.jdbc.core.DefaultDataAccessStrategy; +import org.springframework.data.jdbc.core.DelegatingDataAccessStrategy; +import org.springframework.data.jdbc.core.SqlGeneratorSource; +import org.springframework.data.jdbc.mapping.event.BeforeSave; +import org.springframework.data.jdbc.mapping.model.*; +import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import org.springframework.lang.Nullable; + +import javax.sql.DataSource; +import java.sql.Clob; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Jens Schauder + */ +@Configuration +@EnableJdbcRepositories +public class AggregateConfiguration { + + @Bean + public ApplicationListener idSetting() { + + final AtomicInteger id = new AtomicInteger(0); + + return (ApplicationListener) 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()); + } + } + }; + } + + @Bean + public NamingStrategy namingStrategy() { + + Map tableAliases = new HashMap(); + tableAliases.put("Manual", "Handbuch"); + + Map columnAliases = new HashMap(); + columnAliases.put("LegoSet.intMaximumAge", "maxAge"); + columnAliases.put("LegoSet.intMinimumAge", "minAge"); + columnAliases.put("Handbuch.id", "Handbuch_id"); + + Map reverseColumnAliases = new HashMap(); + reverseColumnAliases.put("manual", "Handbuch_id"); + + Map keyColumnAliases = new HashMap(); + keyColumnAliases.put("models", "name"); + + return new DefaultNamingStrategy() { + + @Override + public String getColumnName(JdbcPersistentProperty property) { + + String defaultName = super.getColumnName(property); + String key = getTableName(property.getOwner().getType()) + "." + defaultName; + return columnAliases.computeIfAbsent(key, __ -> defaultName); + } + + @Override + public String getTableName(Class type) { + + return tableAliases.computeIfAbsent(super.getTableName(type),key -> key); + } + + @Override + public String getReverseColumnName(JdbcPersistentProperty property) { + return reverseColumnAliases.computeIfAbsent(property.getName(), __ -> super.getReverseColumnName(property)); + } + + @Override + public String getKeyColumn(JdbcPersistentProperty property) { + return keyColumnAliases.computeIfAbsent(property.getName(), __ -> super.getKeyColumn(property)); + } + }; + } + + @Bean + public ConversionCustomizer conversionCustomizer() { + + return conversions -> conversions.addConverter(new Converter() { + + @Nullable + @Override + public String convert(Clob clob) { + + try { + + return Math.toIntExact(clob.length()) == 0 // + ? "" // + : clob.getSubString(1, Math.toIntExact(clob.length())); + } catch (SQLException e) { + throw new IllegalStateException("Failed to convert CLOB to String.", e); + } + } + }); + } + + // temporary workaround for https://jira.spring.io/browse/DATAJDBC-155 + @Bean + DataAccessStrategy defaultDataAccessStrategy(JdbcMappingContext context, DataSource dataSource) { + + NamedParameterJdbcOperations operations = new NamedParameterJdbcTemplate(dataSource); + + DelegatingDataAccessStrategy accessStrategy = new DelegatingDataAccessStrategy(); + + accessStrategy.setDelegate(new DefaultDataAccessStrategy( // + new SqlGeneratorSource(context), // + operations, // + context, // + accessStrategy) // + ); + + return accessStrategy; + } +} diff --git a/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/LegoSet.java b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/LegoSet.java new file mode 100644 index 00000000..3c2e2e09 --- /dev/null +++ b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/LegoSet.java @@ -0,0 +1,89 @@ +/* + * Copyright 2017 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.Data; +import org.springframework.data.annotation.AccessType; +import org.springframework.data.annotation.AccessType.Type; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Transient; + +import java.time.Period; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; + +/** + * A Lego Set consisting of multiple Blocks and a manual + * + * @author Jens Schauder + */ +@Data +@AccessType(Type.PROPERTY) +public class LegoSet { + + @Id + private int id; + + private String name; + + @Transient + private Period minimumAge; + @Transient + private Period maximumAge; + + /** + * Since Manuals are part of a {@link LegoSet} and only make sense inside a {@link LegoSet} it is considered part of the Aggregate. + */ + private Manual manual; + + // You can build multiple models from one LegoSet + private final Map models = new HashMap<>(); + + // conversion for custom types currently has to be done through getters/setter + marking the underlying property with @Transient. + public int getIntMinimumAge() { + return toInt(this.minimumAge); + } + + public void setIntMinimumAge(int years) { + minimumAge = toPeriod(years); + } + + public int getIntMaximumAge() { + return toInt(this.maximumAge); + } + + public void setIntMaximumAge(int years) { + maximumAge = toPeriod(years); + } + + + private static int toInt(Period period) { + return (int) (period == null ? 0 : period.get(ChronoUnit.YEARS)); + } + + private static Period toPeriod(int years) { + return Period.ofYears(years); + } + + public void addModel(String name, String description) { + + Model model = new Model(); + model.name = name; + model.description = description; + models.put(name, model); + } +} diff --git a/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/LegoSetRepository.java b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/LegoSetRepository.java new file mode 100644 index 00000000..2718fda9 --- /dev/null +++ b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/LegoSetRepository.java @@ -0,0 +1,26 @@ +/* + * Copyright 2017 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 org.springframework.data.repository.CrudRepository; + +/** + * A repository for {@link LegoSet}. + * @author Jens Schauder + */ +public interface LegoSetRepository extends CrudRepository { + +} diff --git a/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/Manual.java b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/Manual.java new file mode 100644 index 00000000..5e5185b2 --- /dev/null +++ b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/Manual.java @@ -0,0 +1,43 @@ +/* + * Copyright 2017 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 org.springframework.data.annotation.Id; + +import lombok.Data; + +/** + * A manual instructing how to assemble a {@link LegoSet}. + * + * @author Jens Schauder + */ +@Data +public class Manual { + + + Manual(String text, String author) { + + this.id = null; + this.author = author; + this.text = text; + } + + @Id + private Long id; + + private String author; + private String text; +} diff --git a/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/Model.java b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/Model.java new file mode 100644 index 00000000..7130a032 --- /dev/null +++ b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/aggregate/Model.java @@ -0,0 +1,35 @@ +/* + * Copyright 2017 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.ToString; +import org.springframework.data.annotation.Id; + +/** + * One of potentially multiple models that can be build from a single {@link LegoSet}. + * + * No getters or setters needed. + * + * @author Jens Schauder + */ +@ToString +public class Model { + + @Id + String name; + + String description; +} diff --git a/jdbc/basics/src/main/java/example/springdata/jdbc/basics/simpleentity/Category.java b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/simpleentity/Category.java new file mode 100644 index 00000000..a6c1ae81 --- /dev/null +++ b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/simpleentity/Category.java @@ -0,0 +1,62 @@ +/* + * Copyright 2017 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.simpleentity; + +import java.time.LocalDateTime; + +import example.springdata.jdbc.basics.aggregate.AgeGroup; +import example.springdata.jdbc.basics.aggregate.LegoSet; +import org.springframework.data.annotation.Id; + +import lombok.Data; +import lombok.Setter; + +/** + * Coarse classification for {@link LegoSet}s, like "Car", "Plane", "Building" and so on. + * + * @author Jens Schauder + */ +@Data +public class Category { + + public Category(String name, String description, AgeGroup ageGroup) { + + this.id = null; + this.name = name; + this.description = description; + this.ageGroup = ageGroup; + } + + @Id + private final Long id; + + private String name; + private String description; + + private LocalDateTime created = LocalDateTime.now(); + + @Setter + private long inserted; + + public void timeStamp() { + + if (inserted == 0) { + inserted = System.currentTimeMillis(); + } + } + + private AgeGroup ageGroup; +} diff --git a/jdbc/basics/src/main/java/example/springdata/jdbc/basics/simpleentity/CategoryConfiguration.java b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/simpleentity/CategoryConfiguration.java new file mode 100644 index 00000000..6ddd2cb6 --- /dev/null +++ b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/simpleentity/CategoryConfiguration.java @@ -0,0 +1,88 @@ +/* + * Copyright 2017 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.simpleentity; + +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jdbc.core.DataAccessStrategy; +import org.springframework.data.jdbc.core.DefaultDataAccessStrategy; +import org.springframework.data.jdbc.core.DelegatingDataAccessStrategy; +import org.springframework.data.jdbc.core.SqlGeneratorSource; +import org.springframework.data.jdbc.mapping.event.BeforeSave; +import org.springframework.data.jdbc.mapping.event.JdbcEvent; +import org.springframework.data.jdbc.mapping.model.JdbcMappingContext; +import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; + +import javax.sql.DataSource; + +/** + * Contains infrastructure necessary for creating repositories and two listeners. + *

+ * Not that a listener may change an entity without any problem. + * + * @author Jens Schauder + */ +@Configuration +@EnableJdbcRepositories +public class CategoryConfiguration { + + + @Bean + public ApplicationListener loggingListener() { + + return (ApplicationListener) event -> { + if (event instanceof JdbcEvent) { + System.out.println("received an event: " + event); + } + }; + } + + + @Bean + public ApplicationListener timeStampingSaveTime() { + + return event -> { + + Object entity = event.getEntity(); + if (entity instanceof Category) { + Category category = (Category) entity; + category.timeStamp(); + } + }; + } + + // temporary workaround for https://jira.spring.io/browse/DATAJDBC-155 + @Bean + DataAccessStrategy defaultDataAccessStrategy(JdbcMappingContext context, DataSource dataSource) { + + NamedParameterJdbcOperations operations = new NamedParameterJdbcTemplate(dataSource); + + DelegatingDataAccessStrategy accessStrategy = new DelegatingDataAccessStrategy(); + + accessStrategy.setDelegate(new DefaultDataAccessStrategy( // + new SqlGeneratorSource(context), // + operations, // + context, // + accessStrategy) // + ); + + return accessStrategy; + } +} diff --git a/jdbc/basics/src/main/java/example/springdata/jdbc/basics/simpleentity/CategoryRepository.java b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/simpleentity/CategoryRepository.java new file mode 100644 index 00000000..eab90b1a --- /dev/null +++ b/jdbc/basics/src/main/java/example/springdata/jdbc/basics/simpleentity/CategoryRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2017 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.simpleentity; + +import org.springframework.data.repository.CrudRepository; + +/** + * Repository for Categories. + * + * @author Jens Schauder + */ +public interface CategoryRepository extends CrudRepository { + +} diff --git a/jdbc/basics/src/main/resources/application.properties b/jdbc/basics/src/main/resources/application.properties new file mode 100644 index 00000000..2804353d --- /dev/null +++ b/jdbc/basics/src/main/resources/application.properties @@ -0,0 +1,2 @@ +logging.level.org.springframework.data=INFO +logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG \ No newline at end of file diff --git a/jdbc/basics/src/main/resources/schema.sql b/jdbc/basics/src/main/resources/schema.sql new file mode 100644 index 00000000..6be18330 --- /dev/null +++ b/jdbc/basics/src/main/resources/schema.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS category (id INTEGER IDENTITY PRIMARY KEY, name VARCHAR(100), description VARCHAR(2000), agegroup VARCHAR(20), created DATETIME, inserted BIGINT); + +CREATE TABLE IF NOT EXISTS LegoSet (id INTEGER, name VARCHAR(100), minAge INTEGER, maxAge INTEGER); +CREATE TABLE IF NOT EXISTS Handbuch (handbuch_id INTEGER, author CHAR(100), text CLOB); +CREATE TABLE IF NOT EXISTS Model (name VARCHAR(100), description CLOB, legoset INTEGER); diff --git a/jdbc/basics/src/test/java/example/springdata/jdbc/basics/aggregate/AggregateTests.java b/jdbc/basics/src/test/java/example/springdata/jdbc/basics/aggregate/AggregateTests.java new file mode 100644 index 00000000..602f1895 --- /dev/null +++ b/jdbc/basics/src/test/java/example/springdata/jdbc/basics/aggregate/AggregateTests.java @@ -0,0 +1,71 @@ +package example.springdata.jdbc.basics.aggregate;/* + * Copyright 2017 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. + */ + +import example.springdata.jdbc.basics.Output; +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.context.junit4.SpringRunner; + +import java.time.Period; + +/** + * Demonstrates various possibilities to customize the behavior of a repository. + * + * @author Jens Schauder + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = AggregateConfiguration.class) +@AutoConfigureJdbc +public class AggregateTests { + + @Autowired + private LegoSetRepository repository; + + @Test + public void exerciseSomewhatComplexEntity() { + + LegoSet smallCar = createLegoSet(); + 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"); + + 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"); + + smallCar.setManual(new Manual("One last attempt: Just build a car! Ok?", "Jens Schauder")); + + repository.save(smallCar); + Output.list(repository.findAll(), "Manual replaced"); + } + + private LegoSet createLegoSet() { + + LegoSet smallCar = new LegoSet(); + smallCar.setName("Small Car 01"); + smallCar.setMinimumAge(Period.ofYears(5)); + smallCar.setMaximumAge(Period.ofYears(12)); + return smallCar; + } +} diff --git a/jdbc/basics/src/test/java/example/springdata/jdbc/basics/simpleentity/SimpleEntityTests.java b/jdbc/basics/src/test/java/example/springdata/jdbc/basics/simpleentity/SimpleEntityTests.java new file mode 100644 index 00000000..ad3fd4ad --- /dev/null +++ b/jdbc/basics/src/test/java/example/springdata/jdbc/basics/simpleentity/SimpleEntityTests.java @@ -0,0 +1,67 @@ +package example.springdata.jdbc.basics.simpleentity;/* + * Copyright 2017 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. + */ + +import example.springdata.jdbc.basics.Output; +import example.springdata.jdbc.basics.aggregate.AgeGroup; +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.context.junit4.SpringRunner; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Demonstrates simple CRUD operations with a simple entity without any references. + * + * @author Jens Schauder + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = CategoryConfiguration.class) +@AutoConfigureJdbc +public class SimpleEntityTests { + + @Autowired + private CategoryRepository repository; + + @Test + public void exerciseRepositoryForSimpleEntity() { + + // create some categories + Category cars = new Category("Cars", "Anything that has approximately 4 wheels", AgeGroup._3to8); + + Category buildings = new Category("Buildings", null, AgeGroup._12andOlder); + + // save categories + repository.saveAll(asList(cars, buildings)); + Output.list(repository.findAll(), "`Cars` and `Buildings` got saved"); + + assertThat(cars.getId()).isNotNull(); + assertThat(buildings.getId()).isNotNull(); + + // update one + buildings.setDescription("Famous and impressive buildings incl. the 'bike shed'."); + repository.save(buildings); + Output.list(repository.findAll(), "`Buildings` has a description"); + + // delete stuff again + repository.delete(cars); + Output.list(repository.findAll(), "`Cars` is gone."); + + } +} diff --git a/jdbc/pom.xml b/jdbc/pom.xml new file mode 100644 index 00000000..b211c0b8 --- /dev/null +++ b/jdbc/pom.xml @@ -0,0 +1,43 @@ + + 4.0.0 + + spring-data-jdbc-examples + pom + + + org.springframework.data.examples + spring-data-examples + 2.0.0.BUILD-SNAPSHOT + + + Spring Data JDBC - Examples + Sample projects for Spring Data JDBC + http://projects.spring.io/spring-data-jdbc + 2017 + + + basics + + + + + + org.springframework.data + spring-data-jdbc + 1.0.0.BUILD-SNAPSHOT + + + + org.springframework.data + spring-data-commons + 2.1.0.BUILD-SNAPSHOT + + + + org.hsqldb + hsqldb + + + + diff --git a/pom.xml b/pom.xml index 6cf1864e..a158ca07 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,7 @@ cassandra couchbase elasticsearch + jdbc jpa ldap map @@ -64,6 +65,11 @@ Mark Paluch mpaluch@pivotal.io + + jschauder + Jens Schauder + jschauder@pivotal.io +