SX-DbUtilsは、Apacheより提供されている commons-dbutils を、もっと便利に使うユーティリティです。
sx-dbutilsバージョン | commons-dbutils バージョン | Javaバージョン |
sx-dbutils-1.0.x | commons-dbutils-1.1 | JRE 1.4.2 以上 |
sx-dbutils-1.0.x-tiger | commons-dbutils-1.1 | JRE 1.5 以上 |
sx-dbutils-1.2.x | commons-dbutils-1.2 | JRE 1.4.2 以上 |
sx-dbutils-1.2.x-tiger | commons-dbutils-1.2 | JRE 1.5 以上 |
sx-dbutils-1.3.x | commons-dbutils-1.3 | JRE 1.5 以上 |
このサンプルでは、次のテーブルを使用します。
- create table TABLE01(
- id IDENTITY PRIMARY KEY ,
- name varchar(255),
- value varchar(255)
- );
- insert into TABLE01 (name,value) values('なまえ1','値1');
- insert into TABLE01 (name,value) values('なまえ2','値2');
- insert into TABLE01 (name,value) values('なまえ3','値3');
- insert into TABLE01 (name,value) values('なまえ4','値4');
- insert into TABLE01 (name,value) values('なまえ5','値5');
- commit;
id | name | value |
1 | なまえ1 | 値1 |
2 | なまえ2 | 値2 |
3 | なまえ3 | 値3 |
4 | なまえ4 | 値4 |
5 | なまえ5 | 値5 |
jp.sourceforge.sxdbutils.handlers.SingleHandler
java.sql.ResultSet の最初1行のみ処理したいときは、 SingleHandler を使用します。 SingleHandlerは jp.sourceforge.sxdbutils.SxRowProcessor インターフェースを引数として受け取り、次のように動作します。
- static void execute(Connection conn) throws Exception {
- QueryRunner qr = new QueryRunner();
- // 結果を1行処理するオブジェクト
- SxRowProcessor<String> processor = new SxRowProcessor<String>() {
- // 結果が帰ってきたときに1度だけ呼ばれます。
- public void init(ResultSetMetaData rsmd) throws SQLException {
- System.out.println("結果が帰ってきました。");
- }
- // ResultSet#nextメソッドがtrueを返すたびに呼ばれます。
- public String process(ResultSet rs) throws SQLException {
- return String.format("name=%s,
- value=%s",
- rs.getString("name"),
- rs.getString("value"));
- }
- };
- // 1行を受け取るときは、SingleHandlerを使います。
- // 最初の1行を処理したら終了するResultSetHandlerです。
- ResultSetHandler<String> rsh
- = new SingleHandler<String>(processor);
- String s = qr.query(conn, "select * from TABLE01", rsh);
- System.out.println(s);
- }
結果が帰ってきました。 name=なまえ1, value=値1
jp.sourceforge.sxdbutils.handlers.ListHandler
java.sql.ResultSet の全行を処理したいときは、 ListHandler を使用します。 ListHandlerは jp.sourceforge.sxdbutils.SxRowProcessor インターフェースを引数として受け取り、次のように動作します。
- static void execute(Connection conn) throws Exception {
- QueryRunner qr = new QueryRunner();
- SxRowProcessor<String> processor = new SxRowProcessor<String>() {
- public void init(ResultSetMetaData rsmd) throws SQLException {
- System.out.println("結果が帰ってきました。");
- }
- public String process(ResultSet rs) throws SQLException {
- return String.format(
- "name=%s,
- value=%s",
- rs.getString("name"),
- rs.getString("value"));
- }
- };
- ResultSetHandler<List<String>> rsh
- = new ListHandler<String>(processor);
- List<String> list
- = qr.query(conn, "select * from TABLE01", rsh);
- for (String string : list) {
- System.out.println(string);
- }
- }
結果が帰ってきました。 name=なまえ1, value=値1 name=なまえ2, value=値2 name=なまえ3, value=値3 name=なまえ4, value=値4 name=なまえ5, value=値5
1行を受け取る場合もListを受け取る場合も、SxRowProcessor は変わりません。 commons-dbutilsでは、このあたりの切り分けがうまくいっておらず、結局拡張性に乏しい実装となっています。
jp.sourceforge.sxdbutils.handlers.ResultTableHandler
SELECT結果全体を、.NETのDataTableのように 2次元の表オブジェクト ResultTable として受け取ることができます。
- static void execute(Connection conn) throws Exception {
- ResultSetHandler<ResultTable> rsh = new ResultTableHandler();
- SxQueryRunner runner = new SxQueryRunner();
- ResultTable table
- = runner.query(conn, "select * from ALL_TYPES", rsh);
- ResultColumnMetaData[] metas = table.getColumnMetaDatas();
- for (ResultColumnMetaData meta : metas) {
- System.out.println(meta.getColumnName());
- }
- System.out.println("-------カラム名でデータを取得する--------");
- for (ResultRow resultRow : table) {
- for (ResultColumnMetaData meta : metas) {
- System.out.println(
- meta.getColumnName()
- + " = "
- + resultRow.getString(meta.getColumnName()));
- }
- }
- System.out.println("-------カラム番号でデータを取得する--------");
- for (ResultRow resultRow : table) {
- for (int i = 0; i < metas.length; i++) {
- ResultColumnMetaData meta = metas[i];
- System.out.println(
- meta.getColumnName()
- + " = "
- + resultRow.getString(i));
- }
- }
- }
jp.sourceforge.sxdbutils.rstable.ResultTable
ResultTableは2次元の表形式データオブジェクトで、次のような特徴を持っています。
ResultTable は、行インデックスやカラムインデックスなどから直接アクセスすることもできますが、java.sql.ResultSetと異なり 0から開始している点に注意してください。
ResultTable によって、commons-dbutilsで一般的に使われている MapHandler / MapListHandler、またSX-DbUtilsのMapRowProcessor など使用するよりも、
高機能で安全性の高いプログラミングが可能となります。
OracleのDATE型項目を取得した場合、時間部分が消えてしまいます。 これを防ぐにはDate型を取り扱う部分をTimestamp型で扱うように変更します。
ResultTableHandler をnewする際に、次のようにcreateDateTypeColumnメソッドを書き換えます。
jp.sourceforge.sxdbutils.handlers.KeyedHandler
ResultSet の1項目がその行を特定するIDとして使用出来る場合、Map形式で受け取ると便利な時があります。 このような場合には、 KeyedHandler を使用できます。
- static void execute(Connection conn) throws Exception {
- QueryRunner qr = new QueryRunner();
- SxRowProcessor<String> processor = new SxRowProcessor<String>() {
- public void init(ResultSetMetaData rsmd) throws SQLException {
- System.out.println("結果が帰ってきました。");
- }
- public String process(ResultSet rs) throws SQLException {
- return String.format(
- "name=%s, value=%s",
- rs.getString("name"),
- rs.getString("value"));
- }
- };
- ResultSetHandler<Map<String, String>> rsh
- = new KeyedHandler<String, String>(
- "ID",
- String.class,
- processor);
- Map<String, String> map
- = qr.query(conn, "select * from TABLE01", rsh);
- for (Map.Entry<String, String> entry : map.entrySet()) {
- System.out.printf(
- "KEY:%s; VALUE:%s\n",
- entry.getKey(),
- entry.getValue());
- }
- }
結果が帰ってきました。 KEY:1; VALUE:name=なまえ1, value=値1 KEY:2; VALUE:name=なまえ2, value=値2 KEY:3; VALUE:name=なまえ3, value=値3 KEY:4; VALUE:name=なまえ4, value=値4 KEY:5; VALUE:name=なまえ5, value=値5
commons-dbutilsにも同名のHandlerが存在しますが、取得できる結果がMap<String,Map<String,Object>>で固定されてしまいます。 SxDbUtilsではこのような制限はなく、SxRowProcessor 次第でどのような形式でも扱うことができます。
jp.sourceforge.sxdbutils.SxRowProcessor
SX-DbUtilsが提供するResultSetHandler の実装は、コンストラクタの引数として、インターフェース SxRowProcessor を受け取ることが出来ます。 標準で次のような実装を提供しています。
jp.sourceforge.sxdbutils.processors.BeanRowProcessor
ResultSet をBeanに変換するには、 BeanRowProcessor を使います。 commons-dbutilsではBeanHandler/BeanListHandlerを使用していましたが、 sx-dbutilsでは BeanRowProcessor とSingleHandler / ListHandlerを組み合わせて使用します。
- static void execute(Connection conn) throws Exception {
- QueryRunner qr = new QueryRunner();
- BeanRowProcessor<Table01> processor
- = new BeanRowProcessor<Table01>(Table01.class);
- ResultSetHandler<List<Table01>> rsh
- = new ListHandler<Table01>(processor);
- List<Table01> list
- = qr.query(
- conn,
- "select * from TABLE01",
- rsh);
- for (Table01 table01 : list) {
- System.out.println(
- ToStringBuilder.reflectionToString(
- table01,
- ToStringStyle.SHORT_PREFIX_STYLE));
- }
- }
Table01[id=1,name=なまえ1,value=値1] Table01[id=2,name=なまえ2,value=値2] Table01[id=3,name=なまえ3,value=値3] Table01[id=4,name=なまえ4,value=値4] Table01[id=5,name=なまえ5,value=値5]
このあたりは通常のcommons-dbutilsの通常の使用方法と比べて大差ありません。 ただしcommons-dbutilsは内部で非効率敵な処理が多すぎるため、SX-DbUtilsのほうがパフォーマンス面で優れています。 特にcommons-dbutils-1.1の BeanListHandler の実装はcommons-dbutils-1.0の時よりも処理効率が悪くなっています。 この問題はcommons-dbutils-1.2で修正されましたが、今後も使用するには十分注意した方がいいでしょう。
また、SX-DbUtilsのBeanRowProcessorは、CLOBをStringのプロパティへ、BLOBをbyte[]のプロパティへ、自動的に変換してセットします。
jp.sourceforge.sxdbutils.mapping.NameMapping
BeanRowProcessor は、デフォルトで カラム名とBeanのプロパティ名が同じであればマッピングする というルールです。 これはcommons-dbutilsのBeanHandler/BeanListHandler に合わせています。 このルールを変更するには NameMapping を使用します。これはBeanRowProcessor のコンストラクタで指定が可能です。
sx-dbutilsが提供する、NameMappingの標準実装は次のようなものです。
クラス名 | 説明 |
ColumnNameMapping | デフォルトで使用されるNameMappingです。カラム名とプロパティ名が一致した場合にマッピングします。 |
CamelNameMapping | プロパティ名を変換してアンダースコア形式にした名前と、カラム名が一致した場合にマッピングします。 |
RemoveUnderScoreNameMapping | カラム名からアンダースコアを除去した名前と、プロパティ名が一致した場合にマッピングします。 |
OverwriteNameMapping | カラム名とプロパティ名のマッピングをMapで渡してマッピングします。 |
ConstantAnnoNameMapping | 定数アノテーション形式でカラム名を指定してマッピングします。 |
いずれの場合も大文字小文字の違いは意識しません。 何も指定しなかった場合、ColumnNameMapping が適用されます。
次のようなテーブルがあったとします。
- create table TABLE02(
- id IDENTITY PRIMARY KEY ,
- decimal_value decimal(18),
- text_value_1 varchar(255),
- text_value_2 varchar(255),
- text_value3 varchar(255)
- );
- insert into TABLE02 (decimal_value,text_value_1,text_value_2,text_value3)
- values(10,'テキスト11','テキスト12','テキスト12');
- insert into TABLE02 (decimal_value,text_value_1,text_value_2,text_value3)
- values(20,'テキスト21','テキスト22','テキスト22');
- insert into TABLE02 (decimal_value,text_value_1,text_value_2,text_value3)
- values(30,'テキスト31','テキスト32','テキスト32');
- insert into TABLE02 (decimal_value,text_value_1,text_value_2,text_value3)
- values(40,'テキスト41','テキスト42','テキスト42');
- insert into TABLE02 (decimal_value,text_value_1,text_value_2,text_value3)
- values(50,'テキスト51','テキスト52','テキスト52');
ID | DECIMAL_VALUE | TEXT_VALUE_1 | TEXT_VALUE_2 | TEXT_VALUE3 |
1 | 10 | テキスト11 | テキスト12 | テキスト12 |
2 | 20 | テキスト21 | テキスト22 | テキスト22 |
3 | 30 | テキスト31 | テキスト32 | テキスト32 |
4 | 40 | テキスト41 | テキスト42 | テキスト42 |
5 | 50 | テキスト51 | テキスト52 | テキスト52 |
このテーブルを次のようなBeanにマッピングしたいとします。
jp.sourceforge.sxdbutils.mapping.CamelNameMapping
jp.sourceforge.sxdbutils.mapping.RemoveUnderScoreNameMapping
アンダースコアを除去した形式でマッピングするにはCamelNameMapping、もしくはRemoveUnderScoreNameMappingを使用します。 この二つの違いは、次のように数値の前にアンダースコアが入っている場合に現れます。
カラム名 | プロパティ名 | Camel... | RemoveUnder... |
TEXT_VALUE_1 | textValue1 | ○ | ○ |
TEXT_VALUE3 | textValue3 | × | ○ |
これは、RemoveUnderScoreNameMappingがカラム名からプロパティ名を探すのに対して、 CamelNameMappingはプロパティ名からカラム名を探すことが原因です。
RemoveUnderScoreNameMappingは"TEXT_VALUE3"というカラム名を変換し、"textvalue3"というプロパティ名を探します。 大文字小文字は無視されるため、textValue3プロパティとマッピングされます。 CamelNameMappingは"textValue3"というプロパティ名を変換し、"TEXT_VALUE_3"というカラム名を探します。 このようなカラム名が存在しないため、マッピング出来ません。
CamelNameMappingは一見不便に見えますが、プロパティ名からカラム名が確定するという特徴から、 後述するINSERT/UPDATE文を作成するときに使用することができます。
- static void execute(Connection conn) throws Exception {
- QueryRunner qr = new QueryRunner();
- BeanRowProcessor<Table02> camelProcessor
- = new BeanRowProcessor<Table02>(
- Table02.class, new CamelNameMapping());
- BeanRowProcessor<Table02> ruscoreProcessor
- = new BeanRowProcessor<Table02>(
- Table02.class,
- new RemoveUnderScoreNameMapping());
- System.out.println("[CamelNameMapping]--------------------");
- List<Table02> camelResult
- = qr.query(
- conn,
- "select * from TABLE02",
- new ListHandler<Table02>(camelProcessor));
- for (Table02 table02 : camelResult) {
- System.out.println(
- ToStringBuilder.reflectionToString(
- table02,
- ToStringStyle.SHORT_PREFIX_STYLE));
- }
- System.out.println("[RemoveUnderScoreNameMapping]---------");
- List<Table02> ruscoreResult
- = qr.query(
- conn,
- "select * from TABLE02",
- new ListHandler<Table02>(ruscoreProcessor));
- for (Table02 table02 : ruscoreResult) {
- System.out.println(
- ToStringBuilder.reflectionToString(
- table02,
- ToStringStyle.SHORT_PREFIX_STYLE));
- }
- }
[CamelNameMapping]-------------------- Table02[id=1,decimalValue=10,textValue1=テキスト11,textValue2=テキスト12,textValue3=<null>] Table02[id=2,decimalValue=20,textValue1=テキスト21,textValue2=テキスト22,textValue3=<null>] Table02[id=3,decimalValue=30,textValue1=テキスト31,textValue2=テキスト32,textValue3=<null>] Table02[id=4,decimalValue=40,textValue1=テキスト41,textValue2=テキスト42,textValue3=<null>] Table02[id=5,decimalValue=50,textValue1=テキスト51,textValue2=テキスト52,textValue3=<null>] [RemoveUnderScoreNameMapping]--------- Table02[id=1,decimalValue=10,textValue1=テキスト11,textValue2=テキスト12,textValue3=テキスト12] Table02[id=2,decimalValue=20,textValue1=テキスト21,textValue2=テキスト22,textValue3=テキスト22] Table02[id=3,decimalValue=30,textValue1=テキスト31,textValue2=テキスト32,textValue3=テキスト32] Table02[id=4,decimalValue=40,textValue1=テキスト41,textValue2=テキスト42,textValue3=テキスト42] Table02[id=5,decimalValue=50,textValue1=テキスト51,textValue2=テキスト52,textValue3=テキスト52]
jp.sourceforge.sxdbutils.mapping.OverwriteNameMapping
上の アンダースコアを除去した形式でマッピングする。 にて、CamelNameMappingを使用した際、TEXT_VALUE3がマッピング出来ませんでした。 これに次のようなルールが適用したいとします。
このような「一部だけマッピング出来ない」といった場合、OverwriteNameMapping を使用出来ます。
- static void execute(Connection conn) throws Exception {
- QueryRunner qr = new QueryRunner();
- //マッピングをMapで作る。
- Map<String, String> map = new HashMap<String, String>();
- map.put("textValue3", "TEXT_VALUE3");
- //mapにマッピング情報が無ければCamelNameMappingが適用される。
- NameMapping nameMapping = new OverwriteNameMapping(
- map,
- new CamelNameMapping());
- BeanRowProcessor camelProcessor = new BeanRowProcessor(
- Table02.class,
- nameMapping);
- List<Table02> camelResult = (List<Table02>) qr.query(
- conn,
- "select * from TABLE02",
- new ListHandler(camelProcessor));
- for (Table02 table02 : camelResult) {
- System.out.println(
- ToStringBuilder.reflectionToString(
- table02,
- ToStringStyle.SHORT_PREFIX_STYLE));
- }
- }
Table02[id=1,decimalValue=10,textValue1=テキスト11,textValue2=テキスト12,textValue3=テキスト12] Table02[id=2,decimalValue=20,textValue1=テキスト21,textValue2=テキスト22,textValue3=テキスト22] Table02[id=3,decimalValue=30,textValue1=テキスト31,textValue2=テキスト32,textValue3=テキスト32] Table02[id=4,decimalValue=40,textValue1=テキスト41,textValue2=テキスト42,textValue3=テキスト42] Table02[id=5,decimalValue=50,textValue1=テキスト51,textValue2=テキスト52,textValue3=テキスト52]
jp.sourceforge.sxdbutils.mapping.ConstantAnnoNameMapping
OverwriteNameMapping で使用したルールは以下の通りでした。
OverwriteNameMapping では、Mapを使ってこのルールを定義しました。 ConstantAnnoNameMapping を使えば、定数フィールドを使って定義できます。
- static void execute(Connection conn) throws Exception {
- QueryRunner qr = new QueryRunner();
- // 定数アノテーションが無ければCamelNameMappingが適用される。
- NameMapping nameMapping
- = new ConstantAnnoNameMapping<Table02>(
- Table02.class,
- new CamelNameMapping());
- BeanRowProcessor<Table02> camelProcessor
- = new BeanRowProcessor<Table02>(
- Table02.class,
- nameMapping);
- List<Table02> camelResult
- = qr.query(
- conn,
- "select * from TABLE02",
- new ListHandler<Table02>(camelProcessor));
- for (Table02 table02 : camelResult) {
- System.out.println(
- ToStringBuilder.reflectionToString(
- table02,
- ToStringStyle.SHORT_PREFIX_STYLE));
- }
- }
Table02[id=1,decimalValue=10,textValue1=テキスト11,textValue2=テキスト12,textValue3=テキスト12] Table02[id=2,decimalValue=20,textValue1=テキスト21,textValue2=テキスト22,textValue3=テキスト22] Table02[id=3,decimalValue=30,textValue1=テキスト31,textValue2=テキスト32,textValue3=テキスト32] Table02[id=4,decimalValue=40,textValue1=テキスト41,textValue2=テキスト42,textValue3=テキスト42] Table02[id=5,decimalValue=50,textValue1=テキスト51,textValue2=テキスト52,textValue3=テキスト52]
SX-DbUtilsでは、エンティティの継承をサポートしています。
ID | EMP_NAME | DEPT_ID |
1 | 社員1 | 1 |
2 | 社員2 | 1 |
3 | 社員3 | 2 |
4 | 社員4 | 3 |
5 | 社員5 | 3 |
6 | 社員6 | 3 |
ID | DEPT_NAME |
1 | 部門1 |
2 | 部門2 |
3 | 部門3 |
- create table EMP(
- id IDENTITY PRIMARY KEY ,
- emp_name varchar(255),
- dept_id BIGINT
- );
- insert into EMP(emp_name,dept_id) values('社員1',1);
- insert into EMP(emp_name,dept_id) values('社員2',1);
- insert into EMP(emp_name,dept_id) values('社員3',2);
- insert into EMP(emp_name,dept_id) values('社員4',3);
- insert into EMP(emp_name,dept_id) values('社員5',3);
- insert into EMP(emp_name,dept_id) values('社員6',3);
- create table DEPT(
- id IDENTITY PRIMARY KEY ,
- dept_name varchar(255)
- );
- insert into DEPT (dept_name) values('部門1');
- insert into DEPT (dept_name) values('部門2');
- insert into DEPT (dept_name) values('部門3');
ID | EMP_NAME | DEPT_ID | DEPT_NAME |
1 | 社員1 | 1 | 部門1 |
2 | 社員2 | 1 | 部門1 |
3 | 社員3 | 2 | 部門2 |
4 | 社員4 | 3 | 部門3 |
5 | 社員5 | 3 | 部門3 |
6 | 社員6 | 3 | 部門3 |
- static void execute(Connection conn) throws Exception {
- QueryRunner qr = new QueryRunner();
- BeanRowProcessor<DeptNameEmp> processor
- = new BeanRowProcessor<DeptNameEmp>(DeptNameEmp.class);
- ResultSetHandler<List<DeptNameEmp>> rsh
- = new ListHandler<DeptNameEmp>(processor);
- List<DeptNameEmp> list = qr.query(conn,
- "select EMP.* ,DEPT.dept_name from EMP inner join DEPT on EMP.dept_id = DEPT.id ",
- rsh);
- for (DeptNameEmp deptNameEmp : list) {
- System.out.println(
- ToStringBuilder.reflectionToString(
- deptNameEmp,
- ToStringStyle.SHORT_PREFIX_STYLE));
- }
- }
DeptNameEmp[dept_name=部門1,id=1,emp_name=社員1,dept_id=1] DeptNameEmp[dept_name=部門1,id=2,emp_name=社員2,dept_id=1] DeptNameEmp[dept_name=部門2,id=3,emp_name=社員3,dept_id=2] DeptNameEmp[dept_name=部門3,id=4,emp_name=社員4,dept_id=3] DeptNameEmp[dept_name=部門3,id=5,emp_name=社員5,dept_id=3] DeptNameEmp[dept_name=部門3,id=6,emp_name=社員6,dept_id=3]
jp.sourceforge.sxdbutils.processors.FieldRowProcessor
FieldRowProcessorを使用すれば、publicなフィールドにResultSetをマッピングさせることが出来ます。
FieldRowProcessorは、CLOBをStringのフィールドへ、BLOBをbyte[]のフィールドへ、自動的に変換してセットします。
jp.sourceforge.sxdbutils.processors.MapRowProcessor
commons-dbutilsの MapHandler/MapListHandler は、MapRowProcessor を使用することで同等の機能を実現できます。
- static void execute(Connection conn) throws Exception {
- QueryRunner qr = new QueryRunner();
- MapRowProcessor processor = new MapRowProcessor();
- ResultSetHandler<List<Map<String, Object>>> rsh
- = new ListHandler<Map<String, Object>>(processor);
- List<Map<String, Object>> list
- = qr.query(conn, "select * from TABLE01", rsh);
- for (Map<String, Object> map : list) {
- System.out.println(Util.toString(map));
- }
- }
{id:1,name:なまえ1,value:値1} {id:2,name:なまえ2,value:値2} {id:3,name:なまえ3,value:値3} {id:4,name:なまえ4,value:値4} {id:5,name:なまえ5,value:値5}
SX-DbUtilsのMapRowProcessorは、自動的にCLOBをStringへ、BLOBをbyte[]へ変換します。 SX-DbUtils 1.0.8/1.2.5 以降からは ResultTable が使用できるため、MapRowProcessor を必要とするケースはほとんどありません。
jp.sourceforge.sxdbutils.processors.ValueRowProcessor
commons-dbutils の ScalarHandler/ColumnListHandler は、ValueRowProcessor を使用することで同等の機能を実現できます。
- static void execute(Connection conn) throws Exception {
- QueryRunner qr = new QueryRunner();
- SxRowProcessor<String> processor = new ValueRowProcessor<String>("NAME");
- ResultSetHandler<List<String>> rsh = new ListHandler<String>(processor);
- List<String> list = qr.query(conn, "select * from TABLE01", rsh);
- for (String name : list) {
- System.out.println(name);
- }
- }
なまえ1 なまえ2 なまえ3 なまえ4 なまえ5
SX-DbUtilsの ValueRowProcessor は、自動的にCLOBをStringへ、BLOBをbyte[]へ変換します。
jp.sourceforge.sxdbutils.query.Query
SX-DbUtilsでは、SQLとバインド変数を一緒に扱う Query オブジェクトを用意しています。
Queryオブジェクトは、オブジェクト配列とStringから構成される普遍クラスです。 joinメソッドで、Queryオブジェクト同士を結合出来ますが、新しいQueryオブジェクトが生成されます。
Queryオブジェクトは文字列化の際、わかりやすい情報として表示させるだけでなく、リテラル表記のSQLを出力できることで、デバッグ効率を上げています。
- public static void main(String[] args) {
- List<Object> parameters=new ArrayList<Object>();
- parameters.add("SAMPLE");
- Query query = new Query("select * from HOGE WHERE ID=?",parameters);
- System.out.println("そのまま表示");
- System.out.println(query);
- System.out.println("リテラルで表示");
- System.out.println(query.literal());
- }
jp.sourceforge.sxdbutils.query.SimpleQueryBuilder
SimpleQueryBuilderを使用すると、以下のように簡単にQueryを生成できます。
- public static void main(String[] args) {
- SimpleQueryBuilder builder = new SimpleQueryBuilder();
- builder.append("select * from HOGE")
- .append(" WHERE ID=?")
- .bind("SAMPLE");
- Query query = builder.toQuery();
- System.out.println("そのまま表示");
- System.out.println(query);
- System.out.println("リテラルで表示");
- System.out.println(query.literal());
- }
- static void execute(Connection conn) throws Exception {
- QueryRunner qr = new QueryRunner();
- BeanRowProcessor<Table01> processor
- = new BeanRowProcessor<Table01>(Table01.class);
- ResultSetHandler<List<Table01>> rsh
- = new ListHandler<Table01>(processor);
- SimpleQueryBuilder builder = new SimpleQueryBuilder();
- builder.append("select * from TABLE01 where ID=?").bind(1);
- Query query = builder.toQuery();
- List<Table01> list
- = qr.query(
- conn,
- query.getSql(),
- rsh,
- query.getParameters());
- for (Table01 table01 : list) {
- System.out.println(
- ToStringBuilder.reflectionToString(
- table01,
- ToStringStyle.SHORT_PREFIX_STYLE));
- }
- }
SimpleQueryBuilder でQueryを作成し、QueryからSQLとパラメータを取り出し、QueryRunner で実行するのが、通常の使い方です。 また、SxQueryRunner を使用すると、直接Queryを渡すことができます。
jp.sourceforge.sxdbutils.query.SimpleInsertBuilder
jp.sourceforge.sxdbutils.query.SimpleUpdateBuilder
SimpleInsertBuilder および SimpleUpdateBuilder は、SQL文字列を手動で組み立てる際に便利なクラスです。
insert into TABLE01 (NAME,VALUE ) values( ?,?) ==parameter==================== 000 java.lang.String #追加する名前 001 java.lang.String #追加する値 ====================parameter==
update TABLE01 set NAME=?,VALUE=? where ID=? ==parameter==================== 000 java.lang.String #更新する名前 001 java.lang.String #更新する値 002 java.lang.Integer #6 ====================parameter==
QueryFactoryはインスタンス作成時にSQLを保持しておき、Queryオブジェクトを高速に生成することができます。 QueryFactoryはQueryFactoryBuuilderを使用して生成し、staticなフィールドなどに保持しておくことを推奨します。
- static QueryFactory insertFactory =
- new BeanQueryFactoryBuilder(Table01.class)
- .excludeColumn("ID")
- .buildInsert();
- public static void main(String[] args) {
- Table01 table01 = new Table01();
- table01.setName("なまえ");
- table01.setValue("あたい");
- Query query=insertFactory.toQuery(table01);
- System.out.println(query);
- }
insert into Table01(name,value ) values ( ?,? ) ==parameter==================== 000 java.lang.String #なまえ 001 java.lang.String #あたい ====================parameter==
- static QueryFactory insertFactory =
- new MapQueryFactoryBuilder("TABLE02")
- .addColumn("decimal_value")
- .addColumn("text_value_1")
- .addColumn("text_value_2")
- .addColumn("text_value3")
- .buildInsert();
- public static void main(String[] args) {
- Map<String, Object> map = new HashMap<String, Object>();
- map.put("decimal_value", "なまえ");
- map.put("text_value_1", "あたい1");
- map.put("text_value_2", "あたい2");
- map.put("text_value3","あたい3");
- Query query = insertFactory.toQuery(map);
- System.out.println(query);
- }
insert into TABLE02(decimal_value,text_value_1,text_value_2,text_value3 ) values ( ?,?,?,? ) ==parameter==================== 000 java.lang.String #なまえ 001 java.lang.String #あたい1 002 java.lang.String #あたい2 003 java.lang.String #あたい3 ====================parameter==
Types/class | int | long | byte | short | float | double | char ※1 | BigDecimal | String ※1 | boolean ※1 |
BIGINT | △ | ○ | △ | △ | △ | △ | △ | ○ | ○ | ● |
DECIMAL | △ | △ | △ | △ | △ | △ | △ | ○ | ○ | ● |
DOUBLE | △ | △ | △ | △ | △ | ○ | △ | ○ | ○ | ● |
FLOAT | △ | △ | △ | △ | ○ | ○ | △ | ○ | ○ | ● |
INTEGER | ○ | △ | △ | △ | △ | △ | △ | ○ | ○ | ● |
NUMERIC | △ | △ | △ | △ | △ | △ | △ | ○ | ○ | ● |
REAL | △ | △ | △ | △ | ○ | ○ | △ | ○ | ○ | ● |
SMALLINT | △ | △ | ○ | △ | △ | △ | △ | ○ | ○ | ● |
TINYINT | △ | △ | △ | ○ | △ | △ | △ | ○ | ○ | ● |
○正しくマッピングできる。 △マッピングできるが、精度が落ちる可能性がある。 ●特定のルールに従い、マッピングされる。 ※1 Bean → DBでは、SQL型を指定する必要があります。
プリミティブのラッパークラスにも対応しています。プリミティブはnullのときにデフォルトがセットされますが、ラッパークラスはnullをがセットされます。
char | String | |
VARCHAR | ○ | ○ |
CHAR | ○ | ○ |
CLOB | × | ○ |
バイナリの項目(BLOBなど)は、byte[]へマッピングされます。
commons-dbutils-1.2まではJava1.4ベースでした。 そのため sx-dbutils.jar の他に、sx-dbutils-tiger.jar が必要となります。
このパッケージ内に用意されたクラスを使用することでジェネリクスを利用したコーディングが可能となります。
commons-dbutils-1.3 からはJava5に依存しています。 Sx-DbUtilsも1.3からはJava5対応をしていますので、sx-dbutils-tiger.jar は不要になります。
template 機能を使うことで、より簡単にsx-dbutilsを使用することができます。 template 機能は、sx-dbutilsを使用したDaoパターンの標準構成です。
- create table USERS(
- id identity primary key ,
- user_cd varchar(10) not null,
- name varchar(255),
- dept_id bigint default 0 not null,
- age integer default 0 not null,
- description clob,
- add_user_id bigint default 0 not null,
- add_timestamp datetime,
- update_user_id bigint default 0 not null,
- update_timestamp datetime,
- version_no integer default 0 not null
- );
- import java.util.Date;
- public class Users {
- private long id;
- private String userCd;
- private String name;
- private long deptId;
- private int age;
- private String description;
- private long addUserId;
- private Date addTimestamp;
- private long updateUserId;
- private Date updateTimestamp;
- private int versionNo;
- ・・・・
- //setter.getter
プロジェクトごとに多少要件が異なるため、次のようなDaoの基底クラスを準備します。 ここではSpringフレームワークを使ってDataSourceをDIしています。
- import java.sql.Connection;
- import java.sql.SQLException;
- import javax.annotation.Resource;
- import javax.sql.DataSource;
- import jp.sourceforge.sxdbutils.query.QueryFactory;
- import jp.sourceforge.sxdbutils.tiger.template.AbstractCrudTemplate;
- import org.springframework.jdbc.datasource.DataSourceUtils;
- /*
- * プロジェクトごとにDataSourceやConnectionの取得方法は異なるので、このようなクラスで違いを吸収する。
- */
- public abstract class ProjectBaseTemplate<E> extends
- AbstractCrudTemplate<E> {
- public ProjectBaseTemplate(
- QueryFactory insertFactory,
- QueryFactory updateFactory,
- QueryFactory deleteFactory) {
- super(insertFactory, updateFactory, deleteFactory);
- }
- private DataSource dataSource;
- @Resource(name = "dataSource")
- public void setDataSource(DataSource dataSource) {
- this.dataSource = dataSource;
- }
- @Override
- protected Connection getConnection() throws SQLException {
- return DataSourceUtils.getConnection(dataSource);
- }
- }
- import java.sql.SQLException;
- import java.util.List;
- import jp.sourceforge.sxdbutils.tiger.template.InsertTemplate;
- import jp.sourceforge.sxdbutils.tiger.template.SelectTemplate;
- import jp.sourceforge.sxdbutils.tiger.template.UpdateTemplate;
- import sample.entity.Users;
- /*
- * ここではDeleteTemplateを使用しないことで、削除禁止としている。
- */
- public interface UserDao extends InsertTemplate<Users>, UpdateTemplate<Users>,
- SelectTemplate<Users> {
- public abstract List<Users> selectAll() throws SQLException;
- public abstract Users selectByUserCd(String userCd) throws SQLException;
- }
- import java.sql.SQLException;
- import java.util.List;
- import jp.sourceforge.sxdbutils.mapping.CamelNameMapping;
- import jp.sourceforge.sxdbutils.tiger.processors.BeanRowProcessor;
- import jp.sourceforge.sxdbutils.query.BeanQueryFactoryBuilder;
- import jp.sourceforge.sxdbutils.query.Query;
- import jp.sourceforge.sxdbutils.query.QueryFactory;
- import jp.sourceforge.sxdbutils.query.SimpleQueryBuilder;
- import jp.sourceforge.sxdbutils.tiger.SxRowProcessor;
- import org.springframework.stereotype.Repository;
- import sample.ProjectBaseTemplate;
- import sample.entity.Users;
- @Repository
- public class UserDaoImpl extends ProjectBaseTemplate<Users> implements UserDao {
- private static final QueryFactory insertFactory =
- new BeanQueryFactoryBuilder(
- Users.class,
- new CamelNameMapping())
- .excludeColumn("ID")
- .excludeColumn("UPDATE_USER_ID")
- .excludeColumn("UPDATE_TIMESTAMP")
- .buildInsert();
- private static final QueryFactory updateFactory =
- new BeanQueryFactoryBuilder(
- Users.class,
- new CamelNameMapping())
- .excludeColumn("ADD_USER_ID")
- .excludeColumn("ADD_TIMESTAMP")
- .whereKeyColumn("ID")
- .versionColumnName("VERSION_NO")
- .buildUpdate();
- //DeleteTemplateは呼ばれないので、実質このFactoryは無効。
- //ここではサンプルとして記述してある。
- private static final QueryFactory deleteFactory =
- new BeanQueryFactoryBuilder(
- Users.class,
- new CamelNameMapping())
- .whereKeyColumn("ID")
- .versionColumnName("VERSION_NO")
- .buildDelete();
- public UserDaoImpl() {
- super(insertFactory, updateFactory, deleteFactory);
- }
- @Override
- protected Class<Users> getEntityClass() {
- return Users.class;
- }
- @Override
- protected <X extends Users> SxRowProcessor<X> createRowProcessor(
- Class<X> beanClass) {
- return new BeanRowProcessor<X>(beanClass, new CamelNameMapping());
- }
- // -----------------------------------------------------------
- /*
- * (non-Javadoc)
- *
- * @see sample.dao.UserDao#selectAll()
- */
- public List<Users> selectAll() throws SQLException {
- SimpleQueryBuilder builder = new SimpleQueryBuilder();
- Query query = builder.append("select * from USERS").toQuery();
- return executeQueryToEntityList(query);
- }
- @Override
- public Users selectByUserCd(String userCd) throws SQLException {
- SimpleQueryBuilder builder = new SimpleQueryBuilder();
- Query query = builder.append(
- "select * from USERS where USER_CD =?",
- userCd).toQuery();
- return executeQueryToBean(query);
- }
- }