
エンジニアとしてJavaでのデータベース連携を行う中で、「設定が多すぎる」「構造が複雑すぎる」といったORM(Object-Relational Mapping)への不満を感じたことはありませんか?
実際、HibernateやJPAのようなライブラリは便利である一方、ちょっとしたDB操作にも大量のアノテーションや設定ファイルが必要になるケースが多く、プロジェクトが肥大化する原因にもなりかねません。
そこで私たちは、あえてORMを使わず、SQLを直接制御できる共通DBアクセスクラス群を独自に設計しました。目的はただ一つ、シンプルに、確実に、そして再利用性の高い仕組みを作ることです。
本記事では、その中でも中核となる「DbAccessController」クラスに焦点を当てます。このクラスは、SQLの実行処理(SELECT、UPDATEなど)を統一的な形で扱えるように設計されており、煩雑な設定やフレームワークに縛られることなく、Javaらしい明快なコードでデータベース操作を可能にします。
これから紹介する実装と設計の工夫が、あなたの開発を軽くし、プロジェクトの保守性を大きく向上させる助けになれば幸いです。
本シリーズで使用する共通DBアクセスクラスの検証には、PostgreSQLを想定しています。ソースコード一式はGitHubに保管しており、テーブル構成やクラス構造を含めてすべて確認可能です。再現性のある実装環境を前提として設計されています。
👉 GitHubリポジトリ: https://github.com/bepro-engineer/db-access-core
なお、本記事で紹介している共通DBアクセスクラス群は、エラー処理や操作ログの記録において、共通ログ出力クラス(Logger)との連携を前提としています。SQLの実行結果や例外情報はすべてLogger経由で出力され、運用時のトラブルシュートや監視にも対応できる構成です。
まだLoggerクラスを導入していない場合は、以下の記事を先に参照してください。ログ出力に特化したシンプルな共通クラスを構築する方法を紹介しています。
▶︎【Javaの基礎知識】設定地獄はもう嫌!シンプルな共通ログ出力クラスを作ってみた
DbAccessControllerクラスの概要と構成
共通DBアクセスクラス群の中核を担うのがDbAccessControllerクラスです。このクラスはSQL操作の統一的なインターフェースを提供し、SQLの実行種別ごとに適切な処理クラスを自動的に呼び出す役割を果たします。
実装上は、SQL定義やデータエンティティと密接に連携し、アプリケーションコードをシンプルに保ちつつ、実行パフォーマンスを維持できるよう設計されています。
クラス全体の役割と配置パッケージ

DbAccessControllerクラスは、開発プロジェクト内でデータアクセス機能を統括する役割を持ちます。クライアントコードはSQLの種類(SELECT、INSERT、UPDATE、DELETE)にかかわらず、同一のインターフェースを通じてSQL操作を実行することができます。パッケージ構成は以下の通りです。
要素 | パス |
---|---|
クラス名 | DbAccessController |
パッケージ | com.beengineer.common.db |
関連ファイル | DbAccessFactory, DbAccessSelect, DbAccessInsert など |
ソース全体はこちら
👉 GitHub – db-access-core / data ディレクトリ
主なフィールドと初期化処理
DbAccessControllerクラスでは、SQL定義のキー(sqlId)とエンティティ情報を受け取り、ファクトリクラスを通じて内部的に適切なDbAccessインスタンスを取得します。
内部には、実行種別に応じたマッピングロジックが実装されており、doSelectやdoExecの呼び出し時に適切な処理クラスへ制御を移す構造です。
public static List<?> doSelect(String sqlId, ITableEntity entity) throws SystemException {
return DbAccessFactory.getDbAccess(DbAccessSelectType.SELECT).doSelect(sqlId, entity);
}
このように、呼び出しは一行で済みますが、裏側ではSQL種別の判定からSQL定義の取得、PreparedStatementの組み立てまでが行われています。
使用される補助クラスと依存関係
DbAccessControllerが機能するためには、複数の補助クラスが連携しています。
代表的なものにはDbAccessFactory(実行種別による処理クラスの分岐)、DbTableEntity(SQLにバインドする値情報の保持)、SystemInfo(共通設定取得)があります。
これらのクラスは、単一責任の原則に従ってそれぞれ独立した役割を担いながら、DbAccessController内で統合され、シームレスな処理を実現しています。
クラス名 | 主な役割 |
---|---|
DbAccessFactory | SQL種別に応じたアクセスクラスの生成 |
DbTableEntity | SQLバインド対象データの保持と提供 |
SystemInfo | ログ設定やDB接続設定の取得 |
このように、DbAccessControllerは単体で完結する構造ではなく、各コンポーネントと役割分担を行いながら機能する共通クラス群のハブとして機能しています。
DbAccessFactoryによる実行クラスの動的切り替え

このセクションでは、DbAccessController#doExec() から各実行クラス(Select/Insert/Update/Delete)へ処理を振り分ける「DbAccessFactory」の動作を解説します。このFactoryクラスの役割は、SQLの実行種別(DbAccessSelectType)に応じて適切なクラスインスタンスを生成し、共通的なインターフェースで処理を委譲できるようにすることです。
Java共通DBアクセスクラスの全ソースコードはこちら
👉 GitHub – db-access-core / data ディレクトリ
doExec内で実行対象が切り替わる仕組み
DbAccessControllerのdoExecメソッドでは、受け取った DbAccessSelectType に応じて、DbAccessFactoryを利用して実行クラスを切り替えています。実行対象がSELECT/INSERT/UPDATE/DELETEのいずれかであるかにより、処理経路が決まります。
IDbAccessBean access = DbAccessFactory.getInstance(type);
return access.doExec(sqlId, entity);
この1行のコードで、型に応じたクラスがインスタンス化され、インターフェースを通して共通処理が実行されます。
SELECT/INSERT/UPDATE/DELETEの種別に応じた分岐処理
DbAccessFactory#getInstanceメソッドでは、渡されたEnum値(DbAccessSelectType)に基づいて、対応するクラスを生成するswitch文が実装されています。以下はその一部の例です。
public static IDbAccessBean getInstance(DbAccessSelectType type) throws SystemException {
switch (type) {
case SELECT:
return new DbAccessSelect();
case INSERT:
return new DbAccessInsert();
case UPDATE:
return new DbAccessUpdate();
case DELETE:
return new DbAccessDelete();
default:
throw new SystemException("未定義のタイプが指定されました");
}
}
この構造により、どの処理種別であっても、呼び出し元は常に IDbAccessBean 型で処理でき、コードの再利用性が高まります。
DbAccessFactoryの内部構造とメリット
DbAccessFactoryの本質的な目的は「処理種別ごとのif文を呼び出し元に書かせない」ことです。処理クラスの選定はFactoryに任せ、呼び出し側は getInstance() だけを呼び出せばよいため、以下のメリットがあります。
- 処理分岐が一元化されて保守性が向上する
- 新しい処理クラスを追加する際もFactoryに追加するだけで済む
- 呼び出し元が処理クラスの内部を意識しなくてよい
この設計は、共通DBアクセスクラス全体において非常に重要な役割を担っており、「どのようにdoExecがInsertやUpdateにルーティングされるか」を理解する上で欠かせないポイントです。
DbAccessFactoryの仕組みを「DIOとジョースター家」で例える

この処理は一見難しそうに見えますが、ジョジョの奇妙な冒険で例えると非常に分かりやすくなります。DIOがジョースター家の肉体を乗っ取ったように、SQL種別(たとえば SELECT や INSERT)という"意志"に応じて、それに最も適したクラス(DbAccessSelect など)へ処理が宿るイメージです。
SQL種別は「DIOの魂」で、DbAccessXxx クラスは「憑依される肉体」。DbAccessFactoryは「魂を運ぶスタンド」だと考えれば、内部で何が起きているか直感的に理解しやすくなります。
Factoryパターン
オブジェクト指向の中でも、インターフェースや抽象化(Abstract)は特に難しいとされる考え方です。この考え方を使うと、「動作の決まりだけを決めておいて、実際の中身(処理)は後から自由に入れ替えられる」という柔軟な仕組みが作れます。
これを分かりやすく言えば、「役を演じる台本だけが先にあって、実際に誰が演じるかは後で決める」ような仕組みです。
このような設計の仕方は「Factoryパターン」と呼ばれ、GOF(Gang of Four)の有名なデザインパターンの1つでもあります。SQLの種類ごとに異なる処理を行うこの仕組みは、まさに「中身を入れ替えて使う」Factoryパターンそのものです。
SQL操作の基本:doSelect/doExecの流れ

DbAccessControllerクラスは、SQLの種類に応じて複数の操作メソッドを提供しています。
その中でも、検索処理を担当するdoSelectと、更新系処理を担当するdoExec(およびdoInsert、doUpdate)は、内部でDbAccessFactoryと連携し、適切な処理クラスを自動選択する仕組みになっています。
このセクションでは、まず検索処理であるdoSelectを中心に、その流れと内部構造を詳しく解説します。
doSelectによる検索処理の基本構造

doSelectメソッドは、SQL定義キーとエンティティ情報を受け取り、内部でSELECT文を組み立てて実行する役割を持ちます。戻り値はList型で、実行結果の行データが格納されたオブジェクト群となります。
public static List<?> doSelect(String sqlId, ITableEntity entity) throws SystemException {
return DbAccessFactory.getDbAccess(DbAccessSelectType.SELECT).doSelect(sqlId, entity);
}
このように、実行対象のクラスはDbAccessFactoryを通じて取得され、実行種別であるDbAccessSelectType.SELECTが引数として渡されます。
DbAccessSelectType.SELECTの役割
DbAccessSelectTypeは、SQL実行種別を明示するための列挙型です。doSelectを実行する場合は、DbAccessSelectType.SELECTが使用され、これによりDbAccessFactoryがDbAccessSelectクラスのインスタンスを返すよう制御されます。
public enum DbAccessSelectType {
SELECT,
INSERT,
UPDATE,
DELETE
}
この仕組みにより、SQLの目的に応じた処理クラスが動的に選ばれ、同一のインターフェースで異なる処理を実行可能となります。
DbAccessFactoryを介したクラス分岐の流れ
DbAccessFactoryは、DbAccessSelectTypeの値に基づき、対応する実行クラス(DbAccessSelect、DbAccessInsert、DbAccessUpdate、DbAccessDelete)を返すファクトリクラスです。これにより、呼び出し元はSQL種別だけを意識すればよく、処理クラスの実装に依存しない構成が実現されています。
public static IDbAccessBean getDbAccess(DbAccessSelectType type) throws SystemException {
switch (type) {
case SELECT:
return new DbAccessSelect();
case INSERT:
return new DbAccessInsert();
case UPDATE:
return new DbAccessUpdate();
case DELETE:
return new DbAccessDelete();
}
throw new SystemException("Invalid type");
}
DbAccessSelect#doSelectの内部処理

DbAccessSelectクラスは、doSelectメソッドをオーバーライドし、SQL文の組み立てからPreparedStatementの生成、そしてResultSetの読み取りまでを担当します。SQLは定数から取得され、プレースホルダにはDbTableEntityで指定された値がバインドされます。
String sql = SqlConstants.SQL_AREA_METHOD_GET_MENU_LIST_01;
PreparedStatement pstmt = conn.prepareStatement(sql);
List<Object> bindValues = entity.getValues();
for (int i = 0; i < bindValues.size(); i++) {
pstmt.setObject(i + 1, bindValues.get(i));
}
ResultSet rs = pstmt.executeQuery();
このように、SQLの構文自体は事前に定義され、実行時には値のバインド処理と実行結果のオブジェクト化が行われます。
PreparedStatementによる検索の実行と戻り値
検索処理の実行結果は、ResultSetを順に読み取り、List形式で格納されます。エンティティクラスで定義されたカラム構成に従い、フィールドごとに値がマッピングされていきます。
while (rs.next()) {
Map<String, Object> row = new HashMap<>();
for (String col : entity.getFields()) {
row.put(col, rs.getObject(col));
}
resultList.add(row);
}
こうして取得されたリストは、呼び出し元に返却され、必要に応じて画面表示やロジック処理に利用されます。
doExecによるINSERT/UPDATE/DELETEの実行
DbAccessControllerクラスでは、検索以外のSQL実行処理として、INSERT、UPDATE、DELETEに対応したdoExec系メソッドが用意されています。これらのメソッドはそれぞれ異なる目的を持ちつつも、内部構造としては共通化された実行処理により、効率的かつ保守性の高い構成となっています。
DbAccessController#doInsertの仕組み

)」処理の流れを例にしたアクティビティ図
doInsertメソッドは、新規レコードの追加処理を担当します。実行対象のSQLは定数で指定され、エンティティに設定された値がプレースホルダにバインドされてSQLが発行されます。
public static int doInsert(String sqlId, ITableEntity entity) throws SystemException {
return DbAccessFactory.getDbAccess(DbAccessSelectType.INSERT).doExec(sqlId, entity);
}
このように、doInsertは内部でDbAccessFactoryを通じてDbAccessInsertクラスを取得し、そのdoExecメソッドを呼び出します。呼び出し元から見ると、SQLの実行種別を意識することなく、シンプルな呼び出し形式でINSERT処理を実現できます。
DbAccessController#doUpdateの仕組み

doUpdateメソッドは、既存レコードの更新処理を実行するためのインターフェースです。doInsertと同様に、SQLキーとエンティティ情報を受け取り、内部でDbAccessUpdateのdoExecを呼び出します。
public static int doUpdate(String sqlId, ITableEntity entity) throws SystemException {
return DbAccessFactory.getDbAccess(DbAccessSelectType.UPDATE).doExec(sqlId, entity);
}
この実装により、更新処理もINSERTと同様の構文で呼び出すことができ、呼び出し側のコードはSQLの種別に依存しない形となっています。
DbAccessController#doDeleteの仕組み

DbAccessControllerクラスでは、DELETE処理もINSERTやUPDATEと同様に doExec() を内部的に呼び出す構成となっており、DbAccessDelete クラスに処理を委譲します。
呼び出し元からは doDelete(String sqlId, ITableEntity entity) を使用するだけで、共通インターフェースに従った削除処理が実行されるため、呼び出しコード側はSQL種別を意識せずに済みます。 doDeleteメソッドの主な処理は以下のとおりです。
public List<Map<String, Object>> doDelete(String sqlId, ITableEntity entity) throws SystemException { return doExec(DbAccessSelectType.DELETE, sqlId, entity); }
このように、doDeleteはDbAccessSelectType.DELETEを指定して doExec() を呼び出すだけのラッパーです。実際の削除処理は、DbAccessFactoryが DbAccessDelete クラスを選択し、そこに処理が委譲されます。
SQL文の取得、プレースホルダへのバインド、DELETE文の実行といった具体的な処理はDbAccessDelete#doExec内に記述されています。 この仕組みにより、SQLの種別が異なっても統一された流れで操作できるという、共通DBアクセスクラス群の設計思想が貫かれています。
読者が独自にINSERTやUPDATEと同じようなコードをDELETEにも適用できる点も、学習コストの削減に貢献します。
DbAccessSelectTypeによる実行種別の切り替え
DbAccessFactoryが返すアクセスクラスの切り替えには、DbAccessSelectTypeという列挙型が使用されています。この列挙型はSELECT、INSERT、UPDATE、DELETEという4つの種別を定義しており、それぞれの処理に応じて適切なクラスが選択される仕組みとなっています。
public enum DbAccessSelectType {
SELECT,
INSERT,
UPDATE,
DELETE
}
呼び出し時にこの列挙型の値を指定することで、DbAccessFactoryは該当するアクセスクラスをインスタンス化し、戻り値として提供します。
DbAccessInsert/Update/Delete#doExecの共通構造
INSERT、UPDATE、DELETEの各クラスは、共通インターフェースであるIDbAccessBeanを実装しており、それぞれにdoExecメソッドが定義されています。doExecは、SQLの取得、PreparedStatementの構築、バインド処理、実行処理の4ステップで構成されており、構造はほぼ共通です。
PreparedStatement pstmt = conn.prepareStatement(sql);
List<Object> values = entity.getValues();
for (int i = 0; i < values.size(); i++) {
pstmt.setObject(i + 1, values.get(i));
}
int result = pstmt.executeUpdate();
これにより、各SQL処理の実行ロジックが統一され、再利用性が高まると同時に、保守性も飛躍的に向上しています。
更新結果の戻り値とその意味
doExecメソッドの戻り値は、SQL文の実行結果としての影響行数を表す整数値です。これは、INSERTなら挿入された行数、UPDATEなら更新された行数、DELETEなら削除された行数を意味します。
int updatedRows = DbAccessController.doUpdate("SQL_USER_UPDATE", userEntity);
if (updatedRows == 0) {
// 更新対象が存在しなかった場合の処理
}
このように、実行結果を受け取ることで、アプリケーション側は更新の成否や影響範囲を柔軟に判定できるようになっています。
実用例:定数キーとSQL文のマッピング
DbAccessControllerのような共通データアクセスクラス群を活用するうえで、SQL文の管理は重要な要素となります。
本セクションでは、定数クラスであるSqlConstantsを中心に、SQL文の定義とそれに対応する定数キーの管理方法について解説します。これにより、SQL定義の見通しや再利用性が大きく向上し、保守性の高い設計が実現できます。
SqlConstantsの設計方針
SqlConstantsクラスは、アプリケーション全体で使用するSQL文の識別キーを一元管理するための定数クラスです。
SQL文は通常、複数箇所で再利用されることが多く、直接SQL文を書き込んでしまうと重複・誤記・管理不能といった問題が生じやすくなります。これを回避するために、SQLを一意に特定できる定数名で定義し、それをキーとしてSQL文にアクセスする構成とします。
SQL定義の命名規則と一覧性
定数名には明確な命名規則を設け、SQLの目的や対象テーブルを識別しやすくする工夫が必要です。命名パターンを統一することで、IDEによる補完機能が活用でき、開発速度の向上にもつながります。
定数名 | 命名の意味 | 対象処理 |
---|---|---|
SQL_USER_SELECT_BY_ID | ユーザ情報をIDで検索するSELECT文 | SELECT |
SQL_ORDER_INSERT | 注文情報を新規登録するINSERT文 | INSERT |
SQL_AREA_METHOD_GET_MENU_LIST_01 | エリアごとのメニューリスト取得(業務仕様) | SELECT |
このように、SQLの種類や目的を明示することで、ソースコード上でもその内容が明確に読み取れるようになります。
SQL_IDとSQL文の1対1対応関係
SqlConstantsで定義された定数は、基本的に1対1で実際のSQL文に対応しています。SQL文自体は外部の設定ファイルやリソースから読み込まれる想定ですが、定数名とSQL文のマッピングを明確に維持することが重要です。
public class SqlConstants {
public static final String SQL_USER_SELECT_BY_ID = "SELECT * FROM users WHERE user_id = ?";
public static final String SQL_ORDER_INSERT = "INSERT INTO orders (user_id, item_id) VALUES (?, ?)";
}
このように、SQL文を直接定数に記述する構成であれば、IDE上でも確認しやすく、SQLの変更時に影響範囲を即座に特定できます。
外部ファイルに分離している場合も、定数名とファイル中のエントリキーが一致するよう管理します。 次のセクションでは、LoggerTestで実際に使用されているSQLキーとその定義内容の関係を具体的に紹介していきます。
SQLキーと実SQL文の関係を明示する例
SqlConstantsに定義された定数と、実際に使用されるSQL文との関係は、コードの可読性とデバッグのしやすさに大きな影響を与えます。このセクションでは、具体的な実行コード例をもとに、SQLキーとSQL文がどのように紐づいて動作しているのかを明確にしていきます。
LoggerTestでの実利用例
実際のテストコードでは、SQL定義キーを直接変数に設定し、DbAccessController経由で検索処理を実行しています。以下はLoggerTest2.javaで使用されている一例です。
String sql = SqlConstants.SQL_AREA_METHOD_GET_MENU_LIST_01;
DbTableEntity entity = new DbTableEntity();
entity.set("area_id", "0101");
List<Map<String, Object>> result = DbAccessController.doSelect(sql, entity);
このコードにおいて、SQL_AREA_METHOD_GET_MENU_LIST_01という定数は、SELECT文そのものに対応しています。SQL文は事前にSqlConstants内で定義されており、呼び出し側はSQLの内容を意識することなく、キーを通じて処理を実行できます。
SQL_AREA_METHOD_GET_MENU_LIST_01の具体内容
この定数に紐づくSQL文は、エリアIDに紐づいたメニュー情報を取得するSELECT文です。業務仕様に基づいた抽出条件が含まれており、複数テーブルをJOINして情報を集約するように設計されています。
public static final String SQL_AREA_METHOD_GET_MENU_LIST_01 =
"SELECT m.menu_id, m.menu_name, a.area_name "
+ "FROM menus m "
+ "JOIN areas a ON m.area_id = a.area_id "
+ "WHERE m.area_id = ?";
このように、SQL文は実行時にエンティティのバインド値と結合され、PreparedStatementとして実行されます。
定数名とSQL文が1対1で定義されていることで、SQL文の内容はクラス内で容易に追跡可能となり、トラブル発生時の確認も迅速に行えます。
また、SQLキーの名前がそのまま意味を表すように命名されているため、コード全体の可読性と保守性を高めることにもつながります。
DbTableEntityによる値のバインド動作
SQL文を安全かつ柔軟に扱うためには、プレースホルダを使ったバインド処理が欠かせません。DbTableEntityはそのバインド値を保持するための専用クラスで、SQLの構文生成やPreparedStatementへのセット操作と密接に連携します。このセクションでは、DbTableEntityがどのように値を保持し、SQL文に埋め込まれていくかを具体的に解説します。
setメソッドで値を登録する仕組み
DbTableEntityでは、対象となるカラム名と値をセットで登録するためのsetメソッドが用意されています。このメソッドを使って、SQL実行時に必要なバインド値を事前に保持しておきます。
DbTableEntity entity = new DbTableEntity();
entity.set("user_id", "A001");
entity.set("user_name", "田中 太郎");
この例では、user_idとuser_nameという2つのカラムに対して、それぞれの値がバインド対象として設定されています。内部的にはLinkedHashMapなどの順序保持型マップで管理されており、後続処理に影響する登録順を維持しています。
getFields/getValuesでの出力順制御
DbTableEntityに登録されたデータは、getFieldsおよびgetValuesメソッドによって、順序を保持したまま抽出されます。getFieldsはカラム名のリストを、getValuesは対応する値のリストを返す構造になっており、これがそのままSQL構文の生成とバインド処理に使用されます。
List<String> fields = entity.getFields();
List<Object> values = entity.getValues();
ここで取得された順序は、SQLのINSERTやUPDATEの構文において、カラム名の列挙順やプレースホルダの並び順として活用されます。この順番が一致しないと、バインド処理で意図しないフィールドに値が入ってしまうため、設計上の一貫性が非常に重要になります。
SQLへのプレースホルダ展開の流れ
実際のSQL文では、DbTableEntityから取得されたフィールド情報と値をもとに、SQL構文が自動生成されます。INSERT文の例で見ると、以下のような処理が行われます。
String sql = "INSERT INTO users (" +
String.join(", ", entity.getFields()) +
") VALUES (" +
entity.getFields().stream().map(f -> "?").collect(Collectors.joining(", ")) +
")";
生成されたSQLは次のようになります。
INSERT INTO users (user_id, user_name) VALUES (?, ?)
その後、getValuesで取得した順序通りの値を、PreparedStatementにバインドしていきます。
PreparedStatement pstmt = conn.prepareStatement(sql);
List<Object> values = entity.getValues();
for (int i = 0; i < values.size(); i++) {
pstmt.setObject(i + 1, values.get(i));
}
このように、DbTableEntityはSQL構文とバインド値の両面において中心的な役割を担い、プレースホルダによる安全なSQL実行を支える仕組みとなっています。
まとめ
この記事では、DbAccessControllerを中心とする共通DBアクセスクラス群の構造と動作について、実際のコードとともに詳しく解説してきました。ここでは、その全体像を振り返りつつ、各技術要素の役割とメリットをセクションごとに整理します。
- 共通DBアクセスクラス群の本質
DbAccessControllerは、SELECT・INSERT・UPDATE・DELETEといった基本的なSQL操作を、統一インターフェースで実現するために設計された中心的なクラスです。呼び出し元のコードはSQL種別に依存せず、どのSQLであっても同じように処理を記述できます。この一貫性が、開発工数の削減とコードの見通しの良さにつながります。 - SQLの定数化による利点
SQL文をSqlConstantsで一元管理することにより、SQLの所在や使用箇所を簡単に把握できるようになります。SQLキーとSQL文を1対1で対応させることで、IDEによる補完や、開発チーム全体でのルール共有が容易になります。特に複数人で開発するプロジェクトにおいて、統一された命名規則のある定数管理は強力な武器となります。
- 値バインドの堅牢性
DbTableEntityを利用することで、SQLプレースホルダにバインドする値を順序付きで安全に保持できます。getFieldsとgetValuesのペアにより、SQLの構文生成とPreparedStatementへのバインドが正確に連携される設計です。この構造により、バインドミスや実行エラーのリスクが極めて小さくなります。 - doSelect / doExec の明確な責務分離
検索系処理(doSelect)と更新系処理(doExec)は、メソッドレベルで明確に分離されています。さらに、doInsert・doUpdateといった用途別の明示的なメソッドが用意されており、呼び出し元で意図をはっきりさせた実装が可能です。これは保守性の観点から非常に優れた設計といえます。
この共通DBアクセスクラスは、SELECT / INSERT / UPDATE / DELETE をそれぞれ個別のクラスに分離し、処理の責務を明確にしたシンプル構造で構成されています。
SQLをJavaコードに直接埋め込まず、DataBeanとFactoryクラスで動的に操作を切り替える設計のため、業務ロジックとSQL処理の分離、テスト性、再利用性に優れた作りとなっています。
Java共通DBアクセスクラスの全ソースコードはこちら
👉 GitHub – db-access-core / data ディレクトリ
次の第3回では、共通DBアクセスクラスを支える重要な土台である「DB接続の最適化」に焦点を当てます。複数の処理で接続を共通化しつつ、安定性とパフォーマンスを両立させるための接続プールの構築方法を解説しています。システム全体の信頼性を支えるインフラ層の設計に興味がある方は、ぜひご確認ください。
▶︎ORMにはうんざり!第3回:JavaでDB接続の最適化と共通プールの構築