spring - 如何使用 Spring Boot 在 H2 控制台中设置默认 JDBC URL 值

标签 spring spring-boot h2

我已在 Spring Boot 中启用 H2 控制台。但是,当我打开控制台连接页面时,默认 URL 是 H2 控制台历史记录中保存的 URL。如何配置项目以在项目启动时填充与 spring.datasource.url 相同的 URL?目前我在控制台中手动设置 url,但我希望项目本身自动设置它。

yaml:

spring:
  h2:
    console:
      enabled: true
      path: /admin/h2


  datasource:
    url: jdbc:h2:mem:foobar

更新: 我知道最后的连接设置保存到 ~/.h2.server.properties 但我需要的是从启动应用程序潜在地设置属性,可能在其中几个之间切换

最佳答案

没有提供用于填写设置的钩子(Hook)。

好消息是我们可以通过一些代码来改变它。

当前状态

登录屏幕创建于 WebApp.index()

String[] settingNames = server.getSettingNames();
String setting = attributes.getProperty("setting");
if (setting == null && settingNames.length > 0) {
    setting = settingNames[0];
}
String combobox = getComboBox(settingNames, setting);
session.put("settingsList", combobox);
ConnectionInfo info = server.getSetting(setting);
if (info == null) {
    info = new ConnectionInfo();
}
session.put("setting", PageParser.escapeHtmlData(setting));
session.put("name", PageParser.escapeHtmlData(setting));
session.put("driver", PageParser.escapeHtmlData(info.driver));
session.put("url", PageParser.escapeHtmlData(info.url));
session.put("user", PageParser.escapeHtmlData(info.user));
return "index.jsp";

我们想要利用server.getSettingNames() ,并精确地输入server.getSettings()下面使用。

synchronized ArrayList<ConnectionInfo> getSettings() {
    ArrayList<ConnectionInfo> settings = new ArrayList<>();
    if (connInfoMap.size() == 0) {
        Properties prop = loadProperties();
        if (prop.size() == 0) {
            for (String gen : GENERIC) {
                ConnectionInfo info = new ConnectionInfo(gen);
                settings.add(info);
                updateSetting(info);
            }
        } else {
            for (int i = 0;; i++) {
                String data = prop.getProperty(Integer.toString(i));
                if (data == null) {
                    break;
                }
                ConnectionInfo info = new ConnectionInfo(data);
                settings.add(info);
                updateSetting(info);
            }
        }
    } else {
        settings.addAll(connInfoMap.values());
    }
    Collections.sort(settings);
    return settings;
}

计划

  • 禁用ServletRegistrationBean<WebServlet>创建者 H2ConsoleAutoConfiguration
  • 将其替换为我们的子类为 WebServlet 的配置类
  • 我们的CustomH2WebServlet将覆盖 init并注册CustomH2WebServer (WebServer 的子类)
  • CustomH2WebServer我们覆盖getSettings()我们就完成了

代码

@EnableConfigurationProperties({H2ConsoleProperties.class, DataSourceProperties.class})
@Configuration
public class H2Config {

    private final H2ConsoleProperties h2ConsoleProperties;

    private final DataSourceProperties dataSourceProperties;

    public H2Config(H2ConsoleProperties h2ConsoleProperties, DataSourceProperties dataSourceProperties) {
        this.h2ConsoleProperties = h2ConsoleProperties;
        this.dataSourceProperties = dataSourceProperties;
    }

    @Bean
    public ServletRegistrationBean<WebServlet> h2Console() {
        String path = this.h2ConsoleProperties.getPath();
        String urlMapping = path + (path.endsWith("/") ? "*" : "/*");
        ServletRegistrationBean<WebServlet> registration = new ServletRegistrationBean<>(
                new CustomH2WebServlet(this.dataSourceProperties.getUrl()), urlMapping);
        H2ConsoleProperties.Settings settings = this.h2ConsoleProperties.getSettings();
        if (settings.isTrace()) {
            registration.addInitParameter("trace", "");
        }
        if (settings.isWebAllowOthers()) {
            registration.addInitParameter("webAllowOthers", "");
        }
        return registration;
    }
}

package org.h2.server.web;

import javax.servlet.ServletConfig;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Enumeration;

public class CustomH2WebServlet extends WebServlet {

    private final String dbUrl;

    public CustomH2WebServlet(String dbUrl) {
        this.dbUrl = dbUrl;
    }

    @Override
    public void init() {
        ServletConfig config = getServletConfig();
        Enumeration<?> en = config.getInitParameterNames();
        ArrayList<String> list = new ArrayList<>();
        while (en.hasMoreElements()) {
            String name = en.nextElement().toString();
            String value = config.getInitParameter(name);
            if (!name.startsWith("-")) {
                name = "-" + name;
            }
            list.add(name);
            if (value.length() > 0) {
                list.add(value);
            }
        }
        String[] args = list.toArray(new String[0]);
        WebServer server = new CustomH2WebServer(dbUrl);
        server.setAllowChunked(false);
        server.init(args);
        setServerWithReflection(this, server);
    }

    private static void setServerWithReflection(final WebServlet classInstance, final WebServer newValue) {
        try {
            final Field field = WebServlet.class.getDeclaredField("server");
            field.setAccessible(true);
            field.set(classInstance, newValue);
        }
        catch (SecurityException|NoSuchFieldException|IllegalArgumentException|IllegalAccessException ex) {
            throw new RuntimeException(ex);
        }
    }
}
package org.h2.server.web;

import java.util.ArrayList;
import java.util.Collections;

class CustomH2WebServer extends WebServer {

    private final String connectionInfo;

    CustomH2WebServer(String dbUrl) {

        this.connectionInfo = "Test H2 (Embedded)|org.h2.Driver|" +dbUrl+"|sa";
    }

    synchronized ArrayList<ConnectionInfo> getSettings() {
        ArrayList<ConnectionInfo> settings = new ArrayList<>();
        ConnectionInfo info = new ConnectionInfo(connectionInfo);
        settings.add(info);
        updateSetting(info);
        Collections.sort(settings);
        return settings;
    }
}
spring.h2.console.enabled=false
spring.datasource.url=jdbc:h2:mem:foobar

一切都很顺利,除了一个需要通过反射设置的私有(private)字段。

提供的代码适用于 H2 1.4.199

关于spring - 如何使用 Spring Boot 在 H2 控制台中设置默认 JDBC URL 值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56245872/

相关文章:

java - 如何解决 HibernateTemplate 的 find(String query) 方法抛出的 NullPointerException

spring - 在多个项目中显示tag.properties

spring - 包含 Spring 芯,为什么会出现 "java.lang.ClassNotFoundException: org.springframework.core.ErrorCoded"错误?

java - JdbcTemplate.execute() 抛出异常 Spring Boot

mysql - H2 "runscript"命令将所有表名转为大写

database - 为什么一个空的 H2 数据库大小为 2 MB?

java - Spring 启动 2 Oauth2

java - Spring数据和错误NoClassDefFoundError ScanEnvironment

spring-boot - 从数据库加载 clientId 和 clientSecret

java - 自动将日期存储到数据库中