自己紹介
- ソリューション・コンサルティング部 技術グループ
- 本ブログをご覧いただき、ありがとうございます。
株式会社ヒューマンインタラクティブテクノロジー(HIT)の技術グループ担当者です。
弊社ホームページ
2015年1月24日土曜日
HornetQについてさわってみた。
しんです。
新年の一発目の技術ネタを。
JBoss Enterprise Application 6 (JBossEAP6) 周りに
どっぷりつかっている今日この頃です。
その中でもJBossEAP6のメッセージングシステムである「HornetQ」に
触れる場面が多かったので、得られたことをブログに書きます。
【HornetQとは】
HorneteQは、JBossから生まれたオープンソースの非同期メッセージングサービスです。
詳しくは、HornetQのサイトを見てください。
【キューのサイズ】
HornetQの特徴の中で、今回のブログに載せるのは、「キューのメモリ制限」です。
(語弊を恐れず言うならば、)キューメモリのサイズといったものはありません。
仕組み上は無制限となります。(詳しくはこちらを参照)
仕組みとしては、受け付けたメッセージのサイズと設定された内容をもとに振る舞いを変えており、
ディスクに書き込む、もしくは、メモリが解放されるまで処理待ちになる仕組みを持っているため、
キューサイズ以上のメッセージが届いてメッセージがロストといった結果にはならないようです。
ポイントは、以下の2つのパラメータです。
max-size-bytes:メッセージの最大サイズ
address-full-policy:振る舞い
設定箇所は以下の通りです。(標準的なインストールで、fullプロファイルを利用しています。)
/subsystem=messaging/hornetq-server=default/address-setting=#/max-size-bytes /subsystem=messaging/hornetq-server=default/address-setting=#/address-full-policy
上記の「address-full-policy」に定義できる要素は、以下の4つの要素となります。
DROP:メッセージを破棄する。
PAGE:ページング処理を実行して、ディスク上にメッセージの内容を出力する。
BLOCK:キューに格納されているメッセージ総量が「address-full-policy」を下回るまで、処理待機。
FAIL:エラーが発生。
詳しくはこちらを参照。
【次回は?】
次回は、もう少し試した結果を載せてみたいと思います。
ひとまず、今回はここまで。
2015年1月22日木曜日
2014年7月5日土曜日
JavaエンジニアがSwiftをさわってみた。 その1
お久しぶりです。しんです。
初投稿から3ヶ月ぶりという、投稿ペースが遅い私ですが、新しいネタを。
(ビッグデータ周りのネタは少々お待ちを。。。)
【Appleが発表した新しい言語】
先月から、私の周りではSwiftに関する話題が持ちきりでした。
新しい言語と聞くとウキウキする私としては、触ってみようかなと。
どうしても慣れている言語で頭の中で置き換えてしまうので、
Appleが公開した開発者向けリファレンスをもとに、
Javaだったらこうかな?という対比する形で記載します。
きっと、Javaに慣れている人なら、どんなことができるかイメージしやすいはず!
では、しばしのお付き合いを。
【準備】
※Swiftを使用するためには、MacとXcode 6 Betaが必要です。
また、Xcode 6 Betaを入手するためにはiOS Devloper Programに参加する必要があります。
【Javaとの対比】
<宣言と代入>
Swiftで変数を宣言する場合は「var」を使用し、定数を宣言する場合は、「let」を使用します。
また、型を指定したい場合は「:データ型」を指定します。
Javaは、文末にセミコロンが必要となりますが、Swiftは、文末にセミコロンが不要です。
型の指定がないので、Javascriptとかをイメージ(安直)してしまいます。
<配列>
配列の定義はJavaとSwiftで大きな違いはありませんが、Swiftには連想配列があります。
JavaでいうところのコレクションクラスのMapクラスですね。
Swiftで、空の配列を作成する場合は、「 var 配列名 = データ型[]() 」となります。
空の連想配列を作成する場合は、「 var 連想配列名 = Dictionary<キーの型, 値の型>() 」となります。
今回は一旦、ここまでとします。
あとで 分岐、繰り返しの構文を追記するかもしれません。
次回は、クラスやファンクションの定義周りをさわってみたいと思います。
初投稿から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についての検証があるのですが
幾つかのスレッドが残ったままになっているように見えますが
例外が発生するたびスレッドがどんどん増え続けるなんて事にならないのかちょっと確かめてみます。
コードは引用記事の内容を拝借しました。
parallelStreamで並列処理中に"ERROR"という文字列が出てきたらRuntimeExceptionをthrowするという処理を10回繰り返しスレッド数の変化を確認します。
動かしてみると………
例外でスレッド数がもりもり増える事はないようです。
他に、気になる点は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操作とかは絶対やっちゃダメですね。
そろそろ世間(?)でも徐々に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点
う~ん……ここら辺はもっと検証してみないと何とも言えないですね。
ただ、親スレッドと同じスレッドで並列処理が走るのは何とも奇妙な……
サーバサイドアプリでの検証
念のためサーバサイドで実装した場合にも同じ動きをするのか検証してみましょう。今回検証に利用したのはPlay Framework 2.2.2
Play Frameworkは、J2EEを完全に排除し複雑なサーバサイドの構築やらTomcatのようなサーブレットコンテナを必要とせずにRubyのRailsやPHPのCakePHPのようにお手軽にMVCアーキテクチャを利用したアプリを作成できる生産性を重視するWebアプリ開発に特化した次世代フレームワークです。
Play Frameworkの構築記事はまた次の機会という事で、さっそく検証してみましょう。
何回か実行してみます。
~~~(中略)~~~
サーバサイドでも特に動きに変わりはないようです。
スレッド数に関しては安心ですが、ParallelStreamの例外時には正常処理のスレッドが残ってしまうので気を付けた方がよさそうです。
やる人はいないと思いますがParallelStreamでDB操作とかは絶対やっちゃダメですね。
2014年4月12日土曜日
IE11で動かないFlash(Flex)を何とか動かしてみる
どうも、トムです。
Flex+BlazeDS+AMFの組み合わせで動作するシステムの構築をしていたおり
どうもIE11限定でFlashPlayer側の挙動がおかしくメッセージングが正常に動作せず、どうしたものかと頭を抱えていました。
……が、どうもIE11側で互換設定を有効にすると正常に動作する事が最近分りましたので
IE側の設定なしで何とか動かないものかと試作してみました。
ちなみにIE11側の互換設定はこれ↓の事です。
以降の説明を読むとわかりますが、HTTPヘッダの情報を書き換えるので、非推奨な方法です。
あくまでも技術的に実現できたという形で捉えてください。
本方法を採用し、発生した障害やトラブルに関して当ブログでは責任は負いません
Apacheの応答ヘッダーにX-UA-Compatibleを仕込んでIE10以上のIEを強制的にIE10モードで表示させてみます。
・参考
META スイッチを Apache に実装する
Apacheを再起動して動作を確認してみると………動かない!
仕方ないので次はIE11の互換モードを有効にしてheaderをのぞいてみます。
IE11には開発者ツール(F12)という便利な機能が搭載されていますのでそれを利用します。
確認手順は以下
ネットワークタブを選択して、ネットワークのキャプチャを有効化
むむむ……互換モードではドキュメントモードだけでなくUser-Agentの中身もIE7になっているようです。
ご存知の方も多いと思いますがIE11では今までのIEとUser-Agentが大幅に変更されています。
IE11 の互換性の変更点 | Internet Explorer 11 開発者向けガイド
本来、IE11のUser-Agentは↓のように表示されます。
上記のリクエストヘッダーではUser-Agentに"MSIE 7.0"の記述があるため、IE7のように振る舞っている事が分かります。
ちなみに、管理者ツールのエミュレーションで現在のエミュレートモードを確認すると
ドキュメントモードがIE7になっているのは分かりますが、User-Agentは別にIE7とは設定されていません。なんでやねん。
まさかと思いつつ、ドキュメントモードは変更せずにUser-Agentだけ変更し、IE11で動かなかったシステムへアクセスしてみると
………動いた!
システム内でIEのUser-Agentを見ているような箇所は存在しないため、FlashPlayer側でUser-Agentに"MSIE"が現れた場合はIEの処理を行うような分岐がされているようです。……推測ですが
いろいろと納得いかない部分はありますが、ひとまず解決の糸口がつかめました。
解決方法を簡潔に言ってしまうと
ApacheでIE11のUser-Agentを無理やり"MSIE"を含めた文字列に変換するという結構バッドノウハウ的な手法で解決しています。
例えばIE11から接続すると以下の様なリクエストヘッダのUser-Agentが
Apacheを通して以下のように変換されます。
動いてしまうとなんだかなー、という内容でした。
……FlashPlayerが早くIE11に対応してくれると良いですね。
Flex+BlazeDS+AMFの組み合わせで動作するシステムの構築をしていたおり
どうもIE11限定でFlashPlayer側の挙動がおかしくメッセージングが正常に動作せず、どうしたものかと頭を抱えていました。
……が、どうもIE11側で互換設定を有効にすると正常に動作する事が最近分りましたので
IE側の設定なしで何とか動かないものかと試作してみました。
ちなみにIE11側の互換設定はこれ↓の事です。
以降の説明を読むとわかりますが、HTTPヘッダの情報を書き換えるので、非推奨な方法です。
あくまでも技術的に実現できたという形で捉えてください。
本方法を採用し、発生した障害やトラブルに関して当ブログでは責任は負いません
試作
まずは、IEバージョン違い問題の応急処置でお約束となるドキュメントモードの変更を試作Apacheの応答ヘッダーにX-UA-Compatibleを仕込んでIE10以上のIEを強制的にIE10モードで表示させてみます。
・参考
META スイッチを Apache に実装する
<Location />
Header set X-UA-Compatible "IE=10"
</Location>
Apacheを再起動して動作を確認してみると………動かない!
仕方ないので次はIE11の互換モードを有効にしてheaderをのぞいてみます。
IE11には開発者ツール(F12)という便利な機能が搭載されていますのでそれを利用します。
確認手順は以下
互換モード設定
まずは適当なサイトを互換モードの対象に指定し管理者ツールのネットワークキャプチャ有効化
F12を押して管理者ツールを起動。ネットワークタブを選択して、ネットワークのキャプチャを有効化
互換表示対象のサイトへアクセス
互換モードの対象のサイトへアクセスして、先頭のリクエストヘッダーを確認してみるとむむむ……互換モードではドキュメントモードだけでなくUser-Agentの中身もIE7になっているようです。
ご存知の方も多いと思いますがIE11では今までのIEとUser-Agentが大幅に変更されています。
IE11 の互換性の変更点 | Internet Explorer 11 開発者向けガイド
互換性 ("compatible") トークンとブラウザー ("MSIE") トークンが削除されました。 "like Gecko" トークンが追加されました (他のブラウザーとの一貫性のため)。 ブラウザーのバージョンが、新しいリビジョン ("rv") トークンによって報告されます。
本来、IE11のUser-Agentは↓のように表示されます。
Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
上記のリクエストヘッダーではUser-Agentに"MSIE 7.0"の記述があるため、IE7のように振る舞っている事が分かります。
ちなみに、管理者ツールのエミュレーションで現在のエミュレートモードを確認すると
ドキュメントモードがIE7になっているのは分かりますが、User-Agentは別にIE7とは設定されていません。なんでやねん。
まさかと思いつつ、ドキュメントモードは変更せずにUser-Agentだけ変更し、IE11で動かなかったシステムへアクセスしてみると
………動いた!
システム内でIEのUser-Agentを見ているような箇所は存在しないため、FlashPlayer側でUser-Agentに"MSIE"が現れた場合はIEの処理を行うような分岐がされているようです。……推測ですが
いろいろと納得いかない部分はありますが、ひとまず解決の糸口がつかめました。
解決編
Apacheのhttpd.confに以下を追加LoadModule headers_module modules/mod_headers.so <IfModule headers_module> Header edit User-Agent "(Trident/[\d]+\.[\d]+;).*rv:([1-9][\d]\.[\d]+)" "$1 MSIE $2" </IfModule>
解決方法を簡潔に言ってしまうと
ApacheでIE11のUser-Agentを無理やり"MSIE"を含めた文字列に変換するという結構バッドノウハウ的な手法で解決しています。
例えばIE11から接続すると以下の様なリクエストヘッダのUser-Agentが
Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Apacheを通して以下のように変換されます。
Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; MSIE 11.0) like Gecko
動いてしまうとなんだかなー、という内容でした。
……FlashPlayerが早くIE11に対応してくれると良いですね。
2014年4月2日水曜日
Java8 Date and Time API 変換メモ
トムです。
少し話題は遅いですが、日本時間で3月19日にJava8が正式に公開されました。
Java8では多くのAPIが追加され、概ね使いやすくなっているのですが
新規に追加されたDate and Time APIに関しては今までの日付型と比べ使い方に癖があり使用の際に躓く方も多いと思います。
今回はメモ書きとしてJava8のDate and Time APIについて動作確認した内容を書いていきます。
他に有用なものがあり次第追加していきます。
少し話題は遅いですが、日本時間で3月19日にJava8が正式に公開されました。
#Java8 Now Available http://t.co/QymKlAtp99
— Java (@java) 2014, 3月 18
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-generatorで以下のファイルが自動生成されました。
今回コンソールでMyBatisが発行したSQLを確認したいのでsetting要素でLog4jでログ出力出来るように設定しています。
ロギング自体は指定しだいでLog4jの他にSLF4JやLog4j2なども使用可能です。
詳細は公式のリファレンスを参照してください。
dataSource要素ではJDBCパラメータ、mappers要素では実際のMapperのパスを記載します。
JDBCのトランザクション分離レベルを指定してセッションを生成したい場合は以下のように記述します。
例えば、postgreSQLの場合はREAD_UNCOMMITTEDを指定してもダーティリードは行われません。
MyBatisを利用したSQL発行は非常にシンプルに実装できます。
サンプル実行前のテーブルデータは以下の通り
サンプルを実行すると以下の結果が得られます。
サンプル実行後もロールバックが正常に働きテーブルデータが変化しない事が確認出来ます。
まず、Mapperファイルと同一のパッケージ内に任意のMapper(今回はEmpDeptSqlMap.xml)を作成します。
MyBatisのマッピングは以下の基準で紐付けが行われているのですが、今回は分かりやすいように敢えてMapperの名前を「~Mapper」ではなく「SqlMap」に変えています。
Mapperファイルには静的SQLをそのまま記述することも可能です。
次にmybatis-config.xmlにMapperファイルのパスを追記します。
Entityを作成します。
Mapperのinterfaceクラスを作成します。
実行すると以下の結果が得られます。
Mapperファイル内には複数のSQLが記述可能なため、例えば親テーブル毎にMapperを分けるなどすれば管理もしやすくなると思います。
今回でひとまずMyBatisサンプルの動作まで確認できました
・・・が、Mapperを追加するたびに毎度設定ファイルを書き換えたり、セッション管理ロジックを書いたり、Mapperの取得したりするのはめんどくさいと思います。
MyBatisにはSpringFrameworkとシームレスに連携出来してセッションの生成、Mapperの注入などを自動で行うMyBatis-Springという拡張フレームワークが存在します。
この拡張フレームワークのおかげでMyBatisとSpringの連携は簡単に実装できるのでそちらの方がメインの使い方になると思いますが、今回はとりあえずMyBatisだけに絞りました。
MyBatis-Springの方の記事も時間があればいずれ書きたいと思います。
前準備は終わりましたので第三章では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マッピングクラスを自動生成してくれるツールです。
これらを導入することで開発時にテーブル変更などがあった際、比較的楽に関連ファイルを作成することが出来ます。
前回で以下のテーブルが出来ていることを前提で進んでいきます。
mybatis-3.2.5.jar
記事作成時点での最新バージョンは「mybatis 3.2.5」
mybatis-generator-core-1.3.2.jar
※クラスパスには含めません
postgresql-9.3-1101.jdbc41.jar
記事作成時点での最新バージョンは「9.3-1101」
対応するドライバの種類は以下の通りです。
ここでエラーになる場合は、generatorConfig.xmlのJDBC設定やDBの設定周りを見直してみてください。
自動生成に成功すればパッケージ以下にMapperクラス、Entityクラス、Mapperが作成されます。
今回はMapperファイルを利用してのクエリ発行を行うのでJava Clientの設定値でtype="XMLMAPPER"を指定しました。
mybatisにはMapperファイルを利用したクエリ発行以外にもアノテーションを利用したクエリ発行の方法も可能です。
アノテーションを利用したクエリ発行のメリットは余計な外部ファイルを持たせずに全てclass内でクエリの定義が行えることでしょうか。
機会があればそちらの方法も試してみようと思います。
今回は前回からの続きで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/releasesmybatis-3.2.5.jar
記事作成時点での最新バージョンは「mybatis 3.2.5」
mybatis-generator
https://github.com/mybatis/generator/releasesmybatis-generator-core-1.3.2.jar
※クラスパスには含めません
postgreSQL jdbcドライバ
http://jdbc.postgresql.org/download.htmlpostgresql-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.cgijavassist.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.zip2. パッケージ構成
今回作成する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内でクエリの定義が行えることでしょうか。
機会があればそちらの方法も試してみようと思います。
2014年3月12日水曜日
MyBatisで遊んでみる(1) 第一章 postgreSQLの導入から設定(軽め)
どうも、はじめまして 技術グループのトムです。
今回はJavaのFrameworkの一つMyBatisで遊んでみます。
RDBMSとの連携において開発の効率化の為には、Hibernate のようなO/Rマッピングフレームワークを利用するのが一般的ですが 既存のSQLを利用使用しつつも、DAOやEntityの自動生成、クエリの動的生成など、ちょっとした部分だけFrameworkで代用したいということはあると思います。
そんな時は比較的簡単に利用できるSQLマッピングフレームワークとしてMyBatisが便利です。
という訳で、今回から
今回の記事ではMyBatisで遊ぶための前準備としてデータを準備します。
接続対象のDBはリモートに建てました環境は以下の通りです。
記事作成時点(2014/2/26)での最新バージョンは「9.3.2」でした。
※いつの間にか「9.3.3」が出ているみたいですが手順は同じだと思います。
collationを聞かれた場合は”C”を指定してください。
最後にアドオンツール導入のために、「Stack Builder」の起動を確認をされますが、今回は特に必要ないので、チェックを外してFinishです。
デフォルトインストールの場合「C:\Program Files\PostgreSQL\9.3\data」以下配下にファイルがあります。
以下変更が必要な場合は設定値を変更する
一時的にPATHを追加するには以下の通り
これで第二章のmybatis-generatorを利用するためのデータの準備が整いました。
ユーザとスキーマは作らなくてもmybatis-generatorが動くことは動くのですが、自動生成の対象スキーマやテーブルを絞らない場合information_schema、pg_catalogのカタログオブジェクトやテーブルも全て取得してしまいます。
昔は「postgresql.conf」のlisten_addressesのデフォルト値は'localhost'だったのですが、 今見るとデフォルトで全てのIPアドレスをlistenするようになっていますね。
postgresqlの設定値はバージョンによって細かい違いが出てきているようです。
今回はJavaのFrameworkの一つMyBatisで遊んでみます。
RDBMSとの連携において開発の効率化の為には、Hibernate のようなO/Rマッピングフレームワークを利用するのが一般的ですが 既存のSQLを利用使用しつつも、DAOやEntityの自動生成、クエリの動的生成など、ちょっとした部分だけFrameworkで代用したいということはあると思います。
そんな時は比較的簡単に利用できるSQLマッピングフレームワークとしてMyBatisが便利です。
という訳で、今回から
- 第一章 postgreSQLの導入から設定(軽め)
- 第二章 mybatis-generatorによるMapper自動生成
- 第三章 MyBatis実装サンプル
今回の記事ではMyBatisで遊ぶための前準備としてデータを準備します。
第一章 postgreSQLの導入から設定(軽め)
検証に使用するDBはpostgreSQLを選択。接続対象のDBはリモートに建てました環境は以下の通りです。
| OS | Windows 7 Enterprise SP1 64bit |
| RDBMS | postgreSQL 9.3.2 |
| FW | Mybatis 3.2.5 |
| ホストIP | 192.168.100.100 |
1. postgreSQLのインストール
(1)ファイルダウンロード
公式サイトからインストーラー「postgresql-9.3.2-3-windows-x64.exe」をダウンロード記事作成時点(2014/2/26)での最新バージョンは「9.3.2」でした。
※いつの間にか「9.3.3」が出ているみたいですが手順は同じだと思います。
(2)インストール
インストール手順は、ウィザードの「Next」を押していけば完了するので省略します。collationを聞かれた場合は”C”を指定してください。
最後にアドオンツール導入のために、「Stack Builder」の起動を確認をされますが、今回は特に必要ないので、チェックを外してFinishです。
(3)設定変更(リモートホストからpostgresへ接続する為の設定)
今回はリモート環境のDBへ接続するので、「postgresql.conf」と「pg_hba.conf」を少しいじります。デフォルトインストールの場合「C:\Program Files\PostgreSQL\9.3\data」以下配下にファイルがあります。
以下変更が必要な場合は設定値を変更する
#------------------------------------------------------------------------------
# CONNECTIONS AND AUTHENTICATION
#------------------------------------------------------------------------------
# - Connection Settings -
listen_addresses = '*' # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost'; use '*' for all
# (change requires restart)
port = 5432 # (change requires restart)
max_connections = 100 # (change requires restart)
# TYPE DATABASE USER ADDRESS METHOD # IPv4 local connections: host all all 127.0.0.1/32 md5 # IPv6 local connections: host all all ::1/128 md5 host all postgres 192.168.100.100/32 md5 # 接続を許可するデータベース、ユーザ、アドレスの範囲を指定 # 以下の場合はtestデータベースに対してmybatisuserユーザでのアクセスを192.168.100.0~192.168.100.255からの接続にのみに許可 host test mybatisuser 192.168.100.0/24 md5
2. ユーザ、データベース、スキーマ、テーブル作成
以下psqlで実行します。 psqlにPATHを通しておくと便利です。一時的にPATHを追加するには以下の通り
PATH %PATH%;C:\Program Files\PostgreSQL\9.3\bin常用する場合は環境変数に追記してください。
(1)ユーザとロールの関連付け、及びデータベースの作成
-- スーパーユーザーでログイン psql -U postgres -- クエリ実行結果の表示形式変更(お好みでどうぞ) \x -- ユーザ・ロール作成 ※サンプルなのでパスワードは適当です create role mybatisuser with NOSUPERUSER NOINHERIT LOGIN PASSWORD 'password'; -- データベース作成 create database test with owner mybatisuser; -- データベース作成結果確認 \l -[ RECORD 4 ]-----+---------------------- 名前 | test 所有者 | mybatisuser エンコーディング | UTF8 照合順序 | C Ctype(変換演算子) | C アクセス権 |
(2)スキーマ、テーブルの作成とサンプルデータの投入
-- ユーザ切り替え
\c test mybatisuser
-- スキーマ作成
CREATE SCHEMA my;
-- テーブル作成
create table my.dept (
id int4,
name varchar(256) not null,
PRIMARY KEY(id)
);
create table my.emp (
id int4,
dept_id int4 not null references my.dept(id),
name varchar(256) not null,
tel varchar(12) not null,
PRIMARY KEY(id, dept_id)
);
-- テーブル確認
\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)
-- データ挿入
insert into my.dept values
(1,'Foundation')
,(2,'Sales')
,(3,'System');
insert into my.emp values
(1,1,'Alfa','111-111-1111')
,(2,1,'Aileen','123-123-1234')
,(3,3,'Bravo','222-222-2222')
,(4,2,'Charlie','333-333-3333');
-- データ確認
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
これで第二章のmybatis-generatorを利用するためのデータの準備が整いました。
ユーザとスキーマは作らなくてもmybatis-generatorが動くことは動くのですが、自動生成の対象スキーマやテーブルを絞らない場合information_schema、pg_catalogのカタログオブジェクトやテーブルも全て取得してしまいます。
昔は「postgresql.conf」のlisten_addressesのデフォルト値は'localhost'だったのですが、 今見るとデフォルトで全てのIPアドレスをlistenするようになっていますね。
postgresqlの設定値はバージョンによって細かい違いが出てきているようです。
2014年3月5日水曜日
Hive2/Impalaを使ってみる。その1:Hadoopのインストール
初めまして。しんです。
タイムリーな話題ではありませんが、Hive/Impalaを触る場面がありました。
初めてのビッグデータのソフトウェアに触ったため、インストールするのに結構苦労をしました。
今回の経験を踏まえてインストール手順を複数回に分けて書きたいと思います。
環境
OS : CentOS
6.5 64bit (Minimal構成)
Memory :4GB以上
Java :Java SE 6 以上
事前準備
■wgetをインストール
Minimal構成では、wgetがインストールされていないので、インストールします。
# yum
install wget
|
■hostnameの設定
ホスト名を設定します。Hadoopでは、出力するファイル名にホスト名を使用している箇所があり、
未設定のままだと、うまく動作しませんでした。
hostsファイル
# vi
/etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4
localhost4.localdomain4
::1 localhost localhost.localdomain
localhost6 localhost6.localdomain6
<IPアドレス> <ホスト名>
|
networkファイル
# vi
/etc/sysconfig/network
NETWORKING=yes
HOSTNAME=<ホスト名>
|
両方のファイルを変更した後にnetworkを再起動して反映させます。
# /etc/init.d/network
restart
|
正しく設定できたかをチェックするためには以下のコマンドを実行します。
NGの場合
#
hostname –f
hostname:
Host name lookup failure
|
OKの場合
#
hostname –f
<ホスト名>
|
■セキュリティの設定
試しに使うのであれば、SELinux、iptable系のサービスを無効にする。
通常の利用時には、適切な設定を行いますが、今回はSELinuxの設定を無効にしました。
試しに使うのであれば、SELinux、iptable系のサービスを無効にする。
通常の利用時には、適切な設定を行いますが、今回はSELinuxの設定を無効にしました。
変更前
# vi
/etc/sysconfig/selinux
SELINUX= enforcing
|
変更後
# vi
/etc/sysconfig/selinux
SELINUX=disabled
|
同様にiptablesの設定を無効にします。
# service
iptables stop
iptables:
Setting chains to policy ACCEPT: filter
[ OK ]
iptables:
Flushing firewall rules: [ OK ]
iptables:
Unloading modules: [ OK ]
#
chkconfig iptables off
|
同様にip6tablesの設定を無効にします。
# service
ip6tables stop
ip6tables:
Setting chains to policy ACCEPT: filter [
OK ]
ip6tables:
Flushing firewall rules: [ OK ]
ip6tables:
Unloading modules: [ OK ]
#
chkconfig ip6tables off
|
■Javaのインストール
JavaはOracleのサイトからrpmをダウンロードして、対象の環境にアップロードしてからインストールを
行います。
# rpm –ivh
<javaのrpmファイル>
|
インストール後、バージョンの確認も忘れずに。バージョンはJava SE 1.6以上が必須となります。
# java –version
java
version "1.7.0_51"
|
Hadoopのインストール
■yumレポジトリの追加
今回はCDH(Cloudera’s Distribution Including Apache Hadoop)を利用して
Hadoopをインストールしたいと思います。
以下の手順はClouderaのドキュメントを参考に実施した手順となります。
はじめに、yumレポジトリにClouderaのサイトを追加します。
# cd
/etc/yum.repos.d/
|
■CDHのインストール
各種コンポーネントをインストールします。なお、今回使用したのはMRv1/MRv2(YARN)の
うち、MRv1でしたので、以下のコンポーネントとなります。
# yum -y
install hadoop-0.20-mapreduce-jobtracker
# yum -y
install hadoop-hdfs-namenode
# yum -y
install hadoop-hdfs-secondarynamenode
# yum -y
install hadoop-0.20-mapreduce-tasktracker
# yum -y
install hadoop-hdfs-datanode
# yum -y
install hadoop-client
|
疑似分散モードをインストールする。
#yum -y
install hadoop-0.20-conf-pseudo
|
Hadoopのノードを初期化する。
sudo -u
hdfs hdfs namenode –format
|
Hadoopの初期化が終わった本ノードで各種ディレクトリをHadoop上に作成する。
sudo -u
hdfs hadoop fs -mkdir /tmp
sudo -u
hdfs hadoop fs -chmod -R 1777 /tmp
su - hdfs
hadoop fs
-mkdir /var/lib/hadoop-hdfs/cache/mapred/mapred/staging
hadoop fs
-chmod 1777 /var/lib/hadoop-hdfs/cache/mapred/mapred/staging
hadoop fs
-chown -R mapred /var/lib/hadoop-hdfs/cache/mapred
exit
|
正しくインストールができたか、Hadoopのサービスを起動して確認する。
# for
service in /etc/init.d/hadoop-hdfs-*; do $service start; done
# for
service in /etc/init.d/hadoop-0.20-mapreduce-*; do $service start; done
|
今回のまとめ
l ホスト名を設定する。
l 試しに使うのであれば、SELinux、iptable系のサービスを無効にする。
l yumコマンドで必要なコンポーネントをインストールする。
l 1つのサーバ上で稼働するため、疑似分散モードで実行する。
今回はHadoopのインストールまでとします。
次回はHiveのインストール手順を公開する予定です。
登録:
投稿 (Atom)







