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

半世苍凉 提交于 2019-11-30 17:00:30

问题


I'm able to start the H2 TCP server (database in a file) when running app as Spring Boot app by adding following line into the SpringBootServletInitializer main method:

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

But if I run the WAR file on Tomcat it doesn't work because the main method is not called. Is there a better universal way how to start the H2 TCP server on the application startup before beans get initialized? I use Flyway (autoconfig) and it fails on "Connection refused: connect" probably because the server is not running. Thank you.


回答1:


This solution works for me. It starts the H2 server if the app runs as Spring Boot app and also if it runs on Tomcat. Creating H2 server as a bean did not work because the Flyway bean was created earlier and failed on "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);
        }
    }
}



回答2:


Yup, straight from the documentation, you can use a bean reference:

<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" />

There's also a servlet listener option that auto-starts/stops it.

That answers your question, but I think you should probably be using the embedded mode instead if it's deploying along with your Spring Boot application. This is MUCH faster and lighter on resources. You simply specify the correct URL and the database will start:

jdbc:h2:/usr/share/myDbFolder

(straight out of the cheat sheet).




回答3:


For WAR packaging you can do this:

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

}



回答4:


You can do like this:

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



回答5:


There's a caveat that hasn't been considered in the other answers. What you need to be aware of is that starting a server is a transient dependency on your DataSource bean. This is due to the DataSource only needing a network connection, not a bean relationship.

The problem this causes is that spring-boot will not know about the h2 database needing to be fired up before creating the DataSource, so you could end up with a connection exception on application startup.

With the spring-framework this isn't a problem as you put the DB server startup in the root config with the database as a child. With spring boot AFAIK there's only a single context.

To get around this what you can do is create an Optional<Server> dependency on the data-source. The reason for Optional is you may not always start the server (configuration parameter) for which you may have a production DB.

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


来源:https://stackoverflow.com/questions/37068808/how-to-start-h2-tcp-server-on-spring-boot-application-startup

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!