Java 11 Gold 取得に向けた学習記録
Java JDBC
Javaプログラムから
- データベースへの接続
- SQLクエリの実行
- 結果操作
を行うためのAPIをJDBC(Java Database Connectivity)と言う。
データベースには、
- Oracle Database
- SQL Server
- PostgreSQL
- MySQL
など各社から開発された複数の製品があるが、JDBCは特定の製品に依存しない作りになっている。そのため利用時に特定の製品を意識する必要がない。
具体的にはインターフェース、例外クラスがjava.sql
パッケージ、javax.sql
パッケージから提供される。これらはどちらもjava.sql
モジュールに属している。
ドライバ
java.sql
パッケージ、javax.sql
パッケージから提供されるインターフェースの実装クラスは、RDBMS製品の開発元であるベンダからドライバとして提供されている。
そのため、開発時は使用したいDBのベンダからドライバをダウンロードなどによって取得し、アプリケーションに組み込む必要がある。
データベースとの接続
Connection
データベースとの接続を表現するインターフェース。
インターフェースを実装したクラスはDriverManagerクラスのstatic
メソッドを介して取得される。
データベース操作のためのセッション(接続 ▶️ 操作 ▶️ 切断
)を確立し、トランザクション制御を行うために利用される。
主に次のような抽象メソッドが定義されている。
-
Statement createStatement()
- SQLを送信するための
Statemant
インターフェースを実装したクラスのオブジェクトを生成する
- SQLを送信するための
-
PreparedStatement prepareStatement(String sql)
- パラメータ付きのSQl文を送信するための
PreparedStatement
インターフェースを実装したクラスのオブジェクトを生成する
- パラメータ付きのSQl文を送信するための
-
void setAutoCommit(boolean autoCommit)
- 自動コミットモードの ON / OFF を設定する
-
void commit()
- コミットする
-
void rollback()
- ロールバックする
-
void close()
- 接続を閉じる
DriverManager
ドライバをロード、管理し、データベース接続を確立するためのstatic
メソッドが定義されたクラス。
下記のメソッドを使用することで Connection
インターフェースの実装クラスのオブジェクトを取得することができる。
-
static Connection getConnection(String url)
-
url
: 接続文字列
-
-
static Connection getConnection(String url, String user, String password)
-
user
: データベースのユーザー名 -
password
: データベースのユーザーパスワード
-
-
static Connection getConnection(String url, Properties info)
-
info
: パラメータをまとめたjava.util.Properties
オブジェクト(ユーザ名、パスワードは最低限必要)
-
String url = "jdbc:mysql://localhost:3306/mydatabase";
try (Connection conn = DriverManager.getConnection(url)) {
// 処理
} catch (SQLException e) {
throw new RuntimeException(e);
}
ドライバをロードする際に、ドライバの実体がクラスパス上に配置されていないと例外が発生する。
接続文字列
Connection String
データベースに接続するための情報を含む文字列を接続文字列と言う。
接続文字列にはDBの種類、ホスト名、ポート番号、DB名、ユーザ名、パスワード等が含まれる。
String url = "jdbc:<データベース種類>://<ホスト名>:<ポート番号>/<データベース名>?<オプション>";
データベース種類にはmysql
、postgresql
、oracle
、sqlserver
などがある。
SQL文の送信
SQLクエリをdデータベースに送信するためのインターフェースには、クエリの種類に応じて Statement
、PreparedStatement
、CallableStatement
の3種類がある。
-
Statement
- パラメータなしのSQL
- 文字列としてDBに送信後、DB側でコンパイルを行う
-
PreparedStatement
- パラメータ付きのSQL
- プリコンパイル(prepared=準備)してからDBに送信される(パフォーマンスが良い)
-
CallableStatement
- ストアドプロシージャ用
PreparedStatement
パラメータ付きでSQL文を実行するためのインターフェース。SQL文にはパラメータ(?
)を含めることができる。
String sql = "INSERT INTO table_1 (column_1, column_2, column_3) VALUES (?, ?, ?)";
try (Connection conn = DriverManager.getConnection(url);
PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
// sql に含まれる1つ目のパラメータ(?)に値を設定する
preparedStatement.setInt(1, 100);
// sql に含まれる2つ目のパラメータ(?)に値を設定する
preparedStatement.setString(2, "aaa");
// sql に含まれる3つ目のパラメータ(?)に値を設定する
preparedStatement.setDouble(3, 1.23);
// SQLクエリの実行
preparedStatement.execute();
} catch (SQLException e) {
}
SQL実行
PreparedStatement
に定義されている実行系メソッド。
execute()
boolean execute()
- あらゆるSQL文を実行できる
- 戻り値が
ResultSet
の場合、true
を返す
executeQuery()
ResultSet executeQuery()
-
SELECT
文の実行によって生成されたReseultSet
オブジェクトを返す
executeUpdate()
int executeUpdate()
-
INSERT
文、UPDATE
文、DELETE
文を実行する - 操作を行った行数を返す
クエリ結果の取得
ResultSet
クエリの結果を表すインターフェース。
データベースから情報を取得した後、Javaプログラム内で操作するためのメソッドが定義されている。
ResultSet
では、SQLクエリ結果を行ごとに操作するためのカーソルを扱う。
カーソルは始め、最初の要素の一つ手前に位置していて next()
メソッドによって1ずつ移動させることができる。それ以上行が存在しない場合、next()
は false
を返す。
try (Connection conn = DriverManager.getConnection(url);
PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
}
} catch (SQLException e) {
}
カーソル移動後、特定の列の情報を取得するためには getter 系メソッドを使用する。引数には列のインデックスを指定する。インデックスの最小値は 1
(0列目は存在しない)。
while (resultSet.next()) {
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
int age = resultSet.getInt(3);
}
例外
SQLException
JDBC操作中に発生する例外を表すクラス。データベースに関するエラーが発生した場合にthrow
される。
実践学習の記録
実際に手を動かしながら学んだ記録まとめ。
環境
OS : macOS
PostgreSQL version: 16.3
Java version : 11
データベースをインストールする
インストール可能な PostgreSQL のバージョンを確認する。インストール済みの場合、バージョンの横に✅が表示される。
$ brew search postgresql
==> Formulae
postgresql@10 postgresql@12 postgresql@14 postgresql@16 postgrest
postgresql@11 postgresql@13 postgresql@15 qt-postgresql
==> Casks
navicat-for-postgresql posture-pal
一番新しいバージョンを指定してインストールを行う。
$ brew install postgresql@16
インストールが成功していれば、バージョンが出力される。
$ psql --version
zsh: command not found: psql
見つからないため、確かにインストールされたことを確認する。
# brew list | grep postgresql
postgresql@16
確かにインストールされている。
インストール先を確認する。
$ find /opt/homebrew -name psql
/opt/homebrew/Cellar/postgresql@16/16.3/bin/psql
環境変数 $PATH を確認し、インストール先のパスが含まれているかを確認する。
$ echo $PATH
/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin
直接パスを指定して実行してみる。
$ /opt/homebrew/Cellar/postgresql@16/16.3/bin/psql --version
psql (PostgreSQL) 16.3 (Homebrew)
シンボリックリンクが正しく作成されていない場合、手動で作成する。
$ ln -s /opt/homebrew/Cellar/postgresql@16/16.3/bin/psql /opt/homebrew/bin/psql
実際のインストール先のパスに対して、Homebrewが使用している$PATH
へのシンボリックリンクを作成したことで解決した。(/opt/homebrew/bin/psql
はHomebrewのインストールしたパッケージのダウンロード先。)
$ psql --version
psql (PostgreSQL) 16.3 (Homebrew)
ドライバをプロジェクトに追加する
ビルドツールとして Gradle を使用しているため、プロジェクトフォルダ直下のbuild.gradle
のdependencies
ブロックに追加する。
dependencies {
implementation group: 'org.postgresql', name: 'postgresql', version: '42.7.3'
}
サービスを起動する
$ brew services start postgresql@16
パスワードの設定
PostgreSQLのデフォルトのユーザー名はpostgres
であり、パスワードを設定するにはログインが必要。
ログインは以下のコマンドによって行う。パスワードは2回入力が求められる。1回目はOSのユーザーパスワード、2回目が PostgreSQL のパスワード。パスワードはpassword
で入力した。
$ sudo -u postgres psql
Password: OSのパスワード
Password for user postgres: password
psql (16.3 (Homebrew), server 12.9)
Type "help" for help.
postgres=#
ログインに成功すると、
postgres=#
という形式になる。
postgres=# \q
でログアウトできる。
データベースとテーブルを作成する
String url = "jdbc:postgresql://localhost:5432/";
String user = "postgres";
String password = "password";
Properties props = new Properties();
props.setProperty("user", user);
props.setProperty("password", password);
String dbname = "mydatabase";
// データベースの作成
try (Connection conn = DriverManager.getConnection(url, props);
Statement statement = conn.createStatement()) {
String sql = "CREATE DATABASE " + dbname;
statement.executeUpdate(sql);
System.out.println("データベース作成の完了");
} catch (SQLException e) {
System.err.println("データベース作成に失敗: " + e.getMessage());
}
// テーブルの作成
url = "jdbc:postgresql://localhost:5432/" + dbname;
try (Connection conn = DriverManager.getConnection(url, props);
Statement statement = conn.createStatement()) {
String sql = "CREATE TABLE mytable ("
+ "id SERIAL PRIMARY KEY, "
+ "name VARCHAR(20) NOT NULL, "
+ "age INTEGER NOT NULL"
+ ")";
statement.execute(sql);
System.out.println("テーブル作成の完了");
} catch (SQLException e) {
System.err.println("テーブル作成に失敗: " + e.getMessage());
}
サンプルデータを登録する
String url = "jdbc:postgresql://localhost:5432/mydatabase";
String user = "postgres";
String password = "password";
Properties props = new Properties();
props.setProperty("user", user);
props.setProperty("password", password);
try (Connection conn = DriverManager.getConnection(url, props);
PreparedStatement preparedStatement = conn.prepareStatement(
"INSERT INTO mytable (id, name, age) VALUES (?, ?, ?)")) {
String[] names = new String[]{
"name_1",
"name_2",
"name_3",
"name_4",
"name_5",
"name_6",
"name_7",
"name_8",
"name_9",
"name_10",
};
for (int i = 0; i < names.length; i++) {
preparedStatement.setInt(1, i);
preparedStatement.setString(2, names[i]);
preparedStatement.setInt(3, 18 + i * 2);
preparedStatement.executeUpdate();
}
System.out.println("サンプルデータ作成に成功");
} catch (SQLException e) {
System.out.println("サンプルデータ作成に失敗: " + e.getMessage());
}