web-dev-qa-db-ja.com

JNDIを使用してSpring Bootで複数のDataSourceを構成する

Application Serverの組み込み機能を使用してmultipleDataSourceを管理し、JNDIを使用してそれにアクセスしたい。 Spring JPAデータでSpringブートを使用しています。

単一のデータソースの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"/>

すべてが正常に動作します。

しかし、2つのデータソースを設定できない場合。

私は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ブートの詳細を教えてください。私は何日もこの構成を探していました。

2番目のトライアル スプリングブートドキュメント

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サーバーは開始されていますが。エラーはログに出力されません。

3番目のトライアル: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

顧客スキーマにすべてのテーブルを作成しますが、他のテーブルも見つけられません(2番目のスキーマから)。

9
Manu

これは、3回目のトライアルのソリューションを少し変更したものです。このソリューションを検討してください(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リポジトリ。

7
Andrey Sarul

これにはプレーンなJndiObjectFactoryBeanを使用できます。 DataSourceBuilderJndiObjectFactoryBeanに置き換えるだけでうまくいきます。

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を参照してください。ただし、cacheまたはlookup-on-startupなどを設定することもできます)。

注:JNDIルックアップを実行するときにdestroyMethod""に設定すると、アプリケーションがJNDIリソースもクローズ/シャットダウンされます。これは、共有環境で必要なものではありません。

5
M. Deinum

それは私のために働き、より少ないコードが含まれています

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

私が成功し、より多くを探求する簡潔な方法

  1. 外部のTomcatに多くのjndiリソースを設定します。これはEclipseで開始/停止できます。注-EclipseでTomcatをダブルクリックし、ワークスペースmetedataの使用を選択します。これは、アプリを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タイプのプロパティを自動配線し、systemoutにその詳細を取得して、さらに調査します。

0
subrat22

上記の回答は適切ですが、jndiと完全なデータ接続構成を混在させる場合の致命的な問題を説明するために、もう1つ追加します。典型的な開発環境では、ローカル開発環境でデータベース接続を完全に修飾し、qnなどにプッシュするときに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

springのドキュメント で提案されているように、データソースBeanをそのように使用する-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に戻り、WRONGデータベース接続を返します。解決方法は非常に簡単です。現在の環境をテストし、作成されるデータベース接続のタイプをカスタマイズします。アプリケーションが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);
    }
  }
0
Tom Bonavia