Can JPA return results as a map?

半城伤御伤魂 提交于 2019-11-29 00:30:01

问题


We are currently build a map manually based on the two fields a named query returns, because JPA only provides a getResultList().

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


回答1:


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.




回答2:


You can retrieve a list of java.util.Map.Entry instead. Therefore the collection in your entity should be modeled as a Map:

@OneToMany
@MapKeyEnumerated(EnumType.STRING)
public Map<PhoneType, PhoneNumber> phones;

In the example PhoneType is a simple enum, PhoneNumber is an entity. In your query use the ENTRY keyword that was introduced in JPA 2.0 for map operations:

public List<Entry> getPersonPhones(){
    return em.createQuery("SELECT ENTRY(pn) FROM Person p JOIN p.phones pn",java.util.Map.Entry.class).getResultList();
}

You are now ready to retrieve the entries and start working with it:

List<java.util.Map.Entry> phoneEntries =   personDao.getPersonPhoneNumbers();
for (java.util.Map.Entry<PhoneType, PhoneNumber> entry: phoneEntries){
    //entry.key(), entry.value()
}

If you still need the entries in a map but don't want to iterate through your list of entries manually, have a look on this post Convert Set<Map.Entry<K, V>> to HashMap<K, V> which works with Java 8.




回答3:


This works fine.
Repository code :

@Repository
public interface BookRepository extends CrudRepository<Book,Id> {

    @Query("SELECT b.name, b.author from Book b")
    List<Object[]> findBooks();
}

service.java

      List<Object[]> list = bookRepository.findBooks();
                for (Object[] ob : list){
                    String key = (String)ob[0];
                    String value = (String)ob[1];
}

link https://codereview.stackexchange.com/questions/1409/jpa-query-to-return-a-map




回答4:


Map<String,Object> map = null;
    try {
        EntityManager entityManager = getEntityManager();
        Query query = entityManager.createNativeQuery(sql);
            query.setHint(QueryHints.RESULT_TYPE, ResultType.Map);
        map = (Map<String,Object>) query.getSingleResult();
    }catch (Exception e){ }

 List<Map<String,Object>> list = null;
    try {
        EntityManager entityManager = getEntityManager();
        Query query = entityManager.createNativeQuery(sql);
            query.setHint(QueryHints.RESULT_TYPE, ResultType.Map);
            list = query.getResultList();
    }catch (Exception e){  }



回答5:


With custom result class and a bit of Guava, this is my approach which works quite well:

public static class SlugPair {
    String canonicalSlug;
    String slug;

    public SlugPair(String canonicalSlug, String slug) {
        super();
        this.canonicalSlug = canonicalSlug;
        this.slug = slug;
    }

}

...

final TypedQuery<SlugPair> query = em.createQuery(
    "SELECT NEW com.quikdo.core.impl.JpaPlaceRepository$SlugPair(e.canonicalSlug, e.slug) FROM "
      + entityClass.getName() + " e WHERE e.canonicalSlug IN :canonicalSlugs",
    SlugPair.class);

query.setParameter("canonicalSlugs", canonicalSlugs);

final Map<String, SlugPair> existingSlugs = 
    FluentIterable.from(query.getResultList()).uniqueIndex(
        new Function<SlugPair, String>() {
    @Override @Nullable
    public String apply(@Nullable SlugPair input) {
        return input.canonicalSlug;
    }
});



回答6:


using java 8 (+) you can get results as a list of array object (each column will from select will have same index on results array) by hibernate entity manger, and then from results list into stream, map results into entry (key, value), then collect them into map of same type.

 final String sql = "SELECT ID, MODE FROM MODES";
     List<Object[]> result = entityManager.createNativeQuery(sql).getResultList();
        return result.stream()
                .map(o -> new AbstractMap.SimpleEntry<>(Long.valueOf(o[0].toString()), String.valueOf(o[1])))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));



回答7:


How about this ?

@NamedNativeQueries({
@NamedNativeQuery(
  name="myQuery",
  query="select c.name, c.number from Client c",
  resultClass=RegularClient.class
)
})

and

     public static List<RegularClient> runMyQuery() {
     return entityManager().createNamedQuery("myQuery").getResultList();
 }



回答8:


Please refer, JPA 2.0 native query results as map

In your case in Postgres, it would be something like,

List<String> list = em.createNativeQuery("select cast(json_object_agg(c.number, c.name) as text) from schema.client c")
                   .getResultList();

//handle exception here, this is just sample
Map map = new ObjectMapper().readValue(list.get(0), Map.class);

Kindly note, I am just sharing my workaround with Postgres.



来源:https://stackoverflow.com/questions/4371384/can-jpa-return-results-as-a-map

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!