Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
995 views
in Technique[技术] by (71.8m points)

dictionary - 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)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...