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

后端 未结 5 1071
青春惊慌失措
青春惊慌失措 2021-01-05 06:44

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:

相关标签:
5条回答
  • 2021-01-05 06:55

    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;
        }
    }
    
    0 讨论(0)
  • 2021-01-05 07:00

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-05 07:03

    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).

    0 讨论(0)
  • 2021-01-05 07:18

    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[] { "/" };
        }
    
    }
    
    0 讨论(0)
  • 2021-01-05 07:18

    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;
    }
    
    0 讨论(0)
提交回复
热议问题