Kohei Nozaki's blog 

Entries tagged [jpa]

NativeQueryでエンティティとスカラ値を同時にSELECTしてみる


Posted on Sunday Jan 26, 2014 at 01:06PM in Technology


NativeQueryで関連持ちエンティティをSELECTしてみるの続きです。今回はエンティティとCOUNT関数の結果を同時にSELECTしてみます。

環境

  • Hibernate 4.3.0.Final
  • WildFly8.0.0.CR1
  • Oracle JDK7u51
  • postgresql-9.3-1100.jdbc41.jar
  • PostgreSQL 9.2.4

何をするか

エンティティと関連の構造は前回と同じです。

今回は部署エンティティと一緒に社員数のカウントを取ってみます。

準備

配置図

図中で選択されている資源を作成または編集します。

  • orm.xmlにNamedNativeQuery “findDeptWithCount” とResultSetMapping “deptWithCountResult” を追加します
  • EntityWithScalarSelectTestを作ります
  • テストデータは前回のを流用します

資源

orm.xml

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd">

    <named-native-query name="findEmployee" result-set-mapping="employeeResult">
        <query><![CDATA[
            SELECT
                emp.id,
                emp.firstName,
                emp.lastName
            FROM
                Employee AS emp
        ]]></query>
    </named-native-query>

    <named-native-query name="findEmployeeWithDept" result-set-mapping="employeeResult">
        <query><![CDATA[
            SELECT
                emp.id,
                emp.firstName,
                emp.lastName,
                emp.dept_id
            FROM
                Employee AS emp
        ]]></query>
    </named-native-query>

    <named-native-query name="findDeptWithCount" result-set-mapping="deptWithCountResult">
        <query><![CDATA[
            SELECT
                dep.id,
                dep.deptName,
                COUNT(dep.id) AS employeeCount
            FROM
                Dept AS dep,
                Employee AS emp
            WHERE
                dep.id = emp.dept_id
            GROUP BY
                dep.id
        ]]></query>
    </named-native-query>

    <sql-result-set-mapping name="employeeResult">
        <entity-result entity-class="org.arquillian.example.Employee"/>
    </sql-result-set-mapping>       

    <sql-result-set-mapping name="deptWithCountResult">
        <entity-result entity-class="org.arquillian.example.Dept"/>
        <column-result name="employeeCount"/>
    </sql-result-set-mapping>       
</entity-mappings>

EntityWithScalarSelectTest.java

この環境だとCOUNT()の結果はBigIntegerになるようですが、別の環境ではLongだったりいろいろかもしれません

package org.arquillian.example;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.persistence.UsingDataSet;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class EntityWithScalarSelectTest {
    @Deployment
    public static Archive<?> createDeployment() {
        Archive<?> a = ShrinkWrap.create(WebArchive.class, "test.war")
            .addPackage(Employee.class.getPackage())
            .addAsResource("test-persistence.xml", "META-INF/persistence.xml")
            .addAsResource("META-INF/orm.xml", "META-INF/orm.xml")
            .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
            .addAsWebInfResource("jbossas-ds.xml");
        return a;
    }

    @PersistenceContext
    EntityManager em;

    @Test
    @Transactional
    @UsingDataSet({"datasets/relativeSelect/dept.yml", "datasets/relativeSelect/employees.yml"})
    public void select() throws Exception {
        dumpEntityList(em.createNamedQuery("findDeptWithCount").getResultList());
    }

    protected void dumpEntityList(List<?> list){
        for(Object o : list){
            Object[] array = (Object[])o;
            Dept dept = (Dept) array[0];
            Number employees = (Number) array[1];
            System.out.printf("id=%d, deptName=%s, employees=%s, contains=%s\n",
                    dept.getId(), dept.getDeptName(), employees, em.contains(dept));
        }
    }
}

実行結果

13:05:40,545 INFO  [stdout] (pool-2-thread-9) Hibernate: SELECT dep.id, dep.deptName, COUNT(dep.id) AS employeeCount FROM Dept AS dep, Employee AS emp WHERE dep.id = emp.dept_id GROUP BY dep.id
13:05:40,548 INFO  [stdout] (pool-2-thread-9) id=-1, deptName=Sales, employees=2, contains=true
13:05:40,548 INFO  [stdout] (pool-2-thread-9) id=-2, deptName=Legal, employees=1, contains=true

この取り方でもちゃんとmanagedで返ってきているのがすごい。

続き

NativeQueryで複数のエンティティを1度にSELECTしてみる

参考文献

  1. Pro JPA 2


NativeQueryで関連持ちエンティティをSELECTしてみる


Posted on Sunday Jan 26, 2014 at 11:05AM in Technology


NativeQueryでSELECTしてみるの続きです。前回は関連の無いエンティティでしたが今回は関連のあるエンティティをSELECTしてみます

環境

  • Hibernate 4.3.0.Final
  • WildFly8.0.0.CR1
  • Oracle JDK7u51
  • postgresql-9.3-1100.jdbc41.jar
  • PostgreSQL 9.2.4

用意するエンティティ

Deptエンティティを追加してこういう感じにします。

Hibernateが作るテーブルはこういう感じになります

jpaprac=# \d dept
              Table "public.dept"
  Column  |          Type          | Modifiers 
----------+------------------------+-----------
 id       | bigint                 | not null
 deptname | character varying(255) | 
Indexes:
    "dept_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "employee" CONSTRAINT "fk_1n3sqh4h9gtmwb1o1twpoi30l" FOREIGN KEY (dept_id) REFERENCES dept(id)

jpaprac=# \d employee
            Table "public.employee"
  Column   |          Type          | Modifiers 
-----------+------------------------+-----------
 id        | bigint                 | not null
 firstname | character varying(255) | 
 lastname  | character varying(255) | 
 dept_id   | bigint                 | 
Indexes:
    "employee_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "fk_1n3sqh4h9gtmwb1o1twpoi30l" FOREIGN KEY (dept_id) REFERENCES dept(id)

jpaprac=# 

準備

配置図

図中で選択されている資源を作成または編集します。Employee.javaは前回も使いましたが関連を追加するために若干変更します。orm.xmlには新たなクエリを追加します。

Dept.java

package org.arquillian.example;

import java.io.Serializable;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Dept implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column
    private String deptName;
    @OneToMany(mappedBy = "dept")
    private List<Employee> employees;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    public List<Employee> getEmployees() {
        return employees;
    }

    public void setEmployees(List<Employee> employees) {
        this.employees = employees;
    }

    @Override
    public String toString() {
        return "Dept [id=" + id + ", deptName=" + deptName + ", employees=" + employees.size() + "]";
    }

}

Employee.java

package org.arquillian.example;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

@Entity
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column
    private String firstName;
    @Column
    private String lastName;
    @ManyToOne
    @JoinColumn
    private Dept dept;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", dept=" + dept + "]";
    }

}

orm.xml

findEmployeeWithDeptを追加します

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd">

    <named-native-query name="findEmployee" result-set-mapping="employeeResult">
        <query><![CDATA[
            SELECT
                emp.id,
                emp.firstName,
                emp.lastName
            FROM
                Employee AS emp
        ]]></query>
    </named-native-query>

    <named-native-query name="findEmployeeWithDept" result-set-mapping="employeeResult">
        <query><![CDATA[
            SELECT
                emp.id,
                emp.firstName,
                emp.lastName,
                emp.dept_id
            FROM
                Employee AS emp
        ]]></query>
    </named-native-query>

    <sql-result-set-mapping name="employeeResult">
        <entity-result entity-class="org.arquillian.example.Employee"/>
    </sql-result-set-mapping>       
</entity-mappings>

RelativeSelectTest.java

package org.arquillian.example;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.persistence.UsingDataSet;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class RelativeSelectTest {
    @Deployment
    public static Archive<?> createDeployment() {
        Archive<?> a = ShrinkWrap.create(WebArchive.class, "test.war")
            .addPackage(Employee.class.getPackage())
            .addAsResource("test-persistence.xml", "META-INF/persistence.xml")
            .addAsResource("META-INF/orm.xml", "META-INF/orm.xml")
            .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
            .addAsWebInfResource("jbossas-ds.xml");
        return a;
    }

    @PersistenceContext
    EntityManager em;

    @Test
    @Transactional
    @UsingDataSet({"datasets/relativeSelect/dept.yml", "datasets/relativeSelect/employees.yml"})
    public void select() throws Exception {
        dumpEntityList(em.createNamedQuery("findEmployeeWithDept", Employee.class).getResultList());
    }

    protected void dumpEntityList(List<Employee> employees){
        for(Employee e : employees){
            String fmt = String.format("id=%s, firstName=%s, lastName=%s, deptName=%s",
                    e.getId(), e.getFirstName(), e.getLastName(), e.getDept().getDeptName());
            System.out.println(fmt);
        }
    }
}

dept.yml

dept:
  - id: -1
    deptName: Sales
  - id: -2
    deptName: Legal

employees.yml

employee:
  - id: -1
    firstname: Taro
    lastname: Yamada
    dept_id: -1
  - id: -2
    firstname: Jiro
    lastname: Suzuki
    dept_id: -1
  - id: -3
    firstname: Saburo
    lastname: Tanaka
    dept_id: -2

実行結果

11:48:22,373 INFO  [stdout] (pool-2-thread-4) Hibernate: SELECT emp.id, emp.firstName, emp.lastName, emp.dept_id FROM Employee AS emp
11:48:22,375 INFO  [stdout] (pool-2-thread-4) Hibernate: select dept0_.id as id1_0_0_, dept0_.deptName as deptName2_0_0_ from Dept dept0_ where dept0_.id=?
11:48:22,376 INFO  [stdout] (pool-2-thread-4) Hibernate: select dept0_.id as id1_0_0_, dept0_.deptName as deptName2_0_0_ from Dept dept0_ where dept0_.id=?
11:48:22,377 INFO  [stdout] (pool-2-thread-4) id=-1, firstName=Taro, lastName=Yamada, deptName=Sales
11:48:22,377 INFO  [stdout] (pool-2-thread-4) id=-2, firstName=Jiro, lastName=Suzuki, deptName=Sales
11:48:22,377 INFO  [stdout] (pool-2-thread-4) id=-3, firstName=Saburo, lastName=Tanaka, deptName=Legal

備考

NativeQueryではEmployeeテーブルのデータしか取ってきていないのですが、親レコードへの参照であるdept_idを拾ってきていれば、DeptテーブルのデータはJPA側で勝手に別途SELECT文を発行して取ってきてくれるようですね。

続き

NativeQueryでエンティティとスカラ値を同時にSELECTしてみる

参考文献

  1. Pro JPA 2


NativeQueryでSELECTしてみる


Posted on Saturday Jan 25, 2014 at 11:37PM in Technology


ここではNativeQueryを使うまでもない簡単な単独のエンティティをSELECTしてみます。複雑なのは後々。

環境

  • Hibernate 4.3.0.Final
  • WildFly8.0.0.CR1
  • Oracle JDK7u51
  • postgresql-9.3-1100.jdbc41.jar
  • PostgreSQL 9.2.4

前提条件

関連を持たない単独のエンティティをSELECTしてみる

エンティティクラス等を配置

配置図

図で選択されている資源を作る必要が有ります

Employee.java

package org.arquillian.example;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column
    private String firstName;
    @Column
    private String lastName;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + "]";
    }

}

orm.xml

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd">

    <named-native-query name="findEmployee" result-set-mapping="employeeResult">
        <query><![CDATA[
            SELECT
                emp.id,
                emp.firstName,
                emp.lastName
            FROM
                Employee AS emp
        ]]></query>
    </named-native-query>

    <sql-result-set-mapping name="employeeResult">
        <entity-result entity-class="org.arquillian.example.Employee"/>
    </sql-result-set-mapping>       
</entity-mappings>

IndependentSelectTest.java

package org.arquillian.example;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.persistence.UsingDataSet;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class IndependentSelectTest {
    @Deployment
    public static Archive<?> createDeployment() {
        Archive<?> a = ShrinkWrap.create(WebArchive.class, "test.war")
            .addPackage(Employee.class.getPackage())
            .addAsResource("test-persistence.xml", "META-INF/persistence.xml")
            .addAsResource("META-INF/orm.xml", "META-INF/orm.xml")
            .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
            .addAsWebInfResource("jbossas-ds.xml");
        return a;
    }

    @PersistenceContext
    EntityManager em;

    @Test
    @Transactional
    @UsingDataSet("datasets/independentSelect/employees.yml")
    public void select() throws Exception {
        dumpEntityList(em.createNamedQuery("findEmployee", Employee.class).getResultList());
    }

    protected void dumpEntityList(List<?> list){
        for(Object o : list){
            System.out.println(o + ", contains=" + em.contains(o));
        }
    }
}

employees.yml

employee:
  - id: -1
    firstname: Taro
    lastname: Yamada
  - id: -2
    firstname: Jiro
    lastname: Suzuki
  - id: -3
    firstname: Saburo
    lastname: Tanaka

jbossas-ds.xml

ローカルで動いているPostgreSQLにjpapracというDBを作っておきます

<?xml version="1.0" encoding="UTF-8"?>
<datasources xmlns="http://www.jboss.org/ironjacamar/schema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.jboss.org/ironjacamar/schema
        http://docs.jboss.org/ironjacamar/schema/datasources_1_0.xsd">
    <datasource enabled="true" jndi-name="jdbc/arquillian" pool-name="ArquillianPostgresPool">
        <connection-url>jdbc:postgresql://localhost:5432/jpaprac</connection-url>
        <driver>postgresql-9.3-1100.jdbc41.jar</driver>
        <security>
            <user-name>postgres</user-name>
            <password>***</password>
        </security>
    </datasource>
</datasources>

test-persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="test">
        <jta-data-source>jdbc/arquillian</jta-data-source>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="update" />
            <property name="hibernate.show_sql" value="true" />
        </properties>
    </persistence-unit>
</persistence>

実行結果

コンソールの出力を関係ありそうなところだけ抜粋

10:23:35,245 INFO  [stdout] (pool-2-thread-12) Hibernate: SELECT emp.id, emp.firstName, emp.lastName FROM Employee AS emp
10:23:35,248 INFO  [stdout] (pool-2-thread-12) Employee [id=-1, firstName=Taro, lastName=Yamada], contains=true
10:23:35,248 INFO  [stdout] (pool-2-thread-12) Employee [id=-2, firstName=Jiro, lastName=Suzuki], contains=true
10:23:35,248 INFO  [stdout] (pool-2-thread-12) Employee [id=-3, firstName=Saburo, lastName=Tanaka], contains=true

普通に出てますね。NamedNativeQueryにしてResultSetMappingまで定義するとManagedになる。

備考

@UsingDataSetアノテーションをテストメソッドに付けると、Arquillian Persistence Extensionがデータ投入してくれますが、投入したデータはテスト後にTRUNCATEされてしまうので、テスト後にテーブルで確認しようとしても見えないようです[3]。これを抑制する設定は現状存在しないらしい。まあデバッガか何かで止めれば見られるだろうけど。

arquillian.xmlにこういう内容を書けばTRUNCATEがテスト実行前に走るようになるので、テスト実行後のデータを簡単に確認できます

<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://jboss.org/schema/arquillian
        http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
    <extension qualifier="persistence">
        <property name="defaultCleanupPhase">BEFORE</property>
    </extension>
</arquillian>

HibernateのSQLログは、persistence.xmlのproperties要素内に以下のようなのを追記すると複数行使って見やすくインデントとかしてくれる

<property name="hibernate.format_sql" value="true"/>

続き

NativeQueryで関連持ちエンティティをSELECTしてみる

参考文献

  1. Hibernate - orm.xml - night, night, plane, plane, utf-8, utf-8
  2. Single Table ResultSet Mapping : ResultSet Mapping « JPA « Java Tutorial
  3. Table truncate after @UsingDataSet | Community
  4. Pro JPA 2


JPQLをorm.xmlに書いてみる


Posted on Tuesday Jan 21, 2014 at 08:04AM in Technology


何をするの?

JPQLをorm.xmlに書いて動かしてみます。

環境

  • WildFly8.0.0CR1
  • Eclipse Kepler SR1
  • PostgreSQL 9.2.4
  • Oracle JDK7u51

準備

これの続きです。

orm.xmlに書いたクエリを動かしてみる

orm.xmlを作る

src/main/java/META-INFの下に作ります。右クリックメニューからNew→JPA ORM Mapping File

場所を確かめてFinish

出来たら開きましょう

クエリを書く

named-queryを書いてみます。エンティティに直書きしてたのとほとんど同じです。違うのはidで逆順ソートかけてるとこだけです

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="2.1"
	xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd">
	<named-query name="findHogeEntityAllDesc">
                <query>
                 SELECT
                     e
                 FROM
                     HogeEntity e
                 ORDER BY
                     e.id DESC
                </query>
	</named-query>
</entity-mappings>

この書き方だと < とか > をエスケープしないといけないのが面倒くさいですが、こういう感じで書くとそのまま書けます。

Daoにメソッドを追加

findAllDesc()を追加します

package com.example.dao;

import java.util.Date;
import java.util.List;

import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

import com.example.entity.HogeEntity;

@Named
public class HogeEntityDao {

	@PersistenceContext
	private EntityManager em;

	@Transactional
	public void save() {
		HogeEntity e = new HogeEntity();
		e.setHogeDate(new Date());
		em.persist(e);
	}

	public List<HogeEntity> findAll() {
		return em.createNamedQuery("findHogeEntityAll", HogeEntity.class).getResultList();
	}
	
	public List<HogeEntity> findAllDesc() {
		return em.createNamedQuery("findHogeEntityAllDesc", HogeEntity.class).getResultList();
	}
}

サーブレットから呼び出すメソッドを変更

package com.example.servlet;

import java.io.IOException;
import java.io.Writer;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.example.dao.HogeEntityDao;
import com.example.entity.HogeEntity;

@WebServlet("/HogeServlet")
public class HogeServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Inject
	HogeEntityDao dao;

	public HogeServlet() {
		super();
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		dao.save();
		try (Writer w = response.getWriter()) {
//			for (HogeEntity e : dao.findAll()) {
			for (HogeEntity e : dao.findAllDesc()) {
				w.write("id=" + e.getId() + ", hogeDate=" + e.getHogeDate() + "\n");
			}
		}
	}
}

動かしてみる

逆順になりました。改行とかも自由なので、やっぱりクエリはエンティティのアノテーション内に書くよりorm.xmlに書いた方が良い気がします。


動かしてみる


Posted on Tuesday Jan 21, 2014 at 07:57AM in Technology


何をするの?

開発環境を設定して、JPAを叩くCDI管理Beanと、それを叩くサーブレットを作って動かしてみます。

環境

  • WildFly8.0.0CR1
  • Eclipse Kepler SR1
  • PostgreSQL 9.2.4
  • Oracle JDK7u51

準備

プロジェクトを作る

  1. まずこの要領で空っぽのプロジェクトjpahelloを作ります
  2. Project Facetsを開き、CDIとJPAにチェックを入れてOKを押します

  3. 何故かbeans.xmlが出来ないので作ります。web-resourcesを右クリックしてNew→File beans.xmlを選択

  4. フォルダを「jpahello/src/main/webapp/WEB-INF」にしてFinish

  5. persistence.xmlとbeans.xmlができた事を確認します。

データベース作成とデータソース定義

このへんを参考にすると良いかも

persistence.xmlの設定をする

Persistence provider

先ほど作ったpersistence.xmlをダブルクリックして開いてみましょう

Persistence providerを設定します。WildFlyにはHibernateが入っているので

org.hibernate.ejb.HibernatePersistence
になります。

Connection

次はデータソースの設定です。画面下部のConnectionタブを押して画面を切り替えます

JTA data sourceにAPサーバに登録してあるデータソースのJNDI名を入力します

Schema Generation

次はDDL自動生成の設定です。画面下部のSchema Generationタブを押して画面を切り替えます

Database actionをDrop and createにしておきます。こうするとデプロイの度にDROPとCREATEが走ります。ちょっといじりたいだけなのでこれで。

Sourceを確認

画面下部のSourceタブを押して画面を切り替えるとpersistence.xmlが確認出来ます。こんな感じになってるのを確認したら保存しましょう。

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
	<persistence-unit name="jpahello">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<jta-data-source>java:jboss/jdbc/TestDS</jta-data-source>
		<properties>
			<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
		</properties>
	</persistence-unit>
</persistence>

エンティティを作る

  1. src/main/javaで右クリック→New→JPA Entityを選択

  2. Java packageとClass nameを埋めてFinish

  3. 内容はこんな感じで

  4. package com.example.entity;
    
    import java.io.Serializable;
    import java.util.Date;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.NamedQueries;
    import javax.persistence.NamedQuery;
    import javax.persistence.Temporal;
    import javax.persistence.TemporalType;
    
    @Entity
    @NamedQueries({ @NamedQuery(name = "findHogeEntityAll", query = "SELECT e FROM HogeEntity e") })
    public class HogeEntity implements Serializable {
    
    	private static final long serialVersionUID = 1L;
    
    	public HogeEntity() {
    		super();
    	}
    
    	@Id
    	@GeneratedValue(strategy = GenerationType.AUTO)
    	@Column(nullable = false)
    	private Long id;
    
    	@Column
    	@Temporal(TemporalType.TIMESTAMP)
    	private Date hogeDate;
    
    	public Long getId() {
    		return id;
    	}
    
    	public void setId(Long id) {
    		this.id = id;
    	}
    
    	public Date getHogeDate() {
    		return hogeDate;
    	}
    
    	public void setHogeDate(Date hogeDate) {
    		this.hogeDate = hogeDate;
    	}
    
    }
    

Dao的なクラスを作る

package com.example.dao;

import java.util.Date;
import java.util.List;

import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

import com.example.entity.HogeEntity;

@Named
public class HogeEntityDao {

	@PersistenceContext
	private EntityManager em;

	@Transactional
	public void save() {
		HogeEntity e = new HogeEntity();
		e.setHogeDate(new Date());
		em.persist(e);
	}

	public List<HogeEntity> findAll() {
		return em.createNamedQuery("findHogeEntityAll", HogeEntity.class).getResultList();
	}
}

サーブレットを作る

package com.example.servlet;

import java.io.IOException;
import java.io.Writer;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.example.dao.HogeEntityDao;
import com.example.entity.HogeEntity;

@WebServlet("/HogeServlet")
public class HogeServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Inject
	HogeEntityDao dao;

	public HogeServlet() {
		super();
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		dao.save();
		try (Writer w = response.getWriter()) {
			for (HogeEntity e : dao.findAll()) {
				w.write("id=" + e.getId() + ", hogeDate=" + e.getHogeDate() + "\n");
			}
		}
	}
}

作った資源を確認

5つ全部あるか確認しましょう。

デプロイしてみる

コンソールにこんなのが流れるはず

17:05:24,199 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-14) JBAS015876: Starting deployment of "jpahello.war" (runtime-name: "jpahello.war")
17:05:24,219 INFO  [org.jboss.as.jpa] (MSC service thread 1-3) JBAS011401: Read persistence.xml for jpahello
17:05:24,227 INFO  [org.jboss.as.jpa] (ServerService Thread Pool -- 264) JBAS011409: Starting Persistence Unit (phase 1 of 2) Service 'jpahello.war#jpahello'
17:05:24,227 INFO  [org.hibernate.jpa.internal.util.LogHelper] (ServerService Thread Pool -- 264) HHH000204: Processing PersistenceUnitInfo [
	name: jpahello
	...]
17:05:24,236 INFO  [org.jboss.weld.deployer] (MSC service thread 1-15) JBAS016002: Processing weld deployment jpahello.war
17:05:24,247 INFO  [org.jboss.weld.deployer] (MSC service thread 1-9) JBAS016005: Starting Services for CDI deployment: jpahello.war
17:05:24,249 INFO  [org.jboss.weld.deployer] (MSC service thread 1-16) JBAS016008: Starting weld service for deployment jpahello.war
17:05:24,252 INFO  [org.jboss.as.jpa] (ServerService Thread Pool -- 264) JBAS011409: Starting Persistence Unit (phase 2 of 2) Service 'jpahello.war#jpahello'
17:05:24,425 INFO  [org.hibernate.dialect.Dialect] (ServerService Thread Pool -- 264) HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL9Dialect
17:05:24,426 INFO  [org.hibernate.engine.jdbc.internal.LobCreatorBuilder] (ServerService Thread Pool -- 264) HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
17:05:24,428 INFO  [org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory] (ServerService Thread Pool -- 264) HHH000397: Using ASTQueryTranslatorFactory
17:05:24,437 INFO  [org.hibernate.dialect.Dialect] (ServerService Thread Pool -- 264) HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL9Dialect
17:05:24,443 WARN  [org.hibernate.jpa.internal.schemagen.GenerationTargetToDatabase] (ServerService Thread Pool -- 264) Unable to execute JPA schema generation drop command [drop table HogeEntity cascade]
17:05:24,443 WARN  [org.hibernate.jpa.internal.schemagen.GenerationTargetToDatabase] (ServerService Thread Pool -- 264) Unable to execute JPA schema generation drop command [drop sequence hibernate_sequence]
17:05:24,663 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-8) JBAS017534: Register web context: /jpahello
17:05:24,671 INFO  [org.jboss.as.server] (DeploymentScanner-threads - 1) JBAS018559: Deployed "jpahello.war" (runtime-name : "jpahello.war")

DBはどうなっているかというと

testdb=# \d
                 List of relations
 Schema |        Name        |   Type   |  Owner   
--------+--------------------+----------+----------
 public | hibernate_sequence | sequence | postgres
 public | hogeentity         | table    | postgres
(2 rows)

testdb=# \d hogeentity
             Table "public.hogeentity"
  Column  |            Type             | Modifiers 
----------+-----------------------------+-----------
 id       | bigint                      | not null
 hogedate | timestamp without time zone | 
Indexes:
    "hogeentity_pkey" PRIMARY KEY, btree (id)

testdb=# \d hibernate_sequence 
     Sequence "public.hibernate_sequence"
    Column     |  Type   |        Value        
---------------+---------+---------------------
 sequence_name | name    | hibernate_sequence
 last_value    | bigint  | 1
 start_value   | bigint  | 1
 increment_by  | bigint  | 1
 max_value     | bigint  | 9223372036854775807
 min_value     | bigint  | 1
 cache_value   | bigint  | 1
 log_cnt       | bigint  | 0
 is_cycled     | boolean | f
 is_called     | boolean | f

testdb=# 

自動で作ってくれると楽ですね

アクセスしてみる

リロードするたびに増えていきます。

テーブルも見てみましょう

testdb=# select * from hogeentity ;
 id |        hogedate         
----+-------------------------
  1 | 2014-01-20 17:09:57.694
  2 | 2014-01-20 17:09:58.091
  3 | 2014-01-20 17:09:59.28
  4 | 2014-01-20 17:09:59.475
  5 | 2014-01-20 17:09:59.629
  6 | 2014-01-20 17:09:59.812
  7 | 2014-01-20 17:09:59.966
  8 | 2014-01-20 17:10:03.878
(8 rows)

testdb=# 

ちゃんと動いているようですね

参考文献