wiQuery - WicketとJQueryを統合するライブラリー その3
wiQueryの1.0.1がリリースされてます。
maven用のRepositoryが用意されたようなので今後はわざわざローカルにダウンロードして(ryというようなことをする必要がなくなりました。
ちなみにpomには以下を追加すればOKです。
<repositories> <!-- wiQuery用リポジトリー --> <repository> <id>wiquery repository</id> <name>wiQuery repository</name> <url>http://wiquery.googlecode.com/svn/repo/</url> <layout>default</layout> </repository> </repositories> <dependencies> <!-- wiQueryの依存関係追加 --> <dependency> <groupId>org.odlabs.wiquery</groupId> <artifactId>wiquery</artifactId> <version>1.0.1</version> </dependency> <!-- logger追加 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.5.8</version> </dependency> </dependencies>
また、デフォルトではwiQueryの依存関係はwicket1.4.3なので、最新のWicketのバージョンにしたい人とLog4jやめてlogback使いたい人は下記のように設定すればOKです。
<dependencies> <dependency> <groupId>org.odlabs.wiquery</groupId> <artifactId>wiquery</artifactId> <version>1.0.1</version> <exclusions> <exclusion> <groupId>org.apache.wicket</groupId> <artifactId>wicket</artifactId> </exclusion> <exclusion> <artifactId>log4j</artifactId> <groupId>log4j</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.wicket</groupId> <artifactId>wicket</artifactId> <version>1.4.7</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>0.9.20</version> </dependency> </dependencies>
JPAのManyToOneにおける各ベンダーの違い
JPAで*1エンティティー間の多対一のリレーションを表現する@ManyToOneですが、こやつの振る舞いがベンダー間でちょっと違うようなのでメモしておきます。
振る舞いの違い
どう違うのかというと、参照先のエンティティが物理的に存在しなければ取得時にEntityNotFoundExceptionをthrowするかどうか。
例えば下記のテーブルとデータがあるとします。
EMPLOYEEテーブル
EMPLOYEE_ID | NAME |
---|---|
1 | Hoge |
2 | Foo |
3 | Bar |
EXPENSEテーブル
EXPENSE_ID | AMOUNT | EMPLOYEE_ID |
---|---|---|
1 | 130 | 1 |
2 | 930 | 2 |
3 | 2,340 | 4 |
EXPENSEテーブルのEMPLOYEE_IDとEMPLOYEEテーブルのEMPLOYEE_IDとの間には論理的な参照が存在します(物理的な制約(参照整合性制約)があるわけではありません)。また、EXPENSEテーブルの3行目のデータは存在しないEMPLOYEEのIDを参照してしまっているゴミデータです。
この時の各テーブルに対するエンティティは下記になります。
Employeeエンティティ
@Entity @Table(name = "EMPLOYEE") public class Employee implements Serializable { private static final long serialVersionUID = 1L; @Id @Column(name = "EMPLOYEE_ID") private Integer employeeId; @Column(name = "ZIP") private String zip; @OneToMany(cascade = CascadeType.ALL, mappedBy = "employee") private List<Expense> expenseList; //アクセサは省略
Expenseエンティティ
@Entity @Table(name = "EXPENSE") public class Expense implements Serializable { private static final long serialVersionUID = 1L; @Id @Column(name = "EXPENSE_ID") private Integer expenseId; @Column(name = "AMOUNT") private BigDecimal amount; @JoinColumn(name="EMPLOYEE_ID",referencedColumnName="EMPLOYEE_ID") @ManyToOne private Employee employee; //アクセサは省略
この時EXPENSE_IDが3(ゴミデータ)を取得しようとした時にベンダー間で振る舞いが変わってきます。
調べたベンダーとバージョンは以下です。
- OpenJPA 1.2.2(JPA1.0実装)
- Hibernate EntityManager 3.2.0-GA(JPA1.0実装)
- Hibernate EntityManager 3.5.0-FINAL(JPA2.0実装)
- OpenJPA 2.0.0Beta3(JPA2.0実装)
- EclipseLink2.0.0(JPA2.0実装)
んで、実際どうなのか
- EntityNotFoundExceptionをthrowする
- EntityNotFoundExceptionをthrowせずに取得時(getEmployeeが呼び出された時)にnullを返す
- OpenJPA 1.2.2(JPA1.0実装)
- OpenJPA 2.0.0Beta3(JPA2.0実装)
- EclipseLink2.0.0(JPA2.0実装)
この振る舞いの違いって結構デカいと思うのですがどうでしょうか??「ちょっくらJPAの実装変えてみようZE!」的なノリでやった場合に予期せぬ場所で例外が吹っ飛ぶことになります。テーブルに対して物理的に参照整合性制約を付けていればバッチ等でデータが流された場合も整合性は保たれるでしょうが、そもそも参照整合性制約がかけれない状況だってあるわけで。というか調べた限りHibernateだけ独自っぽいです。自分はHibernateのJPA実装歴が長かったのでOpenJPAやEclipseLinkを試すまで例外が吹っ飛ぶのがJPAの仕様だと勘違いしてました。(そういや初期のJPA-Hibernate実装はfindメソッドで存在しない場合はEntityNotFoundExceptionをthrowしてました。今はちゃんとJPAの仕様どおりnullを返すようです。こちらも自分はずっとそれがJPAの仕様だと勘違いしてました。)
じゃあHibernateでOpenJPAやEclipseLinkのような振る舞いにすることはできないのかというともちろんできます。@ManyToOneのカラムに@NotFoundアノテーションを付けます。
@NotFound(action=NotFoundAction.IGNORE) @JoinColumn(name="EMPLOYEE_ID",referencedColumnName="EMPLOYEE_ID") @ManyToOne private Employee employee;
しかしここでも問題があります。@NotFoundアノテーションはHibernate独自のアノテーションです。つまりベンダー依存になっちゃうんですよね〜。これじゃあせっかくの標準仕様が台無しです><。persistence.xmlへの記述でなんとか回避出来ないか調べてみたんですが今のところ見つけられず・・・。個人的には例外が吹っ飛んで欲しいのでOpenJPAとEclipseLinkがHibernateに合わせて(というかJPAの標準仕様にして)、例外が吹っ飛んで欲しくない場合は@ManyToOneのオプションで制御とかやって欲しい感じです。
ちなみにOpenJPAやEclipseLinkで参照先が存在しないエンティティをそもそも取得したくない場合は下記のようにします。
@ManyToOne(optional=false)
ただし、これだとfindメソッドでPK検索した場合も取得できないという仕様みたいです・・・。う〜ん、それもどうなの?って感じですねぇ〜。
*1:1.0、2.0の両方
TDD Boot Camp北陸に行ってきますた
楽しかったです。ほんとに行ってよかったと思える勉強会でした。
id:katzchangさんや開催の準備に携わった方々、参加された方々、本当にお疲れさまでした&有難うございました。
感想とかそのへんはまた別のエントリーで書きたいと思います。
TDD Boot Camp北陸 - 感想とか
感想と、参加した上での今後の目標とか書いていこうかなと思います。
旅立ち
実は新幹線に乗り遅れそうでしたw。まじであせりましたが、なんとか乗れてよかった。途中米原からの特急に乗ってからは一緒に行った友人とPC立ち上げて「あーだこーだ」言いながらペアプロしてました。ぶっちゃけ周りからはかなりイタイ感じでみられていたんじゃないかとw。
id:t-wadaさんのお話
ジョジョとスティール・ボール・ランTDDに関するお話。以前の資料は拝見させていただいてのですが、やっぱり直接聞くのは全然違いますね、三本柱の例えが非常に印象的でした。あとassertファーストに関してはどこかの資料か本で読んでたはずが忘れていました・・・反省してちゃんと実践します。
TDD & ペアプロ
お題に対してテストファーストでペアプロでコーディングするという形式。前後半でペアを替えてやったのですが、やっぱりペアプロは楽しいですね〜。ペアになった人の意見をダイレクトに聞けるのが何より良いです。仕事でもずっとペアプロやってますがいつもと違う方々とペアプロできて非常に勉強になりました。自分とペアを組んだ方も楽しいと思っていただけていたら良いのですが・・・、そのあたりの感想を聞くのを忘れていた事を非常に後悔してます。
コードレビュー
みんなが書いたソースのコードレビューを行ったのですが、これがまた凄く勉強になりました。今回はいろいろな言語でそれぞれTDD&ペアプロしていたので各言語でのテストの書き方とか知ることができて良かったです。以下ちょっとした感想。
夕食、その後
鍋が美味しかったです。温泉もよかった〜。id:t-wadaさんにテストの事、設計の事、DDDの事、Mockの事とか日頃自分が疑問に思ってることを聞けて凄く勉強になりました。丁寧にお答え頂きほんとにありごうとございます。あとtwitterやってないと人として認められない的なことを大阪の人に言われたのでさっそくtwitterアカウント取得しときましたw。
2日目の午前中
レガシーコード(テストの無いコード)に対するテストの書き方をid:t-wadaさんとid:katzchangさんがペアプロしながらライブコーディングする形式。レガシーコードに対して他人がテストを書く所を生で見れたのが一番よかったです。やはり他の人の考え方とかアプローチの仕方とかは勉強になりますね。
2日目の午後
午前をふまえた上で、各言語に分かれてレガシーコードに対してテストを書いていくという形式。Javaのお題になったソースコードはそもそもテストを書く事に対して敷居の高いGUIのアプリケーションだったので中途半端な所で時間が来てしまい個人的に不完全燃焼感がのこったのですが、1つのソース(課題)に対して複数人が取り組みながらコーディングしていく形式は面白かったです。ここでも他の人の考えを色々聞けて凄く勉強になりました。
帰り
帰りの新幹線でもまた友人とペアプロしてましたw。まわりから(ry
全体的な感想
凄く勉強になりました。特にJava以外の言語で同じ課題に対するテストの書き方を見れたのがよかった〜。いろんな宗教戦争(OSとかキーボード配列とかPCとかテスト用ライブラリとか言語とか)は楽しかったですw。あと、ほとんどの人が複数言語を当たり前のように扱えててSUGEE!って思いました。自分まだまだヘボいなぁというのが再確認できて勉強の意欲が上がってよかったです。
今回参加した結果今後こういうことやっていこうとかいうの
「勉強になった」「楽しかった」だけでは無く今後どうやっていくかってのもこういう勉強会に参加した後は凄く大事だと思うのでそのへんつらつら書いていこうかなと
最後に
主催者の方々、参加された方々ほんとにお疲れさまでした&ありがとうございました!