Spring Data JDBCでの複数データソース対応

#環境
Spring Boot 2.4.2
Spring Data JDBC 2.1.3
Lombok 1.18.16
Java15

#概要
Spring Data JDBCの複数データソース(multi datasource)対応について、まとめてみた。

#設定ファイル

spring.datasource.urlで指定すると、「Caused by: java.lang.IllegalArgumentException : jdbcUrl is required with driverClassName」が発生する。複数データソースでは、コネクションプールのjdbcURLに変換できないとのことで、直接指定する。

```yml:application.yml
spring:
  datasource1:
    driverClassName: oracle.jdbc.OracleDriver
    jdbcUrl: jdbc:oracle:thin:@//localhost:1521/orclpdb
    username: user1
    password: password1
  datasource2:
    driverClassName: oracle.jdbc.OracleDriver
    jdbcUrl: jdbc:oracle:thin:@//localhost:1521/orclpdb
    username: user2
    password: password2
```


#DB設定

```java:JdbcConfig.java
@Configuration
public class JdbcConfig {
    @Primary
    @Bean("db1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource1")
    public DataSource createDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean(name = "db1NamedJdbcTemplate")
    NamedParameterJdbcTemplate createNamedParameterJdbcTemplate(@Qualifier("db1DataSource") DataSource ds) {
        return new NamedParameterJdbcTemplate(ds);
    }

    @Bean("db2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource2")
    public DataSource createDataSource2() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "db2NamedJdbcTemplate")
    NamedParameterJdbcTemplate createNamedParameterJdbcTemplate2(@Qualifier("db2DataSource") DataSource ds) {
        return new NamedParameterJdbcTemplate(ds);
    }
}
```


#リポジトリ(JdbcTemplate版)


```java:Db1Repository.java
@Repository
public class Db1Repository {
    private final NamedParameterJdbcTemplate jdbcTemplate;

    public Db1Repository(@Qualifier("db1NamedJdbcTemplate") NamedParameterJdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public List<String> getRecord() {
        return jdbcTemplate.query("select * from SAMPLE", (rs, rowNum) -> rs.getString("CODE"));
    }
}
```

@RequiredArgsConstructorを利用する場合は、以下のように書き換えも可能。ただし、IntelliJで@__が「シンボルが解決できない」というエラーが発生する。@Autowiredでの書き換えも可能だが、Spring Boot 2.1以降で、非推奨となったため、割愛。

```java:Db1Repository.java
@Repository
@RequiredArgsConstructor(onConstructor=@__(@Autowired))
public class DemoRepository {
    @Qualifier("db1NamedJdbcTemplate")
    private final NamedParameterJdbcTemplate jdbcTemplate;
```

#リポジトリ(CrudRepository版)

CurdRepositoryは、Primaryのみであれば接続可能であったが、複数データソースは対応できなかった。
(参考URL)
https://stackoverflow.com/questions/58960333/multiple-datasources-using-spring-data-jdbc-and-crudrepository-interface
※調べた限りでは、@EnableJdbcRepositoriesで、repositoryFactoryBeanClassを再定義できれば、接続できそうな感じだったが、詳細不明。

```java:Db1Repository.java
@Repository
public interface Db1Repository extends CrudRepository<Sample, Long> {
}
```

```java:Sample.java
@ToString
@AllArgsConstructor
@Table("SAMPLE")
public class Sample {
    @Id
    private Long code;
    private String name;
}

```

なので、Primay以外のデータソースは、必要なメソッドをImplで実装してあげるのがよいかと思われる。もし、CrudRepositoryが使えるようになったら、更新しておきます。

```java:Db2Repository.java
public interface Db2Repository {
    List<Sample> findAll();
}
```

```java:impl/Db2RepositoryImpl.java
@Repository
public class Db2RepositoryImpl implements Db2Repository {
    private final NamedParameterJdbcTemplate jdbcTemplate;

    public Db1Repository(@Qualifier("db2NamedJdbcTemplate") NamedParameterJdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public List<Sample> findAll() {
        return jdbcTemplate.query("select * from SAMPLE",
                (rs, rowNum) -> new Sample(rs.getLong("CODE"), rs.getString("NAME")));

    }
}
```
余談ですが、Java13から登場したテキストブロック(""")を利用すると、複数行のSQLも簡単に書けます。Java15からStandardになりました。

# 使い方

```java:SampleController.java
@RestController
@RequiredArgsConstructor
public class SampleController {
    private final Db1Repository db1Repository;
    private final Db2Repository db2Repository;

    @GetMapping("/")
    void test() {
        System.out.println(db1Repository.findAll());
        System.out.println(db2Repository.findAll());
    }
}
```