views:

685

answers:

2

I would like to use subreports with grails jasper plugins, I followed the manual at this url (http://www.grails.org/plugin/jasper). Here is my code :

Domain Book :

class Book {

  static belongsTo = Library

    Library library

    String title
    String author
    String publisher
    String category

    static constraints={
        title()
        author()
        publisher()
        category()
    }
}

Domain Library :

class Library {

  static hasMany = [ books : Book ]

  String name
  String adresse
  Date dateMaturity

    static constraints = {
    }

        String toString()
    {
        return name
    }
}

In my BookController, I have :

def createReport = {
    def books = Book.list()
    chain(controller:'jasper',action:'index',model:[data:books],params:params)
}

In my LibraryController, I have :

def createReport = {
    def library = Library.list()
    chain(controller:'jasper',action:'index',model:[data:library],params:params)
}

My jasper part is :

I have a SubReport file : books.jasper (get a list of books).
Also a MasterReport : library.jasper (get a list of library).

In my MasterReport(library), I added subreport, I would like, for each library, show list of books it contains ; here is my library code :

<parameter name="SUBREPORT_DIR" class="java.lang.String" isForPrompting="false">
...
<field name="books" class="java.util.Collection"/>
...
<subreport isUsingCache="true">
<reportElement x="0" y="25" width="437" height="100"/>
<dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{books})]]></dataSourceExpression>
<subreportExpression class="java.lang.String"><![CDATA[$P{SUBREPORT_DIR} + "books.jasper"]]>
</subreportExpression>
</subreport>

And I have this error :

Error 500: Executing action [index] of controller [JasperController] in plugin [jasper] caused exception: net.sf.jasperreports.engine.fill.JRExpressionEvalException: Error evaluating expression : Source text : new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{books})
Exception Message: failed to lazily initialize a collection of role: bookshelf.Library.books, no session or session was closed

Thank you for help.

A: 

I find the issue :

In my Library domain, I just add a mapping with "books lazy: false" :

class Library {

  static hasMany = [ books : Book ]

  String name
  String adresse
  Date dateMaturity

    static constraints = {
    }

    static mapping = {
    books lazy: false
  }

        String toString()
    {
        return name
    }
}

And now, I have my report witout troubles !


The grails jasper plugin use chain to chains the model from one action to the next (chain(controller:'jasper',action:'index',model:[data:library],params:params).

After, in jasper controller we get the model by this line :

def testModel = this.getProperties().containsKey('chainModel')?chainModel:null

For some reason, in Library collection, we have an error on books list, eg : org.hibernate.LazyInitializationException: failed to lazily initialize a collection - no session or session was closed

By using "lazy: false", we pull all other instances of the domain class.

Is there another way to fix this issue ?

Fabien Barbier
+1  A: 

Jasper reports just expects the list of objects. It doesn't understand GORM queries. So the way we do is by creating a separate list of objects which we name as 'View Objects' and then send them to jasper reports instead of domain classes.

 class LibraryVO {
   List books
   String name
   String adresse
   Date dateMaturity
  }

 class bookVO {
  String title
    String author
    String publisher
    String category
  }

You can initializ the list like

 List data=[]

 LibraryVo libVo= new LibraryVO(...) // inalise it here
 libVo.books = [new BookVO(),new BookVO()]

 data << libVO

And pass the list to jasper controller

(chain(controller:'jasper',action:'index',model:[data:data],params:params).
Amit Jain
By using "def library = Library.list()", I have also a list, and I pass also my list to jasper controller. What difference ?
Fabien Barbier
Library.list() gets you the list of domain objects, lazily fetched so books are yet not retrieved from the database. They will be retrieved on demand. But the LibraryVO list we pass would already have book objects. And so we don't need to say lazy:false. Fetching objects lazily is the job of hibernate session and jasper reports are completely unaware of it.
Amit Jain