Подтвердить что ты не робот

Загрузка нескольких объектов по id эффективно в HIbernate 4

Итак, я получаю несколько экземпляров определенного объекта с помощью id

for(Integer songId:songGroup.getSongIds())
{
   session = HibernateUtil.getSession();
   Song song = (Song) session.get(Song.class,id);
   processSong(song);
}

это генерирует SQL-запрос для каждого идентификатора, поэтому мне казалось, что я должен сделать это в одном, но я не мог найти способ получить несколько объектов за один вызов, кроме как запустив запрос. Поэтому я написал запрос

return (List) session.createCriteria(Song.class)
       .add(Restrictions.in("id",ids)).list();

но если я разрешаю кэширование второго уровня, это не означает, что мой старый метод сможет вернуть объекты из кеша второго уровня (если они были запрошены ранее), но мой запрос всегда будет поступать в базу данных.

Каков правильный способ сделать это?

4b9b3361

Ответ 1

То, что вы просите сделать здесь, - это Hibernate, чтобы делать специальную обработку дела для ваших критериев, что очень много, чтобы спросить.

Вам нужно будет сделать это самостоятельно, но это не сложно. Используя SessionFactory.getCache(), вы можете получить ссылку на фактическое хранилище для кешированных объектов. Сделайте что-то вроде следующего:

for (Long id : allRequiredIds) {
  if (!sessionFactory.getCache().containsEntity(Song.class, id)) {
    idsToQueryDatabaseFor.add(id)
  } else {
    songs.add(session.get(Song.class, id));
  }
}

List<Song> fetchedSongs = session.createCriteria(Song.class).add(Restrictions.in("id",idsToQueryDatabaseFor).list();
songs.addAll(fetchedSongs);

Затем песни из кеша будут извлечены оттуда, а те, которые не будут вытаскиваться с помощью одного select.

Ответ 2

Если вы знаете, что идентификаторы существуют, вы можете использовать load (..) для создания прокси-сервера без фактического попадания в БД:

Возвращает постоянный экземпляр данного класса сущности с данным идентификатором, получая указанный режим блокировки, предполагая, что экземпляр существует.

List<Song> list = new ArrayList<>(ids.size());
for (Integer id : ids)
  list.add(session.load(Song.class, id, LockOptions.NONE));

После того, как вы получите доступ к неидентификатору, Hibernate проверит кэши и вернется в DB, ​​если это необходимо, используя пакетную выборку, если она настроена.

Если идентификатор не существует, объект ObjectNotFoundException будет возникать после загрузки объекта. Это может быть где-то в вашем коде, где вы бы не ожидали исключения - в конце вы используете простой аксессор. Таким образом, либо на 100% убедитесь, что ID существует, либо, по крайней мере, вынуждает ObjectNotFoundException раньше, когда вы ожидаете его, например. сразу после заполнения списка.

Ответ 3

Существует разница между спящим кэшем второго уровня для спящего кэша запросов. Следующая ссылка очень хорошо объясняет: http://www.javalobby.org/java/forums/t48846.html

В двух словах, Если вы используете один и тот же запрос много раз с теми же параметрами, вы можете уменьшить количество обращений к базе данных, используя комбинацию обоих.

Ответ 4

Еще одна вещь, которую вы можете сделать, - сортировать список идентификаторов и идентифицировать подпоследовательности последовательных идентификаторов, а затем запрашивать каждую из этих подпоследовательностей в одном запросе. Например, при задании List<Long> ids выполните следующие действия (предполагая, что у вас есть класс Pair в Java):

List<Pair> pairs=new LinkedList<Pair>();
List<Object> results=new LinkedList<Object>();
Collections.sort(ids);
Iterator<Long> it=ids.iterator();

Long previous=-1L;
Long sequence_start=-1L;
while (it.hasNext()){
    Long next=it.next();

    if (next>previous+1) {
        pairs.add(new Pair(sequence_start, previous));
        sequence_start=next;
    }
    previous=next;
}
pairs.add(new Pair(sequence_start, previous));

for (Pair pair : pairs){
    Query query=session.createQuery("from Person p where p.id>=:start_id and p.id<=:end_id");
    query.setLong("start_id", pair.getStart());
    query.setLong("end_id", pair.getEnd());

    results.addAll((List<Object>)query.list());

}