ResultSet を Map に変換するには

ResultSet を Map に変換する方法として、Apache Commons の DbUtils に次のような便利なメソッドがあります。

org.apache.commons.dbutils.BasicRowProcessor#toMap(ResultSet) throws SQLException

ResultSet の行を Map へ変換します。 この実装では、大小文字を区別しないカラム名をキーとしたMapを返します。 例えば、map.get(“COL”) の呼び出しと
map.get(“col”) の呼び出しでは、 同一の値が返されるようになります。

Commons DbUtils: JDBC Utility Component
BasicRowProcessor javadoc

または、自前実装でこのような方法もあります。

public List resultSetToArrayList(ResultSet rs) throws SQLException{
  ResultSetMetaData md = rs.getMetaData();
  int columns = md.getColumnCount();
  ArrayList list = new ArrayList(50);
  while (rs.next()){
     HashMap row = new HashMap(columns);
     for(int i=1; i&lt;=columns; ++i){<br />
      row.put(md.getColumnName(i),rs.getObject(i));
     }
      list.add(row);
  }

return list;
}

参考: http://stackoverflow.com/questions/7507121/efficient-way-to-handle-resultset-in-java

UPDATE で同じ条件の任意数のレコードだけ更新したい

– ORDER BY と LIMIT を使えばよい。UPDATE にもあるとは知らなかった..迂闊。
<http://dev.mysql.com/doc/mysql/ja/update.html>
– MySQL リファレンスマニュアル :: 6.4.4 UPDATE 構文

 UPDATE [LOW_PRIORITY] [IGNORE] tbl_name
    SET col_name1=expr1 [, col_name2=expr2 …]
    [WHERE where_definition]
    [ORDER BY …]
    [LIMIT row_count]

最初こういうことをやって、同じテーブルに対して更新と参照を同時にやるなと怒られた。

UPDATE footable SET name = ‘foo’ WHERE id = (SELECT id FROM footable
  WHERE gender = 1)

Java のバイトコード書き換え

コンパイル済みのクラスの動作を、バイトコードを書き換えることによって変更することが可能になる。既存のクラスの実装をハンドリングする事もできるため、AOP の実現のために用いられる。JBoss, Bugdel が利用している。
http://www.javassist.org/
– Javassist 公式(JBoss.com)
http://www.csg.is.titech.ac.jp/~chiba/notes/javapress03/
– Javassist 入門
http://muimi.com/j/aop/javassist/
– Javassist にさわる

Postfix + Cyrus SASL で Outlook でも SMTP Auth

– broken なメールクライアントであるところの Outlook で SMTP Auth を使うには、 postfix の main.cf で次の記述が必要。

smtpd_sasl_auth_enable = yes
smtpd_sasl_local_domain = $mydomain

ただし LOGIN しか使えないので POP before SMTP のほうがまだマシ。
http://uniunix.net/contents/postfix.html
– Linux Lovers -Fedora Coreで始めるサーバ構築-: Postfix の設定

最後に挿入したレコードの一意な ID を取得する

– LAST_INSERT_ID() を使うとよい。

INSERT INTO user_list (id,name) values (555, “foo”);
SELECT LAST_INSERT_ID();
 +——————+
 | LAST_INSERT_ID() |
 +——————+
 | 555 |
 +——————+

という感じ。コネクション単位で保持しているので、同時アクセスがあっても干渉しない。ただし、取得する ID は AUTO_INCREMENT でなければならない。
http://dev.mysql.com/doc/mysql/ja/getting-unique-id.html
– MySQL リファレンスマニュアル :: 11.1.12.3 最後に挿入したレコードの一意な ID はどのように取得するのか

Tomcat 5.x で Servlet をマップできない

– web.xml で定義とマッピングをちゃんとしても、その URL でアクセスすると 500 エラーになって表示されず、*.war でやるとそのファイルだけデプロイされないという状態になっていた。

javax.servlet.ServletException: ラッパがサーブレットクラス com.example.my.FooServlet 又はそれが依存するクラスを見つけることができません

となって、

java.lang.ClassNotFoundException: com.example.my.FooServlet

が続く。
– 原因は一言では $TOMCAT_HOME/server/lib に servlet-api.jar が無かったこと($TOMCAT_HOME/common/lib にはあったがだめだった…何かが原因で親である common の設定がクリアされた?)。サンプルのサーブレットが動いてたかは確認してないが、とりあえずこんな感じで対処:

cp -p common/lib/server-api.jar server/lib/

– 参考:
http://jakarta.apache.org/tomcat/tomcat-5.0-doc/class-loader-howto.html
– The Apache Jakarta Tomcat 5 Servlet/JSP Container – Class Loader HOW-TO
http://www.atmarkit.co.jp/fjava/javatips/049jspservlet025.html
– @IT: Java TIPS — クラスローダの仕組みを知る

JDBC のログとりメモ

– close() するのは PreparedStatement#close() でなく ResultSet#close() でもなく、絶対に Connection でなければならない。
– ログ情報には PreparedStatement#toString() がよさげ(少なくとも com.mysql.jdbc では)。
理由は、ログのための利便性から。SQLException がどのクエリでエラーになったかを残してくれないので、 PreparedStatement インスタンスがクエリ周りの情報をきっちり保持してくれていれば、toString() でログにどのクエリ文を実行したかが残せる。しかし、PreparedStatement#close() は DB 接続を閉じる仕様とは限らないため、Connection#close() を呼ぶ必要がある。PreparedStatement#close() だけにしたらプールがあふれ、思わぬ場所で DB 接続が開くのをずっと待ち続けることになってしまった。
– 例:

boolean isExist(int id){
    PreparedStatement ps = null;
    try{
        final Connection con = getConnection();
        try{
            ps = con.prepareStatement(“SELECT id FROM list WHERE id = ? LIMIT 1”);
            ps.setInt(1, id);
            return ps.executeQuery().next();
        }finally{
            con.close();
        }
    }catch(SQLException e){
        log(“Query error [“+ps+”]”, e); // void log(String, Throwable); とする
        return false;
    }
}