如何在 Spring 引导应用程序启动时启动 H2 TCP 服务器?

How to start H2 TCP server on Spring Boot application startup?

当 运行ning 应用程序作为 Spring 引导应用程序时,我可以通过将以下行添加到 SpringBootServletInitializer main 来启动 H2 TCP 服务器(文件中的数据库)方法:

@SpringBootApplication
public class NatiaApplication extends SpringBootServletInitializer {
    public static void main(String[] args) {
        Server.createTcpServer().start();
        SpringApplication.run(NatiaApplication.class, args);
    }
}

但是如果我 运行 Tomcat 上的 WAR 文件它不起作用,因为没有调用 main 方法。有没有更好的通用方法如何在 bean 初始化之前在应用程序启动时启动 H2 TCP 服务器?我使用 Flyway(自动配置),它在 "Connection refused: connect" 上失败,可能是因为服务器不是 运行ning。谢谢。

对于 WAR 包装,您可以这样做:

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        Server.createTcpServer().start();
        return new Class[] { NatiaApplication.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

是的,straight from the documentation,您可以使用 bean 引用:

<bean id = "org.h2.tools.Server"
        class="org.h2.tools.Server"
        factory-method="createTcpServer"
        init-method="start"
        destroy-method="stop">
<constructor-arg value="-tcp,-tcpAllowOthers,-tcpPort,8043" />

还有一个servlet listener option that auto-starts/stops it.

这回答了您的问题,但我认为如果它与您的 Spring 启动应用程序一起部署,您可能应该使用嵌入式模式。这是更快和更轻的资源。您只需指定正确的 URL,数据库就会启动:

jdbc:h2:/usr/share/myDbFolder

(straight out of the cheat sheet).

这个解决方案适合我。如果应用程序作为 Spring 启动应用程序运行并且在 Tomcat 上运行,它会启动 H2 服务器。将 H2 服务器创建为 bean 无效,因为 Flyway bean 较早创建并且在 "Connection refused".

上失败
@SpringBootApplication
@Log
public class NatiaApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        startH2Server();
        SpringApplication.run(NatiaApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        startH2Server();
        return application.sources(NatiaApplication.class);
    }

    private static void startH2Server() {
        try {
            Server h2Server = Server.createTcpServer().start();
            if (h2Server.isRunning(true)) {
                log.info("H2 server was started and is running.");
            } else {
                throw new RuntimeException("Could not start H2 server.");
            }
        } catch (SQLException e) {
            throw new RuntimeException("Failed to start H2 server: ", e);
        }
    }
}

你可以这样做:

@Configuration
public class H2ServerConfiguration {

  @Value("${db.port}")
  private String h2TcpPort;

  /**
   * TCP connection to connect with SQL clients to the embedded h2 database.
   *
   * @see Server
   * @throws SQLException if something went wrong during startup the server.
   * @return h2 db Server
   */
   @Bean
    public Server server() throws SQLException {
        return Server.createTcpServer("-tcp", "-tcpAllowOthers", "-tcpPort", h2TcpPort).start();
   }

   /**
    * @return FlywayMigrationStrategy the strategy for migration.
    */
    @Bean
    @DependsOn("server")
    public FlywayMigrationStrategy flywayMigrationStrategy() {
        return Flyway::migrate;
    }
}

其他答案中没有考虑到一个警告。您需要注意的是,启动服务器是对 DataSource bean 的暂时依赖。这是因为DataSource只需要网络连接,不需要bean关系。

这导致的问题是 spring-boot 将不知道在创建 DataSource 之前需要启动的 h2 数据库,因此您最终可能会在应用程序上出现连接异常启动。

使用 spring-framework 这不是问题,因为您将数据库服务器启动放在根配置中,并将数据库作为子数据库。使用 spring 引导 AFAIK 只有一个上下文。

要解决此问题,您可以做的是创建对数据源的 Optional<Server> 依赖项。 Optional 的原因是您可能并不总是启动您可能拥有生产数据库的服务器(配置参数)。

@Bean(destroyMethod = "close")
public DataSource dataSource(Optional<Server> h2Server) throws PropertyVetoException {
    HikariDataSource ds = new HikariDataSource();
    ds.setDriverClassName(env.getProperty("db.driver"));
    ds.setJdbcUrl(env.getProperty("db.url"));
    ds.setUsername(env.getProperty("db.user"));
    ds.setPassword(env.getProperty("db.pass"));
    return ds;
}