0% found this document useful (0 votes)
12 views

JPA-join-Tutorial

Uploaded by

Mohamamd
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views

JPA-join-Tutorial

Uploaded by

Mohamamd
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 42

JPA Tutorial - JPA Query Select Two

Entities Example
The following JPQL selects from two entities.

List l = em.createQuery("SELECT d, m FROM Department d, Professor m WHERE d = m.depa


rtment").getResultList();

Example
The following code is from Professor.java.

package com.java.common;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
public class Professor {
@Id
private int id;
private String name;
private long salary;
@Temporal(TemporalType.DATE)
private Date startDate;

@OneToOne
private Address address;

@OneToMany(mappedBy="employee")
private Collection<Phone> phones = new ArrayList<Phone>();

@ManyToOne
private Department department;

@ManyToOne
private Professor manager;

@OneToMany(mappedBy="manager")
private Collection<Professor> directs = new ArrayList<Professor>();

@ManyToMany(mappedBy="employees")
private Collection<Project> projects = new ArrayList<Project>();

public int getId() {


return id;
}

public void setId(int empNo) {


this.id = empNo;
}

public String getName() {


return name;
}

public void setName(String name) {


this.name = name;
}

public long getSalary() {


return salary;
}

public void setSalary(long salary) {


this.salary = salary;
}

public Date getStartDate() {


return startDate;
}

public void setStartDate(Date startDate) {


this.startDate = startDate;
}

public Collection<Phone> getPhones() {


return phones;
}

public void addPhone(Phone phone) {


if (!getPhones().contains(phone)) {
getPhones().add(phone);
if (phone.getProfessor() != null) {
phone.getProfessor().getPhones().remove(phone);
}
phone.setProfessor(this);
}
}
public Department getDepartment() {
return department;
}

public void setDepartment(Department department) {


if (this.department != null) {
this.department.getProfessors().remove(this);
}
this.department = department;
this.department.getProfessors().add(this);
}

public Collection<Professor> getDirects() {


return directs;
}

public void addDirect(Professor employee) {


if (!getDirects().contains(employee)) {
getDirects().add(employee);
if (employee.getManager() != null) {
employee.getManager().getDirects().remove(employee);
}
employee.setManager(this);
}
}

public Professor getManager() {


return manager;
}

public void setManager(Professor manager) {


this.manager = manager;
}
public Collection<Project> getProjects() {
return projects;
}

public void addProject(Project project) {


if (!getProjects().contains(project)) {
getProjects().add(project);
}
if (!project.getProfessors().contains(this)) {
project.getProfessors().add(this);
}
}

public Address getAddress() {


return address;
}

public void setAddress(Address address) {


this.address = address;
}

public String toString() {


return "Professor " + getId() +
": name: " + getName() +
", salary: " + getSalary() +
", phones: " + getPhones() +
", managerNo: " + ((getManager() == null) ? null : getManager().getId(
)) +
", deptNo: " + ((getDepartment() == null) ? null : getDepartment().get
Id());
}

}
The following code is from Address.java.

package com.java.common;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Address {
@Id
private int id;
private String street;
private String city;
private String state;
private String zip;

public int getId() {


return id;
}

public void setId(int id) {


this.id = id;
}

public String getStreet() {


return street;
}

public void setStreet(String address) {


this.street = address;
}

public String getCity() {


return city;
}

public void setCity(String city) {


this.city = city;
}

public String getState() {


return state;
}

public void setState(String state) {


this.state = state;
}

public String getZip() {


return zip;
}

public void setZip(String zip) {


this.zip = zip;
}
public String toString() {
return "Address id: " + getId() +
", street: " + getStreet() +
", city: " + getCity() +
", state: " + getState() +
", zip: " + getZip();
}

The following code is from PersonDaoImpl.java.

package com.java.common;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.transaction.annotation.Transactional;

@Transactional
public class PersonDaoImpl {
public void test(){
prepareData();
List l = em.createQuery("SELECT d, m FROM Department d, Professor m WHERE d = m.
department").getResultList();
for(Object p:l){
System.out.println(p);
}
}

private void prepareData(){


Professor p = new Professor();
p.setId(0);
p.setName("TOM");

Department d = new Department();


d.setId(1);
d.setName("Design");

p.setDepartment(d);
d.getProfessors().add(p);

em.persist(p);
em.persist(d);
}
@PersistenceContext
private EntityManager em;
}

The following code is from Phone.java.

package com.java.common;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

@Entity
public class Phone {
@Id
private long id;
private String number;
private String type;
@ManyToOne
Professor employee;

public long getId() {


return id;
}

public void setId(long id) {


this.id = id;
}

public String getNumber() {


return number;
}

public void setNumber(String phoneNo) {


this.number = phoneNo;
}

public String getType() {


return type;
}

public void setType(String phoneType) {


this.type = phoneType;
}

public Professor getProfessor() {


return employee;
}

public void setProfessor(Professor employee) {


this.employee = employee;
}

public String toString() {


return "Phone id: " + getId() +
", no: " + getNumber() +
", type: " + getType();
}
}

The following code is from Project.java.

package com.java.common;

import java.util.ArrayList;
import java.util.Collection;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.ManyToMany;

@Entity
@Inheritance
public class Project {
@Id
protected int id;
protected String name;
@ManyToMany
protected Collection<Professor> employees = new ArrayList<Professor>();

public int getId() {


return id;
}

public void setId(int projectNo) {


this.id = projectNo;
}

public String getName() {


return name;
}

public void setName(String projectName) {


this.name = projectName;
}

public Collection<Professor> getProfessors() {


return employees;
}
public void addProfessor(Professor employee) {
if (!getProfessors().contains(employee)) {
getProfessors().add(employee);
}
if (!employee.getProjects().contains(this)) {
employee.getProjects().add(this);
}
}

public String toString() {


return getClass().getName().substring(getClass().getName().lastIndexOf('.')+1
) +
" no: " + getId() +
", name: " + getName();
}
}

The following code is from Department.java.

package com.java.common;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Department {
@Id
private int id;
private String name;
@OneToMany(mappedBy="department")
private Set<Professor> employees = new HashSet<Professor>();
public int getId() {
return id;
}

public void setId(int deptNo) {


this.id = deptNo;
}

public String getName() {


return name;
}

public void setName(String deptName) {


this.name = deptName;
}

public Set<Professor> getProfessors() {


return employees;
}

public String toString() {


return "Department no: " + getId() +
", name: " + getName();
}
}

The following is the database dump.

Table Name: ADDRESS


Table Name: DEPARTMENT
Row:
Column Name: ID,
Column Type: INTEGER:
Column Value: 1

Column Name: NAME,


Column Type: VARCHAR:
Column Value: Design

Table Name: PHONE

Table Name: PROFESSOR


Row:
Column Name: ID,
Column Type: INTEGER:
Column Value: 0

Column Name: NAME,


Column Type: VARCHAR:
Column Value: TOM

Column Name: SALARY,


Column Type: BIGINT:
Column Value: 0
Column Name: STARTDATE,
Column Type: DATE:
Column Value: null

Column Name: ADDRESS_ID,


Column Type: INTEGER:
Column Value: null

Column Name: DEPARTMENT_ID,


Column Type: INTEGER:
Column Value: 1

Column Name: MANAGER_ID,


Column Type: INTEGER:
Column Value: null

Table Name: PROJECT

Table Name: PROJECT_PROFESSOR

JPA Tutorial - JPA Query Join Fetch


Example
The following code shows the join fetch syntax for JPQL.

List l = em.createQuery(
"SELECT e FROM Professor e JOIN FETCH e.address")
.getResultList();

Example
The following code is from Department.java.

package com.java.common;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Department {
@Id
private int id;
private String name;
@OneToMany(mappedBy="department")
private Set<Professor> employees = new HashSet<Professor>();

public int getId() {


return id;
}

public void setId(int deptNo) {


this.id = deptNo;
}
public String getName() {
return name;
}

public void setName(String deptName) {


this.name = deptName;
}

public Set<Professor> getProfessors() {


return employees;
}

public String toString() {


return "Department no: " + getId() +
", name: " + getName();
}
}

The following code is from Project.java.

package com.java.common;

import java.util.ArrayList;
import java.util.Collection;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.ManyToMany;

@Entity
@Inheritance
public class Project {
@Id
protected int id;
protected String name;
@ManyToMany
protected Collection<Professor> employees = new ArrayList<Professor>();

public int getId() {


return id;
}

public void setId(int projectNo) {


this.id = projectNo;
}

public String getName() {


return name;
}

public void setName(String projectName) {


this.name = projectName;
}

public Collection<Professor> getProfessors() {


return employees;
}

public void addProfessor(Professor employee) {


if (!getProfessors().contains(employee)) {
getProfessors().add(employee);
}
if (!employee.getProjects().contains(this)) {
employee.getProjects().add(this);
}
}

public String toString() {


return getClass().getName().substring(getClass().getName().lastIndexOf('.')+1
) +
" no: " + getId() +
", name: " + getName();
}
}

The following code is from Phone.java.

package com.java.common;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

@Entity
public class Phone {
@Id
private long id;
private String number;
private String type;
@ManyToOne
Professor employee;

public long getId() {


return id;
}

public void setId(long id) {


this.id = id;
}
public String getNumber() {
return number;
}

public void setNumber(String phoneNo) {


this.number = phoneNo;
}

public String getType() {


return type;
}

public void setType(String phoneType) {


this.type = phoneType;
}

public Professor getProfessor() {


return employee;
}

public void setProfessor(Professor employee) {


this.employee = employee;
}

public String toString() {


return "Phone id: " + getId() +
", no: " + getNumber() +
", type: " + getType();
}
}

The following code is from Professor.java.

package com.java.common;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
public class Professor {
@Id
private int id;
private String name;
private long salary;
@Temporal(TemporalType.DATE)
private Date startDate;

@OneToOne
private Address address;

@OneToMany(mappedBy="employee")
private Collection<Phone> phones = new ArrayList<Phone>();

@ManyToOne
private Department department;

@ManyToOne
private Professor manager;

@OneToMany(mappedBy="manager")
private Collection<Professor> directs = new ArrayList<Professor>();

@ManyToMany(mappedBy="employees")
private Collection<Project> projects = new ArrayList<Project>();

public int getId() {


return id;
}

public void setId(int empNo) {


this.id = empNo;
}

public String getName() {


return name;
}

public void setName(String name) {


this.name = name;
}

public long getSalary() {


return salary;
}

public void setSalary(long salary) {


this.salary = salary;
}

public Date getStartDate() {


return startDate;
}

public void setStartDate(Date startDate) {


this.startDate = startDate;
}

public Collection<Phone> getPhones() {


return phones;
}

public void addPhone(Phone phone) {


if (!getPhones().contains(phone)) {
getPhones().add(phone);
if (phone.getProfessor() != null) {
phone.getProfessor().getPhones().remove(phone);
}
phone.setProfessor(this);
}
}

public Department getDepartment() {


return department;
}

public void setDepartment(Department department) {


if (this.department != null) {
this.department.getProfessors().remove(this);
}
this.department = department;
this.department.getProfessors().add(this);
}
public Collection<Professor> getDirects() {
return directs;
}

public void addDirect(Professor employee) {


if (!getDirects().contains(employee)) {
getDirects().add(employee);
if (employee.getManager() != null) {
employee.getManager().getDirects().remove(employee);
}
employee.setManager(this);
}
}

public Professor getManager() {


return manager;
}

public void setManager(Professor manager) {


this.manager = manager;
}

public Collection<Project> getProjects() {


return projects;
}

public void addProject(Project project) {


if (!getProjects().contains(project)) {
getProjects().add(project);
}
if (!project.getProfessors().contains(this)) {
project.getProfessors().add(this);
}
}

public Address getAddress() {


return address;
}

public void setAddress(Address address) {


this.address = address;
}

public String toString() {


return "Professor " + getId() +
": name: " + getName() +
", salary: " + getSalary() +
", phones: " + getPhones() +
", managerNo: " + ((getManager() == null) ? null : getManager().getId(
)) +
", deptNo: " + ((getDepartment() == null) ? null : getDepartment().get
Id());
}

The following code is from Address.java.

package com.java.common;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Address {
@Id
private int id;
private String street;
private String city;
private String state;
private String zip;

public int getId() {


return id;
}

public void setId(int id) {


this.id = id;
}

public String getStreet() {


return street;
}

public void setStreet(String address) {


this.street = address;
}

public String getCity() {


return city;
}

public void setCity(String city) {


this.city = city;
}

public String getState() {


return state;
}

public void setState(String state) {


this.state = state;
}

public String getZip() {


return zip;
}

public void setZip(String zip) {


this.zip = zip;
}
public String toString() {
return "Address id: " + getId() +
", street: " + getStreet() +
", city: " + getCity() +
", state: " + getState() +
", zip: " + getZip();
}

The following code is from PersonDaoImpl.java.

package com.java.common;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.transaction.annotation.Transactional;

@Transactional
public class PersonDaoImpl {
public void test() {
prepareData();

List l = em.createQuery(
"SELECT e FROM Professor e JOIN FETCH e.address")
.getResultList();
for (Object p : l) {
printResult(p);
}
}

private void prepareData() {


Address address = new Address();
address.setState("BC");

Professor p = new Professor();


p.setId(0);
p.setName("TOM");
p.setSalary(1111L);
p.setAddress(address);

Department d = new Department();


d.setId(1);
d.setName("Design");

p.setDepartment(d);
d.getProfessors().add(p);

Phone phone = new Phone();


phone.setId(1);
phone.setNumber("111-111-1111");
phone.setProfessor(p);

em.persist(p);
em.persist(phone);
em.persist(address);
em.persist(d);
}

private static void printResult(Object result) {


if (result == null) {
System.out.print("NULL");
} else if (result instanceof Object[]) {
Object[] row = (Object[]) result;
System.out.print("[");
for (int i = 0; i < row.length; i++) {
printResult(row[i]);
}
System.out.print("]");
} else if (result instanceof Long || result instanceof Double
|| result instanceof String) {
System.out.print(result.getClass().getName() + ": " + result);
} else {
System.out.print(result);
}
System.out.println();
}

@PersistenceContext
private EntityManager em;
}

The code above generates the following result.

The following is the database dump.


Table Name: ADDRESS
Row:
Column Name: ID,
Column Type: INTEGER:
Column Value: 0

Column Name: CITY,


Column Type: VARCHAR:
Column Value: null

Column Name: STATE,


Column Type: VARCHAR:
Column Value: BC

Column Name: STREET,


Column Type: VARCHAR:
Column Value: null

Column Name: ZIP,


Column Type: VARCHAR:
Column Value: null

Table Name: DEPARTMENT


Row:
Column Name: ID,
Column Type: INTEGER:
Column Value: 1

Column Name: NAME,


Column Type: VARCHAR:
Column Value: Design

Table Name: PHONE


Row:
Column Name: ID,
Column Type: BIGINT:
Column Value: 1

Column Name: NUMBER,


Column Type: VARCHAR:
Column Value: 111-111-1111

Column Name: TYPE,


Column Type: VARCHAR:
Column Value: null

Column Name: EMPLOYEE_ID,


Column Type: INTEGER:
Column Value: 0

Table Name: PROFESSOR


Row:
Column Name: ID,
Column Type: INTEGER:
Column Value: 0

Column Name: NAME,


Column Type: VARCHAR:
Column Value: TOM

Column Name: SALARY,


Column Type: BIGINT:
Column Value: 1111

Column Name: STARTDATE,


Column Type: DATE:
Column Value: null

Column Name: ADDRESS_ID,


Column Type: INTEGER:
Column Value: 0

Column Name: DEPARTMENT_ID,


Column Type: INTEGER:
Column Value: 1

Column Name: MANAGER_ID,


Column Type: INTEGER:
Column Value: null

Table Name: PROJECT


Table Name: PROJECT_PROFESSOR

LEFT JOIN
The LEFT JOIN statement is similar to the JOIN statement. The main difference is
that a LEFT JOIN statement includes all rows of the entity or table referenced on the
left side of the statement.
I use that in the following example to select all Authors with the lastName “Janssen”
and their Books. If the database contains a Book for a specific Author, the query
returns it as the second element in the Object[]. Otherwise, that array element is null.
A simple JOIN statement would only return the Authors who have written a Book.
The second element of the Object[] would never be null.
1 List<Object[]> authors = em.createQuery("SELECT a, b FROM Author a LEFT JOIN a.books
Hibernate generates the following SQL statement for this query. It selects all columns
mapped by the Author and Book entities and uses the defined association to create a
left join between the Book and the Author tables.
1
2
3 16:54:10,510 DEBUG [org.hibernate.SQL] -
select
4 author0_.id as id1_0_0_,
5 book2_.id as id1_1_1_,
6 author0_.firstName as firstNam2_0_0_,
7 author0_.lastName as lastName3_0_0_,
author0_.version as version4_0_0_,
8
book2_.publisherid as publishe5_1_1_,
9 book2_.publishingDate as publishi2_1_1_,
10 book2_.title as title3_1_1_,
11 book2_.version as version4_1_1_
12 from
Author author0_
13 left outer join
14 BookAuthor books1_
15 on author0_.id=books1_.authorId
16 left outer join
17 Book book2_
on books1_.bookId=book2_.id
18 where
19 author0_.lastName='Janssen'
20
21

JOIN FETCH
The FETCH keyword of the JOIN FETCH statement is JPA-specific. It tells the
persistence provider to not only join the 2 database tables within the query but to
also initialize the association on the returned entity. You can use it with a JOIN and
a LEFT JOIN statement.
Let’s change the first example and replace the JOIN statement with a JOIN
FETCH statement.
List<Author> authors = em.createQuery("SELECT a FROM Author a
1JOIN FETCH a.books b WHERE b.title LIKE '%Hibernate%'", Author.class).getResultList();
The JPQL query selects Author entities. But as you can see in the SQL statement,
Hibernate now selects all columns mapped by the Author and the Book entity.
Hibernate then maps the result set to Author and Book entities. It uses
the Book entities to initialize the books attribute of each Author entity before it returns
a List of Author entities.
1
2
3 16:57:53,384 DEBUG [org.hibernate.SQL] -
4 select
author0_.id as id1_0_0_,
5 book2_.id as id1_1_1_,
6 author0_.firstName as firstNam2_0_0_,
7 author0_.lastName as lastName3_0_0_,
8 author0_.version as version4_0_0_,
book2_.publisherid as publishe5_1_1_,
9
book2_.publishingDate as publishi2_1_1_,
10 book2_.title as title3_1_1_,
11 book2_.version as version4_1_1_,
12 books1_.authorId as authorId2_2_0__,
13 books1_.bookId as bookId1_2_0__
from
14 Author author0_
15 inner join
16 BookAuthor books1_
17 on author0_.id=books1_.authorId
18 inner join
Book book2_
19 on books1_.bookId=book2_.id
20 where
21 book2_.title like '%Hibernate%'
22
23

How to log SQL statements and their


parameters
The following code snippet shows an example of a log4j configuration which
activates both of them.
1 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
2 log4j.appender.stdout.Target=System.out
3 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
4 log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS}
%-5p [%c] - %m%n
5
6 log4j.rootLogger=info, stdout
7 # basic log level for all messages
8 log4j.logger.org.hibernate=info
9
10 # SQL statements and parameters
log4j.logger.org.hibernate.SQL=debug
11 log4j.logger.org.hibernate.type.descriptor.sql=trace
12
Hibernate then writes log messages like the following one to your log file.
17:34:50,353 DEBUG [org.hibernate.SQL] - select author0_.id as id1_0_,
author0_.firstName
as firstNam2_0_, author0_.lastName as lastName3_0_,
1author0_.version as version4_0_ from Author author0_ where author0_.id=1
217:34:50,362 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] –
extracted value ([id1_0_] : [BIGINT]) - [1]
317:34:50,373 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] –
4extracted value ([firstNam2_0_] : [VARCHAR]) - [Joshua]
517:34:50,373 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] –
extracted value ([lastName3_0_] : [VARCHAR]) - [Bloch]
17:34:50,374 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] –
extracted value ([version4_0_] : [INTEGER]) - [0]

The best way to map a


projection query to a DTO
(Data Transfer Object) with
JPA and Hibernate
Introduction
Domain Model
Considering we have the following Post entity:
DTO projections using JPA
When using JPA or Hibernate, you can execute both entity queries via JPQL or Criteria
API or native SQL queries.

DTO projection using JPA Tuple and JPQL


If you don’t want to supply a DTO class for your projection, you can use the JPA Tuple.
So, to use Tuple projection, your JPQL query looks as follows:
List<Tuple> postDTOs = entityManager.createQuery("""
1 select
2 p.id as id,
3 p.title as title
4 from Post p
where p.createdOn > :fromTimestamp
5
""", Tuple.class)
6 .setParameter(
7 "fromTimestamp",
8 Timestamp.from(
9 LocalDate.of(2020, 1, 1)
.atStartOfDay()
10 .toInstant(ZoneOffset.UTC)
11 )
12 )
13 .getResultList();
14
15 assertFalse(postDTOs.isEmpty());
16
Tuple postDTO = postDTOs.get(0);
17
18 assertEquals(
19 1L,
20 postDTO.get("id")
);
21
22 assertEquals(
23 "High-Performance Java Persistence",
24 postDTO.get("title")
25 );
26
27
28
29
30
As you can see, the Tuple is a convenient way of fetching DTO projections as you don’t
require to specify a DTO class for every type of projection that needs to be supported.

DTO projections using a Constructor


Expression and JPQL
If you don’t want to use a Tuple because you want the DTO projection to use a specific
class, you can use a Constructor Expression by specifying the NEW keyword along with
the fully-qualified name of the class representing the DTO projection and the list of
attributes that will be passed as constructor arguments.
The DTO class must provide a constructor that takes all the attributes fetched by the
result set projection.
So, the DTO projection must look as follows:
1
public class PostDTO {
2
3 private Long id;
4
5 private String title;
6
7 public PostDTO(Number id, String title) {
8 this.id = id.longValue();
9 this.title = title;
}
10
11 public Long getId() {
12 return id;
13 }
14
15 public String getTitle() {
return title;
16 }
17 }
18
19
Therefore, the constructor expression JPQL query looks as follows:
1
2 List<PostDTO> postDTOs = entityManager.createQuery("""
3 select new com.java.book.hpjp.hibernate.forum.dto.PostDTO(
4 p.id,
5 p.title
)
6
from Post p
7 where p.createdOn > :fromTimestamp
8 """, PostDTO.class)
9 .setParameter(
10 "fromTimestamp",
Timestamp.from(
11 LocalDate.of(2020, 1, 1)
12 .atStartOfDay()
13 .toInstant(ZoneOffset.UTC)
14 )
15 )
.getResultList();
16
17

DTO projections using Tuple and native


SQL queries

1 List<Tuple> postDTOs = entityManager.createNativeQuery("""


SELECT
2 p.id AS id,
3 p.title AS title
4 FROM Post p
5 WHERE p.created_on > :fromTimestamp
""", Tuple.class)
6
.setParameter(
7 "fromTimestamp",
8 Timestamp.from(
9 LocalDate.of(2020, 1, 1)
10 .atStartOfDay()
.toInstant(ZoneOffset.UTC)
11 )
12 )
13 .getResultList();
14
15 assertFalse(postDTOs.isEmpty());
16
17 Tuple postDTO = postDTOs.get(0);
18
assertEquals(
19 1L,
20 postDTO.get("id")
21 );
22
23 assertEquals(
"High-Performance Java Persistence",
24 postDTO.get("title")
25 );
26
27
28
29
30

DTO projections using a ConstructorResult


For native SQL queries, you can no longer use a Constructor Expression, so you need
to use a named native query and configure a given SqlResultSetMapping so that you can
populate the DTO class either via its constructor or its fields.
If we use the same PostDTO class type introduced previously, we have to provide the
following SqlResultSetMapping:
1
2
3 @NamedNativeQuery(
name = "PostDTO",
4 query = """
5 SELECT
6 p.id AS id,
7 p.title AS title
FROM Post p
8
WHERE p.created_on > :fromTimestamp
9 """,
10 resultSetMapping = "PostDTO"
11 )
12 @SqlResultSetMapping(
name = "PostDTO",
13 classes = @ConstructorResult(
14 targetClass = PostDTO.class,
15 columns = {
16 @ColumnResult(name = "id"),
17 @ColumnResult(name = "title")
}
18 )
19 )
20
21
Now, the SQL projection named native query is executed as follows:
1 List<PostDTO> postDTOs = entityManager
2 .createNamedQuery("PostDTO")
.setParameter(
3 "fromTimestamp",
4 Timestamp.from(
5 LocalDateTime.of(2020, 1, 1, 0, 0, 0)
6 .toInstant(ZoneOffset.UTC)
7 )
)
8 .getResultList();
9
10

DTO projections using Hibernate


While you can use all the JPA features with Hibernate, there are many more
features Hibernate has to offer than the standard Java Persistence specification.

DTO projections using ResultTransformer


and JPQL
As previously explained, the ResultTransformer allows you to customize the result set
any way you like so you can use it to transform the typical Object[] array projection into
a DTO result set.
This time, you don’t need to provide a constructor to match the entity attributes being
selected by the query.
Although you don’t even have to provide setters in your DTO class, here, we need the
setter because BigInteger might be returned for the id database column while we need
it to be cast as a Long.
Hibernate can set the appropriate fields using Reflection, so it’s more flexible than the
previous JPA Constructor Expression alternative.
Considering we have the following DTO class:
public class PostDTO {
1
2 private Long id;
3
4 private String title;
5
6 public Long getId() {
7 return id;
}
8
9 public void setId(Number id) {
10 this.id = id.longValue();
11 }
12
13 public String getTitle() {
14 return title;
}
15
16 public void setTitle(String title) {
17 this.title = title;
18 }
}
19
20
21
22
We can transform the result set using the setResultTransformer method of the
Hibernate-specific org.hibernate.query.Query interface which you can unwrap from the
JPA Query.
1
2 List<PostDTO> postDTOs = entityManager.createQuery("""
3 select
4 p.id as id,
5 p.title as title
from Post p
6
where p.createdOn > :fromTimestamp
7 """)
8 .setParameter(
9 "fromTimestamp",
10 Timestamp.from(
LocalDateTime.of(2020, 1, 1, 0, 0, 0)
11 .toInstant(ZoneOffset.UTC)
12 )
13 )
14 .unwrap(org.hibernate.query.Query.class)
15 .setResultTransformer(Transformers.aliasToBean(PostDTO.class))
.getResultList();
16
17

DTO projections using ResultTransformer


and a Native SQL query
If you want to use a native SQL query, you don’t need to go through all the trouble of
declaring a SqlResultSetMapping since you can use the AliasToBeanResultTransformer just
like it was the case for the aforementioned JPQL example.
1 List<PostDTO> postDTOs = entityManager.createNativeQuery("""
2 select
p.id as "id",
3
p.title as "title"
4 from Post p
5 where p.created_on > :fromTimestamp
6 """)
7 .setParameter(
"fromTimestamp",
8 Timestamp.from(
9 LocalDateTime.of(2020, 1, 1, 0, 0, 0)
10 .toInstant(ZoneOffset.UTC)
11 )
12 )
13 .unwrap(org.hibernate.query.NativeQuery.class)
.setResultTransformer(Transformers.aliasToBean(PostDTO.class))
14 .getResultList();
15
16
17

You might also like