ブログ アーカイブ

自己紹介

本ブログをご覧いただき、ありがとうございます。
株式会社ヒューマンインタラクティブテクノロジー(HIT)の技術グループ担当者です。

弊社ホームページ
ラベル java の投稿を表示しています。 すべての投稿を表示
ラベル java の投稿を表示しています。 すべての投稿を表示

2014年7月5日土曜日

JavaエンジニアがSwiftをさわってみた。 その1

お久しぶりです。しんです。

初投稿から3ヶ月ぶりという、投稿ペースが遅い私ですが、新しいネタを。
(ビッグデータ周りのネタは少々お待ちを。。。)


【Appleが発表した新しい言語】
先月から、私の周りではSwiftに関する話題が持ちきりでした。
新しい言語と聞くとウキウキする私としては、触ってみようかなと。

どうしても慣れている言語で頭の中で置き換えてしまうので、
Appleが公開した開発者向けリファレンスをもとに、
Javaだったらこうかな?という対比する形で記載します。

きっと、Javaに慣れている人なら、どんなことができるかイメージしやすいはず!

では、しばしのお付き合いを。


【準備】
Java側Java 1.7.0_55、Eclipse4.3
Swift側Xcode6 Beta

※Swiftを使用するためには、MacとXcode 6 Betaが必要です。
  また、Xcode 6 Betaを入手するためにはiOS Devloper Programに参加する必要があります。


【Javaとの対比】

<宣言と代入>
 Swiftで変数を宣言する場合は「var」を使用し、定数を宣言する場合は、「let」を使用します。
 また、型を指定したい場合は「:データ型」を指定します。
 Javaは、文末にセミコロンが必要となりますが、Swiftは、文末にセミコロンが不要です。

 型の指定がないので、Javascriptとかをイメージ(安直)してしまいます。
 
Java Swift
文末 String name = "shin"; var name = "shin"

但し、1行に2ステップ記載する場合はセミコロンが必要となります。

var name = "shin";var place = "tokyo"
変数の宣言と代入 データ型 変数名 = ; var 変数名 =

もしくは

var 変数名: データ型 =
定数の宣言と代入 final データ型 変数名 = ; let 変数名 =

もしくは

let 変数名: データ型 =


<配列>
配列の定義はJavaとSwiftで大きな違いはありませんが、Swiftには連想配列があります。
JavaでいうところのコレクションクラスのMapクラスですね。

Java Swift
配列 データ型 配列名 = new データ型[サイズ];

もしくは

データ型 配列名 = new データ型[]{要素1,要素2, …};

※プリミティブ型の場合は、new演算子はつきません。
var 配列名 = [要素1,要素2, …]

もしくは

let 配列名 = [要素1,要素2, …]
連想配列 HashMap<キーの型, 値の型> 連想配列名 = new HashMap<キーの型, 値の型>(); var 連想配列名 = [キー1:要素1,キー2:要素2, …]

もしくは

let 連想配列名 = [キー1:要素1,キー2:要素2, …]


Swiftで、空の配列を作成する場合は、「 var 配列名 = データ型[]() 」となります。
空の連想配列を作成する場合は、「 var 連想配列名 = Dictionary<キーの型, 値の型>() 」となります。



今回は一旦、ここまでとします。
あとで 分岐、繰り返しの構文を追記するかもしれません。


次回は、クラスやファンクションの定義周りをさわってみたいと思います。

2014年4月17日木曜日

[Java8]ParallelStream内で例外が発生した時スレッドは終了するのか?

どうも、トムです。

そろそろ世間(?)でも徐々にJava SE8の話題が増えてきた今日この頃
Web上を彷徨っているとチラッとこんな記事↓を見かけました。

もしもラムダの中で例外が発生したら(前編)
この記事の中でParallelStreamについての検証があるのですが
発生した例外は誰が受け取るのか?どこまで伝播するのか?
例外が発生したスレッド全てでcatch処理が行われるのか?
例外が発生しなかった他のスレッドはどのような影響を受けるのか?
そもそもちゃんと全部のスレッド処理が終了するのか?
皆さんも気になりますよね?
すごく気になりますね。

ParallelStream例外時のスレッドの動きを観察

引用記事の中ではParallelStream内で例外が発生した場合の結果として
幾つかのスレッドが残ったままになっているように見えますが
例外が発生するたびスレッドがどんどん増え続けるなんて事にならないのかちょっと確かめてみます。

コードは引用記事の内容を拝借しました。
parallelStreamで並列処理中に"ERROR"という文字列が出てきたらRuntimeExceptionをthrowするという処理を10回繰り返しスレッド数の変化を確認します。
        List<String> lines = Arrays.asList("A", "B", "C", "D", "ERROR", "F", "G", "ERROR", "I", "J");

        // 取り敢えず10回繰り返して様子見
        for (int i=0; i<10; i++) {
            System.out.println("スレッド数: " + String.valueOf(Thread.activeCount()));
            System.out.println("親スレッド Thread=" + Thread.currentThread().getId());
            Set<String> threads = new CopyOnWriteArraySet<>();
            try {
                lines.parallelStream().forEach(s -> {
                    System.out.println("=>開始[" + Thread.currentThread().getId() + "] 文字[" + s + "]" + ", スレッド数=" + String.valueOf(Thread.activeCount()));
                    threads.add(String.valueOf(Thread.currentThread().getId()));
                    try {
                        Thread.sleep(100L);
                        if (s.equals("ERROR")) {
                            System.out.println("◇◇◇◇ラムダ内で例外発生◇◇◇◇ Thread=" + Thread.currentThread().getId() + ", スレッド数=" + String.valueOf(Thread.activeCount()));
                            System.out.println("<=エラー終了[" + Thread.currentThread().getId() + "] 文字[" + s + "]" + ", スレッド数=" + String.valueOf(Thread.activeCount()));
                            threads.remove(String.valueOf(Thread.currentThread().getId()));
                            throw new RuntimeException();
                        }
                    } catch (InterruptedException e) {
                        System.out.println("InterruptedException in lambda. Thread=" + Thread.currentThread().getId() + ", スレッド数=" + String.valueOf(Thread.activeCount()));
                    }
                    System.out.println("<=終了[" + Thread.currentThread().getId() + "] 文字[" + s + "]" + ", スレッド数=" + String.valueOf(Thread.activeCount()));
                    threads.remove(String.valueOf(Thread.currentThread().getId()));
                });

            } catch (Exception ex) {
                System.out.println("◆◆◆◆ラムダの外で例外キャッチ◆◆◆◆ Thread=" + Thread.currentThread().getId() + ", スレッド数=" + String.valueOf(Thread.activeCount()));
                System.out.println("ラムダ内で終わっていないように見えるスレッド: [" + String.join(",", threads) + "]" + ", スレッド数=" + String.valueOf(Thread.activeCount()));
            }
            System.out.println("スレッド数: " + String.valueOf(Thread.activeCount()));
            System.out.println("----------------------------------------------------------------------");
        }


動かしてみると………
スレッド数: 2
親スレッド Thread=1
=>開始[1] 文字[G], スレッド数=5
=>開始[12] 文字[I], スレッド数=5
=>開始[11] 文字[C], スレッド数=5
=>開始[13] 文字[ERROR], スレッド数=5
<=終了[1] 文字[G], スレッド数=5
<=終了[11] 文字[C], スレッド数=5
=>開始[11] 文字[ERROR], スレッド数=5
<=終了[12] 文字[I], スレッド数=5
=>開始[12] 文字[J], スレッド数=5
◇◇◇◇ラムダ内で例外発生◇◇◇◇ Thread=13, スレッド数=5
<=エラー終了[13] 文字[ERROR], スレッド数=5
=>開始[13] 文字[B], スレッド数=5
=>開始[1] 文字[F], スレッド数=5
◇◇◇◇ラムダ内で例外発生◇◇◇◇ Thread=11, スレッド数=5
<=エラー終了[11] 文字[ERROR], スレッド数=5
=>開始[11] 文字[D], スレッド数=5
<=終了[1] 文字[F], スレッド数=5
◆◆◆◆ラムダの外で例外キャッチ◆◆◆◆ Thread=1, スレッド数=5
ラムダ内で終わっていないように見えるスレッド: [12,13,11], スレッド数=5
スレッド数: 5
----------------------------------------------------------------------
<=終了[12] 文字[J], スレッド数=5
<=終了[13] 文字[B], スレッド数=5
<=終了[11] 文字[D], スレッド数=5
【↑ここまで前回のラムダ処理のスレッド↑】 ※この部分だけ順番入れ替えています

スレッド数: 5
親スレッド Thread=1
=>開始[12] 文字[A], スレッド数=5
=>開始[1] 文字[G], スレッド数=5
=>開始[13] 文字[C], スレッド数=5
=>開始[11] 文字[B], スレッド数=5
<=終了[1] 文字[G], スレッド数=5
=>開始[1] 文字[F], スレッド数=5
<=終了[13] 文字[C], スレッド数=5
=>開始[13] 文字[ERROR], スレッド数=5
<=終了[12] 文字[A], スレッド数=5
=>開始[12] 文字[D], スレッド数=5
<=終了[11] 文字[B], スレッド数=5
=>開始[11] 文字[A], スレッド数=5
◇◇◇◇ラムダ内で例外発生◇◇◇◇ Thread=13, スレッド数=5
<=エラー終了[13] 文字[ERROR], スレッド数=5
<=終了[1] 文字[F], スレッド数=5
=>開始[1] 文字[I], スレッド数=5
=>開始[13] 文字[ERROR], スレッド数=5
<=終了[12] 文字[D], スレッド数=5
<=終了[11] 文字[A], スレッド数=5
<=終了[1] 文字[I], スレッド数=5
◆◆◆◆ラムダの外で例外キャッチ◆◆◆◆ Thread=1, スレッド数=5
ラムダ内で終わっていないように見えるスレッド: [13], スレッド数=5
スレッド数: 5
----------------------------------------------------------------------
◇◇◇◇ラムダ内で例外発生◇◇◇◇ Thread=13, スレッド数=5
<=エラー終了[13] 文字[ERROR], スレッド数=5
【↑ここまで前回のラムダ処理のスレッド↑】 ※この部分だけ順番入れ替えています

スレッド数: 5
親スレッド Thread=1
=>開始[11] 文字[C], スレッド数=5
=>開始[12] 文字[B], スレッド数=5
=>開始[1] 文字[G], スレッド数=5
=>開始[13] 文字[ERROR], スレッド数=5
<=終了[11] 文字[C], スレッド数=5
=>開始[11] 文字[I], スレッド数=5
<=終了[12] 文字[B], スレッド数=5
=>開始[12] 文字[A], スレッド数=5
<=終了[1] 文字[G], スレッド数=5
=>開始[1] 文字[F], スレッド数=5
◇◇◇◇ラムダ内で例外発生◇◇◇◇ Thread=13, スレッド数=5
<=エラー終了[13] 文字[ERROR], スレッド数=5
=>開始[13] 文字[D], スレッド数=5
<=終了[12] 文字[A], スレッド数=5
=>開始[12] 文字[ERROR], スレッド数=5
<=終了[11] 文字[I], スレッド数=5
<=終了[1] 文字[F], スレッド数=5
◆◆◆◆ラムダの外で例外キャッチ◆◆◆◆ Thread=1, スレッド数=5
ラムダ内で終わっていないように見えるスレッド: [13,12], スレッド数=5
スレッド数: 5
----------------------------------------------------------------------

~~~(中略)~~~

スレッド数: 5
----------------------------------------------------------------------
はい、スレッドは増えてはいないですね。
例外でスレッド数がもりもり増える事はないようです。
他に、気になる点は3点
  • ParallelStream内のスレッドでは親スレッドと同じスレッドも使用する…らしい
  • ParallelStream内で例外が発生しても正常処理のスレッドは継続する…らしい
  • ParallelStreamでの同時実行可能スレッド数は5スレッド…らしい
  • ※同時実行可能スレッド数に関してはJVMの設定や環境によるかもしれません

    う~ん……ここら辺はもっと検証してみないと何とも言えないですね。
    ただ、親スレッドと同じスレッドで並列処理が走るのは何とも奇妙な……



    サーバサイドアプリでの検証

    念のためサーバサイドで実装した場合にも同じ動きをするのか検証してみましょう。

    今回検証に利用したのはPlay Framework 2.2.2
    Play Frameworkは、J2EEを完全に排除し複雑なサーバサイドの構築やらTomcatのようなサーブレットコンテナを必要とせずにRubyのRailsやPHPのCakePHPのようにお手軽にMVCアーキテクチャを利用したアプリを作成できる生産性を重視するWebアプリ開発に特化した次世代フレームワークです。

    Play Frameworkの構築記事はまた次の機会という事で、さっそく検証してみましょう。


    何回か実行してみます。


    ~~~(中略)~~~

    サーバサイドでも特に動きに変わりはないようです。
    スレッド数に関しては安心ですが、ParallelStreamの例外時には正常処理のスレッドが残ってしまうので気を付けた方がよさそうです。
    やる人はいないと思いますがParallelStreamでDB操作とかは絶対やっちゃダメですね。

    2014年4月2日水曜日

    Java8 Date and Time API 変換メモ

    トムです。
    少し話題は遅いですが、日本時間で3月19日にJava8が正式に公開されました。

    Java8では多くのAPIが追加され、概ね使いやすくなっているのですが
    新規に追加されたDate and Time APIに関しては今までの日付型と比べ使い方に癖があり使用の際に躓く方も多いと思います。
    今回はメモ書きとしてJava8のDate and Time APIについて動作確認した内容を書いていきます。




    型変換サンプル

    java.util.Date から型変換

    java.util.Date から java.time.ZonedDateTime に変換

            Date d1 = new Date();
            ZonedDateTime date2zd = d1.toInstant().atZone(ZoneId.systemDefault());
    


    java.util.Date から java.time.OffsetDateTime に変換

            Date d2 = new Date();
            // UTC
            OffsetDateTime date2odt1 = d2.toInstant().atOffset(ZoneOffset.UTC);
    
            // System offset
            OffsetDateTime date2odt2 = d2.toInstant().atOffset(ZoneId.systemDefault().getRules().getOffset(d2.toInstant()));
    
            // System offset (一度java.time.ZonedDateTimeに変換)
            OffsetDateTime date2odt3 = d2.toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime();
    

    java.util.Date から java.time.OffsetTime に変換

            Date d3 = new Date();
            //UTC
            OffsetTime date2ot1 = d3.toInstant().atOffset(ZoneOffset.UTC).toOffsetTime();
    
            // System offset
            OffsetTime date2ot2 = d3.toInstant().atOffset(ZoneId.systemDefault().getRules().getOffset(d3.toInstant())).toOffsetTime();
    
            // System offset (一度java.time.ZonedDateTimeに変換)
            OffsetTime date2ot3 = d3.toInstant().atZone(ZoneId.systemDefault()).toOffsetDateTime().toOffsetTime();
    

    java.util.Date から java.time.LocalDateTime に変換

            Date d4 = new Date();
            LocalDateTime date2ldt1 = d4.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
            // または
            LocalDateTime date2ldt2 = LocalDateTime.from(d4.toInstant().atZone(ZoneId.systemDefault()));
    

    java.util.Date から java.time.LocalDate に変換

            Date d5 = new Date();
            LocalDate date2ld1 = d5.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            // または
            LocalDate date2ld2 = LocalDate.from(d5.toInstant().atZone(ZoneId.systemDefault()));
    

    java.util.Date から java.time.LocalTime に変換

            Date d6 = new Date();
            LocalTime date2lt1 = d6.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
            // または
            LocalTime date2lt2 = LocalTime.from(d6.toInstant().atZone(ZoneId.systemDefault()));
    

    java.util.Date から java.time.chrono.JapaneseDate に変換

            Date d7 = new Date();
            JapaneseDate date2jd = JapaneseDate.from(d7.toInstant().atZone(ZoneId.systemDefault()));
    




    java.time.ZonedDateTime から型変換

    java.time.ZonedDateTime から java.util.Date に変換

            ZonedDateTime zd1 = ZonedDateTime.now(ZoneId.systemDefault());
            Date zd2date = Date.from(zd1.toInstant());
    

    java.time.ZonedDateTime から java.time.LocalDateTime に変換

            ZonedDateTime zd2 = ZonedDateTime.now(ZoneId.systemDefault());
    
            LocalDateTime zd2ldt1 = zd2.toLocalDateTime();
            // または
            LocalDateTime zd2ldt2 = LocalDateTime.from(zd2);
    

    java.time.ZonedDateTime から java.time.LocalDate に変換

            ZonedDateTime zd3 = ZonedDateTime.now(ZoneId.systemDefault());
    
            LocalDate zd2ld1 = zd3.toLocalDate();
            // または
            LocalDate zd2ld2 = LocalDate.from(zd3);
    

    java.time.ZonedDateTime から java.time.LocalTime に変換

            ZonedDateTime zd4 = ZonedDateTime.now(ZoneId.systemDefault());
    
            LocalTime zd2lt1 = zd4.toLocalTime();
            // または
            LocalTime zd2lt2 = LocalTime.from(zd4);
    

    java.time.ZonedDateTime から java.time.OffsetDateTime に変換

            ZonedDateTime zd5 = ZonedDateTime.now(ZoneId.systemDefault());
    
            OffsetDateTime zd2odt1 = zd5.toOffsetDateTime();
            // または
            OffsetDateTime zd2odt2 = OffsetDateTime.from(zd5);
    

    java.time.ZonedDateTime から java.time.OffsetTime に変換

            ZonedDateTime zd6 = ZonedDateTime.now(ZoneId.systemDefault());
            OffsetTime zd2ot = OffsetTime.from(zd6);
    

    java.time.ZonedDateTime から java.time.chrono.JapaneseDate に変換

            ZonedDateTime zd7 = ZonedDateTime.now(ZoneId.systemDefault());
            JapaneseDate zd2jd = JapaneseDate.from(zd7);
    




    文字列フォーマット変換サンプル



    java.time.ZonedDateTime の文字列変換

            // System.out.println()のメソッド参照
            Consumer<Object> pl = System.out::println;
    
           //----------------------------------------------------------------------------------------------
           // ZonedDateTime methods
           //----------------------------------------------------------------------------------------------
            ZonedDateTime datetime = ZonedDateTime.now(ZoneId.systemDefault()); // Asia/Tokyo
            pl.accept("[getYear] 年");
            pl.accept(datetime.getYear());
            // 2014
            
            pl.accept("[getMonth] 月(Month型)");
            pl.accept(datetime.getMonth().name());
            // APRIL
            
            pl.accept("[getMonthValue] 月");
            pl.accept(datetime.getMonthValue());
            // 4
            
            pl.accept("[getDayOfMonth] 日");
            pl.accept(datetime.getDayOfMonth());
            // 2
    
            pl.accept("[getDayOfWeek] 曜日(DayOfWeek型)");
            pl.accept(datetime.getDayOfWeek().name());
            // WEDNESDAY
    
            pl.accept("[getDayOfYear] 1/1からの経過日");
            pl.accept(datetime.getDayOfYear());
            // 92
    
            pl.accept("[getHour] 時");
            pl.accept(datetime.getHour());
            // 19
    
            pl.accept("[getMinute] 分");
            pl.accept(datetime.getMinute());
            // 37
    
            pl.accept("[getSecond] 秒");
            pl.accept(datetime.getSecond());
            // 15
    
            pl.accept("[getMillis] ミリ秒");
            pl.accept(datetime.get(ChronoField.MILLI_OF_SECOND));
            // 798
    
            pl.accept("[getMicros] マイクロ秒");
            pl.accept(datetime.get(ChronoField.MICRO_OF_SECOND));
            // 798000
    
            pl.accept("[getNano] ナノ秒");
            pl.accept(datetime.getNano());
            // 798000000
    
            pl.accept("[The aligned day-of-week within a month] 月初から月末まで1からカウントアップして8日で1にリセットされる1~7の整数");
            pl.accept(datetime.get(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH));
            // 2
    
            pl.accept("[The aligned day-of-week within a year] 年初から年末まで1からカウントアップして8日で1にリセットされる1~7の整数");
            pl.accept(datetime.get(ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR));
            // 1
    
            pl.accept("[The clock-hour-of-am-pm] AM/PMを考慮した時間");
            pl.accept(datetime.get(ChronoField.CLOCK_HOUR_OF_AMPM));
            // 7
    
            pl.accept("[The am-pm-of-day] 0=AM/1=PM");
            pl.accept(datetime.get(ChronoField.AMPM_OF_DAY));
            // 1
    
            pl.accept("[Era] 0=紀元前(B.C.)/1=西暦(A.D.)");
            pl.accept(datetime.get(ChronoField.ERA));
            // 1
    
            pl.accept("[InstantSeconds] エポック秒(Unix時間/秒) 1970-01-01T00:00Zからの経過秒");
            pl.accept(datetime.toEpochSecond());
            // 1396435035
    
            pl.accept("[InstantSeconds] エポック日(Unix時間/日) 1970-01-01からの経過日");
            pl.accept(datetime.getLong(ChronoField.EPOCH_DAY));
            // 16162
    
    
           //----------------------------------------------------------------------------------------------
           // format use DateTimeFormatter Constant
           //----------------------------------------------------------------------------------------------
            pl.accept("[toString]");
            pl.accept(datetime.toString());
            // 2014-04-02T19:37:15.798+09:00[Asia/Tokyo]
            
            pl.accept("[BASIC_ISO_DATE]");
            pl.accept(datetime.format(DateTimeFormatter.BASIC_ISO_DATE));
            // 20140402+0900
            
            pl.accept("[ISO_LOCAL_DATE]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_LOCAL_DATE));
            // 2014-04-02
    
            pl.accept("[ISO_OFFSET_DATE]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_OFFSET_DATE));
            // 2014-04-02+09:00
            
            pl.accept("[ISO_DATE]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_DATE));
            // 2014-04-02+09:00
    
            pl.accept("[ISO_LOCAL_TIME]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_LOCAL_TIME));
            // 19:37:15.798
            
            pl.accept("[ISO_OFFSET_TIME]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_OFFSET_TIME));
            // 19:37:15.798+09:00
    
            pl.accept("[ISO_TIME]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_TIME));
            // 19:37:15.798+09:00
    
            pl.accept("[ISO_LOCAL_DATE_TIME]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
            // 2014-04-02T19:37:15.798
    
            pl.accept("[ISO_OFFSET_DATE_TIME]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
            // 2014-04-02T19:37:15.798+09:00
    
            pl.accept("[ISO_ZONED_DATE_TIME]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
            // 2014-04-02T19:37:15.798+09:00[Asia/Tokyo]
    
            pl.accept("[ISO_DATE_TIME]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-04-02T19:37:15.798+09:00[Asia/Tokyo]
    
            pl.accept("[ISO_ORDINAL_DATE]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_ORDINAL_DATE));
            // 2014-092+09:00
    
            pl.accept("[ISO_WEEK_DATE]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_WEEK_DATE));
            // 2014-W14-3+09:00
    
            pl.accept("[ISO_INSTANT]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_INSTANT));
            // 2014-04-02T10:37:15.798Z
    
            pl.accept("[RFC_1123_DATE_TIME]");
            pl.accept(datetime.format(DateTimeFormatter.RFC_1123_DATE_TIME));
            // Wed, 2 Apr 2014 19:37:15 +0900
    
    
           //----------------------------------------------------------------------------------------------
           // format use DateTimeFormatter ofLocalizedDate()
           //----------------------------------------------------------------------------------------------
            pl.accept("[ofLocalizedDate] [DateStyle=FULL]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)));
            // 2014年4月2日
    
            pl.accept("[ofLocalizedDate] [DateStyle=MEDIUM]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)));
            // 2014/04/02
    
            pl.accept("[ofLocalizedDate] [DateStyle=SHORT]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)));
            // 14/04/02
    
            pl.accept("[ofLocalizedDate] [DateStyle=LONG]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)));
            // 2014/04/02
    
    
           //----------------------------------------------------------------------------------------------
           // format use DateTimeFormatter ofLocalizedTime()
           //----------------------------------------------------------------------------------------------
            pl.accept("[ofLocalizedTime] [TimeStyle=FULL]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.FULL)));
            // 19時37分15秒 JST
    
            pl.accept("[ofLocalizedTime] [TimeStyle=MEDIUM]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM)));
            // 19:37:15
    
            pl.accept("[ofLocalizedTime] [TimeStyle=SHORT]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)));
            // 19:37
    
            pl.accept("[ofLocalizedTime] [TimeStyle=LONG]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.LONG)));
            // 19:37:15 JST
    
    
           //----------------------------------------------------------------------------------------------
           // format use DateTimeFormatter ofLocalizedDateTime()
           //----------------------------------------------------------------------------------------------
            pl.accept("[ofLocalizedDateTime] [DateTimeStyle=FULL]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL)));
            // 2014年4月2日 19時37分15秒 JST
    
            pl.accept("[ofLocalizedDateTime] [DateTimeStyle=MEDIUM]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));
            // 2014/04/02 19:37:15
    
            pl.accept("[ofLocalizedDateTime] [DateTimeStyle=SHORT]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)));
            // 14/04/02 19:37
    
            pl.accept("[ofLocalizedDateTime] [DateTimeStyle=LONG]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)));
            // 2014/04/02 19:37:15 JST
    
    
            pl.accept("[ofLocalizedDateTime] [DateStyle=FULL] [TimeStyle=FULL]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.FULL)));
            // 2014年4月2日 19時37分15秒 JST
    
            pl.accept("[ofLocalizedDateTime] [DateStyle=FULL] [TimeStyle=MEDIUM]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.MEDIUM)));
            // 2014年4月2日 19:37:15
    
            pl.accept("[ofLocalizedDateTime] [DateStyle=FULL] [TimeStyle=SHORT]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.SHORT)));
            // 2014年4月2日 19:37
    
            pl.accept("[ofLocalizedDateTime] [DateStyle=LONG] [TimeStyle=MEDIUM]");
            pl.accept(datetime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.MEDIUM)));
            // 2014/04/02 19:37:15
    
    
           //----------------------------------------------------------------------------------------------
           // format use DateTimeFormatter ofPattern()
           //----------------------------------------------------------------------------------------------
            pl.accept("[ofPattern] era 暦:型により異なる");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("G")));
            // 西暦
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("GGGG")));
            // 西暦
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("GGGGG")));
            // A
    
            // java.time.chrono.JapaneseDate の場合
            pl.accept(JapaneseDate.now().format(DateTimeFormatter.ofPattern("G")));
            // 平成
            pl.accept(JapaneseDate.now().format(DateTimeFormatter.ofPattern("GGGG")));
            // 平成
            pl.accept(JapaneseDate.now().format(DateTimeFormatter.ofPattern("GGGGG")));
            // H
    
            pl.accept("[ofPattern] year 年");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("uuuu")));
            // 2014
    
            // java.time.chrono.JapaneseDate の場合
            pl.accept(JapaneseDate.now().format(DateTimeFormatter.ofPattern("uuuu")));
            // 2014
    
            pl.accept("[ofPattern] year-of-era (暦を考慮した)年");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("yyyy")));
            // 2014
    
            // java.time.chrono.JapaneseDate の場合
            pl.accept(JapaneseDate.now().format(DateTimeFormatter.ofPattern("yy")));
            // 26
    
            pl.accept("[ofPattern] day-of-year 1/1からの経過日数");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("D")));
            // 92
    
            pl.accept("[ofPattern] month-of-year 月");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("M")));
            // 4
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("MM")));
            // 04
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("MMM")));
            // 4
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("MMMM")));
            // 4月
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("MMMMM")));
            // A
    
            pl.accept("[ofPattern] day-of-month 日");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("d")));
            // 2
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("dd")));
            // 02
    
            pl.accept("[ofPattern] quarter-of-year クオーター");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("Q")));
            // 2
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("QQ")));
            // 02
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("QQQ")));
            // Q2
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("QQQQ")));
            // 第2四半期
    
            pl.accept("[ofPattern] week-based-year 週基準年");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("YYYY")));
            // 2014
    
            // 例えば2014/1/1は水曜日なので2013/12/31は2014に組み込まれます
            pl.accept(ZonedDateTime.of(2013,12,31,0,0,0,0,ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("YYYY")));
            // 2014
    
            pl.accept("[ofPattern] week-of-week-based-year 年を通しての週基準年の週 (1~52 or 53)");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("w")));
            // 14
    
            // 2013/12/31(火)は2014年の1週目
            pl.accept(ZonedDateTime.of(2013,12,31,0,0,0,0,ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("w")));
            // 1
    
            // 2013/12/28(日)は2013年の52週目
            pl.accept(ZonedDateTime.of(2013,12,28,0,0,0,0,ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("w")));
            // 52
    
            pl.accept("[ofPattern] week-of-month 週");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("W")));
            // 1
    
            pl.accept("[ofPattern] day-of-week 曜日");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("E")));
            // 水
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("EE")));
            // 水
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("EEE")));
            // 水
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("EEEE")));
            // 水曜日
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("EEEEE")));
            // 水
    
            pl.accept("[ofPattern] localized day-of-week");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("e")));
            // 4
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("ee")));
            // 04
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("eee")));
            // 水
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("eeee")));
            // 水曜日
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("eeeee")));
            // 水
    
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("c")));
            // 4
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("ccc")));
            // 水
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("cccc")));
            // 水曜日
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("ccccc")));
            // 3
    
            pl.accept("[ofPattern] week-of-month");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("F")));
            // 2
    
            pl.accept("[ofPattern] am-pm-of-day");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("a")));
            // 午後
    
            pl.accept("[ofPattern] clock-hour-of-am-pm (1-12)");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("h")));
            // 9
    
            pl.accept("[ofPattern] hour-of-am-pm (0-11)");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("K")));
            // 9
    
            pl.accept("[ofPattern] clock-hour-of-am-pm (1-24)");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("k")));
            // 21
    
            pl.accept("[ofPattern] hour-of-day (0-23) 時");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("H")));
            // 21
    
            pl.accept("[ofPattern] hour-of-day (00-23) 時");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("HH")));
            // 21
    
            pl.accept("[ofPattern] minute-of-hour 分");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("mm")));
            // 20
    
            pl.accept("[ofPattern] second-of-minute 秒");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("ss")));
            // 58
    
            pl.accept("[ofPattern] fraction-of-second ミリ秒");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("SSS")));
            // 094
    
            pl.accept("[ofPattern] nano-of-second ナノ秒");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("n")));
            // 94000000
    
            pl.accept("[ofPattern] milli-of-day 1日の経過ミリ秒");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("A")));
            // 76858094
    
            pl.accept("[ofPattern] nano-of-day 1日の経過ナノ秒");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("N")));
            // 76858094000000
    
            pl.accept("[ofPattern] time-zone ID タイムゾーンID ZonedDateTime以外では使用不可");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("VV")));
            // Asia/Tokyo
    
            pl.accept("[ofPattern] time-zone name タイムゾーン名 ZonedDateTime以外では使用不可");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("z")));
            // JST
    
            pl.accept("[ofPattern] localized zone-offset");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("0")));
            // 0
    
            pl.accept("[ofPattern] zone-offset オフセット UTC(+0000)の場合 +0000の代わりにZを表示");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("XXXX")));
            // +0900
    
            // UTCの場合
            pl.accept(ZonedDateTime.now(ZoneId.of("GMT")).format(DateTimeFormatter.ofPattern("XXXX")));
            // Z
    
            pl.accept("[ofPattern] zone-offset オフセット");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("xxxx")));
            // +0900
    
            // UTCの場合
            pl.accept(ZonedDateTime.now(ZoneId.of("GMT")).format(DateTimeFormatter.ofPattern("xxxx")));
            // +0000
    
            pl.accept("[ofPattern] zone-offset オフセット");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("Z")));
            // +0900
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("ZZ")));
            // +0900
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("ZZZ")));
            // +0900
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("ZZZZ")));
            // GMT+09:00
    
            pl.accept("[ofPattern] escape for text / [] # {} 次の文字列はシングルクォートでエスケープが必要です(/[]#{})");
            pl.accept(datetime.format(DateTimeFormatter.ofPattern("'['yyyy'/'MM'/'dd']' '{'HH'#'mm'#'ss'}'")));
            // [2014/04/02] {21#20#58}
    
            pl.accept("[withZoneSameInstant] タイムゾーン変換");
            pl.accept(datetime.withZoneSameInstant(ZoneId.of("America/Los_Angeles")).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-04-02T05:20:58.094-07:00[America/Los_Angeles]
    
    




    java.time.chrono.JapaneseDate の文字列変換

            Consumer<Object> pl = System.out::println;
    
            JapaneseDate jd = JapaneseDate.now();
            pl.accept("[Japanese Era Name List] 和暦名一覧");
            jd.getChronology().eras().forEach(pl);
            // Meiji
            // Taisho
            // Showa
            // Heisei
    
            pl.accept("[toString]");
            pl.accept(jd.toString());
            // Japanese Heisei 26-04-02
    
            pl.accept("[Japanese Era Name] 和暦(名)");
            pl.accept(jd.getEra());
            // Heisei
    
            pl.accept("[Japanese Era Year] 和暦(年)");
            pl.accept(jd.get(ChronoField.YEAR_OF_ERA));
            // 26
    
            pl.accept("[Era] -1=明治/0=大正/1=昭和/2=平成");
            pl.accept(jd.get(ChronoField.ERA));
            // 2
    
            pl.accept("[InstantSeconds] エポック日(Unix時間/日) 1970-01-01からの経過日");
            pl.accept(jd.getLong(ChronoField.EPOCH_DAY));
            // 16162
    
            pl.accept("[ofPattern]");
            pl.accept(jd.format(DateTimeFormatter.ofPattern("Gyy年 MM月dd日")));
            // 平成26年 04月02日
    
    




    日時操作サンプル



    java.time.ZonedDateTime の日時操作

            Consumer<Object> pl = System.out::println;
    
           //----------------------------------------------------------------------------------------------
           // 日時操作
           //----------------------------------------------------------------------------------------------
            pl.accept("[original]");
            pl.accept(datetime.format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-04-02T21:20:58.094+09:00[Asia/Tokyo]
    
            pl.accept("[+1Years] +1年");
            pl.accept(datetime.plusYears(1).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2015-04-02T21:20:58.094+09:00[Asia/Tokyo]
    
            pl.accept("[+1Months] +1ヶ月");
            pl.accept(datetime.plusMonths(1).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-05-02T21:20:58.094+09:00[Asia/Tokyo]
    
            pl.accept("[+1Days] +1日");
            pl.accept(datetime.plusDays(1).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-04-03T21:20:58.094+09:00[Asia/Tokyo]
    
            pl.accept("[+1Hours] +1時間");
            pl.accept(datetime.plusHours(1).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-04-02T22:20:58.094+09:00[Asia/Tokyo]
    
            pl.accept("[+1Minutes] +1分");
            pl.accept(datetime.plusMinutes(1).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-04-02T21:21:58.094+09:00[Asia/Tokyo]
    
            pl.accept("[+1Seconds] +1秒");
            pl.accept(datetime.plusSeconds(1).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-04-02T21:20:59.094+09:00[Asia/Tokyo]
    
            pl.accept("[+1Millis] +1ミリ秒");
            pl.accept(datetime.plus(1, ChronoUnit.MILLIS).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-04-02T21:20:58.095+09:00[Asia/Tokyo]
    
            pl.accept("[+1Micros] +1マイクロ秒");
            pl.accept(datetime.plus(1, ChronoUnit.MICROS).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-04-02T21:20:58.094001+09:00[Asia/Tokyo]
    
            pl.accept("[+1Nanos] +1ナノ秒");
            pl.accept(datetime.plusNanos(1).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-04-02T21:20:58.094000001+09:00[Asia/Tokyo]
    
            pl.accept("[+1Weeks] +1週");
            pl.accept(datetime.plusWeeks(1).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-04-09T21:20:58.094+09:00[Asia/Tokyo]
    
            pl.accept("[+1HalfDays] +12時間");
            pl.accept(datetime.plus(1, ChronoUnit.HALF_DAYS).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2014-04-03T09:20:58.094+09:00[Asia/Tokyo]
    
            pl.accept("[+1Decades] +10年");
            pl.accept(datetime.plus(1, ChronoUnit.DECADES).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2024-04-02T21:20:58.094+09:00[Asia/Tokyo]
    
            pl.accept("[+1Centuries] +100年");
            pl.accept(datetime.plus(1, ChronoUnit.CENTURIES).format(DateTimeFormatter.ISO_DATE_TIME));
            // 2114-04-02T21:20:58.094+09:00[Asia/Tokyo]
    
            pl.accept("[+1Millennium] +1000年");
            pl.accept(datetime.plus(1, ChronoUnit.MILLENNIA).format(DateTimeFormatter.ISO_DATE_TIME));
            // 3014-04-02T21:20:58.094+09:00[Asia/Tokyo]
    
    

    他に有用なものがあり次第追加していきます。

    2014年4月1日火曜日

    MyBatisで遊んでみる(3) 第三章 MyBatis実装サンプル

    どうも、トムです。
    前準備は終わりましたので第三章ではMyBatisを実装してみます。


    第三章 MyBatis実装サンプル

    必要なライブラリは第二章を参照してください。
    本稿では第二章までの構築が完了している前提で進みます。

    前回はmybatis-generatorで以下のファイルが自動生成されました。


    mybatis-config.xmlの設定

    まずはMyBatisを利用するために設定ファイルを記述します。
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
      PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
      <settings>
        <!-- mybatisログ出力用 -->
        <setting name="logImpl" value="LOG4J"/>
      </settings>
      <environments default="development">
        <environment id="development">
          <transactionManager type="JDBC"/>
          <!-- データソース -->
          <dataSource type="POOLED">
            <property name="driver" value="org.postgresql.Driver"/>
            <property name="url" value="jdbc:postgresql://192.168.100.100:5432/test"/>
            <property name="username" value="mybatisuser"/>
            <property name="password" value="password"/>
          </dataSource>
        </environment>
      </environments>
      <mappers>
        <!-- Mapperファイルのパスを記述 -->
        <mapper resource="jp/hit/sample/mybatis/mapper/EmpMapper.xml"/>
        <mapper resource="jp/hit/sample/mybatis/mapper/DeptMapper.xml"/>
      </mappers>
    </configuration>
    

    今回コンソールでMyBatisが発行したSQLを確認したいのでsetting要素でLog4jでログ出力出来るように設定しています。
    ロギング自体は指定しだいでLog4jの他にSLF4JやLog4j2なども使用可能です。
    詳細は公式のリファレンスを参照してください。

    dataSource要素ではJDBCパラメータ、mappers要素では実際のMapperのパスを記載します。



    log4j.xmlの設定

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
    <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" >
    
      <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
         <param name="Target" value="System.out" />
         <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%c{1} - %m%n" />
         </layout>
      </appender>
    
      <!-- SQLログを出力 -->
      <logger name="jp.hit.sample.mybatis.client" additivity="false">
        <level value="debug"/>
        <appender-ref ref="stdout"/>
      </logger>
      <!-- MyBatisSampleのデバッグログを出力 -->
      <logger name="jp.hit.sample.mybatis.logic" additivity="false">
        <level value="debug"/>
        <appender-ref ref="stdout"/>
      </logger>
      <root>
        <level value="info"/>
        <appender-ref ref="stdout"/>
      </root>
    </log4j:configuration>
    
    MyBatisが発行するSQLを確認するためにJava Client(jp.hit.sample.mybatis.client)以下のログレベルをDEBUGに設定してあります。




    セッションの生成

    MyBatisのセッション生成は次の実装で実現できます。
        package jp.hit.sample.mybatis.util;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.apache.log4j.Logger;
    
    public class SessionUtil {
        private static Logger LOG = Logger.getLogger(SessionUtil.class);
        private static final String RESOURCE = "mybatis-config.xml";
    
        private SessionUtil() {
        }
    
        public static SqlSession createSession() {
            SqlSession session = null;
            try {
                InputStream is = Resources.getResourceAsStream(RESOURCE);
                SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(is);
                session = ssf.openSession(false);
            } catch (IOException e) {
                LOG.error(e.getMessage(), e);
            }
            return session;
        }
    }
    
    また、SqlSessionFactoryには以下6つのopenSessionメソッドが用意されており、用途に応じてセッションの生成を行う事が可能です。
    SqlSession openSession()
    SqlSession openSession(boolean autoCommit)
    SqlSession openSession(Connection connection)
    SqlSession openSession(TransactionIsolationLevel level)
    SqlSession openSession(ExecutorType execType,TransactionIsolationLevel level)
    SqlSession openSession(ExecutorType execType)
    SqlSession openSession(ExecutorType execType, boolean autoCommit)
    SqlSession openSession(ExecutorType execType, Connection connection)
    
    今回のように引数を指定せずにセッションを生成した場合はautoCommit=false(自動コミット無効)の状態でセッションが生成されます。
    JDBCのトランザクション分離レベルを指定してセッションを生成したい場合は以下のように記述します。
    openSession(TransactionIsolationLevel.NONE); // トランザクション無効
    openSession(TransactionIsolationLevel.READ_UNCOMMITTED);  // READ UNCOMMITTED (ダーティリード有、非再現リード有、ファントムリード有)
    openSession(TransactionIsolationLevel.READ_COMMITTED); // READ COMMITTED (ダーティリード無、非再現リード有、ファントムリード有)
    openSession(TransactionIsolationLevel.REPEATABLE_READ); // REPEATABLE READ (ダーティリード無、非再現リード無、ファントムリード有)
    openSession(TransactionIsolationLevel.SERIALIZABLE); // SERIALIZABLE (ダーティリード無、非再現リード無、ファントムリード無)
    
    SQLステートメントの実行モードを指定したい場合は以下のように記述します。
    openSession(ExecutorType.SIMPLE); // デフォルト ステートメント実行のたびにPreparedStatementを作成します。
    openSession(ExecutorType.REUSE); // PreparedStatementを再利用します。
    openSession(ExecutorType.BATCH); // 全ての更新ステートメントをバッチ処理します。
    
    TransactionIsolationLevelやExecutorTypeはDBやMyBatisのバージョンにより挙動が変わるので使用する際は注意が必要です。
    例えば、postgreSQLの場合はREAD_UNCOMMITTEDを指定してもダーティリードは行われません。




    SQLの発行

    次に生成したセッションを利用して実際にMapperファイルに記載されたSQLを発行してみます。
    MyBatisを利用したSQL発行は非常にシンプルに実装できます。
        private static Logger LOG = Logger.getLogger(MyBatisSample.class);
    
        private static EmpMapper empMapper;
        private static DeptMapper deptMapper;
    
        public static void main(String[] args) {
            // セッション作成
            SqlSession session = SessionUtil.createSession();
            // Mapperを取得
            empMapper = session.getMapper(EmpMapper.class);
            deptMapper = session.getMapper(DeptMapper.class);
    
            try {
                // EmpMapperに自動生成されたselectByPrimaryKeyクエリを利用したPK指定でのEmpテーブルへのselect発行
                EmpKey key = new EmpKey();
                key.setId(1); // id = 1
                key.setDeptId(1); // dept_id = 1
                LOG.debug("【SQL発行】jp.hit.sample.mybatis.client.EmpMapper.selectByPrimaryKey id = 1 and dept_id = 1");
                Emp emp = empMapper.selectByPrimaryKey(key);
                printEmp(emp); // 取得したEmpデータ出力
    
    
                // EmpMapperに自動生成されたExampleMapperを使用したselect発行
                EmpExample param = new EmpExample();
                List<string> names = Arrays.asList("Bravo", "Charlie", "HIT tom");
                param.createCriteria().andNameIn(names); // IN ('Bravo','Charlie','HIT tom')
                param.setOrderByClause("dept_id desc"); // Order by dept_id desc
                LOG.debug("【SQL発行】selectByExample in ('Bravo','Charlie','HIT tom') order by dept_id desc");
                List<emp> empList = empMapper.selectByExample(param);
                printEmp(emp); // 取得したEmpデータ出力
    
    
                // Insert
                Emp insertEntity = new Emp();
                insertEntity.setId(5);
                insertEntity.setName("HIT tom");
                insertEntity.setDeptId(2);
                insertEntity.setTel("03-5225-0530");
                LOG.debug("【SQL発行】Insert Emp");
                empMapper.insert(insertEntity);
    
    
                // Insert結果取得
                empList = empMapper.selectByExample(param);
                LOG.debug("【SQL発行】selectByExample in ('Bravo','Charlie','HIT tom') order by dept_id desc");
                printEmp(emp); // 取得したEmpデータ出力
    
            } finally {
                // 実行前の状態にロールバックする
                session.rollback();
                // セッションを閉じる
                session.close();
            }
        }
    
    
        /**
         * Empの中身をダンプする
         * @param empList
         */
        private static void printEmp(List<emp> empList) {
            for (Emp emp : empList) {
                printEmp(emp);
            }
        }
    
        private static void printEmp(Emp emp) {
            if (emp != null) {
                LOG.debug("【Result】 " + "id:" + emp.getId() + ", Name:" + emp.getName() + ", Dept:"
                        + String.valueOf(emp.getDeptId()) + ", Tel:" + emp.getTel());
            }
        }
    

    サンプル実行前のテーブルデータは以下の通り
    select * from my.dept;
     id |    name
    ----+------------
      1 | Foundation
      2 | Sales
      3 | System
    
    select * from my.emp;
     id | dept_id |  name   |     tel
    ----+---------+---------+--------------
      1 |       1 | Alfa    | 111-111-1111
      2 |       1 | Aileen  | 123-123-1234
      3 |       3 | Bravo   | 222-222-2222
      4 |       2 | Charlie | 333-333-3333
    

    サンプルを実行すると以下の結果が得られます。
    MyBatisSample - 【SQL発行】jp.hit.sample.mybatis.client.EmpMapper.selectByPrimaryKey id = 1 and dept_id = 1
    selectByPrimaryKey - ooo Using Connection [org.postgresql.jdbc4.Jdbc4Connection@7bfb4d34]
    selectByPrimaryKey - ==>  Preparing: select id, dept_id, name, tel from my.emp where id = ? and dept_id = ? 
    selectByPrimaryKey - ==> Parameters: 1(Integer), 1(Integer)
    selectByPrimaryKey - <==      Total: 1
    MyBatisSample - 【Result】 id:1, Name:Alfa, Dept:1, Tel:111-111-1111
    MyBatisSample - 【SQL発行】selectByExample in ('Bravo','Charlie','HIT tom') order by dept_id desc
    selectByExample - ooo Using Connection [org.postgresql.jdbc4.Jdbc4Connection@7bfb4d34]
    selectByExample - ==>  Preparing: select 'false' as QUERYID, id, dept_id, name, tel from my.emp WHERE ( name in ( ? , ? , ? ) ) order by dept_id desc 
    selectByExample - ==> Parameters: Bravo(String), Charlie(String), HIT tom(String)
    selectByExample - <==      Total: 2
    MyBatisSample - 【Result】 id:3, Name:Bravo, Dept:3, Tel:222-222-2222
    MyBatisSample - 【Result】 id:4, Name:Charlie, Dept:2, Tel:333-333-3333
    MyBatisSample - 【SQL発行】Insert Emp
    insert - ooo Using Connection [org.postgresql.jdbc4.Jdbc4Connection@7bfb4d34]
    insert - ==>  Preparing: insert into my.emp (id, dept_id, name, tel) values (?, ?, ?, ?) 
    insert - ==> Parameters: 5(Integer), 2(Integer), HIT tom(String), 03-5225-0530(String)
    insert - <==    Updates: 1
    MyBatisSample - 【SQL発行】selectByExample in ('Bravo','Charlie','HIT tom') order by dept_id desc
    selectByExample - ooo Using Connection [org.postgresql.jdbc4.Jdbc4Connection@7bfb4d34]
    selectByExample - ==>  Preparing: select 'false' as QUERYID, id, dept_id, name, tel from my.emp WHERE ( name in ( ? , ? , ? ) ) order by dept_id desc 
    selectByExample - ==> Parameters: Bravo(String), Charlie(String), HIT tom(String)
    selectByExample - <==      Total: 3
    MyBatisSample - 【Result】 id:3, Name:Bravo, Dept:3, Tel:222-222-2222
    MyBatisSample - 【Result】 id:4, Name:Charlie, Dept:2, Tel:333-333-3333
    MyBatisSample - 【Result】 id:5, Name:HIT tom, Dept:2, Tel:03-5225-0530
    

    サンプル実行後もロールバックが正常に働きテーブルデータが変化しない事が確認出来ます。
    select * from my.dept;
     id |    name
    ----+------------
      1 | Foundation
      2 | Sales
      3 | System
    
    select * from my.emp;
     id | dept_id |  name   |     tel
    ----+---------+---------+--------------
      1 |       1 | Alfa    | 111-111-1111
      2 |       1 | Aileen  | 123-123-1234
      3 |       3 | Bravo   | 222-222-2222
      4 |       2 | Charlie | 333-333-3333
    





    任意のMapper作成

    自動生成されたMapperの利用を確認出来たら次に新規にMapperファイルを作成して任意のSQLを発行させてみましょう。

    まず、Mapperファイルと同一のパッケージ内に任意のMapper(今回はEmpDeptSqlMap.xml)を作成します。

    MyBatisのマッピングは以下の基準で紐付けが行われているのですが、今回は分かりやすいように敢えてMapperの名前を「~Mapper」ではなく「SqlMap」に変えています。
    • Mapperのinterfaceクラス名 = Mapperファイルのnamespace
    • Mapperのinterfaceクラスのメソッド名 = MapperファイルのSQLID
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="jp.hit.sample.mybatis.client.EmpDeptSqlMap" >
      <resultMap id="EmpDeptResultMap" type="jp.hit.sample.mybatis.entity.EmpDept" >
        <id column="emp_id" property="empId" jdbcType="INTEGER" />
        <id column="dept_id" property="deptId" jdbcType="INTEGER" />
        <id column="dept_name" property="deptName" jdbcType="VARCHAR" />
        <result column="emp_name" property="empName" jdbcType="VARCHAR" />
        <result column="tel" property="tel" jdbcType="VARCHAR" />
      </resultMap>
    
      <!--
        resultMapを定義せずにresultTypeを指定し
        <select id="selectEmpJoinDept" resultType="jp.hit.sample.mybatis.entity.EmpDept" parameterType="jp.hit.sample.mybatis.entity.EmpDept">
        のように直接Entityクラスを戻り値に指定することも可能ですが
        内部的に余計なマッピング処理が発生する為、resultMapでjdbcTypeを定義することが推奨されています。
       -->
      <select id="selectEmpJoinDept" resultMap="EmpDeptResultMap" parameterType="jp.hit.sample.mybatis.entity.EmpDept">
        SELECT
             emp.id      AS emp_id
            ,emp.dept_id AS dept_id
            ,dept.name   AS dept_name
            ,emp.name    AS emp_name
            ,emp.tel     AS tel
        FROM my.emp emp
        LEFT JOIN my.dept dept ON emp.dept_id = dept.id
    
        <!--
             <where> 要素は、内包するタグのどれかが結果を返すときだけ "WHERE" を挿入します。
             更に、内包するタグから返された結果が "AND" または "OR" で始まっていた場合はこれを削除します。
             以下の例の場合 empId、deptId、empName、deptName、tel 全てがnullの場合 where要素が削除されます。
         -->
        <where>
          <if test="empId != null">
            AND emp.id = #{empId,jdbcType=INTEGER}
          </if>
          <if test="deptId != null">
            AND emp.dept_id = #{deptId,jdbcType=INTEGER}
          </if>
          <if test="empName != null">
            AND emp.name LIKE #{empName,jdbcType=VARCHAR}
          </if>
          <if test="deptName != null">
            AND dept.name LIKE #{deptName,jdbcType=VARCHAR}
          </if>
          <if test="tel != null">
            AND emp.tel = #{tel,jdbcType=VARCHAR}
          </if>
        </where>
        ORDER BY emp.id
      </select>
    
    </mapper>
    
    MyBatisによる動的SQLの記法については公式リファレンスを参照してください。
    Mapperファイルには静的SQLをそのまま記述することも可能です。


    次にmybatis-config.xmlにMapperファイルのパスを追記します。
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
      PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
      <settings>
        <!-- mybatisログ出力用 -->
        <setting name="logImpl" value="LOG4J"/>
      </settings>
      <environments default="development">
        <environment id="development">
          <transactionManager type="JDBC"/>
          <!-- データソース -->
          <dataSource type="POOLED">
            <property name="driver" value="org.postgresql.Driver"/>
            <property name="url" value="jdbc:postgresql://192.168.245.131:5432/test"/>
            <property name="username" value="mybatisuser"/>
            <property name="password" value="password"/>
          </dataSource>
        </environment>
      </environments>
      <mappers>
        <!-- Mapperファイルのパスを記述 -->
        <mapper resource="jp/hit/sample/mybatis/mapper/EmpMapper.xml"/>
        <mapper resource="jp/hit/sample/mybatis/mapper/DeptMapper.xml"/>
        <mapper resource="jp/hit/sample/mybatis/mapper/EmpDeptSqlMap.xml"/>
      </mappers>
    </configuration>
    

    Entityを作成します。
    package jp.hit.sample.mybatis.entity;
    
    public class EmpDeptKey {
        private Integer empId;
        private Integer deptId;
    
        public Integer getEmpId() {
            return empId;
        }
        public void setEmpId(Integer empId) {
            this.empId = empId;
        }
        public Integer getDeptId() {
            return deptId;
        }
        public void setDeptId(Integer deptId) {
            this.deptId = deptId;
        }
    }
    
    package jp.hit.sample.mybatis.entity;
    
    public class EmpDept extends EmpDeptKey {
        private String empName;
        private String deptName;
        private String tel;
    
        public String getEmpName() {
            return empName;
        }
        public void setEmpName(String empName) {
            this.empName = empName;
        }
        public String getDeptName() {
            return deptName;
        }
        public void setDeptName(String deptName) {
            this.deptName = deptName;
        }
        public String getTel() {
            return tel;
        }
        public void setTel(String tel) {
            this.tel = tel;
        }
    }
    

    Mapperのinterfaceクラスを作成します。
    package jp.hit.sample.mybatis.client;
    
    import java.util.List;
    
    import jp.hit.sample.mybatis.entity.EmpDept;
    
    public interface EmpDeptSqlMap {
        /**
         * SELCT * FROM Emp LEFT JOIN Dept
         */
        List<EmpDept> selectEmpJoinDept(EmpDept empDeptParam);
    }
    



    任意のMapperを利用したSQL発行

    上記で作成したMapperは自動生成したMapperと同様の実装で利用できます。
        private static EmpDeptSqlMap empDeptSqlMap;
    
        public static void main(String[] args)  {
            // セッション作成
            SqlSession session = SessionUtil.createSession();
            // Mapperを取得
            empMapper = session.getMapper(EmpMapper.class);
            deptMapper = session.getMapper(DeptMapper.class);
            empDeptSqlMap = session.getMapper(EmpDeptSqlMap.class);
    
            try {
                // 手動作成したMapperを利用したSQLの発行
                EmpDept empDeptParam = new EmpDept();
                empDeptParam.setDeptName("%S%"); // dept.name Like '%S%'
                LOG.debug("【SQL発行】selectEmpJoinDept dept.name like '%S%' 】");
                List<EmpDept> empDeptList = empDeptSqlMap.selectEmpJoinDept(empDeptParam);
                printEmpDept(empDeptList);
    
            } finally {
                // 実行前の状態に戻す
                session.rollback();
                // セッションを閉じる
                session.close();
            }
        }
    
        /**
         * EmpDeptの中身をダンプする
         * @param empList
         */
        private static void printEmpDept(List<EmpDept> empDeptList) {
            for (EmpDept ed : empDeptList) {
                printEmpDept(ed);
            }
        }
    
        private static void printEmpDept(EmpDept empDept) {
            if (empDept != null) {
                LOG.debug("【Result】 " + "empId:" + String.valueOf(empDept.getEmpId())
                        + ", deptId:" + String.valueOf(empDept.getDeptId())
                        + ", empName:" + empDept.getEmpName()
                        + ", deptName:" + empDept.getDeptName()
                        + ", Tel:" + empDept.getTel());
            }
        }
    

    実行すると以下の結果が得られます。
    MyBatisSample - 【SQL発行】selectJoinDept dept.name like '%S%' 】
    selectEmpJoinDept - ooo Using Connection [org.postgresql.jdbc4.Jdbc4Connection@2b4d13ef]
    selectEmpJoinDept - ==>  Preparing: SELECT emp.id AS emp_id ,emp.dept_id AS dept_id ,dept.name AS dept_name ,emp.name AS emp_name ,emp.tel AS tel FROM my.emp emp LEFT JOIN my.dept dept ON emp.dept_id = dept.id WHERE dept.name LIKE ? ORDER BY emp.id 
    selectEmpJoinDept - ==> Parameters: %S%(String)
    selectEmpJoinDept - <==      Total: 2
    MyBatisSample - 【Result】 empId:3, deptId:3, empName:Bravo, deptName:System, Tel:222-222-2222
    MyBatisSample - 【Result】 empId:4, deptId:2, empName:Charlie, deptName:Sales, Tel:333-333-3333
    

    Mapperファイル内には複数のSQLが記述可能なため、例えば親テーブル毎にMapperを分けるなどすれば管理もしやすくなると思います。


    今回でひとまずMyBatisサンプルの動作まで確認できました
    ・・・が、Mapperを追加するたびに毎度設定ファイルを書き換えたり、セッション管理ロジックを書いたり、Mapperの取得したりするのはめんどくさいと思います。
    MyBatisにはSpringFrameworkとシームレスに連携出来してセッションの生成、Mapperの注入などを自動で行うMyBatis-Springという拡張フレームワークが存在します。

    この拡張フレームワークのおかげでMyBatisとSpringの連携は簡単に実装できるのでそちらの方がメインの使い方になると思いますが、今回はとりあえずMyBatisだけに絞りました。
    MyBatis-Springの方の記事も時間があればいずれ書きたいと思います。

    2014年3月22日土曜日

    MyBatisで遊んでみる(2) 第二章 mybatis-generatorによるMapper自動生成

    どうも、トムです。
    今回は前回からの続きでmybatis-generatorについて書いていきます。

    mybatis-generatorはmybatisで利用するMapperファイル(SQLMapper)やJava Model(Entity)、Java Client(Mapper)などのSQLマッピングクラスを自動生成してくれるツールです。
    これらを導入することで開発時にテーブル変更などがあった際、比較的楽に関連ファイルを作成することが出来ます。

    前回で以下のテーブルが出来ていることを前提で進んでいきます。
    \d my.dept
                テーブル "my.dept"
      列  |           型           |  修飾語
    ------+------------------------+----------
     id   | integer                | not null
     name | character varying(256) | not null
    インデックス:
        "dept_pkey" PRIMARY KEY, btree (id)
    参照元:
        TABLE "my.emp" CONSTRAINT "emp_dept_id_fkey" FOREIGN KEY (dept_id) REFERENCES my.dept(id)
    
    \d my.emp
                  テーブル "my.emp"
       列    |           型           |  修飾語
    ---------+------------------------+----------
     id      | integer                | not null
     dept_id | integer                | not null
     name    | character varying(256) | not null
     tel     | character varying(12)  | not null
    インデックス:
        "emp_pkey" PRIMARY KEY, btree (id, dept_id)
    外部キー制約:
        "emp_dept_id_fkey" FOREIGN KEY (dept_id) REFERENCES my.dept(id)
    

    第二章 mybatis-generatorによるMapper自動生成

    1. モジュールダウンロード

    まずは必要なモジュールをダウンロードします。
    mybatis 3
    https://github.com/mybatis/mybatis-3/releases
    mybatis-3.2.5.jar
    記事作成時点での最新バージョンは「mybatis 3.2.5」
    mybatis-generator
    https://github.com/mybatis/generator/releases
    mybatis-generator-core-1.3.2.jar
    ※クラスパスには含めません
    postgreSQL jdbcドライバ
    http://jdbc.postgresql.org/download.html
    postgresql-9.3-1101.jdbc41.jar
    記事作成時点での最新バージョンは「9.3-1101」
    対応するドライバの種類は以下の通りです。
    JDKバージョン 対応ドライバ
    JDK 1.5 JDBC3
    JDK 1.6 JDBC4
    JDK 1.7, 1.8 JDBC41

    ognl-3.0.6.jar
    http://struts.apache.org/download.cgi
    javassist.jar
    http://sourceforge.jp/projects/sfnet_jaopframework/downloads/Library/javassist.jar/
    log4j-1.2.17.jar
    http://www.apache.org/dyn/closer.cgi/logging/log4j/1.2.17/log4j-1.2.17.zip


    2. パッケージ構成

    今回作成するMyBatisサンプルのパッケージ構成は以下の通り

    3. Mapper、Entity自動生成の為の準備

    以下の設定ファイルを新規に作成します。

    mybatis-generator用の設定ファイル

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE generatorConfiguration
     PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
     "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
    <generatorConfiguration>
      <context id="context1">
        <!--
          生成するクラスオブジェクトにタイムスタンプを付与するかどうかを設定します。
          value="true" の場合は生成しません。
    
          詳細は公式リファレンス参照
          http://mybatis.github.io/generator/configreference/commentGenerator.html
        -->
        <commentGenerator>
            <property name="suppressDate" value="true" />
        </commentGenerator>
    
        <!-- JDBCの設定 -->
        <jdbcConnection driverClass="org.postgresql.Driver"
                            connectionURL="jdbc:postgresql://192.168.100.100:5432/test" userId="mybatisuser"
                            password="password" />
    
        <!--
           Java Model(Entity)クラスの生成先を指定します。
           targetPackage : 生成先パッケージ
           targetProject : プロジェクトディレクトリから生成先パッケージの親ディレクトリまでのパス
    
           詳細は公式リファレンス参照
           http://mybatis.github.io/generator/configreference/javaModelGenerator.html
         -->
        <javaModelGenerator targetPackage="jp.hit.sample.mybatis.entity"
                            targetProject="src" >
        </javaModelGenerator>
    
        <!--
           SQL Mapperファイルの生成先を指定します。
           targetPackage : 生成先パッケージ
           targetProject : プロジェクトディレクトリから生成先パッケージの親ディレクトリまでのパス
    
           詳細は公式リファレンス参照
           http://mybatis.github.io/generator/configreference/sqlMapGenerator.html
         -->
        <sqlMapGenerator targetPackage="jp.hit.sample.mybatis.mapper"
                            targetProject="resource" />
    
        <!--
           Java Clientクラスの生成先を指定します。
           Java ClientはSQL Mapperファイルを呼び出すためのインターフェースクラスです。
           targetPackage : 生成先パッケージ
           targetProject : プロジェクトディレクトリから生成先パッケージの親ディレクトリまでのパス
    
           詳細は公式リファレンス参照
           http://mybatis.github.io/generator/configreference/javaClientGenerator.html
    
           type :
             type="ANNOTATEDMAPPER"
                アノテーションを定義したMapperインターフェースクラスとアノテーションの実装を定義したSqlProviderクラスを生成する。
                Mapperファイルは生成しない。
                Mapperファイルを使用せずにアノテーションでクエリを発行したい人向け
    
             type="MIXEDMAPPER"
                アノテーションを定義したMapperインターフェースクラスとMapperファイルを生成する。
                SqlProviderクラスは生成しない。
                Mapperファイルとアノテーションでのクエリ発行を両立したい人向け。
    
             type="XMLMAPPER"
                MapperインターフェースクラスとMapperファイルのみ生成する。
                SQLを全部Mapperファイルで管理したい人向け。
         -->
        <javaClientGenerator targetPackage="jp.hit.sample.mybatis.client"
                            targetProject="src" type="XMLMAPPER" />
    
        <!--
         自動生成対象のテーブル名を[tableName]に指定する。
         ※テーブル名の指定はワイルドカードが利用できます
    
         詳しい設定値については公式サイトのリファレンスを参照してください。
         http://mybatis.github.io/generator/configreference/table.html
    
         modelType :
           modelType="flat"
              ドメインクラスのみ作成。
           modelType="hierarchical"
              自動生成するMapperのパラメータの為にPrimary Keyエンティティを生成します。
              MapperでPKを指定したクエリ(selectByPrimaryKey等)のパラメータがデフォルトでPrimary Keyエンティティになります。
         -->
        <table schema="my" tableName="%"
                enableInsert="true"
                enableSelectByPrimaryKey="true"
                enableSelectByExample="true"
                enableUpdateByPrimaryKey="true"
                enableUpdateByExample="false"
                enableDeleteByPrimaryKey="true"
                enableDeleteByExample="false"
                enableCountByExample="false"
                selectByExampleQueryId="false"
                modelType="hierarchical">
        </table>
      </context>
    </generatorConfiguration>
    

    Antでmybatis-generatorを実行するためのビルドファイル

    <?xml version="1.0" encoding="utf-8"?>
    <project default="genfiles" basedir=".">
      <property name="generated.source.dir" value="${basedir}" />
    
      <path id="class.path">
        <fileset dir="lib" includes="*.jar" />
        <dirset dir="bin" />
      </path>
    
      <!-- 自動生成実行 -->
      <target name="genfiles" description="Generate the files" depends="init">
        <!-- mybatis generator 実行 -->
        <taskdef name="mbgenerator" classname="org.mybatis.generator.ant.GeneratorAntTask"
                classpathref="class.path" />
        <mbgenerator overwrite="true" configfile="resource/generatorConfig.xml"
                verbose="false">
        </mbgenerator>
        <!-- postgresの場合 PKオブジェクトとして[テーブル名Pkey]のファイルが作成されるが不要なため削除する -->
        <delete verbose="true">
          <fileset dir="./src/jp/hit/sample/mybatis/entity" includes="*Pkey.java"/>
          <fileset dir="./src/jp/hit/sample/mybatis/entity" includes="*PkeyExample.java"/>
          <fileset dir="./src/jp/hit/sample/mybatis/client" includes="*PkeyMapper.java"/>
          <fileset dir="./src/jp/hit/sample/mybatis/client" includes="*PkeySqlProvider.java"/>
          <fileset dir="./resource/jp/hit/sample/mybatis/mapper"  includes="*PkeyMapper.xml" />
        </delete>
      </target>
    
      <!-- 生成先パッケージ初期化 -->
      <target name="init" description="Initialize">
        <!-- パッケージ作成 -->
        <mkdir dir="./src/jp/hit/sample/mybatis/entity"/>
        <mkdir dir="./src/jp/hit/sample/mybatis/client"/>
        <mkdir dir="./resource/jp/hit/sample/mybatis/mapper"/>
        <!-- パッケージ内削除 ※全消しなので要注意 -->
        <delete verbose="true">
          <fileset dir="./src/jp/hit/sample/mybatis/entity" includes="*.java"/>
          <fileset dir="./src/jp/hit/sample/mybatis/client" includes="*.java"/>
          <fileset dir="./resource/jp/hit/sample/mybatis/mapper"  includes="*.xml" />
        </delete>
      </target>
    </project>
    
    


    4. 自動生成実行

    Antでmybatis-generatorを実行します。
    Buildfile: <workspace_path>\MyBatis\ant_mybatis-gemerator-build.xml
    init:
    genfiles:
    BUILD SUCCESSFUL
    Total time: 3 seconds
    


    ここでエラーになる場合は、generatorConfig.xmlのJDBC設定やDBの設定周りを見直してみてください。
    自動生成に成功すればパッケージ以下にMapperクラス、Entityクラス、Mapperが作成されます。


    今回はMapperファイルを利用してのクエリ発行を行うのでJava Clientの設定値でtype="XMLMAPPER"を指定しました。
    mybatisにはMapperファイルを利用したクエリ発行以外にもアノテーションを利用したクエリ発行の方法も可能です。
    アノテーションを利用したクエリ発行のメリットは余計な外部ファイルを持たせずに全てclass内でクエリの定義が行えることでしょうか。
    機会があればそちらの方法も試してみようと思います。