Hibernate: ordering a Set
Asked Answered
A

4

15

I have a class Person who has a set of Books. It is not meaningful in the particular case to have an ordered or sorted collection.

Say now that I have a search page with a table showing the join of Person and Book. I want to be able to sort the results by fields from both Person AND Book, and then get a List from Hibernate, and iterate over it.

Because the collection is a Set, the ordering of the Books has vanished (PersistentSet of Hibernate wraps a HashSet of Books, which is not ordered).

So, with this approach I cannot have results also ordered by Book fields.

If I change the collection from Set to List, my model is semantically incorrect. There is no meaning for keeping order in the model.

Is there an approach to keep the ordering of Books? Perhaps there is a way for the PersistentSet to wrap a LinkedHashSet (which is ordered), where the order is defined by my search Criteria?

Cheers!

Amil answered 1/4, 2010 at 13:17 Comment(0)
P
9

Hibernate supports mapping a collection as a SortedSet. In your mappings you basically just need to specify an order-by clause. Take a look at this chapter in the reference manual.

Peephole answered 1/4, 2010 at 13:23 Comment(4)
Thanks for the response (+1). Yes, I've seen that. But in the example they have fixed that the ordering is on a certain field. I want that field(s) to be set through the Criteria API, depending on what the user has clicked in the UI.Amil
You can specify an ordering for results in a Criteria query as well, separate from whatever you specify in the mapping. docs.jboss.org/hibernate/stable/core/reference/en/html/…Peephole
Yes you can, but the unfortunately order-by in the mapping (or @OrderBy) takes precedence, which makes the ordering set by the Criteria useless. Or at least, it comes after the sorting of the order-by of the mapping.Amil
The indicated URL was changed meanwhile, here is the updated one: docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/…Isabellaisabelle
S
4

Like said Markos Fragkakis

unfortunately order-by in the mapping (or @OrderBy) takes precedence, which makes the ordering set by the Criteria useless.

But you must set @Order if you want to have the Set ordered.

You can still use HQL instead ( tested on hibernate 3.3.2.GA ) who order firstly by the order in the hql query :

    @Entity
    @Table(name = "Person")
    public class Person  {

        @Id
        @Column(name = "ID_PERSON", unique = true, nullable = false, precision = 8, scale = 0)
        private Long id;

        @OneToMany(fetch = FetchType.LAZY, mappedBy = "person")
        @OrderBy
        private Set<Book> books = new HashSet<Book>(0);

        public Person() {
        }

        public Long getId() {
            return this.id;
        }

        public void setId(Long id) {
            this.id = id;
        }


      public Set<Book> getBooks() {
            return this.books;
        }

        public void setBooks(Set<Book> books) {
            this.books = books;
        }

    }

      /** 
       *  hql Version 
       *    
       *  Result in : 
       *  order by
       *      book1_.TITLE asc,
       *      book1_.ID_BOOK asc  
       **/

        @Override
        public Person getFullPerson(Long idPerson) {

            StringBuilder hqlQuery =  new StringBuilder();
            hqlQuery.append("from Person as p ");
            hqlQuery.append("left join fetch p.books as book ");
            hqlQuery.append("where p.id = :idPerson ");
            hqlQuery.append("order by book.title ");
            Query query = createQuery(hqlQuery.toString());
            query.setLong("idPerson", id);
            return uniqueResult(query);

        }




      /** 
       *  criteria  Version // not usable 
       *    
       *  Result in : 
       *  order by
       *      book1_.ID_BOOK asc,
       *      book1_.TITLE asc  
       **/

      @Override
    public Person getFullPersonCriteria(Long idPerson) {

        Criteria criteria = ...
        criteria.add(Restrictions.eq("id", idPerson));
        criteria.createAlias("books", "book", CriteriaSpecification.LEFT_JOIN);
        criteria.addOrder(Order.asc("book.title"));
            return criteria.uniqueResult();
      }
Sprag answered 25/9, 2012 at 10:40 Comment(0)
A
3

This is an in-memory sort that will take care of duplicates in the join table.

  @OneToMany
  @JoinTable(name = "person_book", joinColumns = @JoinColumn(name = "person_id"),
             inverseJoinColumns = @JoinColumn(name = "book_id"))
  @Sort(type = SortType.COMPARATOR, comparator = MyBookComparator.class)
  private SortedSet<BookEntity> books;
Arnulfoarny answered 1/8, 2013 at 17:13 Comment(0)
A
-1

not sure if i got the question right but if you just want an ordererd version of your list you can just sort it with the java.util.Collections class and a Comparator. Maybe you want to make a transient Method on your pojo like this:

@Transient
public List<Book> getBooksASC()
{
    Collections.sort(this.books, new BookSorter.TitelASCSorter());
    return this.books;
}

Just write a Class which implements Comparator.

Acerose answered 6/1, 2012 at 13:36 Comment(2)
-1: This works for lists, but not for sets and the list is sorted every time the getter is called.Firebreak
you have to sort the collection on every call, if you use a collection which does not retain its ordering on every add/remove.Acerose

© 2022 - 2024 — McMap. All rights reserved.