Может ли JPA вернуть результаты в виде карты?
В настоящее время мы создаем карту вручную на основе двух полей, возвращаемых именованным запросом, поскольку JPA предоставляет только 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);
}
Но я чувствую, что пользовательский сопоставитель или аналогичный будет более результативным, так как этот список может быть легко достигнут 30 000 результатов.
Любые идеи по созданию карты без повторения вручную.
(Я использую OpenJPA, а не hibernate)
Ответы
Ответ 1
Нет стандартного способа вернуть JPA карту.
см. связанный с этим вопрос: исходные результаты запроса JPA 2.0 как карта
Итерация вручную должна быть прекрасной. Время итерации списка/карты в памяти будет малым относительно времени выполнения/возврата результатов запроса. Я бы не стал пытаться работать с внутренними компонентами JPA или настройками, если не было убедительных доказательств того, что ручная итерация не была работоспособной.
Кроме того, если у вас есть другие места, где вы включаете списки результатов запроса в Maps, вы, вероятно, захотите реорганизовать это в метод утилиты с параметром, указывающим свойство ключа карты.
Ответ 2
Вместо этого вы можете получить список java.util. Map.Entry.
Поэтому сбор в вашей организации должен быть смоделирован как карта:
@OneToMany
@MapKeyEnumerated(EnumType.STRING)
public Map<PhoneType, PhoneNumber> phones;
В примере PhoneType является простым перечислением, PhoneNumber является сущностью. В вашем запросе используйте ключевое слово ENTRY
, которое было введено в JPA 2.0 для операций с картой:
public List<Entry> getPersonPhones(){
return em.createQuery("SELECT ENTRY(pn) FROM Person p JOIN p.phones pn",java.util.Map.Entry.class).getResultList();
}
Теперь вы готовы извлечь записи и начать работать с ним:
List<java.util.Map.Entry> phoneEntries = personDao.getPersonPhoneNumbers();
for (java.util.Map.Entry<PhoneType, PhoneNumber> entry: phoneEntries){
//entry.key(), entry.value()
}
Если вам все еще нужны записи на карте, но вы не хотите перебирать свой список записей вручную, посмотрите на этот пост Преобразовать Set < Map.Entry < K, V → к HashMap < K, V > , который работает с Java 8.
Ответ 3
Как насчет этого?
@NamedNativeQueries({
@NamedNativeQuery(
name="myQuery",
query="select c.name, c.number from Client c",
resultClass=RegularClient.class
)
})
и
public static List<RegularClient> runMyQuery() {
return entityManager().createNamedQuery("myQuery").getResultList();
}
Ответ 4
С пользовательским результатом class
и немного Guava, это мой подход, который работает достаточно хорошо:
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;
}
});
Ответ 5
Пожалуйста, обратитесь к исходным результатам запроса JPA 2.0 как к карте
В вашем случае в Postgres
это будет что-то вроде
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);
Просьба отметить, я просто делюсь своим обходным путем с Postgres.