在DropWizard应用程序中初始化嵌入式数据库的正确位置在哪里?

时间:2016-02-13 23:09:02

标签: java guice derby dropwizard

我正在构建一个基于DropWizard的应用程序,它将具有嵌入式Derby数据库。

Dropwizard框架中的哪个位置是测试数据库是否存在以及是否存在的适当位置。

现在我正在DataSourceFactory模块提供的.yml文件中配置数据库,并且在dropwizard-db方法之前不可用调用。

我在这个应用程序中也使用Guice,因此也会接受涉及Guice的解决方案。

是否有更适合测试和创建数据库的地方?

1 个答案:

答案 0 :(得分:2)

如我所知,我将提供我的解决方案。背景故事,我正在使用guicey(https://github.com/xvik/dropwizard-guicey),在我看来,这是一个很棒的框架。我用它来与guice集成,但是我希望大多数实现都是类似的并且可以采用。除此之外,我还使用liquibase进行数据库检查和一致性。

首先,在初始化期间,我正在创建一个包,为我做验证。这个捆绑是一个guicey概念。它将在guice初始化期间自动运行。这个包看起来像这样:

/**
 * Verifying all changelog files separately before application startup.
 * 
 * Will log roll forward and roll back SQL if needed 
 * 
 * @author artur
 *
 */
public class DBChangelogVerifier extends ComparableGuiceyBundle {

    private static final String ID = "BUNDLEID";

    private static final Logger log = Logger.getLogger(DBChangelogVerifier.class);

    private List<LiquibaseConfiguration> configs = new ArrayList<>();

    public void addConfig(LiquibaseConfiguration configuration) {
        this.configs.add(configuration);
    }


    /**
     * Attempts to verify all changelog definitions with the provided datasource
     * @param ds
     */
    public void verify(DataSource ds) {
        boolean throwException = false;
        Contexts contexts = new Contexts("");
        for(LiquibaseConfiguration c : configs) {
            try(Connection con = ds.getConnection()) {
                    Database db = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(con));
                    db.setDatabaseChangeLogLockTableName(c.changeLogLockTableName());
                    db.setDatabaseChangeLogTableName(c.changeLogTableName());
                    Liquibase liquibase = new ShureviewNonCreationLiquibase(c.liquibaseResource(), new ClassLoaderResourceAccessor(), db);
                    liquibase.getLog();
                    liquibase.validate();
                    List<ChangeSet> listUnrunChangeSets = liquibase.listUnrunChangeSets(contexts, new LabelExpression());

                    if(!listUnrunChangeSets.isEmpty()) {
                        StringWriter writer = new StringWriter();
                        liquibase.update(contexts, writer);
                        liquibase.futureRollbackSQL(writer);
                        log.warn(writer.toString());
                        throwException = true;
                    }
            } catch (SQLException | LiquibaseException e) {
                throw new RuntimeException("Failed to verify database.", e);
            }
        }

        if(throwException){
            throw new RuntimeException("Unrun changesets in chengelog.");
        }
    }

    /**
     * Using init to process and validate to avoid starting the application in case of errors. 
     */
    @Override
    public void initialize(GuiceyBootstrap bootstrap) {
        Configuration configuration = bootstrap.configuration();
        if(configuration instanceof DatasourceConfiguration ) {
            DatasourceConfiguration dsConf = (DatasourceConfiguration) configuration;
            ManagedDataSource ds = dsConf.getDatasourceFactory().build(bootstrap.environment().metrics(), "MyDataSource");
            verify(ds);
        }
    }

    @Override
    public String getId() {
        return ID;
    }

}

请注意,ComparableGuiceBundle是我添加的界面,因此我可以在捆绑包及其init函数中订购。

此捆绑包将由guicey自动初始化,并且将调用init方法,为我提供数据源。在init(同一个线程)我调用验证。这意味着,如果验证失败,我的应用程序启动失败,它将拒绝完成启动。

在我的启动代码中,我只是将此捆绑包添加到Guicey配置中,以便Guice可以了解它:

// add all bundles to the bundles variable including the Liquibase bundle. 
// registers guice with dropwizard
        bootstrap.addBundle(GuiceBundle.<EngineConfigurationImpl>builder()
                .enableAutoConfig("my.package")
                .searchCommands(true)    
                .bundles(bundles.toArray( new GuiceyBundle[0]))
                .modules(getConfigurationModule(), new CoreModule())
                                   .build()
        );

这就是我需要做的一切。 Guicey负责其余的工作。在应用程序启动期间,它将初始化传递给它的所有包。由于它具有可比性,验证我的数据库的包是第一个,将首先执行。只有当该捆绑包成功启动时,其他捆绑包才会启动。

对于liquibase部分:

public void verify(DataSource ds) {
        boolean throwException = false;
        Contexts contexts = new Contexts("");
        for(LiquibaseConfiguration c : configs) {
            try(Connection con = ds.getConnection()) {
                    Database db = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(con));
                    db.setDatabaseChangeLogLockTableName(c.changeLogLockTableName());
                    db.setDatabaseChangeLogTableName(c.changeLogTableName());
                    Liquibase liquibase = new ShureviewNonCreationLiquibase(c.liquibaseResource(), new ClassLoaderResourceAccessor(), db);
                    liquibase.getLog();
                    liquibase.validate();
                    List<ChangeSet> listUnrunChangeSets = liquibase.listUnrunChangeSets(contexts, new LabelExpression());

                    if(!listUnrunChangeSets.isEmpty()) {
                        StringWriter writer = new StringWriter();
                        liquibase.update(contexts, writer);
                        liquibase.futureRollbackSQL(writer);
                        log.warn(writer.toString());
                        throwException = true;
                    }
            } catch (SQLException | LiquibaseException e) {
                throw new RuntimeException("Failed to verify database.", e);
            }
        }

        if(throwException){
            throw new RuntimeException("Unrun changesets in chengelog.");
        }
    }

从我的设置中可以看出,我可以有多个可以检查的更改日志配置。在我的启动代码中,我查找它们并将它们添加到捆绑包中。

Liquibase将为您选择正确的数据库。如果没有可用的数据库,则会出错。如果连接未启动,则会出错。

如果找到unran changesets,它将打印出前滚和回滚SQL。如果md5sum不正确,它将打印出来。在任何情况下,如果数据库与变更集不一致,它将拒绝启动。

现在在我的情况下,我不希望liquibase创建任何东西。这是一个纯粹的验证过程。但是,liquibase确实为您提供了运行所有更改集,创建表等的选项。您可以在文档中阅读它。这是相当直接的。

这种方法几乎将liquibase与正常启动集成在一起,而不是使用带有dropwizard的数据库命令来手动执行它们。

我希望有帮助,如果您有任何疑问,请与我联系。

阿图尔