46.7K Views
August 06, 22
スライド概要
JDBCに関して基礎から解説した資料です。
https://javado.connpass.com/event/251865/
Java、Spring、IntelliJ IDEA
入門JDBC 〜Java RDBアクセスの仕組み〜 (株)クレディセゾン 多田真敏 2022年8月6日 Java Doでしょう #201
このセッションについて ▸ JavaでRDBアクセスするための仕組みを、 基礎から丁寧に解説します ▸ 対象者 ▸ [必須] Javaの基本的な文法を知っている方 ▸ [必須] 基本的なSQLを知っている方 ▸ JDBCを初めて使う方 2
使用しているバージョン ▸ JDK 17 ▸ PostgreSQL 14.2 ▸ PostgreSQL JDBC Driver 42.4.0 多少バージョンが違っていても、 内容は変わらないと思います 3
自己紹介 ▸ 多田真敏 (@suke̲masa) ▸ 元・研修トレーナーの Javaプログラマー ▸ IntelliJ IDEAが好き ▸ 日本Springユーザ会(JSUG) スタッフ 4
目次 ① JDBCとは ② JDBCでDBに接続する ③ JDBCでSELECTする ④ JDBCでINSERT・UPDATE・DELETEする ⑤ 高度なトピックス ⑥ ORマッパーとは 5
目次 ① JDBCとは ② JDBCでDBに接続する ③ JDBCでSELECTする ④ JDBCでINSERT・UPDATE・DELETEする ⑤ 高度なトピックス ⑥ ORマッパーとは 6
RDB製品は世の中にたくさんある ▸ Oracle、Db2、SQL Server、MySQL、 PostgreSQL、・・・ ▸ それぞれのRDBで、 アクセスするための通信プロトコルは異なる ▸ どんなRDBでも、同じJavaコードで アクセスできたら嬉しい! 7
JDBCとは ▸ DBアクセスのための標準API ▸ インタフェースの集合=クラスはほとんど無い ▸ インタフェースを実装したクラス群は、 各RDBベンダーによってJAR形式で提供される = JDBCドライバー ▸ 開発者はインタフェースのみ使ってコードを書く → RDBが変わっても、コードは変更しなくてよい! 8
Javadocを見ると ▸ JDBCの実体 java.sqlパッケージ クラスは少し インタフェースはいっぱい https://docs.oracle.com/javase/jp/17/docs/api/java.sql/java/sql/package-summary.html 9
主要なインタフェース ▸ Connection ▸ RDBとの接続を表す ▸ PreparedStatement ▸ RDBに向けてSQLを実行する ▸ ResultSet ▸ SELECTによる検索結果を保持する 10
PostgreSQL JDBC Driver ▸ PostgreSQLの JDBCドライバー = PostgreSQL用の 実装クラスの集合 Connectionインタフェースの 実装クラス PreparedStatement インタフェースの実装クラス ResultSetインタフェースの 実装クラス 11
PostgreSQL JDBC Driverを使う ▸ Mavenの場合は、 pom.xmlにdependencyを追加 <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.4.0</version> <scope>runtime</scope> </dependency> ▸ Mavenとは何ぞやについては、 @yusukeさんの動画をご確認ください 12
目次 ① JDBCとは ② JDBCでDBに接続する ③ JDBCでSELECTする ④ JDBCでINSERT・UPDATE・DELETEする ⑤ 高度なトピックス ⑥ ORマッパーとは 13
JDBCでDBに接続する ▸ 検索などの操作を行うには、 ますDBに接続することが必要 ▸ DBに接続するにはDriverManagerの getConnection()を利用する ▸ DB操作が終わったら、 必ずConnectionのclose()で接続を終了する 14
getConnection()の引数 ① url ▸ DBのIPアドレス(orホスト名)・ポート番号・データベース名を指定 ▸ PostgreSQLの場合は jdbc:postgresql://IPアドレス:ポート番号/データベース名 ② ユーザー名 ▸ DBのユーザー名 RDB製品によって書き方が 微妙に異なるので注意 ③ パスワード ▸ DBのパスワード 15
良くないコード例
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectSample {
public static void main(String[] args) throws SQLException {
String url = "jdbc:postgresql://localhost:5432/postgres";
String username = "postgres";
String password = "postgres";
Connection connection =
DriverManager.getConnection(url, username, password);
System.out.println("DBに接続しました!");
connection.close();
System.out.println("DB接続を終了しました!");
}
}
getConnection()で
例外が発生すると
close()されない!
16
fi
まあまあ良いコード例
Connection connection = null;
try {
connection = DriverManager.getConnection(url, username, password);
System.out.println("DBに接続しました!");
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
nally節で確実にclose()
if (connection != null) {
try {
connection.close();
System.out.println("DB接続を終了しました!");
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
※スペースの都合上、一部省略しています
17
良いコード例
try (Connection connection =
DriverManager.getConnection(url, username, password)) {
System.out.println("DBに接続しました!");
} catch (SQLException e) {
throw new RuntimeException(e);
}
try-with-resourcesで
自動的にclose()
18
目次 ① JDBCとは ② JDBCでDBに接続する ③ JDBCでSELECTする ④ JDBCでINSERT・UPDATE・DELETEする ⑤ 高度なトピックス ⑥ ORマッパーとは 19
SELECTの流れ ① 文字列でSELECT文を書く ② ConnectionのprepareStatement()で PreparedStatementを取得する ③ PreparedStatementのexecuteQuery()で SELECT文を実行する → 戻り値のResultSetが検索結果を保持する ④ ResultSetのnext()で1行ずつ移動する ‣ 次の行がある場合はtrue、無ければfalseを返す ⑤ ResultSetのgetXxx()で1列ずつ値を取りだす 20
SELECTの流れ ① 文字列でSELECT文を書く String sql = """ SELECT id, name, department, salary, joined_date FROM employee WHERE department = AND salary >= ORDER BY id """; ? ? パラメーターに したい部分は?にする 21
SELECTの実行 ② ConnectionのprepareStatement()で PreparedStatementを取得する PreparedStatementも 自動クローズされるようにする 引数にSQLを指定 try (Connection connection = DriverManager.getConnection( url, username, password); PreparedStatement statement = connection.prepareStatement(sql)) { statement.setString(1, "乃木坂46"); statement.setInt(2, 250000); 1番目の?に"乃木坂46"を代入 2番目の?に250000を代入 22
SELECTの実行 ③ PreparedStatementのexecuteQuery()で SELECT文を実行する → 戻り値のResultSetが検索結果を保持する try (ResultSet resultSet = statement.executeQuery()) { ResultSetも 自動クローズされるようにする 23
SELECTの実行 ④ ResultSetのnext()で1行ずつ移動する ‣ 次の行がある場合はtrue、無ければfalseを返す try (ResultSet resultSet = statement.executeQuery()) { while (resultSet.next()) { ... } 1行移動 next() -> true next() -> true id name ・・・ 1 Alice ・・・ 2 Bob ・・・ next() -> false 24
SELECTの実行 ⑤ ResultSetのgetXxx()で1列ずつ値を取りだす 列名を指定して、 1列ずつ値を取得 try (ResultSet resultSet = statement.executeQuery()) { while (resultSet.next()) { int id = resultSet.getInt("id"); String name = resultSet.getString("name"); String department = resultSet.getString("department"); int salary = resultSet.getInt("salary"); LocalDate joinedDate = resultSet.getObject("joined_date", LocalDate.class); System.out.println("%d, %s, %s, %d, %s".formatted( id, name, department, salary, joinedDate)); } 25
目次 ① JDBCとは ② JDBCでDBに接続する ③ JDBCでSELECTする ④ JDBCでINSERT・UPDATE・DELETEする ⑤ 高度なトピックス ⑥ ORマッパーとは 26
INSERT・UPDATE・DELETEの流れ ① ConnectionのprepareStatement()で PreparedStatementを取得する ② PreparedStatementのexecuteUpdate() でINSERT・UPDATE・DELETEを実行する → 戻り値は追加・更新・削除された行数 27
INSERTの実行 String sql = """ INSERT INTO employee(name, department, salary, joined_date) VALUES(?, ?, ?, ?) """; Employee newEmployee = new Employee(null, "髙橋未来虹", "日向坂46", 200000, LocalDate.of(2020, 2, 16)); try (Connection connection = DriverManager.getConnection( url, username, password); PreparedStatement statement = connection.prepareStatement(sql)) { statement.setString(1, newEmployee.name()); statement.setString(2, newEmployee.department()); statement.setInt(3, newEmployee.salary()); statement.setObject(4, newEmployee.joinedDate()); int rows = statement.executeUpdate(); System.out.println(rows + "行追加しました。"); } catch (SQLException e) { throw new RuntimeException(e); } 28
UPDATEの実行 String sql = """ UPDATE employee SET name = ?, department = ?, salary = ?, joined_date = ? WHERE id = ? """; Employee newEmployee = new Employee(7, "賀喜遥香", "乃木坂46", 270000, LocalDate.of(2018, 8, 19)); try (Connection connection = DriverManager.getConnection( url, username, password); PreparedStatement statement = connection.prepareStatement(sql)) { statement.setString(1, newEmployee.name()); statement.setString(2, newEmployee.department()); statement.setInt(3, newEmployee.salary()); statement.setObject(4, newEmployee.joinedDate()); statement.setInt(5, newEmployee.id()); int rows = statement.executeUpdate(); System.out.println(rows + "行更新しました。"); } catch (SQLException e) { throw new RuntimeException(e); } 29
DELETEの実行 String sql = """ DELETE FROM employee WHERE id = ? """; int id = 7; try (Connection connection = DriverManager.getConnection( url, username, password); PreparedStatement statement = connection.prepareStatement(sql)) { statement.setInt(1, id); int rows = statement.executeUpdate(); System.out.println(rows + "行削除しました。"); } catch (SQLException e) { throw new RuntimeException(e); } 30
目次 ① JDBCとは ② JDBCでDBに接続する ③ JDBCでSELECTする ④ JDBCでINSERT・UPDATE・DELETEする ⑤ 高度なトピックス ⑥ ORマッパーとは 31
トランザクション ▸ トランザクション機能を有効化するには、 ConnectionのsetAutoCommit()に falseを指定する ▸ これにより自動コミット(1行ごとのコミット) が無効化される ▸ Connectionのcommit()でコミット、 rollback()でロールバック 32
トランザクション
String sql1 = """
INSERT INTO employee(name, department, salary, joined_date)
VALUES('髙橋未来虹', '日向坂46', 200000, '2020-02-16')
""";
String sql2 = """
INSERT INTO employee(name, department, salary, joined_date)
VALUES('賀喜遥香', '乃木坂46', 270000, '2018-08-19')
""";
boolean throwException = false;
Connection connection = null;
PreparedStatement statement1 = null;
PreparedStatement statement2 = null;
try {
connection = DriverManager.getConnection(
url, username, password);
connection.setAutoCommit(false);
※try-with-resourcesでは書きづらかったため、わざと使っていません
33
トランザクション statement1 = connection.prepareStatement(sql1); int rows1 = statement1.executeUpdate(); if (throwException) { throw new Exception("わざと例外"); } statement2 = connection.prepareStatement(sql2); int rows2 = statement2.executeUpdate(); connection.commit(); System.out.println("コミットしました。"); } catch (Exception e) { if (connection != null) { try { connection.rollback(); System.out.println("ロールバックしました。"); } catch (SQLException e2) { throw new RuntimeException(e2); } } throw new RuntimeException(e); } 34
DDL文の実行 ▸ PreparedStatementのexecute()で実行 String sql = """ CREATE TABLE test( id INTEGER PRIMARY KEY, content VARCHAR(32) ) """; try (Connection connection = DriverManager.getConnection( url, username, password); PreparedStatement statement = connection.prepareStatement(sql)) { statement.execute(); System.out.println("テーブルを作成しました。"); } catch (SQLException e) { throw new RuntimeException(e); } 35
DatabaseMetaData ▸ 接続中のデータベースに関する情報を取得できる ▸ getDatabaseProductName() : DB製品名 ▸ getTables() : テーブル一覧(型はResultSet) 36
DatabaseMetaDataの利用 try (Connection connection = DriverManager.getConnection( url, username, password)) { DatabaseMetaData databaseMetaData = connection.getMetaData(); ResultSet tables = databaseMetaData.getTables( null, null, null, new String[]{"TABLE"}); while (tables.next()) { String tableName = tables.getString("TABLE_NAME"); System.out.println(tableName); } } catch (SQLException e) { throw new RuntimeException(e); } 実行結果 employee 37
ResultSetMetaData ▸ 検索結果に関する情報を取得できる ▸ getTableName() : テーブル名 ▸ getColumnCount() : 検索した列数 ▸ getColumnName() : 検索した列名 38
ResultSetMetaDataの利用
try (ResultSet resultSet = statement.executeQuery()) {
ResultSetMetaData metaData = resultSet.getMetaData();
String tableName = metaData.getTableName(1);
System.out.println("テーブル名: " + tableName);
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i);
System.out.println("列名: " + columnName);
}
} // 以下省略
実行結果
テーブル名: employee
列名: id
列名: name
列名: department
列名: salary
列名: joined_date
39
IntelliJ IDEAのDatabase Tool ▸ DatabaseMetaDataや ResultSetMetaDataで 作られているのかも? ▸ IntelliJ IDEAはJava製 40
目次 ① JDBCとは ② JDBCでDBに接続する ③ JDBCでSELECTする ④ JDBCでINSERT・UPDATE・DELETEする ⑤ 高度なトピックス ⑥ ORマッパーとは 41
JDBCは面倒(特に検索) ① Connectionを作って ② PreparedStatementを作って ③ 検索を実行してResultSetを受け取って ④ 1行ずつ移動して ⑤ 1列ずつ値を取得して ⑥ インスタンスに変換して ⑦ finallyで忘れずにクローズ 😫 42
ORマッパーとは ▸ Object/Relational Mapper ▸ DBアクセスを簡潔に記述できる ▸ 内部ではJDBCとリフレクションを使っている ▸ 参考記事: 入門Javaのリフレクション ORマッパー 業務 コード JDBC RDB 43
OSSのORマッパーたち ▸ Spring JDBC ▸ MyBatis ▸ Doma ▸ DBFlute ▸ jOOQ ▸ Hibernate ▸ ・・・など 44
Spring JDBCの例
String sql = """
SELECT id, name, department, salary, joined_date
FROM employee
WHERE department = ?
AND salary >= ?
ORDER BY id
""";
DataSource dataSource = new DriverManagerDataSource(
url, username, password);
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
List<Employee> employeeList = jdbcTemplate.query(sql,
new DataClassRowMapper<>(Employee.class), "乃木坂46", 250000);
for (Employee employee : employeeList) {
System.out.println(employee);
}
45
ORマッパーでこれだけ簡潔になる JDBC: 21行 Spring JDBC: 1行 46
ORマッパーの選び方 https://www.docswell.com/s/MasatoshiTada/596WW5-how-to-choose-java-orm 47
まとめ ▸ JDBCはインタフェースの集合。 その実装クラスの集合がJDBCドライバー ▸ JDBCはDML/DDLの実行、トランザクション、 メタデータの取得などの機能を備えている ▸ ORマッパーはJDBCとリフレクションで作られている ▸ 素のJDBCをそのまま現場で使うことはほぼ無い (はず) ▸ しかし、仕組みを知っておけば色々と応用が効く 48