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.
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).
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 ?
The LazyInitializationException is because the objects in testModel are not attached to the current hibernate session. I've just got around this by hacking the JasperController as follows:
class JasperController {
JasperService jasperService
// We need this to access the current hibernate session
def sessionFactory
def index = {
println(params)
def testModel = this.getProperties().containsKey('chainModel') ? chainModel : null
// Re-attach model objects to avoid LazyInitializationException.
def session = sessionFactory.getCurrentSession()
testModel?.data?.each
{
session.update(it)
}
JasperReportDef report = jasperService.buildReportDefinition(params, request.getLocale(), testModel)
generateResponse(report)
}
... and so on. This re-attaches the model objects, avoiding the LIE (without having to eager fetch)
I have a *.jasper (DataSource XML. I used iReport). How can pass it (book as XML)??
Thanks beforehand
You can use a jasper report to call your gorm but you need to set the language for the report to groovy not java as it's default. Then if you use subreports you need to disable jasper report multithreading and add additional jars. It's just work perfectly.