Demonstrate Schema Generation in Spring Data Jdbc.

This provides the source examples for the upcoming blog post.

Closes #667
This commit is contained in:
Jens Schauder
2023-08-22 09:51:10 -07:00
parent 321e3e9b88
commit c4d1ca270f
11 changed files with 359 additions and 0 deletions

View File

@@ -24,6 +24,7 @@
<module>caching</module>
<module>idgeneration</module>
<module>selectiveupdate</module>
<module>schema-generation</module>
</modules>
</project>

View File

@@ -0,0 +1 @@
cs-*.yaml

View File

@@ -0,0 +1,5 @@
== Spring Data JDBC How To Generate the Database Schema.
Spring Data JDBC offers an API to generate Liquibase Changesets from your domain model and optionally an existing database.
This project demonstrates how to do this.

View File

@@ -0,0 +1,33 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-jdbc-how-to-schema-generation</artifactId>
<parent>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-jdbc-how-to</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Spring Data JDBC - How to do schema generation</name>
<description>Sample project for Spring Data JDBC demonstrating how it can be used to generate and update your
schema.
</description>
<url>https://projects.spring.io/spring-data-jdbc</url>
<inceptionYear>2023</inceptionYear>
<properties>
<spring-data-bom.version>2023.1.0-M2</spring-data-bom.version>
</properties>
<dependencies>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2023 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 example.springdata.jdbc.howto.caching;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
/**
* Entity we use as a fixture for demonstrating Schema Generation.
* See SchemaGenerationTest in the test directory for the interesting stuff.
*
* @author Jens Schauder
* @since 3.2
*/
@Table
class Minion {
@Id
Long id;
@Name
String name;
Minion(String name) {
this.name = name;
}
public Long getId() {
return id;
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2023 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 example.springdata.jdbc.howto.caching;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Annotation to mark a property as a Name which should have a very specify database type.
*
* @author Jens Schauder
*/
@Varchar(20)
@Retention(RetentionPolicy.RUNTIME)
public @interface Name {
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2023 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 example.springdata.jdbc.howto.caching;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
/**
* Boot application that we use as a fixture for demonstrating Schema Generation.
* See SchemaGenerationTest in the test directory for the interesting stuff.
*
* @author Jens Schauder
* @since 3.2
*/
@EnableCaching
@SpringBootApplication
class SchemaGenerationExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SchemaGenerationExampleApplication.class, args);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2023 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 example.springdata.jdbc.howto.caching;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Marks a property as to be represented by a VARCHAR database type.
*
* @author Jens Schauder
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Varchar {
/**
* the size of the varchar.
*/
int value();
}

View File

@@ -0,0 +1,2 @@
# Disable Liquibase when you don't have changesets configured yet.
# spring.liquibase.enabled=false

View File

@@ -0,0 +1,32 @@
databaseChangeLog:
- changeSet:
id: '1692725820135'
author: Spring Data Relational
objectQuotingStrategy: LEGACY
changes:
- createTable:
columns:
- column:
autoIncrement: true
constraints:
nullable: true
primaryKey: true
name: id
type: BIGINT
- column:
constraints:
nullable: true
name: firstname
type: VARCHAR(255 BYTE)
- column:
constraints:
nullable: true
name: lastname
type: VARCHAR(255 BYTE)
- column:
constraints:
nullable: true
name: special
type: VARCHAR(255 BYTE)
tableName: minion

View File

@@ -0,0 +1,140 @@
/*
* Copyright 2023 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 example.springdata.jdbc.howto.caching;
import liquibase.database.Database;
import liquibase.database.core.HsqlDatabase;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.LiquibaseException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.FileSystemResource;
import org.springframework.data.jdbc.core.mapping.schema.DefaultSqlTypeMapping;
import org.springframework.data.jdbc.core.mapping.schema.LiquibaseChangeSetWriter;
import org.springframework.data.jdbc.core.mapping.schema.SqlTypeMapping;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Collections;
/**
* Example code demonstrating how to use the Schema Generation Feature.
*
* @author Jens Schauder
* @since 3.2
*/
@SpringBootTest
class SchemaGenerationTest {
@Autowired
RelationalMappingContext context;
@Autowired
DataSource ds;
@Test
void minimumExample() throws IOException {
// the change set will get appended, so we delete any pre existing file.
new File("cs-minimum.yaml").delete();
context.setInitialEntitySet(Collections.singleton(Minion.class));
LiquibaseChangeSetWriter writer = new LiquibaseChangeSetWriter(context);
writer.writeChangeSet(new FileSystemResource("cs-minimum.yaml"));
}
@Test
void diffing() {
// the change set will get appended, so we delete any pre existing file.
new File("cs-diff.yaml").delete();
context.setInitialEntitySet(Collections.singleton(Minion.class));
LiquibaseChangeSetWriter writer = new LiquibaseChangeSetWriter(context);
// drop unused columns
writer.setDropColumnFilter((table, column) -> !column.equalsIgnoreCase("special"));
// for comparison with existing schema
try (Database db = new HsqlDatabase()) {
db.setConnection(new JdbcConnection(ds.getConnection()));
writer.writeChangeSet(new FileSystemResource("cs-diff.yaml"), db);
} catch (IOException | SQLException | LiquibaseException e) {
throw new RuntimeException("Changeset generation failed", e);
}
}
@Test
void customizingTypes() throws IOException {
// the change set will get appended, so we delete any pre existing file.
new File("cs-custom.yaml").delete();
context.setInitialEntitySet(Collections.singleton(Minion.class));
LiquibaseChangeSetWriter writer = new LiquibaseChangeSetWriter(context);
writer.setSqlTypeMapping(((SqlTypeMapping) property -> {
if (property.getName().equalsIgnoreCase("name")) {
return "VARCHAR(500)";
}
return null;
}).and(new DefaultSqlTypeMapping()));
writer.writeChangeSet(new FileSystemResource("cs-custom.yaml"));
}
@Test
void customizingTypesUsingAnnotations() throws IOException {
// the change set will get appended, so we delete any pre existing file.
new File("cs-annotation.yaml").delete();
context.setInitialEntitySet(Collections.singleton(Minion.class));
LiquibaseChangeSetWriter writer = new LiquibaseChangeSetWriter(context);
writer.setSqlTypeMapping(((SqlTypeMapping) property -> {
if (!property.getType().equals(String.class)) {
return null;
}
// findAnnotation will find meta annotations
Varchar varchar = property.findAnnotation(Varchar.class);
int value = varchar.value();
if (varchar == null) {
return null;
}
return "VARCHAR(" +
varchar.value() +
")";
}).and(new DefaultSqlTypeMapping()));
writer.writeChangeSet(new FileSystemResource("cs-annotation.yaml"));
}
}