Challenge Java EE !

Java EEを中心に趣味や仕事における開発メモを書いています。

Querydslを触ってみる

今のJava EE開発では、JPQLを書く際

  • 動的クエリ(Dynamic Query)
  • 名前付きクエリ(Named Query)

を使うようにしていて、本来、型のことなど考えるとCriteriaで書くべきだなんだろうな…と思いつつ、可読性や簡易性を優先して上記選択にしています。

ただ、どうしても動的クエリはStringBuilderなどでクエリをダラダラと連結することになって、わりとイケてないらしい…ですね。

ということで、前に@megascusさんがブログで紹介されていたQuerydslを思い出し、これから少し触っていこうかなと。

QuerydslでJPAが思ったよりも捗る

上記ブログ記事の中にあるサンプルコードや、公式サイトのチュートリアルに接頭辞「Q」が付いたEntityクラスみたいなものが出てくるので、なんなんだろあれ?と思ってたのですが、定義したEntityクラスをベースにQuerydslが(というか実際にはapt(Annotation Processing Tool))が自動生成するクラスなんですね。

ってかapt初めて知りました(^^;
ググるとき気を付けないとLinuxのaptが出てきそう…(^^;

……

そんなこんな調べつつ、QuerydlsのドキュメントからQuerying JPAのチュートリアルを参考に触ってみました。

Mavenによる依存性定義

pom.xmlは以下としました。

<dependency>
    <groupId>com.mysema.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <version>3.3.2</version>
</dependency>
<dependency>
    <groupId>com.mysema.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>3.3.2</version>
</dependency>

公式のみるとslf4jも入れたほうが良いようですね(今回は省略しましたが一応動いてます)。

今日時点で最新の3.3.2は2014年3月24日にリリースされたものなので、わりと最近でもメンテされてるライブラリのようです。

3系の最新定義するなら上記pom.xmlのversionタグを

<version>[3,)</version>

のように指定してもいいかもしれません。

maven-apt-pluginの定義

コード自動生成のためにpom.xmlのbuildタグ内のpluginsで以下定義を追加します。

<plugin>
    <groupId>com.mysema.maven</groupId>
    <artifactId>maven-apt-plugin</artifactId>
    <version>1.0</version>
    <executions>
      <execution>
        <goals>
          <goal>process</goal>
        </goals>
        <configuration>
          <outputDirectory>target/generated-sources/java</outputDirectory>
          <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
        </configuration>
      </execution>
    </executions>
  </plugin>

今回Java EE 7、GlassFish 4.0でサンプルを作りましたが、Entityの生成などはJava EE 6と同様にNetBeans任せで、データベースから自動的に作りました。

そしてビルドすると

f:id:kikutaro777:20140421223509j:plain

EntityのActress.java(NetBeansから自動生成したEntity)に対してQActress.java(Querydls&aptで自動生成)が出来ています。

テーブルサンプル

Actressテーブルは以下定義しました。

ID 名前 年齢
1 堀北真希 25
2 橋本環奈 15
3 橋本愛 18
4 能年玲奈 20
5 有村架純 21

サンプルコード

堀北さんをピンポイントで拾ってみます。EJBで

public Actress getHorikita(){
    QActress actress = QActress.actress;
    JPQLQuery query = new JPAQuery(em, EclipseLinkTemplates.DEFAULT);
    return query.
            from(actress).
            where(actress.name.eq("堀北真希")).
            uniqueResult(actress);
}
[ id:1, name:堀北真希, age:25 ]

橋本さんをlike文で検索して、名前でソート昇順に。

public List<Actress> getHashimoto(){
    QActress actress = QActress.actress;
    JPQLQuery query = new JPAQuery(em, EclipseLinkTemplates.DEFAULT);
    return query.
            from(actress).
            where(actress.name.like("橋本%")).
            orderBy(actress.name.asc()).
            list(actress);
}
[[ id:3, name:橋本愛, age:18 ], [ id:2, name:橋本環奈, age:15 ]]

成人した女優さん達は

public List<Actress> getAdult(){
    QActress actress = QActress.actress;
    JPQLQuery query = new JPAQuery(em, EclipseLinkTemplates.DEFAULT);
    return query.
            from(actress).
            where(actress.age.goe(20)).
            list(actress);
}
[[ id:1, name:堀北真希, age:25 ], [ id:4, name:能年玲奈, age:20 ], [ id:5, name:有村架純, age:21 ]]

goeは「>=」でgtが「>」。逆はloe、lt。

テーブルが超簡易なのであれですが、良い感じ!
もう少し複雑なクエリ組んでみよう。

これ採用してもいいんじゃないかなー。開発元はmysemaというフィンランドの企業がやっているらしい。

コードはGitHubで管理されてて、わりと活発そうです。
https://github.com/mysema/querydsl

発行されるJPQLをどうやって確認できるのかな?と思ったのですが、普通にNetBeansでも表示されてるっぽい。

f:id:kikutaro777:20140421233638j:plain

PrimeFaces 5.0 RC1がリリースされました!

JSFのコンポーネントライブラリであるPrimeFacesの5.0 RC1がリリースされました。

少し前に5.0で、どんなコンポーネントが追加されるか、簡単に紹介させて頂きました。

PrimeFaces 5.0の新コンポーネントや新機能!

というわけで早速RC1をちょっと触ってみました。

Maven使うのが楽なのでMavenと、今回はJava EE7(GlassFish 4.0), JDK 8でサンプル作ってみました。

PrimeFaces用のMavenリポジトリがあるのでrepositoriesタグに以下を追加して

<repositories>
    <repository>
        <id>prime-repo</id>
        <name>PrimeFaces Maven Repository</name>
        <url>http://repository.primefaces.org</url>
        <layout>default</layout>
    </repository>
</repositories>

依存性は以下定義すれば準備OKです。

<dependency>
    <groupId>org.primefaces</groupId>
    <artifactId>primefaces</artifactId>
    <version>5.0.RC1</version>
</dependency>

PrimeFacesのDataTableは結構いろんなことができるので、業務システム的にはありがたかったのですが、なぜか今までずっと行固定しかなくてカラム固定がありませんでした…。

海外はそういうものなのか?と思っていましたが、やはり海外でもあちこちそういう要望あって、今回の5.0で無事に入ったみたいです。

ということで、今回はそれをサンプルに。

先に結果から。

カラムを2個固定した場合

f:id:kikutaro777:20140416224847j:plain

スクロールすると

f:id:kikutaro777:20140416224856j:plain

カラムを3個固定に変えてみると

f:id:kikutaro777:20140416224908j:plain

ふむふむ、悪くない。

固定するカラムの数はビュー定義でfrozenColumns属性を指定するだけなので簡単です。
以下、ビューの定義

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>女優さん一覧</title>
    </h:head>
    <h:body>
        <h:form>
            <p:dataTable var="actress" value="#{actressBean.listActress}" 
              frozenColumns="3" scrollable="true" scrollWidth="400">
                <p:column headerText="名前">
                    <h:outputText value="#{actress.name}" />
                </p:column>
                <p:column headerText="年齢">
                    <h:outputText value="#{actress.age}" />
                </p:column>
                <p:column headerText="生年月日">
                    <h:outputText value="#{actress.birth}" />
                </p:column>
                <p:column headerText="身長">
                    <h:outputText value="#{actress.height}" />
                </p:column>
                <p:column headerText="血液型">
                    <h:outputText value="#{actress.blood}" />
                </p:column>
                <p:column headerText="所属事務所">
                    <h:outputText value="#{actress.company}" />
                </p:column>
            </p:dataTable>
        </h:form>
    </h:body>
</html>

Backing Beanは

import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Arrays;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import lombok.Getter;

@ViewScoped
public class ActressBean implements Serializable{

    @Getter
    private List<Actress> listActress;
    
    @PostConstruct
    public void init(){
        System.out.println("hello");
        listActress = Arrays.asList(
                new Actress("堀北真希", 25, LocalDateTime.of(1988, 10, 6, 0, 0), "SweetPower", 160, BloodType.B),
                new Actress("宮崎あおい", 28, LocalDateTime.of(1985, 11, 30, 0, 0), "ヒラタオフィス", 163, BloodType.O),
                new Actress("有村架純", 21, LocalDateTime.of(1993, 2, 13, 0, 0), "フラーム", 160, BloodType.B)
        );
    }
}

Java 8のDate and Time API全然使い慣れず…ちょっと適当です(^^;汗 勉強せんと。

あとは女優さんクラスと

import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter @Setter
@AllArgsConstructor
public class Actress {
    private String name;
    private int age;
    private LocalDateTime birth;
    private String company;
    private int height;
    private BloodType blood;
}

enumだけ

public enum BloodType {
    A,
    B,
    O,
    AB
}

これから少しずつ5.0RC1触っていこうと思います~。

BeanUtils.copyPropertiesによるフィールド値コピー

後輩が使ってたコードをネタとして拝借(^^;

うちでは案件管理システムなどの開発が多いのですが、業務の中で

「見積が確定して受注した際、見積の情報を受注へと引き継ぐ」

みたいなのが結構あったりします。

そんなときに見積のEntityが持つ属性情報から1つ1つ受注Entityに入れていく…みたいなことをする場合もあったりするのですが、BeanUtils使うと、簡単にプロパティ情報をコピーできて便利でした。

既に色々情報あって参考にさせて頂きました。こんなのあるなんて知らなかった(>_<;

Maven使っていればpom.xmlに以下依存性を定義するだけ。

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.1</version>
</dependency>

ちょっとしたお試しコード書いてみました。
JPAのEntityではなく、とりあえず普通のクラスです。
(Lombok使ってます)

/**
 * 見積の情報クラス
 */
@Getter @Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Quote{
    private String quoteId;
    private String name;
    private String customerId;
    private BigDecimal quotePrice;
}
/**
 * 受注の情報クラス
 */
@Getter @Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Order{
    private String orderId;
    private String name;
    private String customerId;
}

で、見積のインスタンス作って、属性にも値をセット。
受注のインスタンス作ってBeansUtils.copyPropertiesで属性コピー

Quote quote = new Quote("quote1", "見積1", "hogehoge",new BigDecimal(3000));
Order order = new Order();

//見積のプロパティを受注へコピー
BeanUtils.copyProperties(order, quote);
System.out.println(quote);
System.out.println(order);

標準出力結果は以下

Quote(quoteId=quote1, name=見積1, customerId=hogehoge, quotePrice=3000)
Order(orderId=null, name=見積1, customerId=hogehoge)

プロパティ名が一致する部分だけコピーされてます。便利。
内部的にはリフレクションとか使ってるのかな?一度コードリーディングしてみたい気も。

前にディープコピーを実装したとき、Dozerとか使ったけど、これ使うのもありかなと思ったり。

というか、オフショア先のコードで、エンティティをディープコピーする際、自前で単純にnewした後、ひたすらコピー元のフィールド値を延々とセットし続けるコードを前にみたのだけど、これ使えば1行で書けるし、属性増えたときも改造いらないんだけどなぁ…。

にほんブログ村 IT技術ブログへ
にほんブログ村
にほんブログ村 IT技術ブログ Javaへ
にほんブログ村
QLOOKアクセス解析