JApex includes what can be considered as a mini reporting server that supports nice features like transparent downloading, custom exporting format and generic sub reporting (in progress).
The concept is to store Compiled Report Templates (see below) in a table in the database, and dynamically generate output documents in desired formats by generically parsing a URL into some special parameters and Report parameters and use such parameters together to generate and serve reports
At first, especially if you used JasperReports before, the design decision to store compiled report templates it the database seems like a bit of an extreme choice. why not simply use file systems.
The reasons behind such decision are many, here is a few of them:
JasperReports is a very capable reporting library, however, it is a headless library. i.e. by itself it has no UI to design, preview and generate report templates.
JasperReports uses the term Template to describe a report design.
A report design is visual depiction of the final report layout, without the actual data of the report.
JasperReport report designs are XML based, commonly having the extensions JRXML (Jasper Reports XML).
Of course JasperReports are conveniently designed using a fully featured visual tool called iReport
Once reports are designed, they are passed through a phase of "compilation" so that they ready to process arbitrary data.
In the following sample SQL DDL script an assumption that the table objects are created in a schema named "JAPEX". please note that this assumption is purely for demonstration purpose, as you will see shortly, not only the schema name, table name, and table structure can be customized to your liking, but also the entire logic of how report templates are stored is shielded behind a single configuration parameter of JApex itself.
Also please note several attributes may not match your tooling like statement seperator etc. please feel free to follow your usual procedure when it comes to running the following scripts
CREATE TABLE JAPEX.JAPEX_REPORT ( REPORT_SN NUMBER NOT NULL, REPORT_ID VARCHAR2(250) NOT NULL, PARENT_REPORT_ID VARCHAR2(250) NULL, DESCRIPTION VARCHAR2(500) NOT NULL, TEMPLATE_JRXML BLOB NULL, TEMPLATE_JASPER BLOB NULL, THUMBNAIL BLOB NULL, CREATED_ON TIMESTAMP(6) NULL, CREATED_BY VARCHAR2(32) NULL, UPDATED_ON TIMESTAMP(6) NULL, UPDATED_BY VARCHAR2(32) NULL ) GO
As you can see a given report is identified by its textual ID (required) while the name of the field can be any thing as you will see later, the choice of the domain for the report id is closely related to both; how JApex works as well as the second field, PARENT_REPORT_ID
The choice for the PK for this table follows the common APEX convention of using identity fields (numeric with attached sequence and triggers)
Report JRXML and Jasper fields store both versions of report templates, source and executable. source is only necessary for report inventorying reasons.
Thumbnail is also optional but can certainly help locate the report template you need to modify in a moderately large application.
The four fields, create_on, update_on, created_by and updated_by are as usual used for auditing purposes to give a glance over the described activities without complex joins with history tables.
CREATE SEQUENCE JAPEX.JAPEX_REPORT_SEQ INCREMENT BY 1 START WITH 1 NOMAXVALUE NOMINVALUE NOCYCLE CACHE 20 NOORDER GO
this is the sequence used to populate the report system number (REPORT_SN) in the previous table.
CREATE UNIQUE INDEX JAPEX.JAPEX_REPORT_PK ON JAPEX.JAPEX_REPORT(REPORT_SN) GO CREATE UNIQUE INDEX JAPEX.JAPEX_REPORT_UK1 ON JAPEX.JAPEX_REPORT(REPORT_ID) GO ALTER TABLE JAPEX.JAPEX_REPORT ADD ( CONSTRAINT JAPEX_REPORT_PK PRIMARY KEY (REPORT_SN) NOT DEFERRABLE INITIALLY IMMEDIATE ) GO ALTER TABLE JAPEX.JAPEX_REPORT ADD ( CONSTRAINT JAPEX_REPORT_UK1 UNIQUE (REPORT_ID) NOT DEFERRABLE INITIALLY IMMEDIATE ) GO ALTER TABLE JAPEX.JAPEX_REPORT ADD ( CONSTRAINT JAPEX_REPORT_PARENT FOREIGN KEY(PARENT_REPORT_ID) REFERENCES JAPEX.JAPEX_REPORT(REPORT_ID) NOT DEFERRABLE INITIALLY IMMEDIATE VALIDATE ) GO
self describing constraints for the table
CREATE TRIGGER JAPEX.BI_JAPEX_REPORT before insert on "JAPEX_REPORT" for each row begin if :NEW."REPORT_SN" is null then select "JAPEX_REPORT_SEQ".nextval into :NEW."REPORT_SN" from dual; end if; if :NEW.CREATED_ON is null then select sysdate into :NEW."CREATED_ON" from dual; end if; if :NEW.UPDATED_ON is null then select sysdate into :NEW."UPDATED_ON" from dual; end if; end; GO
a simple trigger that populates missing field values before inserting a new record into the reports table.
Please note that a common naming convention was used in describing the previous schema as well as throughout the entire JAPEX code level, while it's optional and configurable, it is highly recommended to follow the same naming convention to help present a consistent coherent naming convention throughout the entire deployment.
Of course you may go ahead and build a UI interface for managing this table using Apex convenient report/form setup. or even use Blober to provide a fancy interface to such administration page.
For the following section, it's assumed that you have completed the steps at "JApex Database Connectivity Configuration". this section focuses only on the steps relevant to JApex Reporting Module only
Please consider the following configuration example from the supplied file under /JAPEX_ROOT/WEB-INF/spring/blob-descriptor.xml
<!-- ======================================================================================= this is a concrete implementation for the special blob of report ReportDescriptor extends BlobDescriptor with 1 extra field to retrieve the sub reports (if any) ======================================================================================== --> <bean id = "reportTemplate" class = "net.sourceforge.japex.blober.ReportDescriptor"> <property name="mimeType" value="object/octet-stream"/> <property name="selectSql" value="SELECT TEMPLATE_JASPER FROM JAPEX_REPORT WHERE REPORT_ID=:REPORT_ID"/> <property name="updateSql" value="UPDATE JAPEX_REPORT SET TEMPLATE_JASPER=:BLOB WHERE REPORT_ID=:REPORT_ID"/> <property name="mode" value="merge"/> <property name="stripExtraParams" value="true"/> <property name="insertSql" value="INSERT INTO JAPEX_REPORT (REPORT_ID, TEMPLATE_JASPER) VALUES (:REPORT_ID, :BLOB)"/> <property name="clearSql" value="UPDATE JAPEX_REPORT SET TEMPLATE_JASPER = EMPTY_BLOB() WHERE REPORT_ID=:REPORT_ID"/> <property name="deleteSql" value="DELETE FROM JAPEX_REPORT WHERE REPORT_ID=:REPORT_ID"/> <!-- The Only extra field from those of BlobDescriptor, a report may have sub-reports and this query is used by the reporter servlet to recursively retrieve them, retrieved reports and piled up inside the report parameter map using their report ids as keys hence, sub reports may be accessed in the containing report using and expression identical to normal parameters, i.e. for subreport expression use something like $P{SUB_REPORT_1} --> <property name="subReportsQuery" value="SELECT REPORT_ID, TEMPLATE_JASPER FROM JAPEX_REPORT WHERE PARENT_REPORT_ID=:REPORT_ID"/> <property name="notFoundUrl" value="/images/nophoto.gif" /> </bean>
As you can see the configuration options are almost identical to those described in the similar section for generic blobs. Also as previously said, the report templates table name, schema and exact structure and field names are configurable by means of a single configuration option, that is : selectSql.
As long as the selectSql query returns a single field blob with compiled report content the query takes care of all remaining naming flexibilities.
Please note: the only inflexibly named part of this query is the name of the parameter which is fixed at this point of project lifecycle at the name REPORT_ID.
with the exception of the following predefined parameters, any parameters passed through the URL will be treated as STRING report parameters and will thus be passed unchanged to the report itself at the time of execution.
Parameter Name |
Parameter Meaning |
REPORT_ID | The unique identified used by the report servlet to locate the one report template needed to build the current report |
OUTPUT_FORMAT | The format in which the final report will be generated, example format include pdf, html, xls, odt, doc etc. |
FILE_NAME | An optional parameters that is sent along with the generated report to support "download as" kind of functionality |