概要
Spring MVCでは「データソース」と「データアクセスフレームワーク」を使用してDBアクセスを行う。
今回はそれぞれの概念について簡単にまとめた。
また、「データアクセスフレームワーク」を使用しない場合についてもあわせて紹介する。
データソース
データソースとは、DBやその他のデータストレージへの接続に必要な情報を保持するオブジェクトのこと。
データソースは以下のような役割を持つ。
・コネクションプーリング: 事前にDB接続情報を保持し、必要に応じて再利用する
・トランザクション管理: 整合性を保ちながら、DBへの複数の操作を一連の処理として扱う
定義方法
データソースは以下のような情報を設定して定義する。
・ポート番号: DBサーバーが通信に使用するポート
・DB名: 接続する特定のDBの名前
・ユーザー名とパスワード: DBへのアクセスに必要な認証情報
Spring MVCではデータソースをDIコンテナで管理する。
そのため、上記のような情報をBean定義ファイルに設定する必要がある。
データアクセスフレームワーク
データアクセスフレームワークの目的は、アプリケーションとDB間のやり取りを簡素化し、効率化するためにある。
Spring MVCでは、主に以下のようなものがある。
・Spring Data JPA
・Spring + MyBatis
それぞれ使用方法については別の記事で紹介する。
フレームワークを使用しない場合
データアクセスフレームワークを使用しない場合、JDBC API(Javaの標準APIであるJDBC)を使用してDBアクセスを行う。
データアクセスフレームワークと異なり、手動で冗長な処理を記載することになる。
※DBアクセス例外やコネクションクローズ等を手動で記載する必要がある等
以下、データアクセスフレームワークを使用しない例を紹介する。
コネクション情報の取得
データアクセスを行うためのコネクション情報を取得する。
クラスを分ける必要はないが、ここでは役割をわかりやすくするためユーティリティクラスにDBの接続情報取得メソッドを用意する。
SqlConnector.java
public class SqlConnector {
/** DB接続情報 */
private static String DB_URL = "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1";
private static String DB_USER = "";
private static String DB_PASS = "";
/**
* コネクション情報を返却する
* @return コネクション情報
* @throws SQLException
*/
public static Connection getCon() throws SQLException{
try {
// JDBCドライバーのクラスをロード ①
Class.forName("org.h2.Driver");
} catch (ClassNotFoundException e) {
throw new SQLException("JDBCドライバーのロードに失敗しました", e);
}
return DriverManager.getConnection(DB_URL, DB_USER, DB_PASS); // ②
}
}
Class.forName(“org.h2.Driver”);
org.h2.Driverというクラスを初期化し、DriverManagerに登録される。
※DBの種類ごとにDBアクセスを行うドライバーが異なる
①で初期化したドライバクラスを使用してDBに接続し、DB接続情報を保持したオブジェクトを返却する。
DAOクラス
コネクション情報を取得し、JDBC APIを介してDB接続を行う。
以下ではシンプルな「全検索」、「1件検索」、「1件挿入」の処理の例を紹介する。
どのメソッドも細かい説明はしないが、フレームワークを使用しないと冗長な処理を記述することになる。
StudentDao
public class StudentDao {
/**
* 全件検索
* @return 生徒情報リスト
*/
public List<Student> findAll() {
// SQL文
String sql = "SELECT * FROM student";
// SQL取得結果リスト
var students = new ArrayList<Student>();
// DB接続~SQL実行結果取得
try (Connection conn = SqlConnector.getCon();
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
// 各カラム情報を取得
int id = rs.getInt("id");
int grade = rs.getInt("grade");
String name = rs.getString("name");
String className = rs.getString("class_name");
String memo = rs.getString("memo");
var student = new Student(id, grade, name, className, memo, null);
// 生徒情報を格納
students.add(student);
}
} catch (SQLException e) {
throw new CustomDataAccessException("DB例外発生");
}
return students;
}
/**
* 指定したidに紐づく生徒情報の取得
* @param id 生徒ID
* @return 生徒情報
*/
public Optional<Student> findById(int id) {
// SQL文
String sql = "SELECT * FROM student WHERE id = ?";
// DB接続~SQL実行結果取得
try (Connection conn = SqlConnector.getCon();
PreparedStatement stmt = conn.prepareStatement(sql)) {
// IDをクエリパラメータとして設定
stmt.setInt(1, id);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
// 各カラム情報を取得
int grade = rs.getInt("grade");
String name = rs.getString("name");
String className = rs.getString("class_name");
String memo = rs.getString("memo");
var student = new Student(id, grade, name, className, memo, null);
return Optional.of(student);
}
}
} catch (SQLException e) {
throw new CustomDataAccessException("DB例外発生: " + e.getMessage(), e);
}
return Optional.empty();
}
/**
* 生徒情報を追加
* @param student 追加する生徒情報
* @return true:追加成功、false:追加失敗
*/
public boolean addStudent(Student student) {
// SQL文(プレースホルダーを使用)
String sql = "INSERT INTO student (grade, name, class_name, memo) VALUES (?, ?, ?, ?)";
// DB接続~SQL実行
try (Connection conn = SqlConnector.getCon();
PreparedStatement stmt = conn.prepareStatement(sql)) {
// プレースホルダーに値をセット
stmt.setInt(1, student.getGrade());
stmt.setString(2, student.getName());
stmt.setString(3, student.getClassName());
stmt.setString(4, student.getMemo());
// SQL実行
int result = stmt.executeUpdate();
// 追加された行数をチェック
return result > 0;
} catch (SQLException e) {
// 例外処理
e.printStackTrace();
return false;
}
}
}
DBアクセス
以下のようにDAOを呼び出してDBアクセスを行うことができる。
SampleMain.java
public class SampleMain {
public static void main(String[] args) {
// H2DBのテーブル初期化のため呼び出し
try (ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
var dao = new StudentDao();
// 1件追加
var student = new Student(0, 2, "木村五郎", "2-B", "質実剛健", null);
boolean addResult = dao.addStudent(student);
System.out.println("挿入結果:" + addResult);
// 全件検索
dao.findAll().forEach(s -> System.out.println(s));
// 1件検索
var resStudent = dao.findById(3).orElse(null);
System.out.println(resStudent);
}
}
}