views:

649

answers:

2

Assuming I have classes User and UserGroup. There is an optional 1-many group to user association and the association is mapped from both sides (the UserGroup side via a property called "members", which is a HashSet, the User side via property "group").

Using the Criteria API, how can I query for all groups, sorted by count of group members?

(Edit) I should have pointed out when I asked this that pagination was being performed in the SQL, so the ordering should also be performed in the SQL if possible.

A: 

You could probably so this by defining a "derived property" on your entity, which would be a read-only property which returns the size of the collection inside that entity. The easiest way to define a derived property is documented here:

update, insert (optional - defaults to true): specifies that the mapped columns should be included in SQL UPDATE and/or INSERT statements. Setting both to false allows a pure "derived" property whose value is initialized from some other property that maps to the same column(s), or by a trigger or other application.

Once the property is defined, you should be able to use the property name as an order clause in the criteria API, just like any other property. I haven't tried this myself, though.

If it works, it'll likely be done as an in-memory sort, since it won't be able to map it to SQL. If this is a problem, then the same link above documents how to implement a derived property using a custom SQL fragment:

A powerful feature is derived properties. These properties are by definition read-only. The property value is computed at load time. You declare the computation as an SQL expression. This then translates to a SELECT clause subquery in the SQL query that loads an instance:

skaffman
A: 

Another option would be to use the @Formula annotation which is basically the equivalent of creating a nested select so you can get at your counts.

On your DB objects property / member in question (which you should create), add the annotation to the getter like:

@Formula("(SELECT COUNT(TABLE_NAME.ID) FROM TABLE WHERE whatever...)") public long getNewProperty{ return newProperty; }

Then target the member directly to get hold of your count values...

Note: Be aware that when specifying the sql within the formula annotation, you must specify the field names directly when referencing the current objects table (this) as hibernate deals with this itself. So in your WHERE clause just use ID or whatever on its own when referencing the current objects table which I suspect is UserGroup.

I had to have a setter for my new property in order for hibernate to work and prevent the compiler from complaining.

There may be other clever ways of using @Formula, but I'm quite new to this and there doesn't appear to be very much documentation on the subject...

If you've never used nested SELECTS before, it might be an idea for you to just recreate in sql first - that helped me.

Hope this helps

Mucker 2.0
Whilst this implementation seems slightly dubious (in terms of hibernate documentation), it does indeed work!
ireddick