簡體   English   中英

使用 JNDI 在 Spring Boot 中配置多個數據源

[英]Configure Multiple DataSource in Spring Boot with JNDI

我想使用您的應用程序服務器內置功能管理多個數據源並使用 JNDI 訪問它。 我正在將 Spring boot 與 Spring JPA 數據一起使用。

我能夠為單個數據源配置 application.properties:

spring.datasource.jndi-name=jdbc/customers

我在 context.xml 文件中的配置如下:

<Resource name="jdbc/customer" auth="Container" type="javax.sql.DataSource"
               maxTotal="100" maxIdle="30" maxWaitMillis="10000"
               username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
               url="jdbc:mysql://localhost:3306/customer"/>

一切正常。

但是當我無法配置兩個數據源時。

我確定 context.xml 文件中的配置:

 <Resource name="jdbc/customer" auth="Container" type="javax.sql.DataSource"
                   maxTotal="100" maxIdle="30" maxWaitMillis="10000"
                   username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
                   url="jdbc:mysql://localhost:3306/customer"/>

 <Resource name="jdbc/employee" auth="Container" type="javax.sql.DataSource"
                   maxTotal="100" maxIdle="30" maxWaitMillis="10000"
                   username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
                   url="jdbc:mysql://localhost:3306/employee"/>

我對 application.properties 文件配置有疑問。

我嘗試了以下選項但沒有成功:

spring.datasource.jndi-name=jdbc/customers,jdbc/employee

請讓我知道有關使用 JNDI 進行多數據源 Spring Boot 的任何詳細信息。 我一直在尋找這個配置好幾天了。

根據Spring Boot 文檔進行第二次試用

spring.datasource.primary.jndi-name=jdbc/customer
spring.datasource.secondary.jndi-name=jdbc/project

配置類。

@Bean
@Primary
@ConfigurationProperties(prefix="datasource.primary")
public DataSource primaryDataSource() {
    return DataSourceBuilder.create().build();
}

@Bean
@ConfigurationProperties(prefix="datasource.secondary")
public DataSource secondaryDataSource() {
    return DataSourceBuilder.create().build();
}

應用程序無法啟動。 雖然tomcat服務器正在啟動。 日志中沒有打印錯誤。

第三次嘗試:使用 JndiObjectFactoryBean

我有以下 application.properties

spring.datasource.primary.expected-type=javax.sql.DataSource
spring.datasource.primary.jndi-name=jdbc/customer
spring.datasource.primary.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.datasource.primary.jpa.show-sql=false
spring.datasource.primary.jpa.hibernate.ddl-auto=validate

spring.datasource.secondary.jndi-name=jdbc/employee
spring.datasource.secondary.expected-type=javax.sql.DataSource
spring.datasource.secondary.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.datasource.secondary.jpa.show-sql=false
spring.datasource.secondary.jpa.hibernate.ddl-auto=validate

以及下面的java配置:

@Bean(destroyMethod="")
@Primary
@ConfigurationProperties(prefix="spring.datasource.primary")
public FactoryBean primaryDataSource() {
    return new JndiObjectFactoryBean();
}

@Bean(destroyMethod="")
@ConfigurationProperties(prefix="spring.datasource.secondary")
public FactoryBean secondaryDataSource() {
    return new JndiObjectFactoryBean();
}

但是還是報錯:

Related cause: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'primaryDataSource' defined in class path resource [com/web/initializer/MvcConfig.class]: Invocation of init method failed; nested exception is javax.naming.NameNotFoundException: Name [jdbc/customer] is not bound in this Context. Unable to find [jdbc].
Related cause: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'secondaryDataSource' defined in class path resource [com/web/initializer/MvcConfig.class]: Invocation of init method failed; nested exception is javax.naming.NameNotFoundException: Name [jdbc/employee] is not bound in this Context. Unable to find [jdbc].
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:133)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:474)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
        at org.springframework.boot.context.web.SpringBootServletInitializer.run(SpringBootServletInitializer.java:117)
        at org.springframework.boot.context.web.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:108)
        at org.springframework.boot.context.web.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:68)
        at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:175)

更新:使用以下屬性文件進行試用:

  spring.datasource.primary.expected-type=javax.sql.DataSource
   spring.datasource.primary.jndi-name=java:comp/env/jdbc/customer

   spring.datasource.secondary.jndi-name=java:comp/env/jdbc/employee
   spring.datasource.secondary.expected-type=javax.sql.DataSource

   spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
   spring.jpa.show-sql=false
   spring.jpa.hibernate.ddl-auto=validate

它在客戶模式中創建所有表,但也無法嘗試找到其他表。(來自第二個模式)

這是您第三次試用的解決方案,稍作修改。 考慮這個解決方案(Spring Boot 1.3.2):

application.properties 文件:

spring.datasource.primary.jndi-name=java:/comp/env/jdbc/SecurityDS
spring.datasource.primary.driver-class-name=org.postgresql.Driver

spring.datasource.secondary.jndi-name=java:/comp/env/jdbc/TmsDS
spring.datasource.secondary.driver-class-name=org.postgresql.Driver

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL9Dialect
spring.jpa.show-sql=false

配置:

@Configuration@ EnableConfigurationProperties
public class AppConfig {

    @Bean@ ConfigurationProperties(prefix = "spring.datasource.primary")
    public JndiPropertyHolder primary() {
        return new JndiPropertyHolder();
    }

    @Bean@ Primary
    public DataSource primaryDataSource() {
        JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
        DataSource dataSource = dataSourceLookup.getDataSource(primary().getJndiName());
        return dataSource;
    }

    @Bean@ ConfigurationProperties(prefix = "spring.datasource.secondary")
    public JndiPropertyHolder secondary() {
        return new JndiPropertyHolder();
    }

    @Bean
    public DataSource secondaryDataSource() {
        JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
        DataSource dataSource = dataSourceLookup.getDataSource(secondary().getJndiName());
        return dataSource;
    }

    private static class JndiPropertyHolder {
        private String jndiName;

        public String getJndiName() {
            return jndiName;
        }

        public void setJndiName(String jndiName) {
            this.jndiName = jndiName;
        }
    }
}

然后您可以按照指南http://docs.spring.io/spring-data/jpa/docs/1.3.0.RELEASE/reference/html/jpa.repositories.html使用帶有 jpa 存儲庫的數據源。

它對我有用並且包含更少的代碼

@Configuration
public class Config {
    @Value("${spring.datasource.primary.jndi-name}")
    private String primaryJndiName;

    @Value("${spring.datasource.secondary.jndi-name}")
    private String secondaryJndiName;

    private JndiDataSourceLookup lookup = new JndiDataSourceLookup();

    @Primary
    @Bean(destroyMethod = "") // destroy method is disabled for Weblogic update app ability
    public DataSource primaryDs() {
        return lookup.getDataSource(primaryJndiName);
    }

    @Bean(destroyMethod = "") // destroy method is disabled for Weblogic update app ability
    public DataSource secondaryDs() {
        return lookup.getDataSource(secondaryJndiName);
    }
}

您可以為此使用普通的JndiObjectFactoryBean 只需用JndiObjectFactoryBean替換DataSourceBuilder就可以了。

Java配置

@Bean(destroyMethod="")
@Primary
@ConfigurationProperties(prefix="datasource.primary")
public FactoryBean primaryDataSource() {
    return new JndiObjectFactoryBean();
}

@Bean(destroyMethod="")
@ConfigurationProperties(prefix="datasource.secondary")
public FactoryBean secondaryDataSource() {
    return new JndiObjectFactoryBean();
}

特性

datasource.primary.jndi-name=jdbc/customer
datasource.primary.expected-type=javax.sql.DataSource
datasource.secondary.jndi-name=jdbc/project
datasource.secondary.expected-type=javax.sql.DataSource

您可以使用@ConfigurationProperties注釋設置JndiObjectFactoryBean每個屬性。 (請參閱我添加的expected-type ,但您也可以設置cachelookup-on-startup等)。

注意:在執行 JNDI 查找時,將destroyMethod設置為""否則您可能會遇到這樣的情況,即當應用程序關閉時,您的 JNDI 資源也會關閉/關閉。 這不是您在共享環境中想要的。

在我的情況下,當我使用 Spring Boot App 啟動我的應用程序時,數據庫配置是在 application-dev.properties 上讀取的,當我在 tomcat 上發布時,使用數據源,有必要添加一個驗證來檢查我的配置文件是否是 prod,在這個案例,我做一個 jndi 查找

@Bean(name = "dsName")
@ConfigurationProperties("ds.datasource.configuration")
public DataSource dataSource(@Qualifier("dsProperties") DataSourceProperties db1DataSourceProperties)
{

    if(Arrays.asList(environment.getActiveProfiles()).contains("prod"))
    {
        final JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
        return dataSourceLookup.getDataSource("java:comp/env/jdbc/DS1");
    }
    else
    {
        return db1DataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }
}

我獲得成功和探索更多的簡潔方式

  1. 在外部 tomcat 中設置了許多 jndi 資源,您可以在 eclipse 中啟動/停止。注意 - 在 eclipse 中雙擊 tomcat 並選擇 use workspace metadata ,意味着不要將應用程序部署到 tomcat webapp 文件夾。 在各自的 eclipse 服務器文件中添加 jndi 資源(context.xml - ResourceLink、server.xml - Resource、web.xml - resource-ref)。

  2. 無需在 application.properties 中設置 spring.datasource.*。 因為 jndi-contest 是一種數據源類型(即type="javax.sql.DataSource" )被導出到外部服務器。

  3. 在 SpringBootApplication 注釋類中,通過 jndi 查找從所有 jndi 資源(按照 #1 設置的那些)創建數據源 bean

     @Bean(name = "abcDataSource") public DataSource getAbcDataSource() { JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup(); DataSource dataSource = dataSourceLookup.getDataSource("java:comp/env/jdbc/abcDataSource"); return dataSource; }
  4. 如果您的項目中使用了 spring jdbc,則提供上述數據源以創建 jdbcTemplate bean

     @Bean(name = "jdbcAbcTemplate") public JdbcTemplate abcJdbcTemplate(@Lazy @Qualifier("abcDataSource") DataSource refDS) { return new JdbcTemplate(refDS); }
  5. 只需自動裝配 DataSource 類型的屬性並獲取系統詳細信息以探索更多信息。

雖然上述答案很好,但我將再添加一個來說明混合 jndi 和完整數據連接配置時的致命問題。 在典型的開發環境中,您可以完全限定本地開發環境中的數據庫連接,然后在推送到 qa 等時使用 jndi。您的應用程序 .properties 如下所示:

spring.datasource.url=jdbc:sqlserver://server.domain.org:1433;databaseName=dbxx
spring.datasource.username=userxxyyzz
spring.datasource.password=passxxyyzz
spring.datasource.platform=mssql
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver

和 application-qa.properties 像這樣:

spring.datasource.jndi-name=java:jboss/datasources/dbxx

當您必須定義自己的 bean 以擁有多個數據源時,就會出現問題。 如果您使用默認的 Spring 托管數據源,那么它會自動檢測 jndi 與完全限定的連接,並返回一個無需更改應用程序代碼的數據源。 如果您定義自己的數據源,則不再執行此操作。 如果你有這樣的 application.properties:

spring.custom.datasource.url=jdbc:sqlserver://server.domain.org:1433;databaseName=dbxx
    spring.custom.datasource.username=userxxyyzz
    spring.custom.datasource.password=passxxyyzz
    spring.custom.datasource.platform=mssql
    spring.custom.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver

和 application-qa.properties 像這樣:

spring.custom.datasource.jndi-name=java:jboss/datasources/dbxx

使用像這樣的數據源 bean,如 Spring 文檔中所建議的https://docs.spring.io/spring-boot/docs/2.1.11.RELEASE/reference/html/howto-data-access.html

  @Primary
  @Bean(name="customDataSourcePropertiesBean")
  @ConfigurationProperties("spring.custom.datasource")
  public DataSourceProperties customDataSourceProperties() {
      return new DataSourceProperties();
  }

  @Primary
  @Bean(name="customDataSourceBean")
  @ConfigurationProperties("spring.custom.datasource") 
  public HiakriDataSource customDataSource(@Qualifier("customDataSourcePropertiesBean") DataSourceProperties properties) {
    return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
  }

此數據源構建器不會嘗試讀取 application-qa.properties 中的 jndi 配置,並且會靜默故障返回到 application.properties 返回錯誤的數據庫連接。 解決方法相當簡單 - 測試您所在的環境並自定義創建的數據庫連接類型。 調試這是一個熊,因為症狀是應用程序似乎忽略了 application-qa.properties。 我分享是為了免除他人的痛苦。 將 spring.profiles.active=qa 等添加到您的屬性文件中以了解您所在的環境:

  @Value("${spring.profiles.active}")
  String profile;

  @Value("${spring.custom.jndi-name}")
  String jndi;

  @Primary
  @Bean(name="customDataSourcePropertiesBean")
  @ConfigurationProperties("spring.custom.datasource")
  public DataSourceProperties customDataSourceProperties() {
      return new DataSourceProperties();
  }

  @Primary
  @Bean(name="customDataSourceBean")
  @ConfigurationProperties("spring.custom.datasource") 
  public DataSource customDataSource(@Qualifier("customDataSourcePropertiesBean") DataSourceProperties properties) {
    if(profile.equals("localhost")) {
        return DataSourceBuilder
            .create()
                .username(properties.getDataUsername())
                .password(properties.getPassword())
                .url(properties.getUrl())
                .driverClassName(properties.getDriverClassName())
                .build();
    }else {
        JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
        return dataSourceLookup.getDataSource(jndi);
    }
  }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM