Question

I have an application in production use, when a user goes to a proposal index page it takes a very long time and sometimes times out. I've narrowed down the issue to be a SQL statement that is selecting all the Proposal objects. The problem is the Proposal object has many images (byte[]) stored in memory that aren't being used in the index page. These images are huge thus causing the problem.

What are the different ways I can optimize this query in Grails to remove the attributes I don't need on that page or only add the attributes I have in my GSP?

Here is the controller code (scaffolded):

    def index(Integer max) {
        params.max = Math.min(max ?: 10, 100)
        respond Proposal.list(params), model:[proposalInstanceCount: Proposal.count()]
    }

Thanks!

Was it helpful?

Solution

I wrote a plugin for this scenario, see http://grails.org/plugin/lazylob

Another option is to refactor the domain class into two. Put the image data in the new domain class:

class ProposalImage {
   byte[] image
}

and reference it from the Proposal class:

class Proposal {
   ProposalImage proposalImage
   // other properties
}

Since references are lazy by default, GORM will only load the image data from the new domain class if you specifically refer to it.

EDIT (updated with subselect approaches):

You can also use custom queries to select a subset of the properties. Probably the most convenient would be using "select new map" in an HQL query:

def results = Proposal.executeQuery(
   'select new map(prop1 as prop1, prop2 as prop2) from Proposal',
   [max:params.max as int, params.offset as int])

This is convenient because each element in the results list is a map keyed with the property names, so it will look the same as a real Proposal instance in the GSP.

Another option if you prefer criteria queries is to use projections to limit which properties are returned:

def results = Proposal.withCriteria {
   projections {
      property 'prop1'
      property 'prop2'
   }
   maxResults(params.max as int)
   firstResult(params.offset as int)
}

Each item in the results is an Object[] array and each element in the array is the actual type of the property. You would need to manually build a list of maps, e.g.

results = results.collect { result -> [prop1: result[0], prop2: result[1]] }

Additionally, you can automate this by finding all of the names of the persistent properties and excluding the one (or ones) you want to avoid: def propNames = grailsApplication.getDomainClass(Proposal.name).persistentProperties*.name

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top