1. 概要

このチュートリアルでは、SpringJDBCモジュールの実際の使用例を紹介します。

Spring JDBCのすべてのクラスは、次の4つの個別のパッケージに分割されています。

  • core —JDBCのコア機能。 このパッケージの重要なクラスには、 JdbcTemplate SimpleJdbcInsert SimpleJdbcCall 、およびNamedParameterJdbcTemplateが含まれます。
  • datasource —データソースにアクセスするためのユーティリティクラス。 また、JakartaEEコンテナの外部でJDBCコードをテストするためのさまざまなデータソース実装もあります。
  • object —オブジェクト指向の方法でのDBアクセス。 クエリを実行し、結果をビジネスオブジェクトとして返すことができます。 また、ビジネスオブジェクトの列とプロパティの間でクエリ結果をマップします。
  • support coreおよびobjectパッケージのクラスのサポートクラス。たとえば、SQLException変換機能を提供します。

2. 構成

データソースの簡単な構成から始めましょう。

MySQLデータベースを使用します。

@Configuration
@ComponentScan("com.baeldung.jdbc")
public class SpringJdbcConfig {
    @Bean
    public DataSource mysqlDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/springjdbc");
        dataSource.setUsername("guest_user");
        dataSource.setPassword("guest_password");

        return dataSource;
    }
}

または、組み込みデータベースを開発やテストに活用することもできます。

H2組み込みデータベースのインスタンスを作成し、単純なSQLスクリプトを事前に入力する簡単な構成を次に示します。

@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
      .setType(EmbeddedDatabaseType.H2)
      .addScript("classpath:jdbc/schema.sql")
      .addScript("classpath:jdbc/test-data.sql").build();
}

最後に、データソースのXML構成を使用して同じことを行うことができます。

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
  destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/springjdbc"/>
    <property name="username" value="guest_user"/>
    <property name="password" value="guest_password"/>
</bean>

3. JdbcTemplateおよび実行中のクエリ

3.1. 基本的なクエリ

JDBCテンプレートは、関心のあるほとんどの機能にアクセスするためのメインAPIです。

  • 接続の作成と終了
  • ステートメントの実行とストアドプロシージャの呼び出し
  • ResultSet を反復処理し、結果を返します

まず、簡単な例から始めて、JdbcTemplateで何ができるかを見てみましょう。

int result = jdbcTemplate.queryForObject(
    "SELECT COUNT(*) FROM EMPLOYEE", Integer.class);

そして、ここに簡単なINSERTがあります:

public int addEmplyee(int id) {
    return jdbcTemplate.update(
      "INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)", id, "Bill", "Gates", "USA");
}

文字を使用してパラメーターを提供する標準構文に注意してください。

次に、この構文の代替案を見てみましょう。

3.2. 名前付きパラメーターを使用したクエリ

名前付きパラメーターサポートを取得するには、フレームワークによって提供される他のJDBCテンプレートであるNamedParameterJdbcTemplateを使用します。

さらに、これは JbdcTemplate をラップし、?を使用する従来の構文の代替を提供します。 パラメータを指定します。

内部的には、名前付きパラメーターをJDBC プレースホルダーに置き換え、ラップされたJDBCTemplateに委任してクエリを実行します。

SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("id", 1);
return namedParameterJdbcTemplate.queryForObject(
  "SELECT FIRST_NAME FROM EMPLOYEE WHERE ID = :id", namedParameters, String.class);

MapSqlParameterSourceを使用して名前付きパラメーターの値を提供していることに注目してください。

beanのプロパティを使用して、名前付きパラメーターを決定する方法を見てみましょう。

Employee employee = new Employee();
employee.setFirstName("James");

String SELECT_BY_ID = "SELECT COUNT(*) FROM EMPLOYEE WHERE FIRST_NAME = :firstName";

SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(employee);
return namedParameterJdbcTemplate.queryForObject(
  SELECT_BY_ID, namedParameters, Integer.class);

以前のように名前付きパラメーターを手動で指定する代わりに、BeanPropertySqlParameterSource実装を使用していることに注意してください。

3.3. クエリ結果のJavaオブジェクトへのマッピング

もう1つの非常に便利な機能は、 RowMapper インターフェースを実装することにより、クエリ結果をJavaオブジェクトにマップする機能です。

たとえば、クエリによって返されるすべての行について、Springは行マッパーを使用してJavaBeanにデータを入力します。

public class EmployeeRowMapper implements RowMapper<Employee> {
    @Override
    public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
        Employee employee = new Employee();

        employee.setId(rs.getInt("ID"));
        employee.setFirstName(rs.getString("FIRST_NAME"));
        employee.setLastName(rs.getString("LAST_NAME"));
        employee.setAddress(rs.getString("ADDRESS"));

        return employee;
    }
}

その後、行マッパーをクエリAPIに渡して、完全に入力されたJavaオブジェクトを取得できます。

String query = "SELECT * FROM EMPLOYEE WHERE ID = ?";
Employee employee = jdbcTemplate.queryForObject(
  query, new Object[] { id }, new EmployeeRowMapper());

4. 例外翻訳

Springには、 DataAccessException をルート例外として使用する独自のデータ例外階層が付属しており、基になるすべての生の例外をそれに変換します。

そのため、低レベルの永続性の例外を処理しないことで、健全性を維持しています。 また、Springが低レベルの例外をDataAccessExceptionまたはそのサブクラスの1つでラップするという事実からも恩恵を受けています。

これにより、使用している基盤となるデータベースから独立した例外処理メカニズムも維持されます。

デフォルトのSQLErrorCodeSQLExceptionTranslatorに加えて、SQLExceptionTranslatorの独自の実装を提供することもできます。

カスタム実装の簡単な例を次に示します。重複するキー違反がある場合にエラーメッセージをカスタマイズすると、H2を使用するとエラーコード23505が発生します。

public class CustomSQLErrorCodeTranslator extends SQLErrorCodeSQLExceptionTranslator {
    @Override
    protected DataAccessException
      customTranslate(String task, String sql, SQLException sqlException) {
        if (sqlException.getErrorCode() == 23505) {
          return new DuplicateKeyException(
            "Custom Exception translator - Integrity constraint violation.", sqlException);
        }
        return null;
    }
}

このカスタム例外トランスレータを使用するには、 setExceptionTranslator()メソッドを呼び出してJdbcTemplateに渡す必要があります。

CustomSQLErrorCodeTranslator customSQLErrorCodeTranslator = 
  new CustomSQLErrorCodeTranslator();
jdbcTemplate.setExceptionTranslator(customSQLErrorCodeTranslator);

5. SimpleJdbcクラスを使用したJDBC操作

SimpleJdbc クラスは、SQLステートメントを構成および実行するための簡単な方法を提供します。 これらのクラスは、データベースメタデータを使用して基本的なクエリを作成します。 したがって、SimpleJdbcInsertおよびSimpleJdbcCallクラスは、挿入およびストアドプロシージャ呼び出しを実行するためのより簡単な方法を提供します。

5.1. SimpleJdbcInsert

最小限の構成で単純な挿入ステートメントを実行する方法を見てみましょう。

INSERTステートメントは、SimpleJdbcInsertの構成に基づいて生成されます。必要なのは、テーブル名、列名、および値を指定することだけです。

まず、SimpleJdbcInsertを作成しましょう。

SimpleJdbcInsert simpleJdbcInsert = 
  new SimpleJdbcInsert(dataSource).withTableName("EMPLOYEE");

次に、列の名前と値を指定して、操作を実行します。

public int addEmplyee(Employee emp) {
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("ID", emp.getId());
    parameters.put("FIRST_NAME", emp.getFirstName());
    parameters.put("LAST_NAME", emp.getLastName());
    parameters.put("ADDRESS", emp.getAddress());

    return simpleJdbcInsert.execute(parameters);
}

さらに、 executeAndReturnKey() APIを使用して、データベースが主キーを生成できるようにすることができます。 また、実際の自動生成された列を構成する必要があります。

SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource)
                                        .withTableName("EMPLOYEE")
                                        .usingGeneratedKeyColumns("ID");

Number id = simpleJdbcInsert.executeAndReturnKey(parameters);
System.out.println("Generated id - " + id.longValue());

最後に、BeanPropertySqlParameterSourceおよびMapSqlParameterSourceを使用して、このデータを渡すこともできます。

5.2. SimpleJdbcCallを使用したストアドプロシージャ

ストアドプロシージャの実行についても見てみましょう。

SimpleJdbcCall抽象化を利用します。

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(dataSource)
		                     .withProcedureName("READ_EMPLOYEE");
public Employee getEmployeeUsingSimpleJdbcCall(int id) {
    SqlParameterSource in = new MapSqlParameterSource().addValue("in_id", id);
    Map<String, Object> out = simpleJdbcCall.execute(in);

    Employee emp = new Employee();
    emp.setFirstName((String) out.get("FIRST_NAME"));
    emp.setLastName((String) out.get("LAST_NAME"));

    return emp;
}

6. バッチ操作

もう1つの単純な使用例は、複数の操作を一緒にバッチ処理することです。

6.1. JdbcTemplateを使用した基本的なバッチ操作

JdbcTemplate を使用すると、バッチ操作 batchUpdate()APIを介して実行できます。

ここで興味深いのは、簡潔でありながら非常に便利なBatchPreparedStatementSetterの実装です。

public int[] batchUpdateUsingJdbcTemplate(List<Employee> employees) {
    return jdbcTemplate.batchUpdate("INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)",
        new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setInt(1, employees.get(i).getId());
                ps.setString(2, employees.get(i).getFirstName());
                ps.setString(3, employees.get(i).getLastName());
                ps.setString(4, employees.get(i).getAddress();
            }
            @Override
            public int getBatchSize() {
                return 50;
            }
        });
}

6.2. NamedParameterJdbcTemplateを使用したバッチ操作

NamedParameterJdbcTemplate batchUpdate()APIを使用して操作をバッチ処理するオプションもあります。

このAPIは前のものよりも単純です。 したがって、パラメーター値を設定するための内部プリペアドステートメントセッターがあるため、パラメーターを設定するために追加のインターフェイスを実装する必要はありません。

代わりに、パラメーター値を SqlParameterSourceの配列としてbatchUpdate()メソッドに渡すことができます。

SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(employees.toArray());
int[] updateCounts = namedParameterJdbcTemplate.batchUpdate(
    "INSERT INTO EMPLOYEE VALUES (:id, :firstName, :lastName, :address)", batch);
return updateCounts;

7. SpringBootを使用したSpringJDBC

Spring Bootは、リレーショナルデータベースでJDBCを使用するためのスターターspring-boot-starter-jdbcを提供します。

すべてのSpringBootスターターと同様に、これはアプリケーションをすばやく起動して実行するのに役立ちます。

7.1. Mavenの依存関係

プライマリ依存関係としてspring-boot-starter-jdbc依存関係が必要です。 また、使用するデータベースの依存関係も必要になります。 私たちの場合、これはMySQLです。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

7.2. 構成

Spring Bootは、データソースを自動的に構成します。 プロパティファイルでプロパティを提供する必要があります。

spring.datasource.url=jdbc:mysql://localhost:3306/springjdbc
spring.datasource.username=guest_user
spring.datasource.password=guest_password

以上です。 これらの構成のみを実行するだけで、アプリケーションが稼働します。 これで、他のデータベース操作に使用できます。

前のセクションで標準のSpringアプリケーションについて見た明示的な構成が、SpringBootの自動構成の一部として含まれるようになりました。

8. 結論

この記事では、SpringFrameworkでのJDBCの抽象化について説明しました。 Spring JDBCが提供するさまざまな機能について、実際の例を挙げて説明しました。

また、SpringBootJDBCスターターを使用してSpringJDBCをすばやく開始する方法についても検討しました。

例のソースコードは、GitHubから入手できます。