NativeQueryで複数のエンティティを同時にSELECTしつつconstructor-resultを使ってみる
TweetPosted on Sunday Jan 26, 2014 at 03:18PM in Technology
NativeQueryでconstructor-resultを使ってSELECTしてみるの続きです。2つのエンティティ分のデータを取得するSELECT文から、エンティティ2つに加えて、DTOクラスのオブジェクト1つをconstructor-resultを使って取得してみます。だからなんだって感じもしますが。
環境
- Hibernate 4.3.0.Final
- WildFly8.0.0.CR1
- Oracle JDK7u51
- postgresql-9.3-1100.jdbc41.jar
- PostgreSQL 9.2.4
何をするか
エンティティと関連の構造は前回と同じです。
今回は以下をSELECT一回で取ってきます
- Employeeエンティティ
- Deptエンティティ
- deptName, firstName, lastNameをフィールドにもつDTO
準備
配置図

図中で選択されている資源を作成または編集します。
- orm.xmlにNamedNativeQuery “findVarious” とResultSetMapping “variousResult” を追加します
- テストクラスVariousResultSelectTestを作ります
- DTOクラスEmployeeProfileを作ります
- テストデータは前回のを流用します
EmployeeProfile.java
package org.arquillian.example;
public class EmployeeProfile {
private String deptName;
private String firstName;
private String lastName;
public EmployeeProfile(String deptName, String firstName, String lastName) {
super();
this.deptName = deptName;
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return "EmployeeProfile [deptName=" + deptName + ", 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="findVarious" result-set-mapping="variousResult">
<query><![CDATA[
SELECT
emp.id,
emp.firstName,
emp.lastName,
emp.dept_id,
dep.deptName
FROM
Employee AS emp,
Dept AS dep
WHERE
emp.dept_id = dep.id
]]></query>
</named-native-query>
<sql-result-set-mapping name="variousResult">
<entity-result entity-class="org.arquillian.example.Employee"/>
<entity-result entity-class="org.arquillian.example.Dept">
<field-result name="id" column="dept_id"/>
<field-result name="deptName" column="deptName"/>
</entity-result>
<constructor-result target-class="org.arquillian.example.EmployeeProfile">
<column name="deptName" class="java.lang.String" />
<column name="firstName" class="java.lang.String" />
<column name="lastName" class="java.lang.String" />
</constructor-result>
</sql-result-set-mapping>
</entity-mappings>
VariousResultSelectTest.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 VariousSelectTest {
@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("findVarious").getResultList());
}
protected void dumpEntityList(List<?> list){
for(Object o : list){
Object[] array = (Object[])o;
Employee employee = (Employee) array[0];
Dept dept = (Dept) array[1];
EmployeeProfile employeeProfile = (EmployeeProfile) array[2];
System.out.println("");
System.out.println("---------------------------------");
System.out.printf("id=%d, firstName=%s, lastName=%s, dept_id=%s, deptName=%s, employee=%s, dept=%s\n",
employee.getId(), employee.getFirstName(), employee.getLastName(),
dept.getId(), dept.getDeptName(),
em.contains(employee), em.contains(dept));
System.out.println(employeeProfile);
System.out.println("---------------------------------");
}
}
}
実行結果
15:15:55,405 INFO [stdout] (pool-2-thread-20) Hibernate: SELECT emp.id, emp.firstName, emp.lastName, emp.dept_id, dep.deptName FROM Employee AS emp, Dept AS dep WHERE emp.dept_id = dep.id 15:15:55,409 INFO [stdout] (pool-2-thread-20) 15:15:55,409 INFO [stdout] (pool-2-thread-20) --------------------------------- 15:15:55,409 INFO [stdout] (pool-2-thread-20) id=-1, firstName=Taro, lastName=Yamada, dept_id=-1, deptName=Sales, employee=true, dept=true 15:15:55,409 INFO [stdout] (pool-2-thread-20) EmployeeProfile [deptName=Sales, firstName=Taro, lastName=Yamada] 15:15:55,410 INFO [stdout] (pool-2-thread-20) --------------------------------- 15:15:55,410 INFO [stdout] (pool-2-thread-20) 15:15:55,410 INFO [stdout] (pool-2-thread-20) --------------------------------- 15:15:55,410 INFO [stdout] (pool-2-thread-20) id=-2, firstName=Jiro, lastName=Suzuki, dept_id=-1, deptName=Sales, employee=true, dept=true 15:15:55,410 INFO [stdout] (pool-2-thread-20) EmployeeProfile [deptName=Sales, firstName=Jiro, lastName=Suzuki] 15:15:55,410 INFO [stdout] (pool-2-thread-20) --------------------------------- 15:15:55,410 INFO [stdout] (pool-2-thread-20) 15:15:55,410 INFO [stdout] (pool-2-thread-20) --------------------------------- 15:15:55,411 INFO [stdout] (pool-2-thread-20) id=-3, firstName=Saburo, lastName=Tanaka, dept_id=-2, deptName=Legal, employee=true, dept=true 15:15:55,411 INFO [stdout] (pool-2-thread-20) EmployeeProfile [deptName=Legal, firstName=Saburo, lastName=Tanaka] 15:15:55,411 INFO [stdout] (pool-2-thread-20) ---------------------------------
あんまり意味ないかもしれませんが柔軟に出来ているなぁと。
備考
今回使ったsql-result-set-mappingに、さらに以下を追加してもXML的には不正ではないのですが、
<column-result name="deptName"/>
動かそうとすると何故か以下のような例外が出て動きません。まああまり困ることもなさそうですが。
15:26:45,836 SEVERE [org.jboss.arquillian.protocol.jmx.JMXTestRunner] (pool-2-thread-21) Failed: org.arquillian.example.VariousSelectTest.select: javax.persistence.PersistenceException: org.hibernate.loader.custom.NonUniqueDiscoveredSqlAliasException: Encountered a duplicated sql alias [deptName] during auto-discovery of a native-sql query
参考文献
Tags: jpa
NativeQueryでconstructor-resultを使ってSELECTしてみる
TweetPosted 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を使ってみる
参考文献
Tags: jpa
NativeQueryで複数のエンティティを1度にSELECTしてみる
TweetPosted on Sunday Jan 26, 2014 at 01:51PM in Technology
NativeQueryでエンティティとスカラ値を同時にSELECTしてみるの続きです。今回は2つのエンティティを1回のSELECT文で取得してみます。
環境
- Hibernate 4.3.0.Final
- WildFly8.0.0.CR1
- Oracle JDK7u51
- postgresql-9.3-1100.jdbc41.jar
- PostgreSQL 9.2.4
何をするか
エンティティと関連の構造は前回と同じです。
今回はEmployeeエンティティとDeptエンティティを同時にSELECTしてみます。
準備
配置図

図中で選択されている資源を作成または編集します。
- orm.xmlにNamedNativeQuery “findEmployeeWithDeptAtOnce” とResultSetMapping “employeeWithDeptResult” を追加します
- MultiEntitySelectTestを作ります
- テストデータは前回のを流用します
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="findEmployeeWithDeptAtOnce" result-set-mapping="employeeWithDeptResult">
<query><![CDATA[
SELECT
emp.id,
emp.firstName,
emp.lastName,
emp.dept_id,
dep.deptName
FROM
Employee AS emp,
Dept AS dep
WHERE
emp.dept_id = dep.id
]]></query>
</named-native-query>
<sql-result-set-mapping name="employeeWithDeptResult">
<entity-result entity-class="org.arquillian.example.Employee"/>
<entity-result entity-class="org.arquillian.example.Dept">
<field-result name="id" column="dept_id"/>
<field-result name="deptName" column="deptName"/>
</entity-result>
</sql-result-set-mapping>
</entity-mappings>
ResultSetMappingのfield-result要素で、Deptエンティティのidフィールドの値はクエリ中のdept_idだよ、と教えてやります
MultiEntitySelectTest.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 MultiEntitySelectTest {
@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("findEmployeeWithDeptAtOnce").getResultList());
}
protected void dumpEntityList(List<?> list){
for(Object o : list){
Object[] array = (Object[])o;
Employee employee = (Employee) array[0];
Dept dept = (Dept) array[1];
System.out.println("");
System.out.printf("id=%d, firstName=%s, lastName=%s, dept_id=%s, deptName=%s, employee=%s, dept=%s\n",
employee.getId(), employee.getFirstName(), employee.getLastName(),
dept.getId(), dept.getDeptName(),
em.contains(employee), em.contains(dept));
System.out.println("---------------------------------");
System.out.println("checking relation references");
System.out.println(dept.getEmployees());
System.out.println(employee.getDept());
System.out.println("---------------------------------");
}
}
}
実行結果
4,688 INFO [stdout] (pool-2-thread-15) Hibernate: SELECT emp.id, emp.firstName, emp.lastName, emp.dept_id, dep.deptName FROM Employee AS emp, Dept AS dep WHERE emp.dept_id = dep.id 14:11:54,691 INFO [stdout] (pool-2-thread-15) 14:11:54,692 INFO [stdout] (pool-2-thread-15) id=-1, firstName=Taro, lastName=Yamada, dept_id=-1, deptName=Sales, employee=true, dept=true 14:11:54,692 INFO [stdout] (pool-2-thread-15) --------------------------------- 14:11:54,692 INFO [stdout] (pool-2-thread-15) checking relation references 14:11:54,693 INFO [stdout] (pool-2-thread-15) [Employee [id=-1, firstName=Taro, lastName=Yamada, dept=Dept [id=-1, deptName=Sales, employees=2]], Employee [id=-2, firstName=Jiro, lastName=Suzuki, dept=Dept [id=-1, deptName=Sales, employees=2]]] 14:11:54,693 INFO [stdout] (pool-2-thread-15) Dept [id=-1, deptName=Sales, employees=2] 14:11:54,693 INFO [stdout] (pool-2-thread-15) --------------------------------- 14:11:54,693 INFO [stdout] (pool-2-thread-15) 14:11:54,693 INFO [stdout] (pool-2-thread-15) id=-2, firstName=Jiro, lastName=Suzuki, dept_id=-1, deptName=Sales, employee=true, dept=true 14:11:54,694 INFO [stdout] (pool-2-thread-15) --------------------------------- 14:11:54,694 INFO [stdout] (pool-2-thread-15) checking relation references 14:11:54,694 INFO [stdout] (pool-2-thread-15) [Employee [id=-1, firstName=Taro, lastName=Yamada, dept=Dept [id=-1, deptName=Sales, employees=2]], Employee [id=-2, firstName=Jiro, lastName=Suzuki, dept=Dept [id=-1, deptName=Sales, employees=2]]] 14:11:54,694 INFO [stdout] (pool-2-thread-15) Dept [id=-1, deptName=Sales, employees=2] 14:11:54,694 INFO [stdout] (pool-2-thread-15) --------------------------------- 14:11:54,694 INFO [stdout] (pool-2-thread-15) 14:11:54,694 INFO [stdout] (pool-2-thread-15) id=-3, firstName=Saburo, lastName=Tanaka, dept_id=-2, deptName=Legal, employee=true, dept=true 14:11:54,695 INFO [stdout] (pool-2-thread-15) --------------------------------- 14:11:54,695 INFO [stdout] (pool-2-thread-15) checking relation references 14:11:54,695 INFO [stdout] (pool-2-thread-15) [Employee [id=-3, firstName=Saburo, lastName=Tanaka, dept=Dept [id=-2, deptName=Legal, employees=1]]] 14:11:54,696 INFO [stdout] (pool-2-thread-15) Dept [id=-2, deptName=Legal, employees=1] 14:11:54,696 INFO [stdout] (pool-2-thread-15) ---------------------------------
SELECT文が1回になったので状況によってはクエリを軽くするのに寄与するかもしれません。また、こういう取り方をしてもお互いの参照はちゃんと設定されていて、実際にアクセスしてもよけいなクエリを実行する事もないようです。賢い。
と思ったのだがorg.hibernate.SQLのログレベルを上げて見ると、別途SELECT文が実行されている気配。persistence.xmlにhibernate.show_sqlを設定しただけでは全てのSQLは出ないという事なのか、あるいは見落としたのか…。PostgreSQL側にSQLをログ出力させる設定を加えて見てみるとこんなのが出ている。
Feb 2 11:35:46 kyle-no-MacBook.local postgres[1120]: [877-1] 2014-02-02 11:35:46 JST LOG: execute <unnamed>: SELECT emp.id, emp.firstName, emp.lastName, emp.dept_id, dep.deptName FROM Employee AS emp, Dept AS dep WHERE emp.dept_id = dep.id Feb 2 11:35:46 kyle-no-MacBook.local postgres[1120]: [878-1] 2014-02-02 11:35:46 JST LOG: execute <unnamed>: select employees0_.dept_id as dept_id4_0_0_, employees0_.id as id1_1_0_, employees0_.id as id1_1_1_, employees0_.dept_id as dept_id4_1_1_, employees0_.firstName as firstNam2_1_1_, employees0_.lastName as lastName3_1_1_ from Employee employees0_ where employees0_.dept_id=$1 Feb 2 11:35:46 kyle-no-MacBook.local postgres[1120]: [878-2] 2014-02-02 11:35:46 JST DETAIL: parameters: $1 = '-1' Feb 2 11:35:46 kyle-no-MacBook.local postgres[1120]: [879-1] 2014-02-02 11:35:46 JST LOG: execute <unnamed>: select employees0_.dept_id as dept_id4_0_0_, employees0_.id as id1_1_0_, employees0_.id as id1_1_1_, employees0_.dept_id as dept_id4_1_1_, employees0_.firstName as firstNam2_1_1_, employees0_.lastName as lastName3_1_1_ from Employee employees0_ where employees0_.dept_id=$1 Feb 2 11:35:46 kyle-no-MacBook.local postgres[1120]: [879-2] 2014-02-02 11:35:46 JST DETAIL: parameters: $1 = '-2'
続き
NativeQueryでconstructor-resultを使ってSELECTしてみる
参考文献
Tags: jpa
NativeQueryでエンティティとスカラ値を同時にSELECTしてみる
TweetPosted 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してみる
参考文献
Tags: jpa
NativeQueryで関連持ちエンティティをSELECTしてみる
TweetPosted 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してみる
参考文献
Tags: jpa
