共通DBアクセスクラス

ORMにはうんざり!第1回:シンプルなJava DBアクセスクラスを考えてみた

ORMや巨大なDBアクセスクラスに、正直うんざりしていませんか?

カラムを1つ追加するだけで、エンティティ、DTO、マッピング、設定ファイル……と何か所も修正が必要になる今のDBアクセス構造に限界を感じている人も多いはずです。

「もっと簡単に、それでいて本番でも使えるDBアクセスクラスは作れないのか?」という疑問から、今回、設定不要・高汎用性・保守性重視のシンプルなクラス構造を実装しました。

エンジニアとして複数の現場でORMを使ってきましたが、「設定の複雑さ」「動作の見えづらさ」に対する違和感はずっと抱えていました。特に自作の社内ツールや小〜中規模の業務システムでは、そこまで高度な抽象化は不要で、むしろ原理が見えて保守しやすい構造が望まれると感じていました。

この共通DBアクセスクラスを作った理由は、部下の研修の負担を減らすためでもあります。Javaの基本文法や構造に集中してもらいたくて、トランザクション処理やロールバックなどのデータベース固有の説明に、毎回数週間も費やすのが精神的に厳しかったのです。
そこで「DBアクセス部分はこう使えば良い」と提示できる仕組みを用意して、学習や開発のコストを最小化しました。

このシリーズでは、そうして作成した共通DBアクセスクラスの構造と意図、使い方を段階的に解説していきます。
なお、大規模案件を見据えた抽象設計ではありませんが、実運用で必要なポイントには対応しています。

この記事では、ORMを使わずにJavaだけで柔軟なDBアクセスを実現する方法と、具体的な実装例を紹介します。

本シリーズで使用する共通DBアクセスクラスの検証には、PostgreSQLを想定しています。ソースコード一式はGitHubに保管しており、テーブル構成やクラス構造を含めてすべて確認可能です。再現性のある実装環境を前提として設計されています。

👉 GitHubリポジトリ: https://github.com/bepro-engineer/db-access-core

DBアクセス処理を簡単にしたい理由

毎回、DBアクセスの実装を始めるたびに思っていました。

「たった1カラム追加するだけで、なんでこんなに面倒なんだ?」と。 エンティティクラス、マッピング定義、DTO、SQL、場合によっては変換用のユーティリティまで…。

一つ追加するたびに、関係するファイルをいくつも開いて手を加えないと動かない。 馬鹿馬鹿しいと思ったことはありませんか?

本当に必要なのは「柔軟な拡張性」や「自動マッピング」ではなく、 「いまこのテーブルに対してSQLを1発投げて結果を取りたい」だけのときだってあるはずです。

毎回ORMを導入して、設定と定義で半日消える作業に、疲れてしまった方も多いのではないでしょうか。

ORMで毎回つまずくポイント

アノテーションの書き方を忘れて、ググる。XMLファイルにマッピング書いたつもりが、微妙に型が合わなくて実行時エラー。 構文的にはOKでも、なぜか詰まる。そういう経験、何度もしてきました。

JPAでもMyBatisでも、機能としては優れているのですが、それ以前に“正しく書けないとまともに動かない”という障壁があります。それが、時間のない案件や、とにかく早く動かしたいときの足かせになります。

しかも、ブラックボックスになりがちです。 裏側で何をしているのか把握できないまま、動いたり動かなかったりする。

結局、SQLを素で書いたほうが早いと思ったことは一度や二度ではありません。

カラム追加で何ファイルも修正する現実

DBのテーブルにカラムを1つ追加しただけで、なぜ5ファイルも修正しなければならないのでしょうか。エンティティ、DTO、マッピングファイル、Serviceの処理、テストデータ…。 忘れてはいけない場所が増えるほど、ミスも増えます。

この流れに毎回付き合うのが、本当に嫌になりました。だからこそ、もっとシンプルで、見ればすぐに理解できて、使い回しもしやすい仕組みが欲しかったのです。

今回の構成では、明示的にSQLを書く方針にして、必要な情報の受け渡しにはMapやシンプルなエンティティクラスを使うことで、柔軟性と省力化を両立できるように設計しています。

いちいち定義を増やさずに、テーブル構造の変更にも耐えられる構成を目指しました。

シンプルな仕組みで十分なケースもある

すべての案件で巨大なフレームワークが必要なわけではありません。

「要件は簡単」「使うテーブルも少ない」「納期は明日」。 そんな現場では、わざわざORMを導入して構築するよりも、素直に自前のDBアクセスクラスを書いた方が早くて正確です。もちろん、設計の自由度や移植性を考えるならORMも有効です。

しかし、そこまでの設計が求められていない場面では、過剰な構成は“やりすぎ”になります。

動けばいい、ではなく、すぐに動いて、あとから読んでもわかる構造。これを意識して、必要最小限の部品とシンプルな書き方でまとめました。

今回作ったDBアクセスクラスの要件

今回作成したDBアクセスクラスは、複雑な設定や重たいフレームワークに依存せず、必要なときに必要な処理だけを確実に実行できる仕組みを目指して設計しています。

現場でよくある「小さな機能なのに準備だけで半日消える」といった負担を軽減し、短時間で確実に動作するデータアクセスの基盤を提供します。

学習コストや修正範囲を抑えながら、本番環境でも安心して使える構成にしている点が大きな特徴です。

このクラスで実現したかったこと

開発に着手するとき、まず求められるのは「余計な準備なしで動くこと」です。

ORMのような強力な仕組みは便利ですが、アノテーションや設定ファイルの作成が必ず必要になり、ちょっとした変更でも複数箇所を修正することが発生します。

このクラスでは、そうした重たい前提条件を取り払い、SQLを明示的に書くだけで処理が動く仕組みを整えています。

また、複数の現場で共通して求められるのは「とりあえず動いて、あとから読んでも理解できるコード」です。

今回のクラスは、データの受け渡し方法をMapまたはシンプルなEntityに統一することで、構造が分かりやすく、テーブル構造が変わっても対応しやすい形にしています。

作業量を最小限に抑えつつ、柔軟性と保守性を確保できる点が大きなメリットです。

想定する使い方と対象プロジェクト

今回のクラスは、小規模から中規模のプロジェクトを中心に想定しています。

たとえば、社内ツール、バッチ処理、中間DBとのデータ連携、管理画面用の簡易なデータ取得などが代表的な利用シーンです。

これらの業務では、多機能なORMを導入するよりも、必要なSQLを定義して結果を扱える仕組みのほうが効率的で、開発のスピードが大きく向上します。

また、Spring BootなどのDIやBean定義に慣れていないエンジニアでも扱いやすいよう、JDBCレベルのシンプルな構成にしています。

環境による依存要素を可能な限り排除しているため、どんなプロジェクトにも差し込みやすい柔軟性があります。

本番環境で利用できるだけの安定性を備えながら、開発の手間を圧倒的に減らせる点が特長です。

今回あえて削った処理や仕組み

多くの機能を盛り込むほどシステムは複雑になり、修正のたびに影響範囲が広がってしまいます。

そのため今回のクラスでは、「必要な状況が限られる機能」「プロジェクトごとに方針が異なる機能」をあえて排除し、扱いやすさと軽量性を優先しました。

以下は、今回意図的に削った代表的な機能とその理由です。

省略した機能理由
トランザクション制御呼び出し元でConnectionを明示的に扱う方が柔軟であり、案件によって実装方針が大きく異なるためです。
DTOやMapperなどのマッピング層Mapや汎用Entityで十分に対応でき、定義追加の手間を避けるためです。
アノテーションベースの設定シンプルなSQL処理にアノテーションは不要で、学習コストだけが増えるためです。

必要な機能だけに絞ることで、コード量の削減、修正時の負担軽減、開発スピードの向上が期待できます。扱うべき範囲を明確に絞ったことで、どのプロジェクトでも導入しやすい軽量なDBアクセス基盤として機能します。

最小限で満たしたかったポイント

何を重視したのかというと、以下の3点です。

重視した項目

  • Factoryパターンでの統一的なアクセス
  • 明示的SQL指定と戻り値のMap/Entity統一
  • コネクションプールの再利用による実用性

この3つさえ押さえておけば、「最小限でも本番利用に耐える」構成になると判断しました。

とくにFactoryを使ったのは、SQL種別ごとの呼び出しを明確に分離できるからです。insert・update・delete・selectなどの処理クラスを統一的に生成することで、コード上の迷いがなくなります。

さらに、戻り値の構造をMapと汎用Entityに限定することで、動的な構造にも固定的な構造にも対応できるようにしました。1行だけ返したいとき、複数行をListで返したいとき、どちらでも対応できるように調整しています。

このDBアクセスクラスは、特定の業務要件に縛られず、幅広い開発現場で“今すぐ使えること”を目指して構成しました。

機能を絞った分、必要な仕様は最初にしっかり明示しておく必要があります。

以下に、本クラスが満たす基本仕様と想定している利用シーンをまとめました。「どんな環境で使えるのか?」という視点でご確認ください。

項目内容
使用言語Java(JDK 8 以上)
DB対応PostgreSQL(JDBC準拠)
構成方針Factoryパターンによるクラス分離
接続方式JDBC + コネクションプール
戻り値形式Map または 独自Entity(DbTableEntity)
ログ機能なし(別クラスで制御)
例外処理基本的に呼び出し側で処理
導入対象小規模・短納期・バッチ・社内ツールなど

設計ポリシーとクラス構造の意図

ここでは、このDBアクセスクラスがどういう考えで設計され、なぜこの構成になっているのかを整理して紹介します。

目指したのは「使い捨てで終わらない軽量な基盤」です。単発の開発でも、毎回ゼロから書き直すのではなく、使い回せて修正もしやすいことを最優先にしました。

そのために取った設計ポリシーや、あえて入れなかった要素など、判断の根拠を明示します。

依存関係を減らすための方針

クラス設計の初期段階で一番に考えたのが「外部に依存しない」ことでした。 ライブラリを増やせば一時的には便利になりますが、バージョン不整合や保守対象が増えるだけの結果になりがちです。

そのため、本クラスではJDK標準ライブラリとJDBC以外の外部依存は完全に排除しています。フレームワークに強く依存する設計を続けていると、「フレームワークの都合で仕様が決まる」という本末転倒な状況になりやすくなります。

今回の設計では、JDBCレベルの明確なAPIのみで構成し、どのJavaプロジェクトにでも差し込める形を目指しました。

Factoryパターンの採用理由

DBアクセス系の処理では、「select用の処理クラス」「insert用の処理クラス」など、ロジックの内容に応じて呼び出す処理が異なります。この分岐を呼び出し元で毎回書くのは明らかに冗長であり、保守性も下がります。

そこで、処理ごとの具象クラスをFactoryで生成することで、呼び出し側は型や構造を意識せず、必要な操作を最小限のコードで呼び出すだけの形にしました。

また、Factoryパターンを使うことで、将来的に別のDBや別の処理方針に対応したクラスを追加するのも容易になります。すでにinsert・update・delete・selectに加え、selectType(型指定付き)も用意しています。

データ受け渡しの柔軟性と制約

戻り値や引数の型についても、最小限の制約で柔軟に扱えるよう配慮しています。 ただし、無制限な構造にすると今度は整合性が取れなくなるため、2つのアプローチに絞り込みました。

Mapベースによる柔軟な構造

最も柔軟なのがMap形式による受け渡しです。 selectの戻り値を List<map<string, string="">>で返せば、列数や構造を問わずにデータを扱うことができます。

たとえば、1行だけ取りたい場合はListの先頭を取ればいいですし、画面表示用の汎用ループ処理でも使い勝手は良好です。Mapベースは柔軟性に優れ、画面出力や動的なカラム処理などにも対応しやすい構造です。

決まった型が不要な用途では特に威力を発揮し、小規模や変化の多い業務処理にも適しています。

DbTableEntityによる制限付き汎用性

一方で、汎用Entityである DbTableEntity クラスを使えば、Map よりも型的な扱いがしやすくなります。

このEntityは中身をキー・バリューで保持しますが、インスタンス化して使うことで「1行のデータ」という明確な意味を与えることができます。

たとえば getString("user_name") のように、意図的にカラム名を指定して取り出すことで、読みやすさと意図の明示を両立できます。開発規模や現場のスタイルに応じて、Map と Entity のどちらでも選べるようにしている点が、このクラスの柔軟性の肝になります。

ただし、あくまでこの DbTableEntity は「1行=1オブジェクト」という用途に特化しており、明確にカラム数・データの列構造が分かっている場面での利用が前提です。

テーブルの構造が動的すぎるケースでは Map のほうが適していることもあり、“単純すぎず、複雑すぎない” レイヤーを狙って設計されています。

クラス構成と処理の流れ

このセクションでは、今回作成したDBアクセスクラスの全体構成と、その中での処理の流れを具体的に説明します。

煩雑な設定や冗長な手続きは極力排除し、「必要なものだけを使い切る」ことにこだわった設計です。

各クラスの役割は明確に分け、責務ごとに独立した構成にすることで、再利用や保守のしやすさを担保しています。

前提となる実行環境

共通DBアクセスクラスを使用するためには、JavaおよびJDBC接続を前提とした開発環境が必要です。大規模なフレームワークには依存せず、最小限のライブラリで動作することを意識しています。ここでは、使用を想定しているJavaバージョンやDB接続仕様、パッケージ構成に触れておきます。

ソースコードの配置場所について

本クラス群は、業務ロジックと明確に分離できるよう、パッケージ構成を意識して作成されています。

以下に、すべてのクラスファイルを含めた配置構成を記載します。

ec
└── common
    ├── DataBean.java
    ├── IniFileRead.java
    ├── ITableEntity.java
    ├── package-info.java
    ├── SystemInfo.java
    ├── exception
    |    └── SystemException.java
    ├── log
    |    ├── Logger.java
    |    └── LogWriter.java
    └── data
        ├── DbAccessController.java
        ├── DbAccessDelete.java
        ├── DbAccessFactory.java
        ├── DbAccessInsert.java
        ├── DbAccessSelect.java
        ├── DbAccessSelectType.java
        ├── DbAccessUpdate.java
        ├── DbConnectionPool.java
        ├── DbTableEntity.java
        └── IDbAccessBean.java

Java共通DBアクセスクラスの全ソースコードはこちら

Javaで利用できる共通DBアクセスクラスのソースコードを、GitHubにて公開しています。 クラス構成やデータアクセス層の実装方法を確認したい方は、下記リンクよりご覧ください。

👉 GitHub – db-access-core / data ディレクトリ

各処理クラスの役割と責務分離

最初に、このDBアクセスクラスを構成する各クラスの役割について整理します。

すべての処理は1つの大きなクラスに詰め込むのではなく、処理種別ごとにクラスを分離し、それぞれが明確な責任を持つように設計しています。

たとえば、データの追加(insert)・更新(update)・削除(delete)・取得(select)といった処理は、以下のようにそれぞれの専用クラスに分けて実装しています。

分類クラス名主な役割
共通DBDbAccessInsertINSERT処理のSQLを組み立てて実行。フィールドと値のマッピングも担当。
DbAccessUpdateUPDATE処理のSQLを組み立てて実行。条件指定や更新対象の判定も含む。
DbAccessDeleteDELETE処理のSQLを組み立てて実行。主に主キーや条件句を元に削除。
DbAccessSelectSELECT文を構築し、結果をMap形式で返却する柔軟性重視の汎用取得処理。

共通DBアクセスクラスの全体構成

共通DBアクセスクラスは、SQL種別ごとの処理を専用クラスに分離し、接続管理・データ取得・例外処理などを明確に役割分担させた軽量な構成になっています。下表では、各クラスがどの責務を担っているのかを一覧で整理し、全体像が一目で把握できるようにしています。

分類クラス名主な役割
共通DBDbAccessInsertINSERT処理のSQLを組み立てて実行します。フィールドと値のマッピングも担当します。
DbAccessUpdateUPDATE処理のSQLを組み立てて実行します。条件指定や更新対象の判定も含みます。
DbAccessDeleteDELETE処理のSQLを組み立てて実行します。主に主キーや条件句を元に削除します。
DbAccessSelectSELECT文を構築し、結果をMap形式で返却します。柔軟性重視の汎用取得処理です。
DbAccessSelectType型を指定してSELECT結果を取得する特殊処理クラスです。Enum等に応じた処理に適します。
DbAccessController呼び出し元からの要求に応じて、各DB処理クラスを統括・実行する中核クラスです。
DbAccessFactory要求された処理種別(SELECT/INSERTなど)に応じて、適切なDB処理クラスを生成します。
DbConnectionPoolJDBCによるDB接続を管理します。再接続や接続保持のロジックも含まれます。
DbTableEntity1レコード分のデータ構造を表すEntityクラスで、テーブルと1対1で対応します。
補助
共通
IDbAccessBeanDBアクセスに必要なパラメータ(SQL種別、テーブル名、値マップなど)をまとめるインタフェースです。
DataBeanデータ処理に使う汎用的なBeanクラスです。キーと値のMap操作を簡潔にします。
IniFileReadini形式の設定ファイルから接続先情報やパスを読み込むユーティリティです。
ITableEntityDbTableEntityのインタフェース定義です。汎用的なエンティティ操作を可能にします。
SystemExceptionアプリケーション内で発生する独自例外を一元的に管理する基底クラスです。
SystemInfo環境・サーバー・ユーザー情報などを保持する補助クラスです。
Loggerログ出力処理のメインクラスで、ログ種別(INFO、ERRORなど)に応じた処理を行います。
LogWriterLoggerから呼び出され、ログファイルへの出力処理を実行する下位クラスです。
Constants定数管理クラスです。全体で共通的に使われる定義値(環境、設定値など)を集中管理します。
FieldsConstantsDBフィールド名に関する定数定義を集約したクラスで、ハードコーディングを防ぎます。
SqlConstants頻出SQLの一部(SELECT句やORDER句など)を定数化したクラスで、可読性と再利用性を高めます。

このように、機能ごとにクラスを分割することで、1つの機能を修正しても他への影響を最小限に抑えることができます。

また、それぞれのクラスは独立してテストしやすく、システム全体の安定性向上にもつながります。

データ更新処理の流れ

この処理では、DbAccessController・DbAccessBean・DbTableEntity を中心に、データの取得・変更・削除・追加を行い、最終的にまとめて DB に反映する一連の手順で動作します。

 ① DbAcessController
・・・・・・・ DB制御オブジェクト
上記を用いて構成されたテーブル構成に対しデータのDB更新を制御し、
DbAccessBeanのコントロールを行うための制御オブジェクト。
使用する際には、インスタンス生成したオブジェクトを使用する。

② DbAccessBean
・・・・・・・ DB制御実行オブジェクト
上記を用いて構成されたテーブル構成に対しDBデータを供給し、また
テーブル構成のデータのDB更新を行う為の実行オブジェクト。
使用する際には、インスタンス生成したオブジェクトを使用する。

③ DbTableEntity
・・・・・・・ 取得データを管理するエンティティーオブジェクト
基本的な概念として以下の手順を想定して設計されている。

ポイント

 A) SELECTで、必要なデータを抽出する。
 B) 抽出したデータの変更を行う。
 C) 抽出したレコードの削除を行う。
 D) 新たにレコードの挿入を行う。
 E) まとめてDBへの反映を行う。

B)、C)は、A)で取得したレコードROWIDにて対象レコードを特定するため、 A)を行わない場合は、B)、C)の操作を行うことができない。 A)を行わない場合でもD)の操作は、いつでも可能。 ただし、一意のレコードでない場合には一意制約違反が発生する。

更新時、A)を行った後に他のセッションからA)で取得したデータに変更・削除がかかった場合、 レコード更新日付の日時が異なるため、新対象データ無しのエラーが発生する。

コミットのタイミングは、基本的にアプリケーション側で制御することとする。

まとめ

今回の第1回では、ORMに頼らずに扱えるシンプルなDBアクセス基盤の背景と設計方針を整理しました。
設定に縛られず、SQL と最小限のクラス構成だけで柔軟に扱える仕組みを作ることで、現場での開発負担を大きく減らせることを確認しました。
次回は、この基盤の中心となる DbAccessController を掘り下げ、実際にどのようにSQL操作を統一的に扱うのかを詳しく解説します。

Java共通DBアクセスクラスの全ソースコードはこちら

Javaで利用できる共通DBアクセスクラスのソースコードを、GitHubにて公開しています。 クラス構成やデータアクセス層の実装方法を確認したい方は、下記リンクよりご覧ください。
👉 GitHub – db-access-core / data ディレクトリ

次の第2回では、本シリーズの中核であるDbAccessControllerクラスについて詳しく解説します。このクラスは、SQLの実行制御やデータアクセスの統一インターフェースとして機能し、ORMに頼らずに直感的なSQL操作を可能にする仕組みです。共通DBアクセスクラスの実践的な構造や、実際の使い方を理解するための基礎として、ぜひご覧ください。
▶︎ORMにはうんざり!第2回:共通DBアクセスクラスでSQLを直感的に操作するJava設計

よく読まれている記事

1

「私たちが日々利用しているスマートフォンやインターネット、そしてスーパーコンピュータやクラウドサービス――これらの多くがLinuxの力で動いていることをご存じですか? 無料で使えるだけでなく、高い柔軟 ...

2

Linux環境でよく目にする「Vim」という名前。サーバーにログインしたら突然Vimが開いてしまい、「どうやって入力するの?」「保存や終了ができない!」と困った経験をした人も多いのではないでしょうか。 ...

3

ネットワーク技術は現代のITインフラにおいて不可欠な要素となっています。しかし、ネットワークを深く理解するためには、その基本となる「プロトコル」と「レイヤ」の概念をしっかり把握することが重要です。 こ ...

4

この記事は、Linuxについて勉強している初心者の方向けに「Shellスクリプト」について解説します。最後まで読んで頂けましたら、Shellスクリプトはどのような役割を担っているのか?を理解出来るよう ...

5

Javaは世界中で広く使われているプログラミング言語であり、特に業務システムやWebアプリケーションの開発において欠かせない存在です。本記事では、初心者向けにJavaの基礎知識を網羅し、環境構築から基 ...

-共通DBアクセスクラス