Validate schema programmatically using hibernate - java

In mose projects the way to run your java app with schema validation is with that configuration (when using spring):
spring.jpa.hibernate.ddl-auto=validate
I ran into a problem that I need to validate my schema at a specific times during running, is there any way to implement that?
I saw that hibernate managed it with the AbstractSchemaValidator,
I'm using spring with hibernate, and I didn't found any information how to deal with it, the only thing I found is How to validate database schema programmatically in hibernate with annotations?
, but it was removed in the older versions of spring-boot
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
any ideas?

This is solution, if your use case requires:
granular & explicit control of which part of the schema should be
validated
the need is to validate multiple schemas
the need is to validate schema that is not used by the service, on which scheduled validator is running
db connections used by application should not be influenced by validation in any way (meaning, you don't want to borrow connection from main connections pool)
If above applies for your needs, than this is example of how to do scheduled schema validation:
Sources
#SpringBootApplication
#EnableScheduling
#EnableConfigurationProperties(ScheamValidatorProperties.class)
public class SchemaValidatorApplication {
public static void main(String[] args) {
SpringApplication.run(SchemaValidatorApplication.class, args);
}
}
#ConfigurationProperties("schema-validator")
class ScheamValidatorProperties {
public Map<String, String> settings = new HashMap<>();
public ScheamValidatorProperties() {
}
public Map<String, String> getSettings() {
return this.settings;
}
public void setSome(Map<String, String> settings) {
this.settings = settings;
}
}
#Component
class ScheduledSchemaValidator {
private ScheamValidatorProperties props;
public ScheduledSchemaValidator(ScheamValidatorProperties props) {
this.props = props;
}
#Scheduled(cron = "0 0/1 * * * ?")
public void validateSchema() {
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(props.getSettings())
.build();
Metadata metadata = new MetadataSources(serviceRegistry)
.addAnnotatedClass(Entity1.class)
.addAnnotatedClass(Entity2.class)
.buildMetadata();
try {
new SchemaValidator().validate(metadata, serviceRegistry);
} catch (Exception e) {
System.out.println("Validation failed: " + e.getMessage());
} finally {
StandardServiceRegistryBuilder.destroy(serviceRegistry);
}
}
}
#Entity
#Table(name = "table1")
class Entity1 {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Entity1() {}
public Long getId() {
return id;
}
}
#Entity
#Table(name = "table2")
class Entity2 {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Entity2() {}
public Long getId() {
return id;
}
}
schema.sql
CREATE DATABASE IF NOT EXISTS testdb;
CREATE TABLE IF NOT EXISTS `table1` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `table2` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
);
application.yml
spring:
cache:
type: none
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3309/testdb?useSSL=false&nullNamePatternMatchesAll=true&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: test_user
password: test_password
testWhileIdle: true
validationQuery: SELECT 1
jpa:
show-sql: false
database-platform: org.hibernate.dialect.MySQL8Dialect
hibernate:
ddl-auto: none
naming:
physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
properties:
hibernate.dialect: org.hibernate.dialect.MySQL8Dialect
hibernate.cache.use_second_level_cache: false
hibernate.cache.use_query_cache: false
hibernate.generate_statistics: false
hibernate.hbm2ddl.auto: validate
schema-validator:
settings:
connection.driver_class: com.mysql.cj.jdbc.Driver
hibernate.dialect: org.hibernate.dialect.MySQL8Dialect
hibernate.connection.url: jdbc:mysql://localhost:3309/testdb?autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
hibernate.connection.username: test_user
hibernate.connection.password: test_password
hibernate.default_schema: testdb
docker-compose.yml
version: '3.0'
services:
db:
image: mysql:8.0.14
restart: always
ports:
- 3309:3306
environment:
MYSQL_ROOT_PASSWORD: test_password
MYSQL_DATABASE: testdb
MYSQL_USER: test_user
MYSQL_PASSWORD: test_password

If you want to let the SchemaValidator to reuse the connection configuration and the mapping information that are already configured in the project rather then defining them once again for schema validation, you should consider my solution such that you are DRY and don't need to maintain these configurations in two separate places.
Actually , what SchemaValidator requires is the Metadata instance which is only available during bootstrapping Hibernate . But we can use Hibernate Integrator API (as described in here) to capture it such that we can validate them later.
(1) Create SchemaValidateService which implements Hibernate Integrator API to capture Metadata. Also setup a #Scheduled method to validate the schema at desired time.
#Component
public class SchemaValidateService implements Integrator {
private Metadata metadata;
#Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
this.metadata = metadata;
}
#Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
}
//Adjust the scheduled time here
#Scheduled(cron = "0 0/1 * * * ?")
public void validate() {
try {
System.out.println("Start validating schema");
new SchemaValidator().validate(metadata);
} catch (Exception e) {
//log the validation error here.
}
System.out.println("Finish validating schema....");
}
}
(2) Register SchemaValidateService to Hibernate
#SpringBootApplication
#EnableScheduling
public class App {
#Bean
public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(SchemaValidateService schemaValidateService) {
return (prop -> {
List<Integrator> integrators = new ArrayList<>();
integrators.add(schemaValidateService);
prop.put("hibernate.integrator_provider", (IntegratorProvider) () -> integrators);
});
}
}
Also, this solution should has better performance as it does not need to create a new database connection for validating schema each time as it can just grab the connection from the existing connection pool.

Related

Spring JPA - Repository Query is returning 0 rows

I have this repository class:
public interface ShiftRepository extends CrudRepository<Shift, Long> {
#Query("SELECT s FROM Shift s")
public List<Shift> getAllShifts();
}
All I want it to do is grab all shifts.
The Service calling it is just doing this:
public List<Shift> getAllShifts() {
return shiftRepository.getAllShifts();
}
And the Controller is just doing this:
public ResponseEntity<List<Shift>> getAllShifts() {
List<Shift> shifts;
try {
shifts = shiftService.getAllShifts();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ArrayList<>());
}
if (shifts != null) {
System.out.println(shifts.size());
return ResponseEntity.status(HttpStatus.OK).body(shifts);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ArrayList<>());
}
}
On the frontend I am getting this response.
Empty array of shifts
succesful 200 response
So I don't know what the issue is, I just want all the records. Using Spring boot and Heroku Postgres
application.yml:
spring:
jpa:
properties:
hibernate.default_schema: dev_env
hibernate:
ddl-auto: create-drop
database-platform: org.hibernate.dialect.PostgreSQLDialect
datasource:
hikari:
schema: dev_env
driverClassName: org.postgresql.Driver
type: org.apache.tomcat.jdbc.pool.DataSource
Your problem seems like in that Query annotation. You have to define value tag before your query. As:
#Query(value = "SELECT s FROM Shift s")
Sorry for wasting time. It's late was very enthralled in Spring boot and React debugging my application. Did not realize I had not populated database with test data XD

org.h2.jdbc.JdbcSQLException: Table not found

I'm getting this exception:
org.h2.jdbc.JdbcSQLException:
Table "CUSTOMERS" not found; SQL statement:
SELECT * FROM CUSTOMERS
This is the H2 Console. I have created a table there:
I have the application.yml file. I have tried to add DB_CLOSE_DELAY=-1 and DATABASE_TO_UPPER=false as well:
spring:
database:
url: jdbc:h2:mem:testdb
h2:
console.enabled: true
Also, I have a configuration class, where I have created the H2 Embedded Database:
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
Finally, the query. The table is named CUSTOMERS:
public List<Customer> getAll() {
return jdbcTemplate.query("SELECT * FROM CUSTOMERS", (resultSet, rowNum) -> {
Customer customer = new Customer();
customer.setId(resultSet.getLong("id"));
customer.setName(resultSet.getString("name"));
customer.setAge(resultSet.getInt("age"));
return customer;
});
}
What should I do?
I had the same concern as you for a few days.
I solved it by adding this:
;TRACE_LEVEL_FILE=3;TRACE_LEVEL_SYSTEM_OUT=3
ie : jdbc:h2:mem:testdb;TRACE_LEVEL_FILE=3;TRACE_LEVEL_SYSTEM_OUT=3
It helps to know why H2 has a problem.
Usually it is a keyword problem.
You can ignore it by using NON_KEYWORDS : https://www.h2database.com/html/commands.html#set_non_keywords

Multiple databases in dropwizard

I am trying to read and write into two databases and I am running into issues:
config.yml
database1:
driverClass: com.mysql.jdbc.Driver
user: user1
password: user!23
url: jdbc:mysql://url.to.connect:3306/db1
properties: charSet: UTF-8
maxWaitForConnection: 1s
minSize: 8
maxSize: 32
checkConnectionWhileIdle: false
checkConnectionHealthWhenIdleFor: 10s
closeConnectionIfIdleFor: 1 minute
database2:
driverClass: com.mysql.jdbc.Driver
user: user2
password: user!23
url: jdbc:mysql://url.to.connect:3306/db2
properties: charSet: UTF-8
maxWaitForConnection: 1s
minSize: 8
maxSize: 32
checkConnectionWhileIdle: false
checkConnectionHealthWhenIdleFor: 10s
closeConnectionIfIdleFor: 1 minute
configuration.java
public class ExampleConfiguration extends Configuration {
#Valid
#NotNull
private DataSourceFactory database1 = new DataSourceFactory();
#Valid
#NotNull
private DataSourceFactory database2 = new DataSourceFactory();
#JsonProperty("database1")
public DataSourceFactory getDb1DataSourceFactory() {
return database1;
}
#JsonProperty("database2")
public DataSourceFactory getDb2DataSourceFactory() {
return database2;
}
}
Now following: Maintain multiple migration files in parallel in dropwizard, I tried to add a bootstrap bundle as follows:
#Override
public void initialize(Bootstrap< ExampleConfiguration > bootstrap) {
bootstrap.addCommand(new RenderCommand());
bootstrap.addBundle(new AssetsBundle());
bootstrap.addBundle(new MigrationsBundle<ExampleConfiguration>() {
#Override
public DataSourceFactory getDataSourceFactory(ExampleConfiguration configuration) {
return configuration.getDb1DataSourceFactory();
}
});
bootstrap.addBundle(new MigrationsBundle<ExampleConfiguration>() {
#Override
public DataSourceFactory getDataSourceFactory(ExampleConfiguration configuration) {
return configuration.getDb2DataSourceFactory();
}
});
bootstrap.addBundle(hibernateBundle);
}
But I got command 'db' has been already used. I am still unclear what is happening in initialize(). How can I switch between DB instances for read and write operations?
Thank you.
You should override each MigrationsBundle implementation's method name to provide unique command's name. Then using CLI you will need to specify correct name to use according database configuration.

SpringBoot: Configuring Spring DataSource for Tests

I have a SpringBoot app.
I have created this test:
#ContextConfiguration(classes={TestConfig.class})
#RunWith(SpringRunner.class)
#SpringBootTest
public class SuncionServiceITTest {
#Test
public void should_Find_2() {
// TODO
}
}
where
#Configuration
#EnableJpaRepositories(basePackages = "com.plats.bruts.repository")
#PropertySource("local-configuration.properties")
#EnableTransactionManagement
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class TestConfig {
}
and local configuration.properties:
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
but when I run the test. I got this error:
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'entityManagerFactory' available
I also tried with:
#EnableJpaRepositories(basePackages = "com.plats.bruts.repository", entityManagerFactoryRef="emf")
but then I have the error:
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'emf' available
Looks like you are missing below starter dependency. This starter dependency has all the necessary dependencies needed to configure the jpa repositories.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
This is an approach of how to configure several data sources in one application. I have tested it for spring-webmvc and graphql-java, but I think it can be useful for spring-boot as well.
Configure several data sources with spring-data-jpa
For each database we should enable JPA repositories and specify the base packages for the corresponding interfaces. Also for each database we should specify the entity manager factory and the base packages for corresponding entities, as well as the transaction manager and the data source, of course.
To do this, we'll include in our application one configuration class for data JPA and three inner classes for each database. See the Simple GraphQL implementation.
DataJpaConfig.java
package org.drakonoved.graphql;
#Configuration
#PropertySource(value = "classpath:resources/application.properties", encoding = "UTF-8")
public class DataJpaConfig {
private final String basePackage = "org.drakonoved.graphql";
#EnableJpaRepositories(
basePackages = basePackage + ".repository.usersdb",
entityManagerFactoryRef = "usersdbEntityManagerFactory",
transactionManagerRef = "usersdbTransactionManager")
public class UsersDBJpaConfig {
#Bean("usersdbEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean usersDBEntityManagerFactoryBean(
#Value("${datasource.usersdb.script}") String script) {
return createEntityManagerFactoryBean(
script, "usersdb", basePackage + ".dto.usersdb");
}
#Bean("usersdbTransactionManager")
public PlatformTransactionManager usersDBTransactionManager(
#Qualifier("usersdbEntityManagerFactory")
LocalContainerEntityManagerFactoryBean factoryBean) {
return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
}
}
#EnableJpaRepositories(
basePackages = basePackage + ".repository.rolesdb",
entityManagerFactoryRef = "rolesdbEntityManagerFactory",
transactionManagerRef = "rolesdbTransactionManager")
public class RolesDBJpaConfig {
#Bean("rolesdbEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean rolesDBEntityManagerFactoryBean(
#Value("${datasource.rolesdb.script}") String script) {
return createEntityManagerFactoryBean(
script, "rolesdb", basePackage + ".dto.rolesdb");
}
#Bean("rolesdbTransactionManager")
public PlatformTransactionManager rolesDBTransactionManager(
#Qualifier("rolesdbEntityManagerFactory")
LocalContainerEntityManagerFactoryBean factoryBean) {
return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
}
}
#EnableJpaRepositories(
basePackages = basePackage + ".repository.usersnrolesdb",
entityManagerFactoryRef = "usersnrolesdbEntityManagerFactory",
transactionManagerRef = "usersnrolesdbTransactionManager")
public class UsersNRolesDBJpaConfig {
#Bean("usersnrolesdbEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean usersNRolesDBEntityManagerFactoryBean(
#Value("${datasource.usersnrolesdb.script}") String script) {
return createEntityManagerFactoryBean(
script, "usersnrolesdb", basePackage + ".dto.usersnrolesdb");
}
#Bean("usersnrolesdbTransactionManager")
public PlatformTransactionManager usersNRolesDBTransactionManager(
#Qualifier("usersnrolesdbEntityManagerFactory")
LocalContainerEntityManagerFactoryBean factoryBean) {
return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
}
}
//////// //////// //////// //////// //////// //////// //////// ////////
private LocalContainerEntityManagerFactoryBean createEntityManagerFactoryBean(
String script, String dbname, String packagesToScan) {
var factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.setName(dbname)
.addScript(script)
.build());
factoryBean.setPersistenceUnitName(dbname + "EntityManagerFactory");
factoryBean.setPackagesToScan(packagesToScan);
factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
var properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", "none");
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
factoryBean.setJpaPropertyMap(properties);
return factoryBean;
}
}
I prefer to use following approach (i don't like to create own bean configurator). As #svr correctly noticed (see previous answer) you don't add starter package for beans auto configure.
I usually create different profiles: for local app running, for dev, prod and finally last one for tests. In my test profile (application-functests.yml) i configure all settings that needs for my functional tests (datasources, hibernate, cache and so on), i.e. my application-functests.yml of one of my projects:
spring:
http:
encoding:
charset: UTF-8
enabled: true
profiles: functests
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
enable_lazy_load_no_trans: true
naming:
physical-strategy: com.goodt.drive.orgstructure.application.utils.SnakePhysicalNamingStrategy
hibernate:
ddl-auto: none
database-platform: org.hibernate.dialect.PostgreSQL9Dialect
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/monitor_service_functests
username: developer
password: 123
sql-script-encoding: UTF-8
liquibase:
change-log: classpath:db/changelog/changelog.xml
I have only specify profile for running test, therefore all of my functional tests are using functests profile, i.e.:
#SpringBootTest
#ActiveProfiles("functests")
public class TestEventRepository extends FunctionalTestBase {
#Test
public void testGetAll() {
Iterable<EventEntity> eventIterable = dbContext.getEventDataSource().findAll();
Iterator<EventEntity> it = eventIterable.iterator();
List<EventEntity> actualEvents = new ArrayList<>();
while (it.hasNext()) {
actualEvents.add(it.next());
}
List<EventCheckData> expectedEvents = new ArrayList<>() {{
add(new EventCheckData(1L, 1L, "body 1", 1L, 1L));
add(new EventCheckData(2L, 2L, "body 2", 2L, 2L));
add(new EventCheckData(3L, 3L, "body 3", 3L, 1L));
add(new EventCheckData(4L, 1L, "body 4", 2L, 1L));
add(new EventCheckData(5L, 2L, "body 5", 1L, 2L));
}};
EventSimpleChecker.check(expectedEvents, actualEvents);
}
}
In my code example i have base test class - FunctionalTestBase which is used for interact with liquibase (toggle it to apply migrations)

Can spring data r2dbc generate a schema?

I am creating a quick project using R2DBC and H2 to familiarize myself with this new reactive stuff. Made a repository that extends ReactiveCrudRepository and all is well with the world, as long as i use the DatabaseClient to issue a CREATE TABLE statement that matches my entity first...
I understand spring data R2DBC is not as fully featured as spring data JPA (yet?) but is there currently a way to generate the schema from the entity classes?
Thanks
No, there is currently no way to generate schema from entities with Spring Data R2DBC.
I'm using it in a project with Postgres DB and it's complicated to manage database migrations, but I managed to wire in Flyway with synchronous Postgre driver (Flyway doesn't work with reactive drivers yet) at startup to handle schema migrations.
Even though you still have to write your own CREATE TABLE statements which shouldn't be that hard and you could even modify your entities in some simple project to create JPA entities and let Hibernate create schema then copy-paste it into a migration file in your R2DBC project.
It is possible for tests and for production.
I production make sure your user has no access to change schema otherwise you may delete tables by mistake!!! or use a migration tool like flyway.
You need to put your schema.sql in the main resources and add the relevant properties
spring.r2dbc.initialization-mode=always
h2 for test and postgres for prod
I use gradle and the versions of driver are:
implementation 'org.springframework.boot.experimental:spring-boot-actuator-autoconfigure-r2dbc'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'io.r2dbc:r2dbc-h2'
runtimeOnly 'io.r2dbc:r2dbc-postgresql'
runtimeOnly 'org.postgresql:postgresql'
testImplementation 'org.springframework.boot.experimental:spring-boot-test-autoconfigure-r2dbc'
The BOM version is
dependencyManagement {
imports {
mavenBom 'org.springframework.boot.experimental:spring-boot-bom-r2dbc:0.1.0.M3'
}
}
That's how I solved this problem:
Controller:
#PostMapping(MAP + PATH_DDL_PROC_DB) //PATH_DDL_PROC_DB = "/database/{db}/{schema}/{table}"
public Flux<Object> createDbByDb(
#PathVariable("db") String db,
#PathVariable("schema") String schema,
#PathVariable("table") String table) {
return ddlProcService.createDbByDb(db,schema,table);
Service:
public Flux<Object> createDbByDb(String db,String schema,String table) {
return ddl.createDbByDb(db,schema,table);
}
Repository:
#Autowired
PostgresqlConnectionConfiguration.Builder connConfig;
public Flux<Object> createDbByDb(String db,String schema,String table) {
return createDb(db).thenMany(
Mono.from(connFactory(connConfig.database(db)).create())
.flatMapMany(
connection ->
Flux.from(connection
.createBatch()
.add(sqlCreateSchema(db))
.add(sqlCreateTable(db,table))
.add(sqlPopulateTable(db,table))
.execute()
)));
}
private Mono<Void> createDb(String db) {
PostgresqlConnectionFactory
connectionFactory = connFactory(connConfig);
DatabaseClient ddl = DatabaseClient.create(connectionFactory);
return ddl
.execute(sqlCreateDb(db))
.then();
}
Connection Class:
#Slf4j
#Configuration
#EnableR2dbcRepositories
public class Connection extends AbstractR2dbcConfiguration {
/*
**********************************************
* Spring Data JDBC:
* DDL: does not support JPA.
*
* R2DBC
* DDL:
* -does no support JPA
* -To achieve DDL, uses R2dbc.DataBaseClient
*
* DML:
* -it uses R2dbcREpositories
* -R2dbcRepositories is different than
* R2dbc.DataBaseClient
* ********************************************
*/
#Bean
public PostgresqlConnectionConfiguration.Builder connectionConfig() {
return PostgresqlConnectionConfiguration
.builder()
.host("db-r2dbc")
.port(5432)
.username("root")
.password("root");
}
#Bean
public PostgresqlConnectionFactory connectionFactory() {
return
new PostgresqlConnectionFactory(
connectionConfig().build()
);
}
}
DDL Scripts:
#Getter
#NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class DDLScripts {
public static final String SQL_GET_TASK = "select * from tasks";
public static String sqlCreateDb(String db) {
String sql = "create database %1$s;";
String[] sql1OrderedParams = quotify(new String[]{db});
String finalSql = format(sql,(Object[]) sql1OrderedParams);
return finalSql;
}
public static String sqlCreateSchema(String schema) {
String sql = "create schema if not exists %1$s;";
String[] sql1OrderedParams = quotify(new String[]{schema});
return format(sql,(Object[]) sql1OrderedParams);
}
public static String sqlCreateTable(String schema,String table) {
String sql1 = "create table %1$s.%2$s " +
"(id serial not null constraint tasks_pk primary key, " +
"lastname varchar not null); ";
String[] sql1OrderedParams = quotify(new String[]{schema,table});
String sql1Final = format(sql1,(Object[]) sql1OrderedParams);
String sql2 = "alter table %1$s.%2$s owner to root; ";
String[] sql2OrderedParams = quotify(new String[]{schema,table});
String sql2Final = format(sql2,(Object[]) sql2OrderedParams);
return sql1Final + sql2Final;
}
public static String sqlPopulateTable(String schema,String table) {
String sql = "insert into %1$s.%2$s values (1, 'schema-table-%3$s');";
String[] sql1OrderedParams = quotify(new String[]{schema,table,schema});
return format(sql,(Object[]) sql1OrderedParams);
}
private static String[] quotify(String[] stringArray) {
String[] returnArray = new String[stringArray.length];
for (int i = 0; i < stringArray.length; i++) {
returnArray[i] = "\"" + stringArray[i] + "\"";
}
return returnArray;
}
}
It is actually possible to load a schema by defining a specific class in this way:
import io.r2dbc.spi.ConnectionFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.io.ClassPathResource
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories
import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer
import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator
#Configuration
#EnableR2dbcRepositories
class DbConfig {
#Bean
fun initializer(connectionFactory: ConnectionFactory): ConnectionFactoryInitializer {
val initializer = ConnectionFactoryInitializer()
initializer.setConnectionFactory(connectionFactory)
initializer.setDatabasePopulator(
ResourceDatabasePopulator(
ClassPathResource("schema.sql")
)
)
return initializer
}
}
Pay attention that IntelliJ gives an error "Could not autowire. No beans of 'ConnectionFactory' type found" but it is actually a false positive. So ignore it and build again your project.
The schema.sql file has to be put in resources folder.

Categories