Reputation: 969
I would like to have a collection of child objects (here cat-kitten example) that are ordered. And keep their order on adding of new elements.
@Entity
public class Cat {
@OneToMany(mappedBy = "cat", cascade = CascadeType.ALL)
@OrderBy("name ASC")
private List<Kitten> kittens;
public void setKittens(List<Kitten> kittens) { this.kittens = kittens; }
public List<Kitten> getKittens() { return kittens; }
}
When I do cat.getKittens.add(newKitten)
the order by name will be broken.
Is it possible to let hibernate do the work of keeping the collection always ordered? By using the @Sort hibernate annotation?
@Sort has the disadvantage that it forces you to implement Comparable interface ...
What would be the right 'pure JPA' way to do that? Saving everything to DB and reloading it?
Makes it sense to combine @OrderBy and @Sort?
I'll keep you posted what I find out... thanks for your help.
Upvotes: 38
Views: 90558
Reputation: 26868
For Hibernate 6, the recommended way is to use a SortedSet
for the collection, annotated with @SortNatural
or @SortComparator
according to the docs.
From the user guide:
An ordered set does not perform any sorting in-memory. If an element is added after the collection is loaded, the collection would need to be refreshed to re-order the elements. For this reason, ordered sets are not recommended - if the application needs ordering of the set elements, a sorted set should be preferred.
You cannot combine @SortNatural
or @SortComparator
with @OrderedBy
. This throws an exception Collection '...' is both sorted and ordered
.
Upvotes: 1
Reputation: 2941
The latest version of Hibernate uses new annotations to accomplish this:
@SortNatural
@OrderBy("name ASC")
private SortedSet<Kitten> kittens = new TreeSet<>();
There are two parts to this:
@OrderBy
annotation specifies that an order by
clause should be added to the database query when fetching the related records.@SortNatural
annotation assumes that Kitten
implements the Comparable
interface and uses that information when constructing the TreeSet
instance. Note that you can replace this with the @SortComparator
annotation, which allows you to specify a Comparator
class that will be passed to the TreeSet
constructor.See documentation: https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#collections-sorted-set
Upvotes: 25
Reputation: 11984
If you want to avoid non-standard annotations, you could make kittens
use some sorted Collection
implementation. This would ensure that kittens
is always in sorted order. Something like this:
@Entity
public class Cat {
@OneToMany(mappedBy = "cat", cascade = CascadeType.ALL)
@OrderBy("name ASC")
private SortedSet<Kitten> kittens = new TreeSet<>();
}
Note that this approach also requires Kitten
to implement Comparable
(alternatively, you could pass a Comparator
to your TreeSet
constructor). Also, I'm using a Set
because I'm not aware of any standard sorted List
implementation and I'm assuming the Cat
does not have any clones in its litter =p.
Update:
I'm not sure how picky Hibernate is with its getter/setter definitions, but with EclipseLink I've been able to remove a setter entirely and wrap the List
returned by my getter in a Collections.unmodifiableList(...)
call. I then defined special methods for modifying the collection. You could do the same thing and force callers to use an add method that inserts elements in sorted order. If Hibernate complains about not having the getter/setter, maybe you could change the access modifier? I guess it comes down to how far you're willing to go to avoid non standard dependencies.
Upvotes: 31