【Java Web入門 #13】標準タグライブラリ(JSTL)の利用(5)|SQLタグライブラリ
2025.03.07
ここまでの記事では、JSTL の core、functions、i18n、XMLの各ライブラリについて説明しました。当記事では、JSTLに準備されているもう1つのタグライブラリである、SQLタグライブラリの概要および仕様について説明します。
◆Java Web入門の過去記事はこちら
◆Java入門 記事一覧はこちら
SQLタグライブラリの概要
JSTLのSQLタグライブラリは、データベースへの操作を目的としたタグライブラリです。SQLタグライブラリを用いることで、JSPからデータベースにクエリを実行してデータを取得したり、データの追加や更新、削除といったデータベースの更新処理を行うことができます。
また、複数のデータベース操作をトランザクション管理したり、他のライブラリやEL式を用いてクエリの取得結果を簡単に操作したりすることも可能です。
近年のWebアプリケーション開発では、データベースへのアクセスはフレームワークを用いるなど部品化され、テンプレートエンジンであるJSPからは直接行わないのが一般的ですが、SQLタグライブラリを利用することで簡単にデータベース操作の学習を行ったり、既存のJSPアプリケーションの保守・改修などの場面で活用できます。
taglibディレクティブ
SQLタグライブラリのプレフィックスには、通例として「sql」が利用されます。(当記事でもプレフィックスを「sql」として説明します)
(JSTL3.0の場合)
<%@ taglib uri="jakarta.tags.sql" prefix="sql" %>
(JSTL2.0以前の場合)
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
事前準備(前提条件)
SQLタグライブラリを利用するためには、接続先のデータベースが必要です。何らかのDBMS(database management system:データベース管理システム)がインストールされていない場合は、事前準備が必要になります。
DBMSがインストールされていない場合
DBMSをインストールして、SQLタグライブラリで接続するためのデータベースを新しく作成する必要があります。今回はオープンソースのDBMSである「PostgreSQL」をインストールする方法(Windows)をご紹介します。
インストーラーのダウンロード
まず、EnterpriseDBのサイトから配布されているインストーラーをダウンロードします。アイコンをクリックすると、ダウンロードが開始されます。(別の画面が開きますが気にしなくてOKです)
今回は最新バージョン(2025年3月時点)の PostgreSQL17 のインストーラーをダウンロードしました。ファイルサイズが大きい(350MB弱)ためご注意ください。

※Windows以外のOSをお使いの方は、PostgreSQL公式サイトのダウンロードページでOS毎のダウンロードおよびインストール方法をご確認ください。
インストール
ダウンロードしたインストーラー(postgresql-17.4-1-windows-x64.exe)を実行してPostgreSQLをインストールします。
インストーラーが起動したら「Next」をクリックします。

次に、インストール先のフォルダを選択して「Next」をクリックします。
基本的にデフォルト設定のままで問題ありませんが、共用のパソコンをお使いの場合などはご注意ください。

次に、インストール先のコンポーネントを選択します。デフォルトで全てチェックがついていますので、そのまま「Next」をクリックします。
最低限のインストールを行いたい場合は「PostgreSQL Server」と「Command Line Tools」のみチェックしてインストールしてください。

次に、データベースの保存先(データディレクトリ)を選択して「Next」をクリックします。
こちらも基本的にデフォルト設定のままで問題ありませんが、共用のパソコンをお使いの場合などはご注意ください。

次に、デフォルトユーザーの「postgres」に設定する、任意のパスワードを入力してください。
このパスワードはDBMSへのログインに利用しますので、忘れないようにご注意ください。

次に、PostgreSQLとの通信で利用するポート番号を指定して「Next」をクリックします。
他にPostgreSQL Serverがインストールされていなければ、ポート番号も基本的にデフォルト(5432)のままで問題ありません。

次に、データベースで利用するロケール(言語・タイムゾーン)を指定します。日本語で利用する場合は「Japanese」を選択して「Next」をクリックします。

インストール構成が表示されますので、確認の上「Next」をクリックします。

最終確認画面が表示されます。「Next」をクリックするとインストールが開始されます。
※インストールには10分前後かかる場合がありますのでご注意ください。

インストールの進行状況はプログレスバーで表示されます。インストールが完了したら、自動的に完了画面に遷移します。

最後にインストール完了画面が表示されたら「Launch Stack Builder at exit?」のチェックを外して「Finish」をクリックすればインストール終了です。
引き続き Stack Builder で追加コンポーネントをインストールしたい場合は、チェックを付けたまま「Finish」をクリックしてください。(追加コンポーネントのインストールは後からでも実行可能です)

データベースとデータの準備
DBMSをインストールしたら、データベース、テーブルを作成し、読み込み/更新用のデータを準備します。
今回はデフォルトユーザーの「postgres」を利用することとし、ロール(データベースのユーザー)の作成は割愛します。また、実行するSQLもなるべく最小限の設定のみで行います。
データベースの作成
PostgreSQL の CUI ツールである psql を利用して、データベースを作成する手順を説明します。
・データベース作成の流れ

まず、 psql を利用して、インストール時にデフォルトで作成されているデータベース(postgres)に接続します。
コマンドプロンプトを起動し、カレントディレクトリを postgreSQL のインストールフォルダの bin フォルダ(psql.exe が格納されているフォルダ)に移動します。デフォルトのインストールフォルダの場合は、以下のコマンドになります。
cd C:\Program Files\PostgreSQL\17\bin
カレントディレクトリを移動したら、「psql -U postgres」と入力します。
データベースのユーザー「postgres」のパスワードの入力を求められますので、インストール時に設定したパスワードを入力します。
psql -U postgres
postgreSQLにログインしたら、以下のSQLを実行して、新しいデータベースを作成します。SQLの実行時は、最後のセミコロン( ; )を忘れないようご注意ください。
CREATE DATABASE sample_db;
SQLの実行後、「CREATE DATABASE」と出力されたらデータベースが作成されています。

テーブルの作成とデータの登録は、作成したデータベースに対して作業するため、最後に以下のコマンドで一旦 PostgreSQL からログアウトします。
\q
テーブル作成とデータの登録
引き続き psql を利用して、データベースにテーブルを作成およびサンプルデータの追加を行います。
・テーブル作成~データ登録の流れ

まず、コマンドプロンプト上で以下のコマンドを実行し、PostgreSQL に再度ログインします。コマンドのオプション「-d」を追加して、psql で接続するデータベースを指定しています。
psql -U postgres -d sample_db
ログイン後、コマンドラインの先頭が「接続先データベース名=#」(上記の例では「sample_db=#」)となっていれば、指定したデータベースに接続されています。

次に、以下のSQLを実行してデータを登録するテーブルを作成します。
CREATE TABLE sample_table
(
id serial NOT NULL ,
name text NOT NULL,
country text ,
created time without time zone NOT NULL DEFAULT now(),
updated time without time zone NOT NULL DEFAULT now(),
CONSTRAINT sample_table_pkey PRIMARY KEY (id)
);
実行後に「CREATE TABLE」と表示されたらテーブルが作成されています。

最後に以下のSQLを実行して、サンプルデータ(2件)を登録します。
INSERT INTO sample_table (name, country) values ('knovus', 'Japan');
INSERT INTO sample_table (name, country) values ('GSI', 'Japan');
それぞれの行の実行後、「INSERT 0 1」と表示されたらデータが登録されています。

データが登録されたかを確認するには、以下のSQLを実行します。
select id, name, country from sample_table;
(実行結果)
id | name | country
----+--------+---------
1 | knovus | Japan
2 | GSI | Japan
最後に \q でpostgreSQLからログアウトしたら、データベースの準備は完了です。
\q
JDBCドライバーの準備
Javaのプログラムからデータベースへ接続するためには、JDBCドライバー(Javaからデータベースへの接続を行うライブラリ)の準備が必要です。
最新のJDBCドライバーは PostgreSQL JDBC Driver(外部サイト)からダウンロードできます。

JDBCドライバーの jar ファイルをダウンロードしたら、JSTL の jar ファイルと同様に、Webアプリケーションの /webapp/WEB-INF/lib フォルダ内にコピーしておきましょう。

JNDI リソースの準備(任意)
JNDI(Java Naming and Directory Interface)は、Javaプログラムから様々な情報の保管場所にアクセスするための仕組み(ディレクトリサービス)です。データベースや設定情報などを、名前を使って探したり利用したりできます。
JNDI リソースを利用してデータベースに接続する場合は、事前に JNDI リソースの定義を行う必要があります。Webアプリケーションの場合、データベース接続の情報は Context の定義の中に追加します。
当連載の【Java Web入門 #2】サーブレットの作成の手順でWebアプリケーションを作成された方は、Webアプリケーションを配備したTomcatの設定の中に Context が定義されています。

(server.xml)
<Context docBase="WebPractice" path="/WebPractice" reloadable="false" source="org.eclipse.jst.jee.server:WebPractice" />
これを、以下のように変更します。
(server.xml)
<Context docBase="WebPractice" path="/WebPractice" reloadable="false" source="org.eclipse.jst.jee.server:WebPractice">
<Resource name="jdbc/sample_db"
auth="Container"
type="javax.sql.DataSource"
username="postgres"
password="password"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://localhost:5432/sample_db"
/>
</Context>
nameは定義する JNDI リソース名ですので、任意の名前で問題ありません。(ディレクトリ構成のようにスラッシュ区切りで「種類/名前」の形式にします)
username、password、url には実際に接続するデータベースに応じて値を設定してください。
これでWebアプリケーション上から、定義した名前(上記の例では「jdbc/sample_db」)でリソースにアクセスすることが可能になります。
各タグの仕様
※以降、各タグのサンプルコードでは、上記の「テーブル作成とデータの登録」で作成したデータベース、テーブルおよび追加したデータがあることを前提としていますのでご注意ください。
データベースへの接続
<sql:setDataSource>
<sql:setDataSource>タグは、データベースへの接続情報を設定し、取得したデータベース接続を保持します。
属性名 | 必須 | 説明 | 式 |
dataSource | ※1 | データベースへの接続情報を指定する。 javax.sql.DataSource クラスのインスタンスまたは文字列で指定し、文字列で指定する場合は JNDI リソースへの相対パス、またはデータソースで定義されている JDBC パラメータ文字列のいずれかを指定する。 | 可 |
url | ※1 | JDBC接続時のデータベース接続文字列を指定する。 | 可 |
driver | JDBC接続時に利用するドライバークラス名を指定する。 | 可 | |
user | JDBC接続時のユーザー名を指定する。 | 可 | |
password | JDBC接続時のパスワードを指定する。 | 可 | |
var | 取得したデータベース接続(コネクション)を保持する変数名を指定する。 | ||
scope | 変数のスコープを指定する。 (page(デフォルト)/request/session/application のいずれか) |
データベースへの接続情報は、dataSourceで指定するか、url + driver + user + password の組み合わせでで指定するか、いずれかの方法で指定します。
<%-- JNDI リソースを利用 --%>
<sql:setDataSource var="ds" dataSource="jdbc/sample_db" />
<%-- url + driver + user + password の組み合わせを利用 --%>
<sql:setDataSource var="ds" driver="org.postgresql.Driver"
url="jdbc:postgresql://localhost:5432/sample_db"
user="username"
password="password"
/>
データの取得・更新
<sql:query>
<sql:query>タグは、データベースからデータを取得するためのクエリ(SQL)を実行します。
属性名 | 必須 | 説明 | 式 |
dataSource | ※2 | データの取得処理を行うデータベース接続(コネクション)を指定する。 JNDI リソース名を直接指定、または、<sql:setDataSource>タグで取得したコネクションを指定できる。 | 可 |
sql | データの取得を行うSQLを指定する。 | 可 | |
startRow | SQLの実行結果の何行目のデータから取得するかを指定する。 指定しない場合は先頭(0)から取得。 | 可 | |
maxRows | 取得する行数 省略か-1を設定した場合は上限なし | 可 | |
var | 〇 | 実行結果(取得データ)を格納する変数名を指定する。 (jakarta.servlet.jsp.jstl.sql.Result オブジェクト) | |
scope | 変数のスコープを指定する。 (page(デフォルト)/request/session/application のいずれか) |
SQLの実行で取得したデータは jakarta.servlet.jsp.jstl.sql.Result クラスのオブジェクトとして、var で指定した変数に格納されます。Result オブジェクトからは getRows メソッドで取得されたデータ件数や、getRows メソッドで全データ行などを取得できます。
以下のサンプルコードでは、SQLで取得された結果を、<c:forEach>タグを利用して一覧表を出力しています。ループ処理内では、データ行の各項目に対して「${item.name}」のようにカラム名でアクセスできます。
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="jakarta.tags.core" prefix="c" %>
<%@ taglib uri="jakarta.tags.sql" prefix="sql" %>
<%-- データソースの定義 --%>
<sql:setDataSource var="ds" dataSource="jdbc/sample_db" />
<%--SQLの実行(データ取得) --%>
<sql:query dataSource="${ds}" var="result" sql="select * from sample_table;" />
<html>
<head>
<meta charset="UTF-8">
<style>table, td, th { border: 1px solid; text-align: center; }</style>
</head>
<body>
<table>
<tr><th width="100">名前</th><th width="100">出身国</th></tr>
<c:forEach items="${result.getRows()}" var="item">
<tr>
<td>${item.name}</td>
<td>${item.country}</td>
</tr>
</c:forEach>
</table>
データ数:${result.getRowCount()} 件
</body>
</html>
実行結果:

<sql:update>
<sql:update>タグは、データベースを更新するためのクエリ(SQL)を実行します。
属性名 | 必須 | 説明 | 式 |
dataSource | ※2 | データの更新処理を行うデータベース接続(コネクション)を指定する。 | 可 |
sql | データの更新を行うSQLを指定する。 | 可 | |
var | 実行結果(更新されたデータ行数)を格納する変数名を指定する。 | ||
scope | 変数のスコープを指定する。 (page(デフォルト)/request/session/application のいずれか) |
<sql:update>タグでは、データの挿入(insert)、更新(update)、削除(delete)のSQLを実行し、影響のあったデータ行数が返され、varで指定した変数に格納されます。例えば update を行うSQLを実行した時の戻り値が「0」であった場合は、更新されたデータ行がないことを示します。
以下のサンプルコードでは、新しいデータ行を作成して一覧を取得、その後追加したデータ行を削除して再度一覧を取得して表示しています。
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="jakarta.tags.core" prefix="c" %>
<%@ taglib uri="jakarta.tags.sql" prefix="sql" %>
<%-- データソースの定義 --%>
<sql:setDataSource var="ds" dataSource="jdbc/sample_db" />
<%-- SQL(データ取得) --%>
<c:set var="select" value="select * from sample_table order by id asc;" />
<%-- SQL(データ挿入) --%>
<c:set var="insert" value="insert into sample_table (name, country) values ('Google', 'USA');" />
<%-- SQL(データ削除) --%>
<c:set var="delete" value="delete from sample_table where name = 'Google';" />
<html>
<head>
<meta charset="UTF-8">
<style>table, td, th { border: 1px solid; text-align: center; }</style>
</head>
<body>
<%--SQLの実行(データ挿入) --%>
<sql:update dataSource="${ds}" var="count" sql="${insert}" />
<%--SQLの実行(データ取得) --%>
<sql:query dataSource="${ds}" var="result" sql="${select}" />
<p>実行SQL:${insert}</p>
${count}件追加しました。
<table>
<tr><th width="100">名前</th><th width="100">出身国</th></tr>
<c:forEach items="${result.getRows()}" var="item">
<tr>
<td>${item.name}</td>
<td>${item.country}</td>
</tr>
</c:forEach>
</table>
データ数:${result.getRowCount()} 件
<hr/>
<p>実行SQL:${delete}</p>
<%--SQLの実行(データ削除) --%>
<sql:update dataSource="${ds}" var="count" sql="${delete}" />
<%--SQLの実行(データ取得) --%>
<sql:query dataSource="${ds}" var="result" sql="${select}" />
${count}件削除しました。
<table>
<tr><th width="100">名前</th><th width="100">出身国</th></tr>
<c:forEach items="${result.getRows()}" var="item">
<tr>
<td>${item.name}</td>
<td>${item.country}</td>
</tr>
</c:forEach>
</table>
データ数:${result.getRowCount()} 件
</body>
</html>
実行結果:

<sql:param>
<sql:param>タグは、<sql:query>または<sql:update>タグで実行する SQL にパラメーターを引き渡します。
属性名 | 必須 | 説明 | 式 |
value | SQLに設定するパラメーターの値を指定する。 | 可 |
<sql:param>タグは、<sql:query>または<sql:update>タグの子要素として記述し、設定されたパラメーターは、SQL内の置換文字列である「?」の部分に対して、タグの設定順に置換されます。
SQL内の置換文字列「?」の数と、子要素として設定したパラメーター(<sql:param>および<sql:dateParam>タグ)の数は一致している必要があります。
以下のサンプルコードではSQLの抽出条件( where name = ? )の部分に、<sql:param>タグ(19行目)でパラメーター値「knovus」を指定しています。<sql:param>タグの value 属性の値に動的な式を設定することで、抽出条件を動的に指定することも可能です。
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="jakarta.tags.core" prefix="c" %>
<%@ taglib uri="jakarta.tags.sql" prefix="sql" %>
<%-- データソースの定義 --%>
<sql:setDataSource var="ds" dataSource="jdbc/sample_db" />
<%-- SQL(データ取得) --%>
<c:set var="select" value="select * from sample_table where name = ?;" />
<html>
<head>
<meta charset="UTF-8">
<style>table, td, th { border: 1px solid; text-align: center; }</style>
</head>
<body>
<%--SQLの実行(データ取得) --%>
<sql:query dataSource="${ds}" var="result" sql="${select}" >
<sql:param value="knovus" />
</sql:query>
<table>
<tr><th width="100">名前</th><th width="100">出身国</th></tr>
<c:forEach items="${result.getRows()}" var="item">
<tr>
<td>${item.name}</td>
<td>${item.country}</td>
</tr>
</c:forEach>
</table>
データ数:${result.getRowCount()} 件
</body>
</html>
実行結果:

<sql:dateParam>
<sql:dateParam>タグも、<sql:query>または<sql:update>タグで実行する SQL にパラメーターを引き渡します。<sql:param>と異なり、<sql:dateParam>では日付や時刻を指定するための java.util.Date クラスのオブジェクトを指定する必要があります。
属性名 | 必須 | 説明 | 式 |
value | 〇 | 日付、時刻、日時を指定するための java.util.Dateオブジェクトを指定する。 | 〇 |
type | 「date」「time」「timestamp」のいずれかを指定する。 | 可 |
以下のサンプルコードでは、日時型のデータ列である created に対する比較対象として、日時型のパラメーター(java.util.date型のオブジェクト)を引き渡しています。
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="jakarta.tags.core" prefix="c" %>
<%@ taglib uri="jakarta.tags.sql" prefix="sql" %>
<%-- データソースの定義 --%>
<sql:setDataSource var="ds" dataSource="jdbc/sample_db" />
<%-- SQL(データ取得) --%>
<c:set var="select" value="select * from sample_table where created < ?;" />
<jsp:useBean id="dateObj" class="java.util.Date" />
<html>
<head>
<meta charset="UTF-8">
<style>table, td, th { border: 1px solid; text-align: center; }</style>
</head>
<body>
<%--SQLの実行(データ取得) --%>
<sql:query dataSource="${ds}" var="result" sql="${select}" >
<sql:dateParam value="${dateObj}" />
</sql:query>
<table>
<tr><th width="100">名前</th><th width="100">出身国</th></tr>
<c:forEach items="${result.getRows()}" var="item">
<tr>
<td>${item.name}</td>
<td>${item.country}</td>
</tr>
</c:forEach>
</table>
データ数:${result.getRowCount()} 件
</body>
</html>
実行結果:

トランザクション管理
<sql:transaction>
<sql:transaction>タグは、複数のデータベース操作の整合性を保つためのトランザクション制御を行います。
属性名 | 必須 | 説明 | 式 |
dataSource | トランザクション処理を行うデータベース接続(コネクション)を指定する。 | 可 | |
isolation | トランザクション分離レベルを read_committed/read_uncommitted/repeatable_read/serializable のいずれかで指定する。 (指定されない場合はデータソースに依存) | 可 |
1つの<sql:transaction>タグ毎にトランザクション管理され、コミットまたはロールバックが自動的に行われます。トランザクションの対象となるのは、<sql:transaction>タグのボディ部に記述された<sql:query>タグ要素、<sql:update>タグ要素です。
データソースは<sql:transaction>タグにのみ設定します。<sql:transaction>タグのボディ部に記述されるタグには、dataSourceを指定できません。
使用例
以下のサンプルコードでは2件のデータを挿入してから、データの一覧を取得しようとしていますが、2件目のデータ挿入のSQL(insert2)でテーブル名を誤って「sample_tabl」としてしまったために例外が発生します。
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="jakarta.tags.core" prefix="c" %>
<%@ taglib uri="jakarta.tags.sql" prefix="sql" %>
<%-- データソースの定義 --%>
<sql:setDataSource var="ds" dataSource="jdbc/sample_db" />
<%-- SQL(データ取得) --%>
<c:set var="select" value="select * from sample_table order by id asc;" />
<%-- SQL(データ挿入) --%>
<c:set var="insert1" value="insert into sample_table (name, country) values ('Google', 'USA');" />
<c:set var="insert2" value="insert into sample_tabl (name, country) values ('mixi', 'Japan');" />
<html>
<head>
<meta charset="UTF-8">
<style>table, td, th { border: 1px solid; text-align: center;}</style>
</head>
<body>
<%-- {<c:catch>タグ で例外を捕捉} --%>
<c:catch var="ex">
<%-- {トランザクションの開始} --%>
<sql:transaction dataSource="${ds}">
<%-- SQL(insert1)の実行(データ挿入に成功する) --%>
<sql:update var="count1" sql="${insert1}" />
<%-- SQL(insert2)の実行(データ挿入に失敗する) --%>
<sql:update var="count2" sql="${insert2}" />
</sql:transaction>
</c:catch>
<%-- {例外が発生していた場合はメッセージ表示} --%>
<c:if test="${ex != null}">
データの挿入時に例外が発生しました。<br/>
例外メッセージ:
<p><c:out value="${ex.message}" /></p>
<hr/>
</c:if>
<%--SQLの実行(データ取得) --%>
<sql:query dataSource="${ds}" var="result" sql="${select}" />
<table>
<tr><th width="100">名前</th><th width="100">出身国</th></tr>
<c:forEach items="${result.getRows()}" var="item">
<tr>
<td>${item.name}</td>
<td>${item.country}</td>
</tr>
</c:forEach>
</table>
データ数:${result.getRowCount()} 件
</body>
</html>
1件目と2件目のデータ挿入(<sql:update>タグ)は同じ<sql:transaction>タグの子要素であるため、2件目の処理に失敗した際に、成功した1件目のデータ挿入処理も取消(ロールバック)されます。
以下の実行結果のとおり、データが1件も追加されていないことがわかります。

参考:Jakarta Standard Tag Library 3.0 Specification Document(公式サイト・英語)
参考:Jakarta Standard Tag Library 3.0 Tagdoc(公式サイト・英語)
ここまで、JSPの標準アクション、EL式、JSTLの各タグライブラリと、JSPを用いた動的な表示や機能について説明しましたが、いかがでしたでしょうか。JSTLとEL式の組み合わせなど、これらを活用することで、複雑な条件分岐や繰り返し処理を簡潔に記述するなどの表現が可能になります。
また、テンプレートエンジンであるJSPとこれら言語式の考え方は、他のツールにも共通する部分がありますので、今後他の技術などを学ぶ際にも役立つと思います。
次回の記事からは、Java Webアプリケーションを迅速に開発するためのフレームワークであるSpringBootと、HTMLのように記述できるテンプレートエンジンであるThymeleafの基本的な使い方について、実装例を交えて解説します。