web-dev-qa-db-fra.com

JPA peut-il renvoyer les résultats sous forme de carte?

Nous construisons actuellement une carte manuellement en fonction des deux champs qu'une requête nommée renvoie, car JPA fournit uniquement un 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);
}

Mais je pense qu'un mappeur personnalisé ou similaire serait plus performant car cette liste pourrait facilement contenir plus de 30 000 résultats.

Toutes les idées pour construire une carte sans itérer manuellement.

(J'utilise OpenJPA, pas en veille prolongée)

29
Eddie

Il n'y a pas de moyen standard pour que JPA retourne une carte.

voir la question connexe: les résultats de la requête native JPA 2.0 comme carte

L'itération manuelle devrait convenir. Le temps pour itérer une liste/carte en mémoire va être petit par rapport au temps pour exécuter/retourner les résultats de la requête. Je n'essaierais pas de jouer avec les internes de JPA ou la personnalisation à moins qu'il n'y ait des preuves concluantes que l'itération manuelle n'était pas réalisable.

De plus, si vous avez d'autres endroits où vous transformez les listes de résultats de requête en cartes, vous souhaiterez probablement refactoriser cela en une méthode utilitaire avec un paramètre pour indiquer la propriété de clé de carte.

15
wrschneider

Vous pouvez récupérer une liste de Java.util. Map.Entry à la place. Par conséquent, la collection de votre entité doit être modélisée comme une carte:

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

Dans l'exemple PhoneType est une énumération simple, PhoneNumber est une entité. Dans votre requête, utilisez le mot clé ENTRY introduit dans JPA 2.0 pour les opérations de carte:

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

Vous êtes maintenant prêt à récupérer les entrées et à commencer à travailler avec elles:

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

Si vous avez toujours besoin des entrées dans une carte mais que vous ne souhaitez pas parcourir manuellement votre liste d'entrées, consultez ce message Convert Set <Map.Entry <K, V >> to HashMap <K, V> qui fonctionne avec Java 8.

8
mika

Cela fonctionne très bien.
Code du référentiel:

@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];
}

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

3
krishna Murari

Que dis-tu de ça ?

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

et

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

Veuillez vous référer, résultats de requête native JPA 2.0 sous forme de carte

Dans votre cas, dans Postgres, ce serait quelque chose comme,

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

Veuillez noter que je partage juste ma solution avec Postgres.

0
Darshan Patel
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){  }
0
Tufik Chediak

Avec un résultat personnalisé class et un peu de goyave, voici mon approche qui fonctionne plutôt bien:

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;
    }
});
0
Hendy Irawan