Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a JPA Query return results as a Java Map?

We are currently building a Map manually based on the two fields that are returned by a named JPA query because JPA 2.1 only provides a getResultList() method:

@NamedQuery{name="myQuery",query="select c.name, c.number from Client c"}  HashMap<Long,String> myMap = new HashMap<Long,String>();  for(Client c: em.createNamedQuery("myQuery").getResultList() ){      myMap.put(c.getNumber, c.getName); } 

But, I feel like a custom mapper or similar would be more performant since this list could easily be 30,000+ results.

Any ideas to build a Map without iterating manually.

(I am using OpenJPA, not hibernate)

like image 255
Eddie Avatar asked Dec 06 '10 22:12

Eddie


People also ask

How do you map native query results to entities?

The easiest way to map a query result to an entity is to provide the entity class as a parameter to the createNativeQuery(String sqlString, Class resultClass) method of the EntityManager and use the default mapping. The following snippet shows how this is done with a very simple query.

How is JPA mapping done?

The One-To-One mapping represents a single-valued association where an instance of one entity is associated with an instance of another entity. In this type of association one instance of source entity can be mapped atmost one instance of target entity.

How do I map a view in JPA?

In JPA you can map to a VIEW the same as a table, using the @Table annotation. You can then map each column in the view to your object's attributes. Views are normally read-only, so object's mapping to views are normally also read-only.


2 Answers

Returning a Map result using JPA Query getResultStream

Since the JPA 2.2 version, you can use the getResultStream Query method to transform the List<Tuple> result into a Map<Integer, Integer>:

Map<Integer, Integer> postCountByYearMap = entityManager.createQuery("""     select        YEAR(p.createdOn) as year,        count(p) as postCount     from        Post p     group by        YEAR(p.createdOn)     """, Tuple.class) .getResultStream() .collect(     Collectors.toMap(         tuple -> ((Number) tuple.get("year")).intValue(),         tuple -> ((Number) tuple.get("postCount")).intValue()     ) ); 

Returning a Map result using JPA Query getResultList and Java stream

If you're using JPA 2.1 or older versions but your application is running on Java 8 or a newer version, then you can use getResultList and transform the List<Tuple> to a Java 8 stream:

Map<Integer, Integer> postCountByYearMap = entityManager.createQuery("""     select        YEAR(p.createdOn) as year,        count(p) as postCount     from        Post p     group by        YEAR(p.createdOn)     """, Tuple.class) .getResultList() .stream() .collect(     Collectors.toMap(         tuple -> ((Number) tuple.get("year")).intValue(),         tuple -> ((Number) tuple.get("postCount")).intValue()     ) ); 

Returning a Map result using a Hibernate-specific ResultTransformer

Another option is to use the MapResultTransformer class provided by the Hibernate Types open-source project:

Map<Number, Number> postCountByYearMap = (Map<Number, Number>) entityManager.createQuery("""     select        YEAR(p.createdOn) as year,        count(p) as postCount     from        Post p     group by        YEAR(p.createdOn)     """) .unwrap(org.hibernate.query.Query.class) .setResultTransformer(     new MapResultTransformer<Number, Number>() ) .getSingleResult(); 

The MapResultTransformer is suitable for projects still running on Java 6 or using older Hibernate versions.

Avoid returning large result sets

The OP said:

But, I feel like a custom mapper or similar would be more performant since this list could easily be 30,000+ results.

This is a terrible idea. You never need to select 30k records. How would that fit in the UI? Or, why would you operate on such a large batch of records?

You should use query pagination as this will help you reduce the transaction response time and provide better concurrency.

like image 198
Vlad Mihalcea Avatar answered Sep 19 '22 19:09

Vlad Mihalcea


There is no standard way to get JPA to return a map.

see related question: JPA 2.0 native query results as map

Iterating manually should be fine. The time to iterate a list/map in memory is going to be small relative to the time to execute/return the query results. I wouldn't try to futz with the JPA internals or customization unless there was conclusive evidence that manual iteration was not workable.

Also, if you have other places where you turn query result Lists into Maps, you probably want to refactor that into a utility method with a parameter to indicate the map key property.

like image 33
wrschneider Avatar answered Sep 17 '22 19:09

wrschneider