2008/01/15

postgresのはまりどころ:配列にNULLがふくまれていた場合

配列の値

Array['aaaa', 'bbbb', null]

という値をJDBCで取得する場合にgetArray()で取得された結果は
Stringの配列となります。

但し、nullの値も'null'に文字列化されセットされます。
これバグだよなー。

2007/12/11

ibatisのはまりどころ:JDBCタイプのARRAYを利用するその2

ibatisで配列を参照できるようになったと思ったら更新ではまった。

ARRAYタイプの更新は当然JDBC実装に依存する。
僕が利用していたDBはPostgres。

JDBC規約?によればsetObjectを利用すべしとのこと。
でもJDBCソースみてみるとsetObject内には配列時の対応はないし、setArrayを利用するにもArrayはIFの為、オブジェクトの生成しずらい。
Postgresは配列対応なのになんでそういう実装がないの?
Webで検索してもよい資料はでてこない。

なので以下のような対応をしました。
一応、これでできることはできるんだけどパフォーマンスがでないだろうなー。

本気でやるならJDBCを拡張するかしたほうがよいと思うがなんでみんな対応しないんだろう?

// -- 以下コード
import java.sql.Array;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.List;


import com.ibatis.sqlmap.client.extensions.ParameterSetter;
import com.ibatis.sqlmap.client.extensions.ResultGetter;
import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;

public class ArrayTypeHandlerCallback implements TypeHandlerCallback {



public Object getResult(ResultGetter resultGetter) throws SQLException {
Array array = resultGetter.getArray();
if (array == null)
return null;
else
return Arrays.asList((Object[])array.getArray());
}

public void setParameter(ParameterSetter parameterSetter, Object obj)
throws SQLException {

if(obj == null){
parameterSetter.setNull(Types.ARRAY);
} else {

List lst = (List)obj;
parameterSetter.setArray(createArray(parameterSetter, lst));
}

}

public Object valueOf(String str) {
// 特につかっていないので今回は実装しない
return "";
}

public Array createArray(ParameterSetter parameterSetter, List list) throws SQLException {

Array array = null;
PreparedStatement ps = null;
try {

String arrayPhrase = toArrayPhrase(list);
if (arrayPhrase == null)
return null;

ps = parameterSetter.getPreparedStatement();
Connection cn = ps.getConnection();
String sql = "SELECT " + arrayPhrase;
ps = cn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
rs.next();
array = rs.getArray(1);
}
finally {

// 初回のPreparedStatementはIbatisによって制御されるのでcloseはしない
if (ps != null)
ps.close();
}

return array;

}

public String toArrayPhrase(List list) {

if (list == null || list.isEmpty())
return null;

int cnt = 0;
int max = list.size();
String value = null;
StringBuffer sb = new StringBuffer();
sb.append("ARRAY[");
for(Object obj : list) {

cnt++;

value = decorateSQLValue(obj);
if (cnt != max)
sb.append(value + ",");
else
sb.append(value);

}
sb.append("]");

return sb.toString();
}

public String decorateSQLValue(Object obj) {

if (obj == null)
return null;

if (obj instanceof String) {
return "'" + obj.toString() + "'";
}
else if (obj instanceof Integer) {
return obj.toString();
}
else {
return "'" + obj.toString() + "'";
}

}

}

2007/11/28

DBのはまりどころ

よく実行するSQLのパフォーマンスを向上させるために
javaのpreparedstatementにてSQLを実行する。

昨今のフレームワークもSQLはプリコンパイルした形で実行することが多い。
SQLインジェクション対策にもなっている。

これは一見、よさそうに見える。
但し、以下の点に注意
パラメータ数が多いもの。
この場合、SQLをプリコンパイルするのにかなりの時間を要するので逆に
パフォーマンス低下を発生させかねない。
簡単な確認しか行っていないがPostgres8.2.5ではだいたい100以上のパラメータ
をつけると急激にパフォーマンス低下が発生した。
みなさんどうですか?

2007/11/07

全文検索時に利用できるパラメータトークンロジック

Pattern pattern = Pattern.compile("(((?<=\\s| |\\A)\".+?\"(?=\\s| |\\Z))|((?<=\\s| |\\A)[^\\s| ]+?(?=\\s| |\\Z)))");
Matcher matcher = pattern.matcher("\"aaaa  \" bbbb  \" cccc\" dddd");
StringBuffer sb = new StringBuffer();
while (matcher.find()) {

System.out.println("発見0:[" + matcher.group(0) + "]");

}
matcher.appendTail(sb);
System.out.println(sb.toString());

または

// Pattern pattern = Pattern.compile("(((?<=\\s| |\t|\\A)\".+?\"(?=\\s| |\t|\\Z))|((\\s|\t| )+))");
// Pattern pattern2 = Pattern.compile("((?<=\\s| |\t|\\A)\".+?\"(?=\\s| |\t|\\Z))");
// Matcher matcher = pattern.matcher("\"aaaa  \" bbbb   \" cccc\" dddd");
// Matcher matcher2;
// StringBuffer sb = new StringBuffer();
// while (matcher.find()) {
//
// System.out.print("発見:[" + matcher.group(0) + "]");
// matcher2 = pattern2.matcher(matcher.group(0).toString());
// // 「"」でかこまれた文字列
// if (matcher2.find()) {
// System.out.println("0:[" + matcher.group(0) + "]");
// matcher.appendReplacement(sb, " +" + matcher.group(0));
// }
// // 単純な空白文字列
// else {
// System.out.println("1:[" + matcher.group(0) + "]");
// matcher.appendReplacement(sb, " +");
// }
// }
// matcher.appendTail(sb);
// System.out.println(sb.toString());

2007/09/14

ibatisのはまりどころ:JDBCタイプのARRAYを利用する

いやー、ぜんぜんドキュメントがないのでまいりました。はまること1時間。やっとみつけました。
これはツールadabarで行う方法ですが直接SQLMAP.xmlに記述でもできると思います。
ではそのやりかたですが


javaType="int" jdbcType="INTEGER" />
javaType="String" jdbcType="VARCHAR" />
javaType="int" jdbcType="INTEGER" />
javaType="java.sql.Array" jdbcType="ARRAY"
typeHandler="com.ibatis.sqlmap.engine.type.ObjectTypeHandler" />


ポイントはtypeHandlerの指定とそれをフルパスで指定すること

そうすれば以下のように
ShainDAO dao =
(ShainDAO) daoManager.getDao(ShainDAO.class);
Shain shain = new Shain();
// shain.setShainId(111);
shain = dao.selectByPrimaryKey(111);
System.out.println(shain.getShainName());
String[] a = (String[])shain.getColary().getArray();
System.out.println(a[0]);

で取得できます。
ibatis便利だー。

2007/09/11

ibatisのはまりどころ:データをDAOを利用してインサート時

インサートするテーブルの主キーがシリアルタイプの場合(オートナンバー)
Eclipseツールのabatorを利用してDAO作成時にabatorConfig.xmlの設定ファイルとして
以下のようにする。





但し、PostGresの場合だけあとは固定値のMySQLとかSQLServerとかを設定。

2007/09/03

postgresのはまりどころ:Lower関数に泣く!

initdbのときにロケールなしで指定しておかないとSQL関数のlower等で以下のようなエラーが発生。

ERROR: invalid multibyte character for locale
SQLステート:22021
ヒント:The server's LC_CTYPE locale is probably incompatible with the database encoding.



回避方法はDBのデータを一旦、pgdumpall等で退避しておいて以下のコマンドで再度DB構築
initdb --locale=C 各種オプション...

以上