Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validate schema programmatically using hibernate

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?

like image 261
Daniel Taub Avatar asked Jul 21 '26 15:07

Daniel Taub


1 Answers

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:

  1. 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;
    }
}
  1. 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`)
);

  1. 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

  1. 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
like image 73
Oleksii Zghurskyi Avatar answered Jul 23 '26 04:07

Oleksii Zghurskyi