NerdGerl

Grails Pagination with Multiple Order Clauses – How To

Posted by: nerdgerl on: August 25, 2009

There is a known bug in grails where you can’t have multiple order by’s when returning a PagedResultList. When talking about the .createCriteria().list() method it seems you can only specify the order by inside the .list() method and not in the builder body itself even if only ordering by one values.

This is very annoying as I think multiple order by’s would be a common requirement of a lot of apps. Below is my work around to this bug. I’ll investigate providing a patch to fix this in the near future.

Let’s start by taking a look at three ways you might use to query the database:

Our domain class looks like this:

@Entity
@Table(name = "tbl_book")
public class Book {

@Id
@Column(name = "bookId")
private int id;
private String author;
private String title;
private Date releaseDate;

// getters and setters

  1. Dynamic Methods:
    def results = Book.findAllByAuthor("Marsden", [max:10, offset:0, sort:"releaseDate", order:"desc"])

    The above method would return a PagedResultList ordered by releaseDate descending, but what if we want to order by venue as well? We can’t! Moving on…

  2. def results = Book.createCriteria()
             def results = Book.createCriteria().list(
                 max: params.max,
                 offset: params.offset) {
                 and {
                     eq('author', 'Marsden')
                     order('releaseDate', 'desc')
                     order('title', 'asc')
                 }
             }  

    The above thows a runtime sql exception, you can get it to work only by including the order in the .list() function and only if you include just one order statement, further details of the bug here. Sigh…

  3. def results = Book.withCriteria()
            def results = Book.withCriteria() {
                maxResults(params.max?.toInteger())
                firstResult(params.offset?.toInteger())
    
                and {
                    eq('author', 'Marsden')
                    order('releaseDate', 'desc')
                    order('title', 'asc')
                }
            }
    

    The above looks fine except it doesn’t return an instance of PagedResultList which means we don’t have access to the results.totalCount method which gives us the total number of results needed for pagination in the g:pagination tag in the view. Still no dice…!

Solution

Use query method 3, Book.withCriteria() and then create a seperate query to return the total number of results eg:

        def rowCount = Book.createCriteria().get() {
            projections {
                rowCount()
            }
            and {
                eq("author", params.author)
            }
        }

rowCount should then be passed to your view so the pagination tag can use it:

            <div class="paginateButtons">
                <g:paginate total="${rowCount}" />
            </div>

This solution is really ugly. Im not a fan of having to execute the query twice just to get the total count. I really hope this bug is fixed soon, or someone tells me im missing something fundamental :)

Nabble questions on this here and here.

Tags: ,

9 Responses to "Grails Pagination with Multiple Order Clauses – How To"

Hi, nice post, I would like to say that your second way fails with the in-memory hsqldb db, but it runs fine with oracle 9 and maybe for other databases. Don’t ask me why ;) (jdbc driver problem?)

Hey, Thanks for trying that out! Im using SQL Server 2008, perhaps you’re right about a jdbc driver problem…

Hi!!!
I have an application and I use double order and only I have one query, like this:

def criteria = Content.createCriteria()
def lastContent = criteria.list {
eq(‘publish’,true)
firstResult(params.offset?.toInteger() ?: 0)
order(‘priority’,'asc’)
order(‘created’,'desc’)
}
println lastContent.size()

and it works perfectly…
I’ll hope that this works for u 2…
Regards
neodevelop

Hi Jose,
Thanks for your response.
The above would work for me too if i didn’t need pagination, but as I do need pagination I need to set the max and offset params as shown in example 2 then I get an error with SQL Server 2008.

Are you able to paginate with your above query?

Cheers
Georgi

mmm, well maybe you can use a dynamic finder :P , a dynamic finder provides you with the count method, I try to explain it:

def rowCount = Race.countByCountry(params.country)

The dynamic finder is based in the properties of your class…

u can send it in the model to the view or even you can put the right side of this instruction in the total parameter in tag g:paginate(but maybe that is a lil’ugly)

I hope that this help u…

When i do this:

def results = Task.withCriteria() {
firstResult(offset)
maxResults(max)
and {
order(‘taskDate’,'desc’)
}

I get the following:
No signature of method: java.lang.String.call() is applicable for argument types: (java.lang.String, java.lang.String) values: [taskDate, desc]

if i remove the order clause everything works fine. Is anybody out there who can help me with this? taskDate is definitely a field in Task.

Hi There.
Without firing up my Grails environment, is it because you don’t have any criteria other than the order by, eg. eq(‘name’,'taskName’)?
Cheers,
Georgi

hey Georgi,
thanks – but no its not, i put in some other criteria and no change, my grails 1.1.1 doesn’t know about order(”,”) in criteria. not at all. doesn’t matter where. it seems like you have a different language ;) Any other assumptions? I am almost before deleting all of it and using hql… but i can’t really believe this behavior.
Nico

“Im not a fan of having to execute the query twice just to get the total count.”

For the record, that’s exactly what a criteria call with pagination parameters does internally, so it’s no quicker or slower to run the separate rowCount query yourself, just a bit less DRY.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.