Does Hibernate PersistentSet maintain order when using @OrderBy


#1

I would appreciate confirmation and some explanation that when I use the Set interface to map a collection in combination with the @OrderBy annotation I am indeed using a class that maintains the insert order and guarantees that order will not change (at least under the condition I use that object as read-only and don’t mess around with the contents).

E.g a patient has many samples expressed in this way:

@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "patient")
@OrderBy("gpol_id DESC")
private Set<Sample> samples = new HashSet<Sample>(0);

With the following getter and setter:

public Set<Sample> getSamples() {
	LOGGER.debug("********* Samples set is a {}",samples.getClass());
	return samples;
}

public void setSamples(Set<Sample> samples) {
	this.samples = samples;
}

Although I instantiate the Set with an empty HashSet this must get overwritten by a call by hibernate to the Setter method which instantiates the Set with its own implementation of Set: PersistentSet. My logger call confirms this. However the docs for this class (https://docs.jboss.org/hibernate/orm/4.3/javadocs/org/hibernate/collection/internal/PersistentSet.html )state:

“A persistent wrapper for a java.util.Set. The underlying collection is a HashSet.”

But don’t mention anything about maintaining order. A reference from jboss does suggest that a LinkedHashSet is used:

http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/collections.html

“If you want the database itself to order the collection elements, use the order-by attribute of set, bag or map mappings. This solution is implemented using LinkedHashSet or LinkedHashMap and performs the ordering in the SQL query and not in the memory.”

However, as stated above it looks to me like hibernate does not use a LinkedHashSet, but instead is a PersistentSet. I can’t find any java docs on a hibernate LinkedHashSet, just those about the java util LinkedHashSet. Overall I am left uncertain about what is really going on. Does it all come down to the unstated (as far as i am aware) that PersistentSet is an implementation of the Set interface analogous to java’s LinkedHashSet?


#2

If you use the @OrderBy annotation, the PersistentSet will use the OrderedSetType behind the scenes. The OrderedSetType will instantiate a LinkedHashSet so the order will be preserved.

public class OrderedSetType extends SetType {

    public OrderedSetType(TypeFactory.TypeScope typeScope, String role, String propertyRef) {
        super( typeScope, role, propertyRef );
    }

    @Override
    public Object instantiate(int anticipatedSize) {
        return anticipatedSize > 0
                ? new LinkedHashSet( anticipatedSize )
                : new LinkedHashSet();
    }

}

Now, that’s for the part when Hibernate loads the Set. For consistency sake, you need to use a LinkedHashSet for the transient state as well:

@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "patient")
@OrderBy("gpol_id DESC")
private Set<Sample> samples = new LinkedHashSet<Sample>(0);

Although Hibernate replaces the LinkedHashSet when loading the entity, this change is important when you create the entity for the first time since you want the order to be preserved right from the start.


#3

Thanks for the detailed explanation and code review Vlad. That makes sense.