

# JDBC 接続
<a name="aws-glue-programming-etl-connect-jdbc-home"></a>

 特定の (通常はリレーショナル) データベースタイプは、JDBC 標準による接続をサポートします。JDBC の詳細については、[Java JDBC API](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) に関するドキュメントを参照してください。AWSGlue は JDBC コネクタを介した特定のデータベースへの接続をネイティブにサポートしています。JDBC ライブラリは AWS Glue Spark ジョブで提供されます。AWS Glue ライブラリを使用してこれらのデータベースタイプに接続すると、標準のオプションセットにアクセスできます。

JDBC connectionType の値には次のようなものがあります。
+ `"connectionType": "sqlserver"`: Microsoft SQL Server データベースへの接続を指定します。
+ `"connectionType": "mysql"`: MySQL データベースへの接続を指定します。
+ `"connectionType": "oracle"`: Oracle データベースへの接続を指定します。
+ `"connectionType": "postgresql"`: PostgreSQL データベースへの接続を指定します。
+ `"connectionType": "redshift"`: Amazon Redshift データベースへの接続を指定します。詳細については、[Redshift 接続](aws-glue-programming-etl-connect-redshift-home.md) を参照してください。

次の表に、AWS Glue がサポートする JDBC ドライバーのバージョンを示します。


| 製品 | Glue 5.1 の JDBC ドライバーのバージョン | Glue 5.0 の JDBC ドライバーのバージョン | Glue 4.0 の JDBC ドライバーのバージョン | Glue 3.0 の JDBC ドライバーのバージョン | Glue 0.9、1.0、2.0 の JDBC ドライバーのバージョン | 
| --- | --- | --- | --- | --- | --- | 
| Microsoft SQL Server | 10.2.0 | 10.2.0 | 9.4.0 | 7.x | 6.x | 
| MySQL | 8.0.33 | 8.0.33 | 8.0.23 | 8.0.23 | 5.1 | 
| Oracle Database | 23.3.0.23.09 | 23.3.0.23.09 | 21.7 | 21.1 | 11.2 | 
| PostgreSQL | 42.7.3 | 42.7.3 | 42.3.6 | 42.2.18 | 42.1.x | 
| Amazon Redshift \* | redshift-jdbc42-2.1.0.29 | redshift-jdbc42-2.1.0.29 | redshift-jdbc42-2.1.0.16 | redshift-jdbc41-1.2.12.1017 | redshift-jdbc41-1.2.12.1017 | 

\* Amazon Redshift 接続タイプの場合は、書式設定オプションを含め、JDBC 接続用に接続オプションに含まれるその他のオプション名/値ペアはすべて、基になる SparkSQL DataSource に直接渡されます。AWS Glue 4.0 以降のバージョンの AWS Glue with Spark ジョブでは、Amazon Redshift の AWS Glue ネイティブコネクタは Apache Spark の Amazon Redshift インテグレーションを使用します。詳細については、「[Apache Spark 用の Amazon Redshift の統合](https://docs.aws.amazon.com/redshift/latest/mgmt/spark-redshift-connector.html)」を参照してください。以前のバージョンでは、「[Spark 用の Amazon Redshift データソース](https://github.com/databricks/spark-redshift)」を参照してください。

JDBC を使用して Amazon RDS データストアに接続するように Amazon VPC を設定するには、「[AWS Glue から Amazon RDS データストアに JDBC 接続するための Amazon VPC の設定](setup-vpc-for-glue-access.md)」を参照してください。

**注記**  
AWS Glue ジョブは、1 回の実行で 1 つのサブネットにのみ関連付けられます。これは、同じジョブで複数のデータソースに接続する能力に影響する可能性があります。この動作は JDBC ソースに限定されません。

**Topics**
+ [JDBC 接続オプションのリファレンス](#aws-glue-programming-etl-connect-jdbc)
+ [sampleQuery を使用](#aws-glue-programming-etl-jdbc-samplequery)
+ [カスタム JDBC ドライバーを使用](#aws-glue-programming-etl-jdbc-custom-driver)
+ [JDBC テーブルからの並列読み取り](run-jdbc-parallel-read-job.md)
+ [AWS Glue から Amazon RDS データストアに JDBC 接続するための Amazon VPC の設定](setup-vpc-for-glue-access.md)

## JDBC 接続オプションのリファレンス
<a name="aws-glue-programming-etl-connect-jdbc"></a>

既に JDBC AWS Glue 接続を定義している場合、URL、ユーザー、パスワードなど、その中で定義されている設定プロパティを再利用できるため、コードで接続オプションとして指定する必要はありません。この機能は、AWS Glue バージョン 3.0 以降でのみ有効です。これを行うには、次の接続プロパティを使用します。
+ `"useConnectionProperties"`: この設定を接続から使用するときは、「true」に設定します。
+ `"connectionName"`: この設定を取得する接続名を入力します。接続は、ジョブと同じリージョンで定義されている必要があります。

JDBC 接続では、次の接続オプションを使用します。
+ `"url"`: (必須) データベースの JDBC URL。
+ `"dbtable"`: (必須) 読み込み元のデータベーステーブル。データベース内でスキーマをサポートする JDBC データストアの場合、`schema.table-name` を指定します。スキーマを指定しない場合、デフォルトの「パブリック」スキーマが使用されます。
+ `"user"`: (必須) 接続時に使用するユーザー名。
+ `"password"`: (必須) 接続時に使用するパスワード。
+ (オプション) 次のオプションを使用すると、カスタム JDBC ドライバーを指定できます。AWS Glue がネイティブでサポートしていないドライバーを使用する必要がある場合は、これらのオプションを使用します。

  ソースおよびターゲットが同じデータベース製品の場合でも、ETL ジョブは、データソースおよびターゲットに対して、異なるバージョンの JDBC ドライバーを使用することができます。これにより、異なるバージョンのソースデータベースとターゲットデータベース間でデータを移行できます。これらのオプションを使用するには、最初に JDBC ドライバーの JAR ファイルを Amazon S3 にアップロードする必要があります。
  + `"customJdbcDriverS3Path"`: カスタム JDBC ドライバーの Amazon S3 パス。
  + `"customJdbcDriverClassName"`: JDBC ドライバーのクラス名。
+ `"bulkSize"`:（オプション）JDBC ターゲットへのバルクロードを高速化するためのパラレル挿入を構成するために使用します。データの書き込みまたは挿入時に使用する並列度の整数値を指定します。このオプションは、Arch User Repository (AUR) などのデータベースへの書き込みのパフォーマンスを向上させるのに役立ちます。
+ `"hashfield"` (オプション) JDBC テーブルから並列で読み込むときにデータをパーティションに分割するために使用される JDBC テーブル内の列の名前を指定するために使用される文字列。「hashfield」または「hashexpression」を指定します。詳細については、[JDBC テーブルからの並列読み取り](run-jdbc-parallel-read-job.md) を参照してください。
+ `"hashexpression"` (オプション) 整数を返す SQL SELECT 句。JDBC テーブルから並列で読み込むときに、JDBC テーブル内のデータをパーティションに分割するために使用されます。「hashfield」または「hashexpression」を指定します。詳細については、[JDBC テーブルからの並列読み取り](run-jdbc-parallel-read-job.md) を参照してください。
+ `"hashpartitions"` (オプション) 正の整数。JDBC テーブルから並列で読み込むときに、JDBC テーブルの並列読み込み数を指定するために使用されます。デフォルト: 7。詳細については、[JDBC テーブルからの並列読み取り](run-jdbc-parallel-read-job.md) を参照してください。
+ `"sampleQuery"`: (オプション) カスタム SQL クエリステートメント。テーブル内の情報のサブセットを指定して、テーブルの内容のサンプルを取得するために使用されます。**データを考慮せずに設定すると、DynamicFrame メソッドよりも効率が悪くなり、タイムアウトやメモリ不足エラーが発生する可能性があります。**詳細については、[sampleQuery を使用](#aws-glue-programming-etl-jdbc-samplequery) を参照してください。
+ `"enablePartitioningForSampleQuery"`: (オプション) ブール値。デフォルト: false。`sampleQuery` の指定時に、JDBC テーブルから並列で読み込むことを可能にするために使用されます。**true に設定すると、AWS Glue でパーティショニング条件を追加するためには、`sampleQuery` が「where」または「and」で終わる必要があります。**詳細については、[sampleQuery を使用](#aws-glue-programming-etl-jdbc-samplequery) を参照してください。
+ `"sampleSize"`: (オプション) 正の整数。サンプルクエリによって返される行数を制限します。`enablePartitioningForSampleQuery` が true の場合にのみ動作します。パーティショニングが有効になっていない場合は、代わりに `sampleQuery` で `"limit x"` を追加してサイズを制限します。詳細については、[sampleQuery を使用](#aws-glue-programming-etl-jdbc-samplequery) を参照してください。

## sampleQuery を使用
<a name="aws-glue-programming-etl-jdbc-samplequery"></a>

このセクションでは、`sampleQuery`、`sampleSize`、`enablePartitioningForSampleQuery` を使用する方法について説明します。

`sampleQuery` ではデータセットのいくつかの行を効率的にサンプリングできます。デフォルトでは、クエリは単一のエグゼキューターによって実行されます。データを考慮せずに設定すると、DynamicFrame メソッドよりも効率が悪くなり、タイムアウトやメモリ不足エラーが発生する可能性があります。ETL パイプラインの一部として基盤となるデータベースで SQL を実行することは、通常、パフォーマンス上の目的でのみ必要になります。データセットの数行をプレビューする場合は、[show](aws-glue-api-crawler-pyspark-extensions-dynamic-frame.md#aws-glue-api-crawler-pyspark-extensions-dynamic-frame-show) を使用することを検討してください。SQL を使用してデータセットを変換する場合は、[toDF](aws-glue-api-crawler-pyspark-extensions-dynamic-frame.md#aws-glue-api-crawler-pyspark-extensions-dynamic-frame-toDF) を使用して DataFrame フォーム内のデータに対して SparkSQL 変換を定義することを検討してください。

クエリではさまざまなテーブルを操作できますが、`dbtable` はやはり必須です。

**sampleQuery を使用してテーブルのサンプルを取得する**

デフォルトの sampleQuery の動作を使用してデータのサンプルを取得する場合、AWS Glue は大きなスループットを期待しないため、クエリは 1 つのエグゼキューターで実行されます。提供するデータを制限し、パフォーマンス上の問題を引き起こさないようにするには、SQL に `LIMIT` 句を指定することをお勧めします。

**Example パーティション化せずに sampleQuery を使用する**  
次のコード例は、パーティション化せずに `sampleQuery` を使用する方法を示しています。  

```
//A full sql query statement.
val query = "select name from {{$tableName}} where age > 0 limit 1"
val connectionOptions = JsonOptions(Map(
    "url" -> url, 
    "dbtable" -> tableName, 
    "user" -> user, 
    "password" -> password, 
    "sampleQuery" -> query ))
val dyf = glueContext.getSource("mysql", connectionOptions)
          .getDynamicFrame()
```

**sampleQuery を大きなデータセットに対して使用する**

 大きなデータセットを読み取る場合は、JDBC パーティション化を有効にして、テーブルを並列にクエリする必要がある場合があります。詳細については、[JDBC テーブルからの並列読み取り](run-jdbc-parallel-read-job.md) を参照してください。`sampleQuery` を JDBC パーティショニングで使用する場合は、`enablePartitioningForSampleQuery` を true に設定します。この機能を有効にするには、`sampleQuery` にいくつかの変更を加える必要があります。

`sampleQuery` で JDBC パーティショニングを使用するときに、パーティショニング条件を追加するためには、AWS Glue でクエリが「where」または「and」で終わる必要があります。

 JDBC テーブルから並列で読み込むときに sampleQuery の結果を制限したい場合は、`LIMIT` 句を指定するのではなく、`"sampleSize"` パラメータを設定してください。

**Example JDBC パーティション化で sampleQuery を使用する**  
次のコード例は、JDBC パーティション化で `sampleQuery` を使用する方法を示しています。  

```
//note that the query should end with "where" or "and" if use with JDBC partitioning.
val query = "select name from {{$tableName}} where age > 0 and"

//Enable JDBC partitioning by setting hashfield.
//to use sampleQuery with partitioning, set enablePartitioningForSampleQuery.
//use sampleSize to limit the size of returned data.
val connectionOptions = JsonOptions(Map(
    "url" -> url, 
    "dbtable" -> tableName, 
    "user" -> user, 
    "password" -> password, 
    "hashfield" -> primaryKey,
    "sampleQuery" -> query,
    "enablePartitioningForSampleQuery" -> true,
    "sampleSize" -> "1" ))
val dyf = glueContext.getSource("mysql", connectionOptions)
          .getDynamicFrame()
```

 **注意と制限** 

サンプルクエリはジョブブックマークと一緒に使用することはできません。両方の設定を指定しても、ブックマークの状態は無視されます。

## カスタム JDBC ドライバーを使用
<a name="aws-glue-programming-etl-jdbc-custom-driver"></a>

次のコード例は、カスタム JDBC ドライバーを使用して JDBC データベースから読み書きする方法を示しています。データベース製品の 1 つのバージョンから読み取り、同じ製品の新しいバージョンに書き込んでいます。

------
#### [ Python ]

```
import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext, SparkConf
from awsglue.context import GlueContext
from awsglue.job import Job
import time
from pyspark.sql.types import StructType, StructField, IntegerType, StringType

sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session

# Construct JDBC connection options
connection_mysql5_options = {
    "url": "jdbc:mysql://<jdbc-host-name>:3306/db",
    "dbtable": "test",
    "user": "admin",
    "password": "pwd"}

connection_mysql8_options = {
    "url": "jdbc:mysql://<jdbc-host-name>:3306/db",
    "dbtable": "test",
    "user": "admin",
    "password": "pwd",
    "customJdbcDriverS3Path": "s3://amzn-s3-demo-bucket/mysql-connector-java-8.0.17.jar",
    "customJdbcDriverClassName": "com.mysql.cj.jdbc.Driver"}

connection_oracle11_options = {
    "url": "jdbc:oracle:thin:@//<jdbc-host-name>:1521/ORCL",
    "dbtable": "test",
    "user": "admin",
    "password": "pwd"}

connection_oracle18_options = {
    "url": "jdbc:oracle:thin:@//<jdbc-host-name>:1521/ORCL",
    "dbtable": "test",
    "user": "admin",
    "password": "pwd",
    "customJdbcDriverS3Path": "s3://amzn-s3-demo-bucket/ojdbc10.jar",
    "customJdbcDriverClassName": "oracle.jdbc.OracleDriver"}

# Read from JDBC databases with custom driver
df_mysql8 = glueContext.create_dynamic_frame.from_options(connection_type="mysql",
                                                          connection_options=connection_mysql8_options)

# Read DynamicFrame from MySQL 5 and write to MySQL 8
df_mysql5 = glueContext.create_dynamic_frame.from_options(connection_type="mysql",
                                                          connection_options=connection_mysql5_options)
glueContext.write_from_options(frame_or_dfc=df_mysql5, connection_type="mysql",
                               connection_options=connection_mysql8_options)

# Read DynamicFrame from Oracle 11 and write to Oracle 18
df_oracle11 = glueContext.create_dynamic_frame.from_options(connection_type="oracle",
                                                            connection_options=connection_oracle11_options)
glueContext.write_from_options(frame_or_dfc=df_oracle11, connection_type="oracle",
                               connection_options=connection_oracle18_options)
```

------
#### [ Scala ]

```
import com.amazonaws.services.glue.GlueContext
import com.amazonaws.services.glue.MappingSpec
import com.amazonaws.services.glue.errors.CallSite
import com.amazonaws.services.glue.util.GlueArgParser
import com.amazonaws.services.glue.util.Job
import com.amazonaws.services.glue.util.JsonOptions
import com.amazonaws.services.glue.DynamicFrame
import org.apache.spark.SparkContext
import scala.collection.JavaConverters._


object GlueApp {
  val MYSQL_5_URI: String = "jdbc:mysql://<jdbc-host-name>:3306/db"
  val MYSQL_8_URI: String = "jdbc:mysql://<jdbc-host-name>:3306/db"
  val ORACLE_11_URI: String = "jdbc:oracle:thin:@//<jdbc-host-name>:1521/ORCL"
  val ORACLE_18_URI: String = "jdbc:oracle:thin:@//<jdbc-host-name>:1521/ORCL"

  // Construct JDBC connection options
  lazy val mysql5JsonOption = jsonOptions(MYSQL_5_URI)
  lazy val mysql8JsonOption = customJDBCDriverJsonOptions(MYSQL_8_URI, "s3://amzn-s3-demo-bucket/mysql-connector-java-8.0.17.jar", "com.mysql.cj.jdbc.Driver")
  lazy val oracle11JsonOption = jsonOptions(ORACLE_11_URI)
  lazy val oracle18JsonOption = customJDBCDriverJsonOptions(ORACLE_18_URI, "s3://amzn-s3-demo-bucket/ojdbc10.jar", "oracle.jdbc.OracleDriver")

  def main(sysArgs: Array[String]): Unit = {
    val spark: SparkContext = new SparkContext()
    val glueContext: GlueContext = new GlueContext(spark)
    val args = GlueArgParser.getResolvedOptions(sysArgs, Seq("JOB_NAME").toArray)
    Job.init(args("JOB_NAME"), glueContext, args.asJava)

    // Read from JDBC database with custom driver
    val df_mysql8: DynamicFrame = glueContext.getSource("mysql", mysql8JsonOption).getDynamicFrame()

    // Read DynamicFrame from MySQL 5 and write to MySQL 8
    val df_mysql5: DynamicFrame = glueContext.getSource("mysql", mysql5JsonOption).getDynamicFrame()
    glueContext.getSink("mysql", mysql8JsonOption).writeDynamicFrame(df_mysql5)

    // Read DynamicFrame from Oracle 11 and write to Oracle 18
    val df_oracle11: DynamicFrame = glueContext.getSource("oracle", oracle11JsonOption).getDynamicFrame()
    glueContext.getSink("oracle", oracle18JsonOption).writeDynamicFrame(df_oracle11)

    Job.commit()
  }

  private def jsonOptions(url: String): JsonOptions = {
    new JsonOptions(
      s"""{"url": "${url}",
         |"dbtable":"test",
         |"user": "admin",
         |"password": "pwd"}""".stripMargin)
  }

  private def customJDBCDriverJsonOptions(url: String, customJdbcDriverS3Path: String, customJdbcDriverClassName: String): JsonOptions = {
    new JsonOptions(
      s"""{"url": "${url}",
         |"dbtable":"test",
         |"user": "admin",
         |"password": "pwd",
         |"customJdbcDriverS3Path": "${customJdbcDriverS3Path}",
         |"customJdbcDriverClassName" : "${customJdbcDriverClassName}"}""".stripMargin)
  }
}
```

------