Last active
December 15, 2020 09:09
-
-
Save nvurgaft/2ebefbf7c2acca78f975f773da7d0baa to your computer and use it in GitHub Desktop.
An example class that uses DynamicReports to generate demo a report with subreports and a timechart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.sql.Connection; | |
import java.sql.DriverManager; | |
import java.sql.PreparedStatement; | |
import java.sql.ResultSet; | |
import java.sql.ResultSetMetaData; | |
import java.sql.SQLException; | |
import java.sql.Statement; | |
import java.util.Date; | |
import java.util.stream.Collectors; | |
import java.util.stream.Stream; | |
import net.sf.dynamicreports.jasper.builder.JasperReportBuilder; | |
import net.sf.dynamicreports.report.base.expression.AbstractSimpleExpression; | |
import static net.sf.dynamicreports.report.builder.DynamicReports.cht; | |
import static net.sf.dynamicreports.report.builder.DynamicReports.cmp; | |
import static net.sf.dynamicreports.report.builder.DynamicReports.col; | |
import static net.sf.dynamicreports.report.builder.DynamicReports.report; | |
import static net.sf.dynamicreports.report.builder.DynamicReports.stl; | |
import static net.sf.dynamicreports.report.builder.DynamicReports.type; | |
import net.sf.dynamicreports.report.builder.chart.CategoryChartSerieBuilder; | |
import net.sf.dynamicreports.report.builder.chart.TimeSeriesChartBuilder; | |
import net.sf.dynamicreports.report.builder.column.TextColumnBuilder; | |
import net.sf.dynamicreports.report.builder.component.SubreportBuilder; | |
import net.sf.dynamicreports.report.builder.style.FontBuilder; | |
import net.sf.dynamicreports.report.constant.TimePeriod; | |
import net.sf.dynamicreports.report.datasource.DRDataSource; | |
import net.sf.dynamicreports.report.definition.ReportParameters; | |
import net.sf.dynamicreports.report.exception.DRException; | |
import net.sf.jasperreports.engine.JRDataSource; | |
import net.sf.jasperreports.engine.JREmptyDataSource; | |
import org.apache.commons.lang3.tuple.ImmutablePair; | |
import org.apache.commons.lang3.tuple.Pair; | |
/** | |
* | |
* @author Nick Vurgaft | |
*/ | |
public class ReportTest { | |
private Connection connection; | |
public void start() { | |
try { | |
Class.forName("org.hsqldb.jdbcDriver"); | |
connection = DriverManager.getConnection("jdbc:hsqldb:mem:test"); | |
createTables(); | |
build(); | |
} catch (SQLException | ClassNotFoundException e) { | |
e.printStackTrace(); | |
} | |
} | |
private void createTables() throws SQLException { | |
Statement st = connection.createStatement(); | |
st.execute("CREATE TABLE sales (item VARCHAR(50), quantity INTEGER, unitprice INTEGER, orderdate DATE NOT NULL)"); | |
st.execute("INSERT INTO sales(item, quantity, unitprice, orderdate) VALUES ('Book', 5, 100, DATE '2090-07-08')"); | |
st.execute("INSERT INTO sales(item, quantity, unitprice, orderdate) VALUES ('Cup', 12, 25, DATE '2090-07-08')"); | |
st.execute("INSERT INTO sales(item, quantity, unitprice, orderdate) VALUES ('Book', 1, 110, DATE '2090-07-09')"); | |
st.execute("INSERT INTO sales(item, quantity, unitprice, orderdate) VALUES ('Cup', 22, 25, DATE '2090-07-09')"); | |
st.execute("INSERT INTO sales(item, quantity, unitprice, orderdate) VALUES ('Book', 8, 110, DATE '2090-07-10')"); | |
st.execute("INSERT INTO sales(item, quantity, unitprice, orderdate) VALUES ('Cup', 22, 20, DATE '2090-07-10')"); | |
st.execute("INSERT INTO sales(item, quantity, unitprice, orderdate) VALUES ('Book', 12, 120, DATE '2090-07-11')"); | |
st.execute("INSERT INTO sales(item, quantity, unitprice, orderdate) VALUES ('Cup', 18, 20, DATE '2090-07-11')"); | |
st.execute("CREATE TABLE inventory (item VARCHAR(50), quantity INTEGER, code INTEGER)"); | |
st.execute("INSERT INTO inventory VALUES('Book', 200, 42)"); | |
st.execute("INSERT INTO inventory VALUES('Cup', 400, 43)"); | |
} | |
private void build() { | |
// define cloumn's name to type mapping | |
Pair<String, Class<Date>> orderdate = new ImmutablePair<>("orderdate", Date.class); | |
Pair<String, Class> item = new ImmutablePair<>("item", String.class); | |
Pair<String, Class> code = new ImmutablePair<>("code", Integer.class); | |
Pair<String, Class> quantity = new ImmutablePair<>("quantity", Integer.class); | |
Pair<String, Class> unitprice = new ImmutablePair<>("unitprice", Integer.class); | |
SubreportBuilder subreport1 = cmp.subreport(new SubreportExpression(orderdate, item, quantity, unitprice)) | |
.setDataSource(new SubreportDataSourceExpression("SELECT item, quantity, unitprice, orderdate FROM sales")); | |
SubreportBuilder subreport2 = cmp.subreport(new SubreportExpression(null, item, quantity, code)) | |
.setDataSource(new SubreportDataSourceExpression("SELECT item, quantity, code FROM inventory")); | |
try { | |
report() | |
.detail(subreport1, cmp.verticalGap(20)) | |
.detail(subreport2, cmp.verticalGap(20)) | |
.setDataSource(createDataSource()) | |
.show(); | |
} catch (DRException e) { | |
e.printStackTrace(); | |
} | |
} | |
private JRDataSource createDataSource() { | |
// the detail bands contain subreports each with their own datasource. | |
// so the empty datasource acts as a placeholder for the details data, | |
// without the empty datasource no details will be printed. | |
return new JREmptyDataSource(1); | |
} | |
private class SubreportExpression extends AbstractSimpleExpression<JasperReportBuilder> { | |
private static final long serialVersionUID = 1L; | |
private Pair<String, Class<Date>> datetimeColumn; | |
private Pair<String, Class>[] columns; | |
public SubreportExpression(Pair<String, Class<Date>> datetimeColumn, Pair<String, Class>... columns) { | |
this.datetimeColumn = datetimeColumn; | |
this.columns = columns; | |
} | |
@Override | |
public JasperReportBuilder evaluate(ReportParameters reportParameters) { | |
int masterRowNumber = reportParameters.getReportRowNumber(); | |
JasperReportBuilder report = report(); | |
report.title(cmp.text("Subreport" + masterRowNumber)); | |
for (Pair<String, Class> column : this.columns) { | |
report.addColumn(col.column(column.getKey(), column.getKey().toUpperCase(), column.getValue())); | |
} | |
if (datetimeColumn != null) { | |
report.summary(buildTimeSeriesLineChart(datetimeColumn, columns)); | |
report.addColumn(col.column(datetimeColumn.getKey(), datetimeColumn.getKey().toUpperCase(), type.dateYearToMonthType())); | |
} | |
return report; | |
} | |
private TimeSeriesChartBuilder buildTimeSeriesLineChart( | |
Pair<String, Class<Date>> datetimeColumn, | |
Pair<String, Class>[] valueColumns) { | |
FontBuilder boldFont = stl.fontArialBold().setFontSize(12); | |
TextColumnBuilder<Date> orderDateColumn = col.column( | |
datetimeColumn.getKey(), | |
datetimeColumn.getKey().toUpperCase(), | |
type.dateYearToMonthType()); | |
// time chart serie need to have numeric values | |
CategoryChartSerieBuilder[] _columns = Stream.of(valueColumns) | |
.filter((c) -> Number.class.isAssignableFrom(c.getValue())) | |
.map((c) -> col.column(c.getKey(), c.getKey().toUpperCase(), c.getValue())) | |
.map((c) -> cht.serie(c)) | |
.collect(Collectors.toList()) | |
.toArray(new CategoryChartSerieBuilder[0]); | |
return cht.timeSeriesChart() | |
.setTitle("Time series chart") | |
.setTitleFont(boldFont) | |
.setTimePeriod(orderDateColumn) | |
.setTimePeriodType(TimePeriod.DAY) | |
.series(_columns) | |
.setTimeAxisFormat( | |
cht.axisFormat().setLabel("Date")); | |
} | |
} | |
private class SubreportDataSourceExpression extends AbstractSimpleExpression<JRDataSource> { | |
private static final long serialVersionUID = 1L; | |
private String sqlQuery; | |
public SubreportDataSourceExpression(String sqlQuery) { | |
this.sqlQuery = sqlQuery; | |
} | |
@Override | |
public JRDataSource evaluate(ReportParameters reportParameters) { | |
try (PreparedStatement stmnt = connection.prepareStatement(sqlQuery)) { | |
try (ResultSet rs = stmnt.executeQuery()) { | |
ResultSetMetaData rsmd = rs.getMetaData(); | |
String[] columns = new String[rsmd.getColumnCount()]; | |
for (int i = 1; i <= rsmd.getColumnCount(); i++) { | |
columns[i - 1] = rsmd.getColumnName(i); | |
} | |
DRDataSource dataSource = new DRDataSource(columns); | |
while (rs.next()) { | |
Object[] values = new Object[rsmd.getColumnCount()]; | |
for (int j = 1; j <= rsmd.getColumnCount(); j++) { | |
values[j - 1] = rs.getObject(j); | |
} | |
dataSource.add(values); | |
} | |
return dataSource; | |
} | |
} catch (Throwable ee) { | |
ee.printStackTrace(); | |
} | |
return null; | |
} | |
} | |
public static void main(String[] args) { | |
ReportTest reportTest = new ReportTest(); | |
reportTest.start(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment