Demonstrate Schema Generation in Spring Data Jdbc.
This provides the source examples for the upcoming blog post. Closes #667
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
<module>caching</module>
|
<module>caching</module>
|
||||||
<module>idgeneration</module>
|
<module>idgeneration</module>
|
||||||
<module>selectiveupdate</module>
|
<module>selectiveupdate</module>
|
||||||
|
<module>schema-generation</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
1
jdbc/howto/schema-generation/.gitignore
vendored
Normal file
1
jdbc/howto/schema-generation/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
cs-*.yaml
|
||||||
5
jdbc/howto/schema-generation/README.adoc
Normal file
5
jdbc/howto/schema-generation/README.adoc
Normal 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.
|
||||||
33
jdbc/howto/schema-generation/pom.xml
Normal file
33
jdbc/howto/schema-generation/pom.xml
Normal 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>
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Disable Liquibase when you don't have changesets configured yet.
|
||||||
|
# spring.liquibase.enabled=false
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -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"));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user