使用休眠模式以编程方式验证架构

时间:2019-01-31 16:24:27

标签: java spring hibernate jpa

在mose项目中,通过架构验证运行java app的方式就是使用该配置(使用spring时):

spring.jpa.hibernate.ddl-auto=validate

我遇到了一个问题,我需要在运行过程中的特定时间验证我的模式,有什么方法可以实现它?

我看到休眠状态是通过AbstractSchemaValidator进行管理的, 我使用的是Spring和Hibernate,但没有找到任何信息来处理它,
我发现的唯一内容是How to validate database schema programmatically in hibernate with annotations? ,但在{{1 }}

spring-boot

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

这是解决方案,如果您的用例需要:

  • 的模式的哪个部分的粒状&显式控制应 已验证
  • 需要验证多个模式
  • 需要验证运行计划的验证程序的服务未使用的架构
  • 应用程序使用的数据库连接不应以任何方式受到验证的影响(这意味着您不想从主连接池借用连接)

如果以上满足您的需求,那么这是如何执行计划的模式验证的示例

  1. 来源
@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

答案 1 :(得分:2)

如果要让1280重用项目中已经配置的连接配置和映射信息,而不是再次定义它们以进行模式验证,则应考虑我的解决方案,使您处于DRY状态无需在两个不同的地方维护这些配置。

实际上,SchemaValidator所需要的是SchemaValidator实例,该实例仅在引导Hibernate期间可用。但是我们可以使用Hibernate Integrator API(如here中所述)捕获它,以便稍后进行验证。

(1)创建实现Hibernate Integrator API的Metadata来捕获SchemaValidateService。还要设置一个Metadata方法来在所需的时间验证架构。

@Scheduled

(2)注册@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...."); } } 进入休眠状态

SchemaValidateService

此外,此解决方案应具有更好的性能,因为它无需每次都创建新的数据库连接来验证架构,因为它只需从现有连接池中获取连接即可。