Kohei Nozaki's blog 

NativeQueryでconstructor-resultを使ってSELECTしてみる


Posted on Sunday Jan 26, 2014 at 02:35PM in Technology


NativeQueryで複数のエンティティを1度にSELECTしてみるの続きです。JPA2.1のNativeQueryの新機能constructor-resultを使ってSELECTしてみます。

constructor-resultはJPQLのNEW句のようなものです。エンティティを拾ってくるクエリでなく、レポート的なのを出すクエリの場合とかに使うとキャストが要らなくなるので便利っぽいです。

環境

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

何をするか

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

今回はDeptエンティティのdeptNameと、EmployeeのカウントをSELECTしてみます。

準備

配置図

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

  • orm.xmlにNamedNativeQuery “findDeptNameAndEmployeeCount” とResultSetMapping “deptNameAndEmployeeCountResult” を追加します
  • テストクラスConstructorResultSelectTestを作ります
  • DTOクラスDeptNameAndEmployeeCountを作ります
  • テストデータは前回のを流用します

DeptNameAndEmployeeCount.java

package org.arquillian.example;

public class DeptNameAndEmployeeCount {

    private String deptName;
    private Number employeeCount;

    public DeptNameAndEmployeeCount(String deptName, Number employeeCount) {
        super();
        this.deptName = deptName;
        this.employeeCount = employeeCount;
    }

    @Override
    public String toString() {
        return "DeptNameAndEmployeeCount [deptName=" + deptName + ", employeeCount=" + employeeCount + "]";
    }
}

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="findDeptNameAndEmployeeCount" result-set-mapping="deptNameAndEmployeeCountResult">
        <query><![CDATA[
            SELECT
                dep.deptName,
                COUNT(dep.deptName) AS employeeCount
            FROM
                Dept AS dep,
                Employee AS emp
            WHERE
                dep.id = emp.dept_id
            GROUP BY
                dep.deptName
        ]]></query>
    </named-native-query>

    <sql-result-set-mapping name="deptNameAndEmployeeCountResult">
        <constructor-result target-class="org.arquillian.example.DeptNameAndEmployeeCount">
            <column name="deptName" class="java.lang.String" />
            <column name="employeeCount" class="java.lang.Number" />
        </constructor-result>
    </sql-result-set-mapping>   

</entity-mappings>
  • column要素のclass属性は省略可みたい(少なくともHibernate4.3.1では動いた)
  • ただcolumn要素自体は省略不可っぽい

ConstructorResultSelectTest.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 ConstructorResultSelectTest {
    @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("findDeptNameAndEmployeeCount").getResultList());
    }

    protected void dumpEntityList(List<?> list){
        for(Object o : list){
            System.out.println(o);
        }
    }
}

実行結果

14:42:45,278 INFO  [stdout] (pool-2-thread-17) Hibernate: SELECT dep.deptName, COUNT(dep.deptName) AS employeeCount FROM Dept AS dep, Employee AS emp WHERE dep.id = emp.dept_id GROUP BY dep.deptName
14:42:45,281 INFO  [stdout] (pool-2-thread-17) DeptNameAndEmployeeCount [deptName=Legal, employeeCount=1]
14:42:45,282 INFO  [stdout] (pool-2-thread-17) DeptNameAndEmployeeCount [deptName=Sales, employeeCount=2]

キャストが要らないのがいいですね。

続き

NativeQueryで複数のエンティティを同時にSELECTしつつconstructor-resultを使ってみる

参考文献

  1. JavaEE 7 JPA 2.1の新機能 ネイティブクエリの改善 - しんさんの出張所 はてな編



No one has commented yet.

Leave a Comment

HTML Syntax: NOT allowed