Критерии гибернации - возвращать записи, где столбец отличается

Пример таблицы базы данных:

  1. ID = 1, msgFrom = 'Hello', foobar = 'meh'
  2. ID = 2, msgFrom = 'Goodbye', foobar = 'comments'
  3. ID = 3, msgFrom = 'Hello', foobar = 'response'

Пример желаемого результата (генерируется запросом гибернации):

  1. ID = 1, msgFrom = 'Hello', foobar = 'meh'
  2. ID = 2, msgFrom = 'Goodbye', foobar = 'comments'

В приведенном выше примере третья запись будет исключена из результатов, поскольку столбец msgFrom такой же. Допустим, класс Java/Hibernate называется Message. Я хотел бы, чтобы результаты были возвращены в виде списка объектов сообщения (или объектов, которые в любом случае могут быть преобразованы в сообщение). Я хочу использовать Criteria API, если это возможно. Я видел этот пример на SO, и он кажется похожим, но я пока не могу правильно его реализовать.

 select e from Message e 
    where e.msgFrom IN (select distinct m.msgFrom 
                          from Message m
                          WHERE m.msgTo = ? 
                          AND m.msgCheck = 0");

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

редактировать: статья показывает в основном то, что я хочу сделать. http://oscarvalles.wordpress.com/2008/01/28/sql-distinct-on-one-column-only/

3 ответа

Решение

Пожалуйста, попробуйте это и дайте мне знать

DetachedCriteria msgFromCriteria = DetachedCriteria.forClass(Message.class);
ProjectionList properties = Projections.projectionList();
properties.add(Projections.groupProperty("messageFrom"));
properties.add(Projections.min("id"),"id");
msgFromCriteria.setProjection(properties);

Criteria criteria = s.createCriteria(Message.class);
criteria.add(Subqueries.propertiesIn(new String[]{"messageFrom","id"}, 
    msgFromCriteria));
List<Message> list = criteria.list();

for(Message message:list){
    System.out.println(message.getId()
        +"-------"
        +message.getMessageFrom()
        +"-----"
        +message.getFoobar());
}

Сложность этого запроса заключается не столько в самом Hibernate, сколько в реляционной модели в целом. В этом примере вы говорите, что ожидаете строки 1 и 2, но почему бы вам так же легко не ожидать строки 2 и 3? Было бы произвольным решением, возвращать ли строку 1 или строку 3, поскольку они оба имеют одинаковое значение в поле msgFrom. Базы данных не будут принимать произвольные решения, подобные этому. Вот почему distinct должен применяться ко всему списку выбранных столбцов, а не к подмножеству. Существуют специфичные для базы данных способы получения первых подходящих строк. Например, посмотрите на

ВЫБЕРИТЕ DISTINCT на одном столбце

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

Как я могу выбрать строки с MAX(значение столбца), DISTINCT по другому столбцу в SQL?

Извлечь строку, которая имеет значение Max для столбца

Если вам не нужны другие столбцы, вы можете просто distinctв сочетании с синтаксисом конструктора Hibernate (не тестировался):

select new Message(msgFrom) from (select distinct msgFrom from Message)

но вы должны принять все остальные столбцы.

В конце концов, я часто просто делаю это в коде как фильтр пост-запросов. Другой вариант - создать другую таблицу, скажем, CurrentMessage, которая включает в себя msgFrom как часть ключа. Будет больше работы по поддержанию этой таблицы в актуальном состоянии (вам нужно будет обновлять строку каждый раз, когда вы добавляете строку в таблицу сообщений), но запросы будут намного проще.

DetachedCriteria msgFromCriteria = DetachedCriteria.forClass(Message.class);
msgFromCriteria.setProjection(Projections.distinct(Projections.property("msgFrom")));
....
Criteria criteria = getSession().createCriteria(Message.class);
criteria.add(Subqueries.propertyIn("msgFrom", msgFromCriteria));
criteria.list();
Другие вопросы по тегам