Spring Framework 入門記 JDBCその6 SqlUpdate
もはや惰性で続いている感じの入門記,今日はRDBの更新に使用する
SqlUpdate
です.
これは前回学習したSqlQuery
と異なり,抽象クラスではないので,そのまま使うこともできるっぽい.ですが,「Reference Documentation」の「9.4.3. SqlUpdate」では,やっぱり派生クラスを作っています.ま,気にしない気にしない.
このSqlUpdate
ですが,SqlQuery
と同じくSqlOperation
の派生クラスで,SqlOperation
はRdbmsOperation
の派生クラスで,そんなわけでSqlQuery
と同じく次のプロパティを持っています.
-
dataSource
- sql
まぁ,いいでしょう.と思いきや,このsql
というプロパティ,SqlOperation
ではなくてRdbmsOperation
で定義されているのですね.不思議.
どうやらSqlOpeartion
というのはJdbcOperation
とでもいうべき存在みたい.RdbmsOperation
はjava.sql
パッケージを全く使ってなくて(javax.sql
はDataSource
のみ使ってますが),JDBCを使った実装がSqlOperation
という役割になっているようです.
そんなことはともかく,上記のプロパティを設定すると,RDBを更新することができます.そのためのメソッドが6個,用意されています.よかった,二桁じゃなくて.
まずは,更新用のSQLにパラメータが含まれていない場合に使う,
-
update()
ですね.
それから,int
のパラメータを1〜2個持つSQLを実行するために,
-
update(int)
update(int, int)
があり,さらにString
のパラメータを1〜2個持つSQLを実行するために,
-
update(String)
update(String, String)
が用意されています.
最後に,任意のパラメータを持つSQLを実行するために,
-
update(Object[])
が用意されています.
でも,SqlQuery
と異なり,コンテキストを渡すメソッドは用意されていません.うーむ,一貫性がないなぁ.
ということで,お試しタぁイム(あゆの北陽タぁイム風)
全然面白くありませんが,前回のサンプルでレコードをinsertしているところをSqlUpdate
を使うようにしてみます(マジでつまらんなぁ).
...
ぐはぁっ!
はまってもーた.そうか,SQLにパラメータが含まれている場合はそうなるのか... 無念だ.
実はSqlUpdate
には(SqlQuery
にも),
-
types
というint
配列のプロパティがあって,SQLが含むパラメータの型を明示的に指定しなくてはいけないみたい.
そうすると,afterPropertiesSet()
が呼び出されたところで
-
compile()
というメソッド呼び出されて,SQL文字列に含まれるパラメータの数とtypes
配列の要素数が一致しているかチェックされるようです.一致していないと,例外が吹っ飛んできます.吹っ飛ばされました.心より恥じる.
しかし,int
でJDBC型を指定するのはいやだなぁ.定義ファイル中にTypes.VARCHAR
とか書けるわけないので,"12
"とか書くはめに.ちなみに"12
"はVARCHAR
です.わっかんないよ,そんなの.
そうか,だから「9.4.3. SqlUpdate」では派生クラスを作っているのか... なんかイマイチだなぁ.
JdbcType
とかいうクラスとJdbcTypeEditorとかいうPropertyEditor
を用意して,その配列プロパティを持つようなSqlUpdate
の派生クラスを作ればいいかなぁ.
ということで,作ってみました.
package study; import java.lang.reflect.Field; import java.sql.Types; public class JdbcType { private String typeName; private int type; public JdbcType(String typeName) { this.typeName = typeName; try { Field field = Types.class.getField(typeName); type = field.getInt(null); } catch (Exception e) { throw (IllegalArgumentException) new IllegalArgumentException("illegal jdbc type name : " + typeName).initCause(e); } } public int getType() { return type; } public String toString() { return typeName; } }
と,
package study; import java.beans.PropertyEditorSupport; public class JdbcTypeEditor extends PropertyEditorSupport { public void setAsText(String text) throws IllegalArgumentException { setValue(new JdbcType(text)); } public String getAsText() { return getValue().toString(); } }
このPropertyEditor
をfactory.xml
に組み込みます.このあたりの話は「Property Editorだよ」を参照.
ということで,このJdbcType
を使うSqlUpdate
の派生クラスを作成.
package study; import javax.sql.DataSource; import org.springframework.jdbc.object.SqlUpdate; public class MySqlUpdate extends SqlUpdate { public MySqlUpdate() { super(); } public MySqlUpdate(DataSource ds, String sql) { super(ds, sql); } public MySqlUpdate(DataSource ds, String sql, JdbcType jdbcTypes) { super(ds, sql, toIntArray(jdbcTypes)); } public MySqlUpdate(DataSource ds, String sql, JdbcType jdbcTypes, int maxRowsAffected) { super(ds, sql, toIntArray(jdbcTypes), maxRowsAffected); } public void setJdbcTypes(JdbcType jdbcTypes) { setTypes(toIntArray(jdbcTypes)); } protected static int toIntArray(JdbcType jdbcTypes) { int types = new int[jdbcTypes.length]; for (int i = 0; i < jdbcTypes.length; ++i) { types[i] = jdbcTypes[i].getType(); } return types; } }
うーみゅ,My
〜っていうクラス名は使いたくなかったなぁ.でも適当な名前が思いつかない... 心より恥じる.
そして,例のFoo
(まぁこの名前もMy
〜と同じくらい心より恥じるですが)を修正.
package study; import java.util.List; import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.object.SqlQuery; import org.springframework.jdbc.object.SqlUpdate; public class Foo { private DataSource dataSource; private SqlQuery sqlQuery; private SqlUpdate sqlUpdate; public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public SqlQuery getSqlQuery() { return sqlQuery; } public void setSqlQuery(SqlQuery sqlQuery) { this.sqlQuery = sqlQuery; } public SqlUpdate getSqlUpdate() { return sqlUpdate; } public void setSqlUpdate(SqlUpdate sqlUpdate) { this.sqlUpdate = sqlUpdate; } /** * @@org.springframework.transaction.interceptor.DefaultTransactionAttribute() */ public void createTable() { JdbcTemplate jt = new JdbcTemplate(dataSource); jt.execute("create table model (name varchar, primary key(name))"); } /** * @@org.springframework.transaction.interceptor.DefaultTransactionAttribute() */ public void insert() { sqlUpdate.update("Yuri Ebihara"); sqlUpdate.update("Yu Yamada"); sqlUpdate.update("Moe Oshikiri"); } /** * @@org.springframework.transaction.interceptor.DefaultTransactionAttribute(readOnly=true) */ public List getModels() { return sqlQuery.execute(); } }
例によってAttributes Compilerでコンパイルします.
これを使う定義ファイルを用意して,
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" > <beans> <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <value>foo</value> </property> <property name="interceptorNames"> <value>transactionInterceptor,trace</value> </property> </bean> <bean id="sqlQuery" class="study.BeanSqlQuery"> <property name="dataSource"> <ref bean="dataSource"/> </property> <property name="sql"> <value>select name from model</value> </property> <property name="beanClass"> <value>study.Model</value> </property> </bean> <bean id="sqlUpdate" class="study.MySqlUpdate"> <property name="dataSource"> <ref bean="dataSource"/> </property> <property name="sql"> <value>insert into model (name) values(?)</value> </property> <property name="jdbcTypes"> <list> <value>VARCHAR</value> </list> </property> </bean> <bean id="foo" class="study.Foo" autowire="byName"> </bean> </beans>
実行用のクラスは前回と同じです.
それを実行!!
BEGIN study.Foo#createTable() - Looking up default SQLErrorCodes for DataSource - Database product name found in cache {12241337}. Name is HSQL Database Engine END study.Foo#createTable() : null - Initiating transaction commit BEGIN study.Foo#insert() END study.Foo#insert() : null - Initiating transaction commit BEGIN study.Foo#getModels() END study.Foo#getModels() : [Moe Oshikiri, Yu Yamada, Yuri Ebihara] - Initiating transaction commit [Moe Oshikiri, Yu Yamada, Yuri Ebihara]
こんなもんですかね.