読者です 読者をやめる 読者になる 読者になる

Challenge Java EE !

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

はじめてのWindows Azure ~NetBeansのコード生成でJPAからSQLデータベースへ~

Windows Azure JPA NetBeans Java EE 7

昨日書いた

はじめてのWindows Azure ~SQLデータベース作成からNetBeansでの接続まで~

にて、AzureでSQLデータベースを生成して、NetBeansのデータベースサービスと簡単に連携できました。

ということは、ここからNetBeansの自動生成機能を使えば簡単にデータベース処理のWebアプリが作れるわけで、やってみました。

ここに来るとあまりAzure関係ないですね…(^^;
ちなみにWidnows 8 Proの64bitでJDK8、NetBeans7.4でGlassFish4.0にて行いました。

NetBeansで新規プロジェクト作成

「ファイル」->「新規プロジェクト」を選択します。
f:id:kikutaro777:20140105231702j:plain

Mavenを使わなくてもいいですが、依存性管理が簡単なので今回はMavenのパターンで書きます。「Maven」->「Webアプリケーション」を選択します。
f:id:kikutaro777:20140105231742j:plain

プロジェクト名を適当に入力します。今回はサンプルなので、あとはそのまま。
f:id:kikutaro777:20140105231910j:plain

Java EE7でいきます。GlassFishは4.0です。
f:id:kikutaro777:20140105231939j:plain

これでプロジェクトができます。

JPAのエンティティ・クラスを生成

プロジェクトができたら、パッケージで右クリックして「新規」を選択します。
f:id:kikutaro777:20140105232051j:plain

「持続性」->「データベースからのエンティティ・クラス」を選択します。
f:id:kikutaro777:20140105232138j:plain

「新しいデータ・ソース」を選択します。
f:id:kikutaro777:20140105232209j:plain

JNDI名を適当に入れて、データベース接続で昨日の手順で作った名称のデータベース接続を選択します。
f:id:kikutaro777:20140105232317j:plain

すると「使用可能な表」にEmployeeテーブルがみえるので、追加を押して以下状態とします。
f:id:kikutaro777:20140105232401j:plain

NamedQuery不要であればチェック外したり、ですが、ここではデフォルトのままにします。
f:id:kikutaro777:20140105232449j:plain

コレクション型をjava.util.Listにして、終了しました。
f:id:kikutaro777:20140105232514j:plain

これでEmployeeエンティティクラスが生成されました。
f:id:kikutaro777:20140105232551j:plain

トランザクション処理用のEJBを生成

先と同じように、パッケージで右クリックして「新規」を選択し、「エンタープライズJavaBeans」から「エンティティ・クラスのセッションBean」を選択します。
f:id:kikutaro777:20140105232715j:plain

使用可能なエンティティ・クラスに先ほど自動生成されたEmployeeクラスがみえるので「追加」します。
f:id:kikutaro777:20140105232813j:plain

本来はパッケージ分けたほうが良いですが、今回はサンプルなのでそのままにして進めます。
f:id:kikutaro777:20140105232843j:plain

これでAbstractFacade.javaとEmployeeFacade.javaが自動生成されました。
AbstractFacadeはCRUD処理+アルファを含んだ抽象クラスで、EmployeeFacadeはそれを継承して生成された具象クラスで、以下のようなコードが生成されます。

package jp.co.hoge.azuredbsample;

import java.util.List;
import javax.persistence.EntityManager;

/**
 *
 * @author kikutaro
 */
public abstract class AbstractFacade<T> {
    private Class<T> entityClass;

    public AbstractFacade(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    protected abstract EntityManager getEntityManager();

    public void create(T entity) {
        getEntityManager().persist(entity);
    }

    public void edit(T entity) {
        getEntityManager().merge(entity);
    }

    public void remove(T entity) {
        getEntityManager().remove(getEntityManager().merge(entity));
    }

    public T find(Object id) {
        return getEntityManager().find(entityClass, id);
    }

    public List<T> findAll() {
        javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        return getEntityManager().createQuery(cq).getResultList();
    }

    public List<T> findRange(int[] range) {
        javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        javax.persistence.Query q = getEntityManager().createQuery(cq);
        q.setMaxResults(range[1] - range[0] + 1);
        q.setFirstResult(range[0]);
        return q.getResultList();
    }

    public int count() {
        javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
        cq.select(getEntityManager().getCriteriaBuilder().count(rt));
        javax.persistence.Query q = getEntityManager().createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    } 
}
package jp.co.hoge.azuredbsample;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

/**
 *
 * @author kikuta
 */
@Stateless
public class EmployeeFacade extends AbstractFacade<Employee> {
    @PersistenceContext(unitName = "jp.co.hoge_AzureDBsample_war_1.0PU")
    private EntityManager em;

    @Override
    protected EntityManager getEntityManager() {
        return em;
    }

    public EmployeeFacade() {
        super(Employee.class);
    } 
}
管理対象ビーン(Backing Bean)を生成します。

画面の入力情報などを管理するクラスを生成します。
前と同じ手順で以下ウィンドウを開き、「JavaServer Faces」から「JSF管理対象Bean」を選択します。
f:id:kikutaro777:20140105233357j:plain

Java EE7ではJSF管理対象ではなくCDI管理対象が推奨なのですが、この手順で作ります。(このMavenの流れでCDI管理対象作れる方法ありましたっけ??)
f:id:kikutaro777:20140105233523j:plain

生成されたコードはJSF管理対象になっています。
f:id:kikutaro777:20140105233752j:plain

@ManagedBeanを@Namedに書き換え、@ViewScopedは一旦パッケージ名を消して、javax.faces.view.ViewScopedをインポートします。ここが本当にややこしいよなぁ…。
f:id:kikutaro777:20140105233915j:plain

これで管理対象ビーンクラスが生成されます。

今回のサンプルでは、Mavenの依存性でLombokを入れておきます。

<dependency> 
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.12.2</version>
</dependency>

コードは以下のように、IDと名前だけ受け取るようにして、それをInsertする処理を1つだけ書いておきます。生成したEJBもInjectしておきます。

ここでやっと初めてコード書いてます(^^;

package jp.co.hoge.azuredbsample;

import java.io.Serializable;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import lombok.Getter;
import lombok.Setter;


/**
 *
 * @author kikuta
 */
@Named
@ViewScoped
public class EmployeeBean implements Serializable{

    @Inject
    private EmployeeFacade empDbFacade;
    
    @Getter @Setter
    private int id;
    
    @Getter @Setter
    private String name;
    
    public void insertNewEmployee(){
        Employee emp = new Employee(id, name);
        empDbFacade.create(emp);
    }
}
JSFでページを生成する。

最後は画面を作るだけです。デフォルトである「index.html」を利用してもよいですが、ここでは一旦削除して、Webページを右クリックして「新規」から以下を選びました。
f:id:kikutaro777:20140105234505j:plain

ファイル名はindexにしておいて終わります。
f:id:kikutaro777:20140105234529j:plain

生成されたindex.xhtmlは以下で定義しました。

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:inputText value="#{employeeBean.id}" />
            <h:inputText value="#{employeeBean.name}" />
            <h:commandButton value="Insert" action="#{employeeBean.insertNewEmployee()}" /> 
        </h:form>
    </h:body>
</html>
実行してみる

実行すると…
f:id:kikutaro777:20140105234624j:plain

こんなエラーが最初でたのですが、これは昨日作ったAzureのSQLデータベースとNetBeansの接続設定で、JDBCのURLのみを入れたためでした。
今回作成したNetBeansプロジェクト内の「その他ソース」->「setup」に「glassfish-resources.xml」がありますが、そこの設定を確認して適切に修正してください。valueが空白になっている値などがあったので、私の場合は昨日作ったUser名をvalueに設定しました。(実際には、URLで全て書いてるからこのプロパティごといらない?のかも)

これらを修正して、再度実行するとページが表示されました。画面がちょっと手抜きすぎますが(^^;

f:id:kikutaro777:20140105234952j:plain

IDが1のEmployeeは昨日手動でinsertしてるので、2として名前も入れてボタンを押します。するとinsertされます。

せっかくなのでAzureから確認してみます。

f:id:kikutaro777:20140105235102j:plain

おお、追加されてる!

ということで、これまたあっさりですね。うーん楽だ。

にほんブログ村 IT技術ブログへ
にほんブログ村
にほんブログ村 IT技術ブログ Javaへ
にほんブログ村