過去に書いた「ディープコピー in JPA」の訂正

去年、Java EE6でWebプロトを作成していた頃に、JPAのEntityをディープコピーする方法に触れました。→Challenge Java EE 2012/10/30記事 ディープコピー in JPA

この当時やった方法は、StackOverflowの元ネタを参考に、コピーしたいEntityをfindで一旦取ってきてdetachし、IDを塗り替えて保存するという、今思うと結構ムチャクチャなやり方…(^^;

今の開発でこの方式を踏襲しようとしたのですが、試験しなおしてみると色々と問題があったので、トリッキーなやり方は止めて、普通にディープコピーすることにしました。
(もし参考にして実装された方がいたら申し訳ないです…うまくいっていることを祈ります;)

ディープコピーは、自前で実装もあると思うのですが、Entityの階層もそこそこに深いので、手間だな…とライブラリを探しました。

なんかJavaでディープコピー、シャローコピーするライブラリはたくさんありすぎて、どれがどういいのか判断に迷う…(-_-;ここがJavaの苦しい所でもあり、時間があれば楽しい所でもある感じです。

まずはStackOverflowにあった以下記事を参考にしました。

Java: recommended solution for deep cloning/copying an instance

pro/consって何?と思ったら、賛否、って感じの意味なんですね(^^;

Java Deep-Cloning Libraryはリンク切れしてますが、以下にありました。
https://code.google.com/p/cloning/

あと、この記事にないライブラリでもJava Generic Deep Copyとかもありました。

で、あれこれやりながら、最終的にはDozerなるライブラリを選びました。
以下、メモです。

Dozerのmaven設定

pom.xml

<dependency>
    <groupId>net.sf.dozer</groupId>
    <artifactId>dozer</artifactId>
    <version>5.4.0</version>
</dependency>

と追加して終わりです。

DozerでEntityのディープコピー

コードもシンプルでサンプルレベルなら以下のような記述だけで動きます。

Mapper mapper = new DozerBeanMapper();
HogeEntity copyHoge = mapper.map(originalEntity, HogeEntity.class);

ただ、Mapperのインスタンスはシングルトンが推奨されていたので、そこら辺だけ工夫すればよいかなと。

結構シンプルに実装できました。

ログ注意

実行して、やたらコピー遅いなぁ、と思っていたら、DEBUGレベルで大量のログが出てました(^^;今の開発ではSLF4j&Logbackなので、logback.xml

<configuration>
    <!-- appenderとか省略 -->

    <logger name="org.dozer" level="off" />

    <!-- rootも省略 -->
</configuration>

という感じで、とりあえず回避しました。
このloggerが全然効かなくてハマってたのですが、pomにlogback-coreがなかったことが駄目だったようで、入れたら効きました。

JSF2.0なのでPrimeFacesを使ってファイルアップロード

今回の開発にファイルアップロードはないかな、と思っていたのですが、ありました(^^;

Java EE7でJSF2.2になれば標準機能でできるようになるとのことですが…↓

@den2snさんの記事
リリース間近!Java EE 7の気になるところ

現時点(2013/5/20)で自分はJava EE6でJSF2.0にて開発してるので標準が使えません(>_<)

幸いPrimeFacesを使っているので、FileUploadコンポが利用できそうです。
既に@kisさんが色々触られています↓

で、さっそく自分も組み込もうと思ったら、意外としょうもない所でつまづいてしまったので、メモ。

まずは簡単なサンプルを、と思ってPrimeFaces3.5のユーザガイドから「Simple File Upload」のコードを参考に(というかほぼ同じ…)以下作りました。

JSFのビュー

ここで貼りつけたコードはbodyタグのみで、他は省略してます。
formにはenctypeを指定します。

<h:body>
    <h:form enctype="multipart/form-data">
        <p:fileUpload value="#{fileUploadBean.file}" mode="simple" />
        <p:commandButton value="upload" ajax="false" />
    </h:form>
</h:body>
BackingBean

PrimeFacesのUploadedFile型の変数を置くだけ(^^;マジすか。

package jp.co.hoge.upload;

import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import org.primefaces.model.UploadedFile;

@ManagedBean(name = "fileUploadBean")
@ViewScoped
public class FileUploadBean implements Serializable {

    private UploadedFile file;

    public UploadedFile getFile() {
        return file;
    }

    public void setFile(UploadedFile file) {
        this.file = file;
    }
}
web.xmlにFilter設定
<filter>
    <filter-name>PrimeFaces FileUpload Filter</filter-name>
    <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
</filter>
    
<filter-mapping>
    <filter-name>PrimeFaces FileUpload Filter</filter-name>
    <servlet-name>Faces Servlet</servlet-name>
</filter-mapping>

ユーザガイド上はこれだけ、なんですが、これだけで動くの!?と半信半疑(^^;

とりあえず実行すると…エラーとなりました。
stacktraceみると

Caused by: java.lang.ClassNotFoundException:org.apache.commons.fileupload.FileItemFactory

みたいなエラーが。

ここが微妙にハマった所ですが、PrimeFacesのFileUploadは裏でcommons-fileupload、commons-ioを利用しているとのことです。
これはユーザガイドにも記述があるのですが、FileUploadの章とは別の所に記述があって最初気づかなかった…です(^^;

commonsはそれぞれ最新バージョンでユーザガイドのより新しいのがありましたが、一応ユーザガイドに従って

  • commons-fileupload version 1.2.1
  • commons-io version 1.4

を使いました。

maven使っていれば

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.2.1</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>1.4</version>
</dependency>

をpom.xmlに書けばOKです。

再度実行

で、無事に実行できて

f:id:kikutaro777:20130520220846j:plain

画面はこんな感じに(今回のビュー定義だけで勝手になるので、なんか色々工夫もいりそう…)

参照押すとファイル選択ダイアログが出てきて

f:id:kikutaro777:20130520220853j:plain

選択します。

f:id:kikutaro777:20130520220954j:plain

で、Uploadボタン押してbean側でブレイクしてみたところ

f:id:kikutaro777:20130520221009j:plain

おお、データが入ってる!

UploadedFileがgetInputstream()とかを持っているので、あとはファイルに書きだす感じですかね。

ユーザガイドみると自動アップロードとか色々なことできそうなので、触る機会があればまとめたい所です。

NetBeans daily buildsでJDK8 Early Accessを触ってみたら…

今日はJava EEではなくJava SE(^^;

先日のJJUG CCC 2013 Springで聴いた櫻庭さんの「Project Lambda Essential」資料が公開されていました!
http://www.slideshare.net/skrb/project-lambda-essential

Java SE 8のリリースは色々あって来年の春頃に延期となってしまったようですが、Javaの世界ってEarly Accessを触ることができるので良いですね。

NetBeanもdaily buildJava 8に対応中の版がダウンロードできたので、少しずつ触ってみようかなーと思います。

なお今回触っているのは

  • JDK1.8.0-ea-b89
  • NetBeans Dev 201305152300

です。今後変わるかもしれないので参考まで。

まずは写経的に公開された資料の例を打ってみる…前に今ならこう書く(streamとかで書き換える前提で)というのを書いてみたら…

f:id:kikutaro777:20130518203237j:plain

あら

f:id:kikutaro777:20130518203320j:plain

ほぅ

f:id:kikutaro777:20130518203338j:plain

おろ

なんかstreamのfilterでてきて書き換えてくれた(^^;

資料を参考に括弧とったりして最後は以下に書き換えました。

f:id:kikutaro777:20130518203434j:plain

てか、IDEでこんなヒント出すんだ(^^)!!

JJUGの発表内にも「Lambda禁止とかにならないように…」ってありましたが、IDEでこういうヒント出ると、現場での利用促進に効いてくるかもしれないですね。
(わざわざヒントでない設定を各人に強いることもないだろし…あるのかなw)

ってか新人教育とかで、従来通りの書き方で教えると

「なんかヒント出てますけど、こう書いたほうがいいってことじゃないんですか?」

「streamってなんですか?この「->」って書き方ってどんな意味ですか?」

とか新人さんからバシバシ質問出てきて、回答に困る状況とかありそう(^^)
そうならないように勉強していかないと。

QLOOKアクセス解析