DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Providing Enum Consistency Between Application and Data
  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • The First Annual Recap From JPA Buddy
  • Implement a Distributed Database to Your Java Application

Trending

  • How to Introduce a New API Quickly Using Micronaut
  • Go 1.24+ Native FIPS Support for Easier Compliance
  • ITBench, Part 1: Next-Gen Benchmarking for IT Automation Evaluation
  • Modern Test Automation With AI (LLM) and Playwright MCP
  1. DZone
  2. Data Engineering
  3. Data
  4. Projection Queries: A Way to Optimize Data Traffic

Projection Queries: A Way to Optimize Data Traffic

JPA provides several solutions for projection queries and DTO mapping. It is up to us to choose the most appropriate solution for each of our use cases.

By 
Sergiy Pylypets user avatar
Sergiy Pylypets
DZone Core CORE ·
Updated Feb. 17, 21 · Tutorial
Likes (7)
Comment
Save
Tweet
Share
22.3K Views

Join the DZone community and get the full member experience.

Join For Free

In our modern, highly concurrent world, enterprise application developers have to deal with new challenges like huge data volumes, diversity of clients, and permanently changing business requirements. Now, it is a usual case when a microservice application has to serve various clients, and some of them are other microservices. These factors imply higher requirements for controlling data traffic. We cannot afford to send any excessive data and we need to respond to each request with data well-tailored for this particular client.

One option of customizing data traffic is the usage of projection queries; that is, queries that return a projection of domain objects. Almost all enterprise applications use some kind of ORM technology, and JPA is a standard way for its implementation. So, let's see how we can implement projection queries based on JPA 2.2 specification.

Suppose we are to implement a collection management online application. The domain system is the following.

Listing 1: CollectionItem.java 

Java
 




x
48


 
1
import javax.persistence.ElementCollection;
2
import javax.persistence.Entity;
3
import javax.persistence.GeneratedValue;
4
import javax.persistence.GenerationType;
5
import javax.persistence.Id;
6
import javax.persistence.JoinColumn;
7
import javax.persistence.Lob;
8
import javax.persistence.OneToOne;
9
import javax.persistence.SequenceGenerator;
10
import javax.persistence.Table;
11
 
          
12
@Entity
13
@Table(name="COLLECTION_ITEMS")
14
public class CollectionItem {
15
 
          
16
   @Id
17
   @SequenceGenerator(name = "CollectionItems_Generator", sequenceName = "CollectionItems_seq", allocationSize = 1)
18
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CollectionItems_Generator")
19
   long id;
20
 
          
21
   BigDecimal price;
22
 
          
23
   @OneToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval=true)
24
   @JoinColumn(name="small_image", unique=true)
25
   Image smallImage;
26
 
          
27
   @OneToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval=true)
28
   @JoinColumn(name="image", unique=true)
29
   Image image;
30
 
          
31
   String name;
32
 
          
33
   String summary;
34
 
          
35
   @Lob
36
   String description;
37
 
          
38
   Short year;
39
 
          
40
   String country;
41
 
          
42
   @ElementCollection
43
   @CollectionTable(name="ITEMS_TOPICS", joinColumns=@JoinColumn(name="ITEM_ID"))
44
   @Column(name="TOPIC")
45
   Set<String> topics;
46
 
          
47
Getters and setters
48
. . . 



Listing 2: Image.java

Java
 




xxxxxxxxxx
1
24


 
1
import javax.persistence.Entity;
2
import javax.persistence.GeneratedValue;
3
import javax.persistence.GenerationType;
4
import javax.persistence.Id;
5
import javax.persistence.Lob;
6
import javax.persistence.SequenceGenerator;
7
import javax.persistence.Table;
8
 
          
9
@Entity
10
@Table(name="IMAGES")
11
public class Image implements java.io.Serializable {
12
 
          
13
   private static final long serialVersionUID = 1L;
14
 
          
15
   @Id
16
   @SequenceGenerator(name = "Images_Generator", sequenceName = "Images_seq", allocationSize = 1)
17
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Images_Generator")
18
   private long id;
19
 
          
20
   @Lob
21
   private String content;
22
 
          
23
Getters and setters
24
.  .  .



Note: In web applications, it is often more efficient to store images in the file system and store the paths in the database.

According to the specifications, we need to display collection items (probably filtered by some search criteria) in a table with the following attributes included: id, name, summary, year, price, country, smallimage. Also, we need to display the following data for each selected item: name, description, year, country, price, image. To address these requirements, we can implement data transfer objects (DTO).

Listing 3: CollectionItemDto.java

Java
 




xxxxxxxxxx
1
36


 
1
import java.math.BigDecimal;
2
 
          
3
public class CollectionItemDto {
4
 
          
5
   long id;
6
   String name;
7
   String summary;
8
   Short year;
9
   String country;
10
   String smallImage;
11
   BigDecimal price;
12
 
          
13
   public CollectionItemDto() {}
14
 
          
15
   public CollectionItemDto(long id, String name, String summary, Short year, String country, BigDecimal price, String smallImage
16
) {
17
      this.id = id;
18
      this.name = name;
19
      this.summary = summary;
20
      this.year = year;
21
      this.country = country;
22
      this.price = price;
23
      this.smallImage = smallImage;
24
   }
25
 
          
26
   public long getId() {
27
      return id;
28
   }
29
 
          
30
   public String getName() {
31
      return name;
32
   }
33
 
          
34
Other getters
35
. . . .
36
}



Listing 4: CollectionItemDetailsDto.java

Java
 




xxxxxxxxxx
1
33


 
1
import java.math.BigDecimal;
2
 
          
3
public class CollectionItemDetailsDto {
4
 
          
5
   String name;
6
   String description;
7
   Short year;
8
   String country;
9
   String image;
10
   BigDecimal price;
11
 
          
12
   public CollectionItemDetailsDto() {}
13
 
          
14
   public CollectionItemDetailsDto(String name, String description, Short year, String country, BigDecimal price, String image) {
15
      this.name = name;
16
      this.description = description;
17
      this.year = year;
18
      this.country = country;
19
      this.price = price;
20
      this.image = image;
21
   }
22
 
          
23
   public String getName() {
24
      return name;
25
   }
26
 
          
27
   public String getDescription() {
28
      return description;
29
   }
30
 
          
31
Other getters
32
. . . .
33
}



Now, we should find a way to populate these DTOs with data from the database. The modern JPA version provides support for mapping table data onto custom objects in JPQL, Criteria API, and for native queries.

1. Projection Queries in JPQL

In JPQL, we can use the new keyword to define the DTO in the body of the query. So, we create the following named query for filtering collection items by country:

Listing 5: Named JPQL projection query for retrieving collection item data

Java
 




xxxxxxxxxx
1


 
1
@NamedQuery(name="findByCountry", query="SELECT new com.collections.entity.dto.CollectionItemDto(st.id, st.name, st.summary, st.year, st.country, st.price, st.smallImage.content) FROM CollectionItem st WHERE st.country = :country")
2
@Entity
3
@Table(name="COLLECTION_ITEMS")
4
public class CollectionItem {
5
 
          
6
   public static final String FIND_BY_COUNTRY = "findByCountry";
7
. . .



We use this query in the corresponding method of CollectionItemDao.

Listing 6: CollectionItemDao.findByCountry() method with the usage of named JPQL projection query

Java
 




xxxxxxxxxx
1


 
1
public Stream<CollectionItemDto> findByCountry(String country, int from, int rows) {
2
return em.createNamedQuery(CollectionItem.FIND_BY_COUNTRY, CollectionItemDto.class)
3
 .setParameter("country", country)
4
           .setFirstResult(from)
5
           .setMaxResults(rows)
6
           .getResultStream();
7
}



For retrieving details of the selected collection item, we define the following named query.

Listing 7: Named JPQL projection query for retrieving details of the selected collection item

Java
 




xxxxxxxxxx
1


 
1
@NamedQuery(name="getDetailsById", query="SELECT new com.collections.entity.dto.CollectionItemDetailsDto(st.name, st.description, st.year, st.country, st.price, st.image.content) FROM CollectionItem st WHERE st.id = :id")



And use it in CollectionItemDao the same way as the findByCountry query (see Listing 6):

Listing 8: CollectionItemDao.getDetails() method with the usage of named JPQL projection query

Java
 




xxxxxxxxxx
1


 
1
public Stream<CollectionItemDetailsDto> getDetails(long stampId) {
2
return em.createNamedQuery(CollectionItem.GET_DETAILS_BY_ID, CollectionItemDetailsDto.class)
3
 .setParameter("id", stampId)
4
 .getResultStream();
5
}



For advanced search, when customers can choose parameters by which to filter collection items, we can create query dynamically, with the usage of Criteria API.

2. Projection Queries in Criteria API

In the Criteria API, we can use the construct() method of CriteriaBuilder. So, we implement the following dynamic search method in CollectionItemDao:

Listing 9: Dynamically created query

Java
 




xxxxxxxxxx
1
23


 
1
public Stream<CollectionItemDto> search(String country, short year, String ... topics) {
2
   CriteriaBuilder cb = em.getCriteriaBuilder();
3
   CriteriaQuery<CollectionItemDto> cq = cb.createQuery(CollectionItemDto.class);
4
   Root<CollectionItem> stamp = cq.from(CollectionItem.class);
5
   Join<CollectionItem, Image> smallImage = stamp.join(CollectionItem_.smallImage);
6
   Predicate topicFilter = cb.conjunction();
7
   for(String topic : topics) {
8
      topicFilter = cb.and(topicFilter, cb.isMember(topic, stamp.get(CollectionItem_.topics)));
9
   }
10
   cq.where(cb.equal(stamp.get(CollectionItem_.country), country),
11
      cb.equal(stamp.get(CollectionItem_.year), year),topicFilter
12
   );
13
   cq.select(cb.construct(CollectionItemDto.class,
14
      stamp.get(CollectionItem_.id),
15
      stamp.get(CollectionItem_.name),
16
      stamp.get(CollectionItem_.summary),
17
      stamp.get(CollectionItem_.year),
18
      stamp.get(CollectionItem_.country),
19
      stamp.get(CollectionItem_.price),
20
      smallImage.get(Image_.content))).distinct(true);
21
   TypedQuery<CollectionItemDto> tq = em.createQuery(cq);
22
   return tq.getResultStream();
23
}  



This code assumes usage of metamodel objects, which are typically generated while building the application, e.g. with the usage of a Maven processor plugin.

Sometimes, we want to use native SQL queries instead of JPQL or criteria queries, e.g. for performance tuning related to a specific database.

3. Native SQL Projection Queries

To customize data mapping in native SQL queries, we use the @SqlResultSetMapping annotation. Suppose, for some reason, we want to use a native query for implementing the CollectionItemDao.findByCountry() method. In this case, we can create the SQL query and the SqlResultSetMapping definition as follows.

Listing 10: CollectionItemDao.findByCountry() method with the usage of native SQL projection query

Java
 




xxxxxxxxxx
1
16


 
1
@SqlResultSetMapping(name="findByCountryResultSet", classes={@ConstructorResult(targetClass=com.collections.entity.dto.CollectionItemDto.class, columns={
2
@ColumnResult(name="id", type=Long.class),
3
@ColumnResult(name="name"),
4
@ColumnResult(name="summary"),
5
@ColumnResult(name="year"),
6
@ColumnResult(name="country"),
7
@ColumnResult(name="price"),
8
@ColumnResult(name="content", type=String.class)
9
})})
10
@Entity
11
@Table(name="COLLECTION_ITEMS")
12
public class CollectionItem {
13
 
          
14
   public static final String FIND_BY_COUNTRY_SQL = "SELECT ci.id, ci.name, ci.summary, ci.year, ci.country, ci.price, img.content FROM collection_items ci JOIN images img WHERE ci.small_image=img.id AND ci.country = ?";
15
   public static final String FIND_BY_COUNTRY_RESULTSET = "findByCountryResultSet";
16
. . . .



Then, we use this stuff in the DAO method.

Listing 11: Implementation of CollectionItemDao.findByCountry() method with the usage of native SQL projection query

Java
 




xxxxxxxxxx
1


 
1
public Stream<CollectionItemDto> findByCountry(String country, int from, int rows) {
2
return em.createNativeQuery(CollectionItem.FIND_BY_COUNTRY_SQL, CollectionItem.FIND_BY_COUNTRY_RESULTSET)
3
   .setParameter(1, country)
4
   .setFirstResult(from)
5
   .setMaxResults(rows)
6
   .getResultStream();
7
}



As we can see, JPA provides several solutions for projection queries and DTO mapping. It is up to us to choose the most appropriate solution for each of our use cases.

Database Data (computing) Listing (computer) Java (programming language) application

Opinions expressed by DZone contributors are their own.

Related

  • Providing Enum Consistency Between Application and Data
  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • The First Annual Recap From JPA Buddy
  • Implement a Distributed Database to Your Java Application

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!