LoginSignup
6
2

More than 5 years have passed since last update.

JDBCTemplateのプレースホルダでハマったお話

Posted at

開発環境

  • Java 1.8.0
  • TERASOLUNA Server Framework for Java (5.4.1.RELEASE)
    • Spring Framework 4.3.14.RELEASE

JDBCTemplateのおさらい

はじめにJDBCTemplateの使い方をざっと説明します。
※関係のないところは割愛しています。

XMLにJDBCのネームスペースを追加する
context.xml
<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-nameURLに設定したものを設定する。省略するとidに設定した文字列がされる。

context.xml
  <jdbc:embedded-database id="dataSource" type="H2" database-name="testdb">
    <jdbc:script location="classpath:/database/schema.sql" />
  </jdbc:embedded-database>

参考:Logbackのデータベース設定例。このurlタグの値とdatabase-nameを合わせる。

logback.xml
      <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のビーン定義

コンストラクタに↑で定義したデータソースを設定するだけ

context.xml
  <bean class="org.springframework.jdbc.core.JdbcTemplate">
    <constructor-arg ref="dataSource" />
  </bean>

JDBCTemplateの使い方

コードで呼び出す

単純にインジェクトするだけ

java.test.java
  @Inject
  JdbcTemplate jdbcTemplate;
SQLを実行して結果を取得

queryForXXXSQLの結果をListMapで受け取ることができる。
以下はLogbackで出力したログのメッセージを取得する例。

test.java
    List<String> logginEvents = jdbcTemplate.queryForList(
        "SELECT formatted_message FROM logging_event ORDER BY timestmp", String.class);

?を使うとプレースホルダになり値をバインドすることもできる。
以下はMDCまたはContextに含まれる、「context」というキーに紐づく値を取得する例。

test.java
    List<String> loggingEventProperties = jdbcTemplate.queryForList(
        "SELECT mapped_value FROM logging_event_property WHERE mapped_key=?", String.class,
        "context");

JDBCTemplateのプレースホルダでハマったお話

  • SQLをハードコーディングするとtypoしそうなので定数化したい:disappointed_relieved:
  • そうだ!カラム名とかもプレースホルダにしたろ:smiley:
  • じゃけん、すぐ書き換えましょうねぇ:stuck_out_tongue_closed_eyes:
test.java

    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:thinking:

カラム名やテーブル名はバインドできない

JDBCTemplateのバインドは内部でPreparedStatementを呼び出していて、PreparedStatementのプレースホルダがパラメータのみ対応しているため、
カラム名やテーブル名はバインドできないらしい。

ドキュメントを探したり、ブレイクポイントをかけてしばらく調べていましたが、カラム名に使ってはいけないという文言は見つけられませんでした。
しかし↑の例でformatted_messageという文字列が返ってきたこと・SQL内で?をエスケープしなくてもバインドされることを考えると、JDBCドライバのエスケープ処理が関係している気がします。

結論

テーブル名やカラム名をバインドしたい時は、String#formatなどを使おう。

投稿に至った経緯

久々にJDBCTemplateを使ったらハマったの記事にしました。

6
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2