Creating Report with List containing List using Jasper Report
Published on: Author: Binu Badarudeen Category: Java & WebCreating reports is a common scenario in any enterprise application. Jasper Reports, together with the Tibco Community edition designer tool Jaspersoft, makes this task easy for you, especially in Java applications.
Most of the normal reports can be generated without any trouble, using Jaspersoft for designing the report template and Java for supplying data and integrating. Still, there are a few scenarios that are not straightforward, and not clearly documented.
Recent project
We faced such a complex case in a recent project. We had to create a report to show a list which internally contained another list. If you know how that works it is easy to implement, otherwise it is going to take lot of time to investigate. I will try to simplify the scenario: I am going to generate a report of all the students, and for each student I want to show their courses.
Scenario:
We want to generate a report of all the students, and the courses they are currently following. We have a List<Student> and each Student contains List<Courses>
Using Jasper Report and Java we are going to generate a report similar to this.
Step 1: Create Project with Jasper dependency
<dependency> <groupId>net.sf.jasperreports</groupId> <artifactId>jasperreports</artifactId> <version>6.7.0</version> </dependency>
Step 2: Create Course Class
Step 3: Create Student Class
public class Student { private List<Course> courseList = new ArrayList<>(); private JRBeanCollectionDataSource coursedataSource; return name; } this.name = name; } return email; } this.email = email; } public List<Course> getCourseList() { return courseList; } public void setCourseList(List<Course> courseList) { this.courseList = courseList; } public JRBeanCollectionDataSource getCoursedataSource() { return new JRBeanCollectionDataSource(courseList, false); } }
Student contains a List<Course> entry; this need to be filled in at run time.
Student also contains an instance variable and a getter to get the courses as a data source for the Jasper report.
private JRBeanCollectionDataSource coursedataSource; public JRBeanCollectionDataSource getCoursedataSource() { return new JRBeanCollectionDataSource(courseList, false); }
Step 4: Create a report Input Class to hold all data needed for Report
public class StudentReportInput { private JRBeanCollectionDataSource studentDataSource; return instituteName; } this.instituteName = instituteName; } public JRBeanCollectionDataSource getStudentDataSource() { return studentDataSource; } public void setStudentDataSource(JRBeanCollectionDataSource studentDataSource) { this.studentDataSource = studentDataSource; } parameters.put("P_INSTITUTE_NAME", getInstituteName()); return parameters; } dataSources.put("studentDataSource", studentDataSource); return dataSources; } }
Step 5: Fill StudentReportInput with your data
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; public class StudentReportDataAssembler { public static StudentReportInput assemble() { StudentReportInput studentReportInput = new StudentReportInput(); studentReportInput.setReportTitle("Student Report"); studentReportInput.setInstituteName("My Institute"); List<Student> students = new ArrayList<>(); //Add Student1 Student student1 = new Student(); student1.setName("Mark"); student1.setEmail("mark1234@gmail.com"); List<Course> student1Courses = new ArrayList<>(); Course course1Student1 = new Course(); course1Student1.setName("History"); course1Student1.setLocation("L1"); student1Courses.add(course1Student1); …... student1.setCourseList(student1Courses); students.add(student1); //Add Student2 JRBeanCollectionDataSource studentDataSource = new JRBeanCollectionDataSource(students, false); studentReportInput.setStudentDataSource(studentDataSource); return studentReportInput; } }
Step 6: Create fields and Datasets in Jrxml
Create field for student datasource.
Create StudentDataSet with coursedataSource as field of type JRBeanCollectionDataSource:
Create CourseDataSet:
Step 7: Create List for Students with studentDataSource
Step 8: Create List for Courses inside Students List
Drag and drop the List component inside Student List.
Notice that here, we use the Jasper internal parameter “REPORT_CONNECTION” as the JRDatasource. You can find this in the list of parameters if you click on the Edit image right of the text box for JRDatasource expression.
- Click on the “Parameters” button.
- Click on the “Add” button.
- Select Parameter Name “REPORT_DATA_SOURCE
- Click on Parameter Expression edit icon and select courseDataSource.
JRXML View:
Step 9: Generate Report
public class StudentReportGenerator { StudentReportInput studentReportInput = StudentReportDataAssembler.assemble(); byte[] reportData = null; try { JRMapArrayDataSource dataSource = new JRMapArrayDataSource(new Object[]{studentReportInput.getDataSources()}); JasperPrint jasperPrint = JasperFillManager.fillReport(TemplateCompiler.studentReportTemplate, studentReportInput.getParameters(), dataSource); reportData = JasperExportManager.exportReportToPdf(jasperPrint); } catch (JRException e) { e.printStackTrace(); e.printStackTrace(); } PdfObject pdfObject = new PdfObject.PdfObjectBuilder() .creationDate(LocalDate.now()) .pdfContent(reportData) .fileName("studentReport") .build(); return pdfObject; } }
Note: TemplateCompiler.studentReportTemplate static variable holds the compiled report file. Compiling the report once and reusing it for every report generation is efficient.
Important steps to note:
- Creating a JRBeanCollectionDataSource field to hold sub list data in parent object.
- Use of REPORT_CONNECTION as the JRDatasource on sub list
- REPORT_DATA_SOURCE should point to the datasource for sub list.
This approach can be used to display the data in Tables too. There are also other ways to implement, such as using subreports. Most of the Jasper Report documentation can be found here.