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

时间:2016-05-06 09:26:19

标签: java spring tomcat spring-boot h2

通过在SpringBootServletInitializer主方法中添加以下行,将应用程序作为Spring Boot应用程序运行时,我能够启动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(autoconfig)并且它失败了#34;连接被拒绝:连接"可能是因为服务器没有运行。谢谢。

5 个答案:

答案 0 :(得分:6)

此解决方案适合我。如果应用程序作为Spring Boot应用程序运行,并且如果它在Tomcat上运行,它将启动H2服务器。创建H2服务器作为bean不起作用,因为Flyway bean是先前创建的并且在#34; 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);
        }
    }
}

答案 1 :(得分:2)

对于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[] { "/" };
    }

}

答案 2 :(得分:2)

是的,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 Boot应用程序一起部署,你应该使用嵌入式模式。这在资源上更快,更轻。您只需指定正确的URL,数据库就会启动:

jdbc:h2:/usr/share/myDbFolder

straight out of the cheat sheet)。

答案 3 :(得分:1)

你可以这样做:

@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;
    }
}

答案 4 :(得分:0)

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

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

使用spring-framework,这不是一个问题,因为您将数据库服务器启动放在root配置中,并将数据库作为子项。使用弹簧靴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;
}