2011年6月26日日曜日

ローンパターンを使用したリソース開放

リソースのオープンとクローズを共通化して、リソースをパラメータで受け取った関数にリソースを受け渡す実装方法。
関数にリソースを貸し出すことからローンパターンと言われるらしい。

  def main(args: Array[String]) {
    // 第二引数に、PrintWriterを受けて
    // ファイル出力を行う関数を指定する。
    writeFile(new File("test.txt"),
      writer => writeData(writer))
  }

  private def writeData(printWriter: PrintWriter) {
    println("wite start")
    printWriter.println("1")
    printWriter.println("2")
    printWriter.println("3")
    printWriter.println("4")
    printWriter.println("5")
    println("wite end")
  }

  def writeFile(file: File, write: PrintWriter => Unit) {
    // リソースを開く
    println("open file.")
    val writer = new PrintWriter(file)
    try {
      // 第二引数の関数にリソースを貸し出す。
      write(writer)
    } finally {
      // 最後にリソースを閉じる
      println("close file.")
      writer.close();
    }
  }

実行結果

ファイルオープン→書き込み→ファイルクローズの順に処理が行われていることがわかる。
open file.
wite start
wite end
close file.

ucpを使用したDB接続のプール

JDK5以降(ojdbc5.jarやojdbc6.jar)を使用してOracleとの接続をキャッシュしたい場合、
jdbcドライバのjar意外にucp.jarなるものが必要となる。

ucpとは、ユニバーサルコネクションプールの略で、汎用的なプール機構を提供してくれる。
汎用的な機構なので、別にOracle専用というわけではなくMySqlやDB2などの接続をプールすることができる。
Webコンテナが提供してくれている接続のプーリング機構を、スタンドアロンアプリからも利用出来るようにしてくれてるイメージかな。

マニュアルはこちら→http://download.oracle.com/docs/cd/E16338_01/java.112/b56283/toc.htm
ucpは、http://www.oracle.com/technetwork/jp/database/enterprise-edition/ucp-096353-ja.htmlからダウンロード出来る。

※ojdbc5.jar以降のjdbcドライバには、ojdbc4.jarで使用していた接続プール機能はすべて非推奨となっている。
しかも、その動作すら無効かされているため、プールを有効化しても完全に無視される。

サンプルコード

ucpを使用した接続キャッシュはこんな感じのコードで実装できる。
PoolDataSourceには、ステートメントキャッシュ用のプロパティなどもあるが、そのへんはマニュアルを見て適宜設定すればよいかなと。
        // プールデータソースを生成
        PoolDataSource dataSource = PoolDataSourceFactory.getPoolDataSource();

        // コネクションファクトリクラス名を完全修飾名で設定する。
        // Oracleの場合は、「oracle.jdbc.pool.OracleDataSource」を設定すれば良い
        dataSource.setConnectionFactoryClassName(
                "oracle.jdbc.pool.OracleDataSource");

        // DB接続情報(URL、ユーザ名、パスワード)を設定する。
        dataSource.setURL("jdbc:oracle:thin:@localhost:1521:xe");
        dataSource.setUser("hoge");
        dataSource.setPassword("hoge");
        // プール情報を設定する。
        dataSource.setInitialPoolSize(5);
        dataSource.setMaxPoolSize(5);

        // 接続を取得する。
        // このタイミングで、PoolDataSourceによって接続がプールされる。
        Connection connection = dataSource.getConnection();

サンプルコード2

接続先のデータベースがmysqlの場合の接続例。
ConnectionFactoryClassNameをmysqlのデータソースに変えてあげて、接続設定をmysqlように変更する。
        // プールデータソースを生成
        PoolDataSource dataSource = PoolDataSourceFactory.getPoolDataSource();

        // コネクションファクトリクラス名を完全修飾名で設定する。
        // 「com.mysql.jdbc.jdbc2.optional.MysqlDataSource」を設定すれば良い
        dataSource.setConnectionFactoryClassName(
                "com.mysql.jdbc.jdbc2.optional.MysqlDataSource");
        // DB接続情報(URL、ユーザ名、パスワード)を設定する。
        dataSource.setURL("jdbc:mysql://localhost/test");
        dataSource.setUser("hoge");
        dataSource.setPassword("hoge");
        // プール情報を設定する。
        dataSource.setInitialPoolSize(5);
        dataSource.setMaxPoolSize(5);

        // 接続を取得する。
        // このタイミングで、PoolDataSourceによって接続がプールされる。
        Connection connection = dataSource.getConnection();

2011年6月12日日曜日

intellijの行ブレークポイントのあれこれ

Intellijのブレークポイントをいじり倒してみたので、メモとして残しておく。

英語力が全く無いので理解が間違ってる部分があるかもだけど・・・。

ブレークポイントを置く方法

実行ステップに対するブレークポイントは、Eclipseと同じように行番号の隣の空白列をクリックすることによっておくことができる。
(行番号は、「menu」→「View」→「Show Line Numbers」で表示される。)

ブレークポイントを置くと、Eclipseと同じようにブレークポイントを示すマークが表示される。









特定のコールスタックの場合のみブレークさせる方法

Intellijでは、ブレークポイント間で依存関係を設定できるため、特定のコールスタックの場合のみブレークさせることができるようになっている。
privateメソッドなどで処理を抽出した場合など、複数箇所から呼び出される場合に使えるかんじになっている。
よく目的のコールスタックまで処理をスキップしようとして再開を連打してたら、目的のとこまで再開をクリックしちゃって最初からやり直しとかあるので、結構便利な機能かなと。

まずは、ブレークしたい行と、ブレークしたいコールスタック上にブレークポイントを置く。
この例だと、処理を中断したいhogeメソッド(7行目)と、hogeメソッドで処理を中断したい呼び出し元(12行目)にブレークポイントをおいている。













次にブレークポイント間での依存関係を設定する。
依存関係の設定は、View BreakPoints(「menu」→「Run」→「View BreakPoints」)で行う。
デフォルトでは、ブレークポイントが下記画像のようにリスト表示される。
右側のTree Viewボタンをクリックするとクラス単位やメソッド単位にグループ化して表示することができるので、見やすくなる。



















今回は、12行目から呼び出された場合のみ7行目を止めたいのでその設定を行う。
設定方法は、処理を中断したい7行目のブレークポイントを選択して、下の方にある「Depends on:」で12行目のブレークポイントを設定してあげる。



















この設定だけだと、処理を中断したくない12行目でも処理が中断されてしまうので、12行目は処理を止めないようにする。
View BreakPointsで12行目のブレークポイントを選択してSuspend policyでNoneを選択すると12行目のブレークポイントはスルーされる。






















静止画だと、実際にデバッグ実行した際の動きを示せないので、実行した結果は割愛。。。


処理を停止せずに標準出力に値のみ出力する方法

デバッグする際に標準出力に値を出力するってよくやると思うけど、これだと「System.out.println」を消し忘れたりするので、ブレークポイントでのコンソール出力機能を使って値を確認する。

この設定も「View BreakPoints」から行うことができる。
値を出力したい行にブレークポイントを設定し、「Log evaluated expression」から、コンソールに出力したい値を設定してあげれば良い。変数名は、補完してくれます。

ちなみに、標準出力には常に値が出力されるのではなく、処理中断の条件と一致した場合にのみ出力されます。
















実際に実行した結果
例がかなり行けてないけど、設定した値がコンソールに出力されていることがわかる。これで、余計な「System.out.println」を使わなくても、標準出力で値の確認ができるようになる。
ただIntellijの場合、「Live Template」の「sysoutv」が非常に便利なんで普通に「System.out.println」使うだろうなと。






条件を設定して処理を中断する方法

これも「View BreakPoints」から設定できる。
右側の「Condition」にBooleanを返す式を書いてあげると、結果がTrueの場合のみ処理が中断される。

2011年5月14日土曜日

scalaのプレースホルダ構文

scalaには、プレースホルダ構文なるものがあって、関数をかなり簡潔にかけるようになっている。
関数脳じゃないから理解するのにかなり苦労した。

Listのforeachで使ってみると

    val list = List(1, 2, 3, 4, 5)

    // プレースホルダ構文を使用しない場合、
    // foreachに対してこんな感じに関数を渡す。
    list.foreach((x:Int) => println(x))

    // プレースホルダ構文を使った場合
    // 関数のパラメータをプレースホルダ(_)として書くことができる。
    list.foreach(println _)

foreachに対して関数を渡すときと比べるとかなり簡潔になっている。
ただし、プレースホルダ構文は関数内で1度のみの使用しか認められていない。
なので、こんなコードはNGとなる。
    val list = List(1, 2, 3, 4, 5)
    // 関数内で、2度プレースホルダを使用しているので構文エラーとなる。
    list.foreach(
      println (_)
      println (_)
    )

Listのsortで使ってみると

    
    val l = List(1, 3, 2, 5, 4)
    // sortは、引数を2つ必要とするので、それぞれをプレースホルダにできる。
    println(l.sort(_ < _))

    val hoges = List(new Hoge(3), new Hoge(1), new Hoge(2))
    // こんな感じにプレースホルダの属性を参照することもできる。
    println(hoges.sort(_.id < _.id))

    class Hoge(x:Int) {
      val id:Int = x

      override def toString = "Hoge{" + id + "}"
    }

2011年5月6日金曜日

JDBC経由でシーケンスを使用して採番した値の取得

insert文などでシーケンスを使用して採番を行った値を取得する場合、
JDBCの自動採番キー機能と、Oracleの拡張SQL文を使用する2パターンの方法がある。
Oracle拡張機能を使用した場合、1レコード更新なら良いが複数レコード更新になると、
クライアント側コードもOracleが拡張したJDBCの機能を使う必要があり、ちと面倒になる。

JDBCの自動採番キー機能

        // Statement取得時に、自動採番キーの項目を第二引数で指定する。
        // この例では、ID列がシーケンスでの採番対象なので『ID』と指定している。
        PreparedStatement statement = con.prepareStatement(
                "insert into test (ID, NAME) values (TEST_SEQ.nextval, ?)",
                new String[]{"ID"});
        try {
            statement.setString(1, "hogehoge");
            statement.execute();
            // SQLを実行後、statementオブジェクトからgetGeneratedKeysを呼び出して、
            // 採番キーの結果を取得する。(1行だけなのにResultSetってのが面倒・・・)
            ResultSet keys = statement.getGeneratedKeys();
            try {
                // ResultSetと同じ感じに値を取得する。
                // ただし、カラム名指定でのアクセスは出来ないので、
                // インデックス指定でアクセスする必要がある。
                keys.next();
                int id = keys.getInt(1);
                System.out.println("id = " + id);
            } finally {
                keys.close();
            }
        } finally {
            statement.close();
        }
        con.commit();
    }

Oracleの拡張SQL機能

        // Oracleで拡張された、returning intoを使用して、
        // IDカラムの値をバインド変数(OUTパラメータに代入する。)
        // なお、SQL文はbegin endで囲い、無名pl/sqlにする必要がある。
        CallableStatement statement = con.prepareCall(
                "begin insert into test (ID, NAME) "
                        + " values (TEST_SEQ.nextval, ?) returning id into ?; end;");

        try {
            statement.setString(1, "hoge");
            // OUTパラメータのタイプを指定する。
            statement.registerOutParameter(2, Types.INTEGER);
            statement.execute();
            // SQL実行後に、OUTパラメータの値を取得する。
            int id = statement.getInt(2);
            System.out.println("id = " + id);
            con.commit();
        } finally {
            statement.close();
        }

Oracleの拡張SQL機能で複数行更新の場合

        // returning intoを使用してID列をOUTパラメータに代入する。
        // SQL文は、通常のSQL文形式で、statementはOracleJDBCのstatementにキャストする。
        OracleCallableStatement statement = (OracleCallableStatement)
                con.prepareCall("update test set id = test_seq.nextval returning id into ?");
        try {
            // registerReturnParameterを呼び出して、データ型を設定する。
            // なお、この時に設定するデータ型はOracleTypesがもつ定数を使用する。
            statement.registerReturnParameter(1, OracleTypes.INTEGER);
            int count = statement.executeUpdate();
            System.out.println("count = " + count);

            // getReturnResultSetを呼び出して、OUTパラメータの結果セット(ResultSet)を取得する。
            ResultSet set = statement.getReturnResultSet();
            try {
                while (set.next()) {
                    System.out.println("set.getInt(1) = " + set.getInt(1));
                }
            } finally {
                set.close();
            }
        } finally {
            statement.close();
        }

2011年5月4日水曜日

scalaのコンストラクタ

scalaでコンストラクタの定義方法

コード

// 基本コンストラクタ
// 基本コンストラクタは、クラス宣言で定義する(クラスのパラメータとして定義する)
class Hoge(s: String, n: Int) {

  // 補助コンストラクタ(基本コンストラクタをオーバーロードする場合に定義する。)
  // 補助コンストラクタの最初のステップでは、必ず他のコンスとラクアを呼び出す必要がある。
  // この仕様に従うと、最後には必ず基本コンストラクタが呼び出される。
  def this(s: String) = this (s, 0)
  def this(n: Int) = this ("hoge", n)

  // コンストラクタの変数は、privateなインスタンス変数となる
  override def toString = s + ":" + n
}

object Hoge {

  def main(args: Array[String]) {
    // 基本コンストラクタに指定した値が出力される。
    println(new Hoge("string", 1))

    // 補助コンストラクタに指定した値と、補助コンストラクタで指定しているデフォルト値が出力される。
    println(new Hoge("hoge"))
    println(new Hoge(-1))
  }
}

実行結果

string:1
hoge:0
hoge:-1

2011年5月2日月曜日

rubyで例外処理

rubyでの例外処理方法

javaで言うところのtry-catchに似たbegin-rescueブロックを使用した例外処理ができる。

例外処理の基本

下記コードだと、beginブロック内で発生した例外(StandardError)をrescueブロックで補足して処理をしている。
rubyでは、rescueブロックで例外を指定しないとStandardError(JavaでのRuntimeException)が暗黙的に補足される。
def hoge
  raise RuntimeError, "message"
end

begin     # javaでのtry
  hoge
rescue    # javaでのcatch

  # 補足した例外は、グローバル変数の「$!」で参照できる。
  puts  "error, #{$!.message}"  
end

begin
  hoge
# 例外を捕捉時に例外を変数に格納することも可能
# 下記のように「=> 変数名」とすると、変数に代入することができる。
rescue => exception
  puts "error, #{exception.message}"
end

特定の例外を捕捉

rescueブロックで例外クラスを指定すると特定の例外のみを補足することができる。
複数のrescueブロックを定義した場合、javaと同じように最初にマッチしたrescueブロックが実行される。
def hoge(n)
  raise TypeError, "Type error." unless n.is_a? Integer
  n / 0
end

begin
  hoge(0)
# ZeroDivisionErrorを捕捉
rescue ZeroDivisionError => e
  puts  "error, #{e.message}"
end

begin
  hoge('a')
# TypeErrorを捕捉
rescue TypeError => e
  puts "error, #{e.message}"
end

begin
  hoge('a')
# ZeroDivisionErrorとTypeErrorを捕捉
rescue ZeroDivisionError, TypeError => e
  puts "error, #{e.message}"
end