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() + "'";
}

}

}