Hibernate Criteria — return records where column i

2019-04-30 03:57发布

Sample database table:

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

Sample desired output (generated by hibernate query):

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

In the above example, the third record would be excluded from the results since the msgFrom column is the same. Let's say the Java/Hibernate class is called Message. I would like the results to be returned as a list of Message objects (or Objects that can be cast to Message, anyway). I want to use the Criteria API if possible. I saw this example on SO and it seems similar but I cannot implement it correctly as of yet.

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

The reason I am doing this is to have the filtering of distinct records done on the database, so I am not interested in answers where I have to filter anything on the application server.

edit: Article showing basically what I want to do. http://oscarvalles.wordpress.com/2008/01/28/sql-distinct-on-one-column-only/

3条回答
2楼-- · 2019-04-30 04:33

The difficulty with this query is not so much with Hibernate, per se, but with the relational model in general. In the example, you say you expect rows 1 and 2, but why wouldn't you just as easily expect rows 2 and 3? It would be an arbitrary decision whether to return row 1 or row 3 since they both have the same value in the msgFrom field. Databases won't make arbitrary decisions like this. That's why distinct must be applied to the entire list of select columns, not a subset. There are database-specific ways of grabbing the first matching rows. For example, have a look at

SELECT DISTINCT on one column

Sometimes there will be a date column that you can use to decide which of the matching rows to return, but again the queries get somewhat complex:

How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL?

Fetch the row which has the Max value for a column

If you don't care about any of the other columns, you can just use a simple distinct, combined with Hibernate's constructor syntax (not tested):

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

but you have to accept throwing away all the other columns.

In the end, I often end up just doing this in code as a post query filter. Another option is to create a another table, say CurrentMessage, that includes msgFrom as part of the key. There will be more work in keeping this table up to date (you need to update a row everytime you add a row to the Message table) but querying will be much easier.

查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-04-30 04:40

Please try this and let me know

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());
}
查看更多
小情绪 Triste *
4楼-- · 2019-04-30 04:41
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();
查看更多
登录 后发表回答