Jasper Report

Creating Report with List containing List using Jasper Report

Published on: Category: Java & Web

Creating 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

  1. <dependency>
  2. <groupId>net.sf.jasperreports</groupId>
  3. <artifactId>jasperreports</artifactId>
  4. <version>6.7.0</version>
  5. </dependency>

Step 2: Create Course Class

  1. public class Course {
  2. private String name;
  3. private String location;
  4. public String getName() {
  5. return name;
  6. }
  7. public void setName(String name) {
  8. this.name = name;
  9. }
  10. public String getLocation() {
  11. return location;
  12. }
  13. public void setLocation(String location) {
  14. this.location = location;
  15. }
  16. }

Step 3: Create Student Class

  1. public class Student {
  2. private String name;
  3. private String email;
  4. private List<Course> courseList = new ArrayList<>();
  5. private JRBeanCollectionDataSource coursedataSource;
  6.  
  7. public String getName() {
  8. return name;
  9. }
  10. public void setName(String name) {
  11. this.name = name;
  12. }
  13. public String getEmail() {
  14. return email;
  15. }
  16. public void setEmail(String email) {
  17. this.email = email;
  18. }
  19. public List<Course> getCourseList() {
  20. return courseList;
  21. }
  22. public void setCourseList(List<Course> courseList) {
  23. this.courseList = courseList;
  24. }
  25. public JRBeanCollectionDataSource getCoursedataSource() {
  26. return new JRBeanCollectionDataSource(courseList, false);
  27. }
  28. }

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.

  1. private JRBeanCollectionDataSource coursedataSource;
  2. public JRBeanCollectionDataSource getCoursedataSource() {
  3. return new JRBeanCollectionDataSource(courseList, false);
  4. }

Step 4: Create a report Input Class to hold all data needed for Report

  1. public class StudentReportInput {
  2. private String reportTitle;
  3. private String instituteName;
  4. private JRBeanCollectionDataSource studentDataSource;
  5. public String getInstituteName() {
  6. return instituteName;
  7. }
  8.  
  9. public void setInstituteName(String instituteName) {
  10. this.instituteName = instituteName;
  11. }
  12. public JRBeanCollectionDataSource getStudentDataSource() {
  13. return studentDataSource;
  14. }
  15. public void setStudentDataSource(JRBeanCollectionDataSource studentDataSource) {
  16. this.studentDataSource = studentDataSource;
  17. }
  18. public Map<String, Object> getParameters() {
  19. Map<String,Object> parameters = new HashMap<>();
  20. parameters.put("P_INSTITUTE_NAME", getInstituteName());
  21.  
  22. return parameters;
  23. }
  24. public Map<String, Object> getDataSources() {
  25. Map<String,Object> dataSources = new HashMap<>();
  26. dataSources.put("studentDataSource", studentDataSource);
  27.  
  28. return dataSources;
  29. }
  30. }

Step 5: Fill StudentReportInput with your data

  1. import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
  2.  
  3. public class StudentReportDataAssembler {
  4. public static StudentReportInput assemble() {
  5. StudentReportInput studentReportInput = new StudentReportInput();
  6. studentReportInput.setReportTitle("Student Report");
  7. studentReportInput.setInstituteName("My Institute");
  8.  
  9. List<Student> students = new ArrayList<>();
  10. //Add Student1
  11. Student student1 = new Student();
  12. student1.setName("Mark");
  13. student1.setEmail("mark1234@gmail.com");
  14. List<Course> student1Courses = new ArrayList<>();
  15.  
  16. Course course1Student1 = new Course();
  17. course1Student1.setName("History");
  18. course1Student1.setLocation("L1");
  19. student1Courses.add(course1Student1);
  20. …...
  21. student1.setCourseList(student1Courses);
  22. students.add(student1);
  23.  
  24. //Add Student2
  25.  
  26. JRBeanCollectionDataSource studentDataSource = new JRBeanCollectionDataSource(students, false);
  27. studentReportInput.setStudentDataSource(studentDataSource);
  28.  
  29. return studentReportInput;
  30. }
  31. }

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

  1. public class StudentReportGenerator {
  2. public PdfObject generateReport() throws IOException {
  3. StudentReportInput studentReportInput = StudentReportDataAssembler.assemble();
  4.  
  5. byte[] reportData = null;
  6. try {
  7. JRMapArrayDataSource dataSource = new JRMapArrayDataSource(new Object[]{studentReportInput.getDataSources()});
  8.  
  9. JasperPrint jasperPrint = JasperFillManager.fillReport(TemplateCompiler.studentReportTemplate,
  10. studentReportInput.getParameters(), dataSource);
  11.  
  12. reportData = JasperExportManager.exportReportToPdf(jasperPrint);
  13. } catch (JRException e) {
  14. e.printStackTrace();
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. }
  18.  
  19. PdfObject pdfObject = new PdfObject.PdfObjectBuilder()
  20. .creationDate(LocalDate.now())
  21. .pdfContent(reportData)
  22. .fileName("studentReport")
  23. .build();
  24.  
  25. return pdfObject;
  26. }
  27. }

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:

  1. Creating a JRBeanCollectionDataSource field to hold sub list data in parent object.
  2. Use of REPORT_CONNECTION as the JRDatasource on sub list
  3. 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.

Binu Badarudeen
About the author Binu Badarudeen

Working as a developer in different J2EE applications from 2005 onward. Certification taken include Cloudera Certified Developer for Apache Hadoop (CCDH), Certified Scrum Master, Certified Scrum Developer, SCJP, SCWCD. Currently working as Software Engineer in the development team of QAFE.

More posts by Binu Badarudeen