開発環境
- Java 1.8.0
- TERASOLUNA Server Framework for Java (5.4.1.RELEASE)
- Spring Framework 4.3.14.RELEASE
JDBCTemplateのおさらい
はじめにJDBCTemplate
の使い方をざっと説明します。
※関係のないところは割愛しています。
XMLにJDBCのネームスペースを追加する
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
">
(データソースの定義)
テストデータなど、実行時にテーブル定義やデータ投入をする場合に必要。
この例ではクラスパス下の/database/schema.sql
に書かれたH2
用のスクリプトを読み込んでいる。database-name
はURL
に設定したものを設定する。省略するとid
に設定した文字列がされる。
<jdbc:embedded-database id="dataSource" type="H2" database-name="testdb">
<jdbc:script location="classpath:/database/schema.sql" />
</jdbc:embedded-database>
参考:Logback
のデータベース設定例。このurl
タグの値とdatabase-name
を合わせる。
<dataSource class="org.apache.commons.dbcp2.BasicDataSource">
<driverClassName>org.h2.Driver</driverClassName>
<url>jdbc:h2:mem:testdb</url>
<username>sa</username>
<password></password>
</dataSource>
JDBCTemplateのビーン定義
コンストラクタに↑で定義したデータソースを設定するだけ
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>
JDBCTemplateの使い方
コードで呼び出す
単純にインジェクトするだけ
@Inject
JdbcTemplate jdbcTemplate;
SQLを実行して結果を取得
queryForXXX
でSQL
の結果をList
やMap
で受け取ることができる。
以下はLogback
で出力したログのメッセージを取得する例。
List<String> logginEvents = jdbcTemplate.queryForList(
"SELECT formatted_message FROM logging_event ORDER BY timestmp", String.class);
?
を使うとプレースホルダになり値をバインドすることもできる。
以下はMDC
またはContext
に含まれる、「context」というキーに紐づく値を取得する例。
List<String> loggingEventProperties = jdbcTemplate.queryForList(
"SELECT mapped_value FROM logging_event_property WHERE mapped_key=?", String.class,
"context");
JDBCTemplateのプレースホルダでハマったお話
-
SQL
をハードコーディングするとtypoしそうなので定数化したい - そうだ!カラム名とかもプレースホルダにしたろ
- じゃけん、すぐ書き換えましょうねぇ
private static final String MESSAGE_COLUM_NAME = "formatted_message";
// 略
List<String> logginEvents = jdbcTemplate.queryForList(
"SELECT ? FROM logging_event ORDER BY timestmp", String.class, MESSAGE_COLUM_NAME);
なんでformatted_message
?
カラム名やテーブル名はバインドできない
JDBCTemplate
のバインドは内部でPreparedStatement
を呼び出していて、PreparedStatement
のプレースホルダがパラメータのみ対応しているため、
カラム名やテーブル名はバインドできないらしい。
- Using a prepared statement and variable bind Order By in Java with JDBC driver
- Cannot use a LIKE query in a JDBC PreparedStatement?
ドキュメントを探したり、ブレイクポイントをかけてしばらく調べていましたが、カラム名に使ってはいけないという文言は見つけられませんでした。
しかし↑の例でformatted_message
という文字列が返ってきたこと・SQL
内で?をエスケープしなくてもバインドされることを考えると、JDBC
ドライバのエスケープ処理が関係している気がします。
結論
テーブル名やカラム名をバインドしたい時は、String#format
などを使おう。
投稿に至った経緯
久々にJDBCTemplate
を使ったらハマったの記事にしました。