我正在使用带有JasperReports的Spring MVC 3.0对Web应用程序进行原型设计。 我已经使用Spring + Jfreechart + iText + Apache POI完成了报告应用程序,并且能够成功使用Spring提供的各个视图类来传输pdf,xls和图像。
这次我想尝试使用JasperReports,以便我可以在应用程序之外设计我的pdf,而不必担心知道底层api(是jfreechart,itext或poi)。
问题
我有一个report1.jrxml文件,其中包含带有两个日期参数的查询的 queryString 标记。当我通过iReport测试报告时,它会编译并成功运行。这里没问题。
现在我正在阅读以下Spring文档中的JasperReports部分http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#view-jasper-reports 我试图让JasperReportsMultiFormatView正常工作,但有一件我不理解:
JasperReportMultiFormatView如何知道要连接的数据库(回想一下,我在报告中嵌入了查询)?
文档声明在视图中使用reportDataKey属性,但我不知道这是如何解决我的问题。
如何传递参数?
可以做什么
JaperReports提供了一组xxxManager对象,负责编译,过滤和导出报告。您可以创建一个实现Spring View界面的自定义类,并执行以下操作:
Connection connection;
ServletOutputStream servletOutputStream = response .getOutputStream();
InputStream reportStream = getServlet().getServletConfig().getServletContext().getResourceAsStream("/reports/report1.jasper");
response.setContentType("application/pdf");
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:
3306/flightstats?user=user&password=secret");
JasperRunManager.runReportToPdfStream(reportStream,
servletOutputStream, new HashMap(), connection);
connection.close();
servletOutputStream.flush();
servletOutputStream.close();
我需要什么
我需要完成上面那些利用Spring类的代码,比如JasperReportsPdfView,JasperReportsXlsView,甚至更好的JasperReportsMultiFormatView
总而言之,我需要能够将以下内容从我的控制器传递给jasper报告:
这就是我所拥有的,输出是一个空白的PDF文档,我假设因为它不知道如何运行查询
@RequestMapping("/reports/**")
@Controller
public class ReportsController {
@RequestMapping(value ="/reports/usage/report", method = RequestMethod.GET)
public ModelAndView handleSimpleReportMulti(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("Made it here");
Map model = new HashMap();
//model.put("format", "pdf");
model.put("START_DATE", new String("09-12-2011"));
model.put("END_DATE", new String("09-17-2011"));
return new ModelAndView("report1", model);
}
}
答案 0 :(得分:8)
我找到了问题的答案。我已将上面的控制器更改为:
@RequestMapping(value ="/reports/usage/report/{format}", method = RequestMethod.GET)
public ModelAndView handleSimpleReportMulti(ModelMap modelMap, @PathVariable("format") String format) throws Exception {
//Map model = new HashMap();
modelMap.put("format", format);
modelMap.put("REPORT_CONNECTION", dataSource.getConnection());
modelMap.put("START_DATE", new String("09-12-2011"));
modelMap.put("END_DATE", new String("09-17-2011"));
return new ModelAndView("report1", modelMap);
}
我已将view.properties更改为:
#report1(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
report1(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsMultiFormatView
report1.url=/WEB-INF/reports/report1.jasper
我希望这会有所帮助。
由于
答案 1 :(得分:4)
我找到了另一种方法将连接作为参数传递,然后关闭它。
问题:
我实现上面的解决方案问题是,每次我调用PDF时,都会创建一个新连接,因此当应用程序达到打开连接的最大限制时,它就会崩溃。
ReportesDAOJDBC reportes;
public void setReportes(ReportesDAOJDBC reportes) {
this.reportes = reportes;
}
public ModelAndView leoTest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
Map < String, Object > model = new HashMap < String, Object >();
model.put("PARAMCONTRARECIBO", new Integer(1101));
model.put("PARAMDOCTOS", new Integer(1101));
model.put("REPORT_CONNECTION", reportes.getConexion());
return new ModelAndView("leoTest",model);
}
将连接传递给JasperReport的参数是REPORT_CONNECTION,但正如我所说,这样做会导致大量的问题。
我的解决方案:
ReportesDAOJDBC reportes;
public void setReportes(ReportesDAOJDBC reportes) {
this.reportes = reportes;
}
public ModelAndView leoTest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
Map < String, Object > model = new HashMap < String, Object >();
model.put("PARAMCONTRARECIBO", new Integer(1101));
model.put("PARAMDOCTOS", new Integer(1101));
model.put("OBJETO_CONEXION", reportes);
return new ModelAndView(new PdfView("leoTest"),model);
}
正如您所看到的,我实现了自己的PdfView,并在构造函数中传递了view.properties文件中键定义的名称,并且还将对DAO(reportes)的引用作为HashMap的参数传递,参数的名称是“OBJETO_CONEXION”。以下是ReportesDAOJDBC的代码:
public class ReportesDAOJDBC extends JdbcDaoSupport {
public Connection getConexion() {
Connection con ;
try {
con = getDataSource().getConnection();
}
catch (Exception e) {
e.printStackTrace();
return null;
}
return con;
}
public void closeConecction(Connection con) {
try {
if (con != null) {
con.close();
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
然后下一步是向您展示我自己的PdfView实现的代码。
import java.io.File;
import java.io.OutputStream;
import java.sql.Connection;
import java.util.Map;
import java.util.ResourceBundle;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import mx.com.mexican.leinksy.dao.jdbc.ReportesDAOJDBC;
import mx.com.mexican.leinksy.utils.Utils;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.util.JRLoader;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.ResourceBundleViewResolver;
public class PdfView implements View {
private static final String CONTENT_TYPE = "application/pdf";
private String JASPER_URL;
public PdfView(String jasperUrl){
this.JASPER_URL = jasperUrl+".url";
}
@Override
public String getContentType() {
return CONTENT_TYPE;
}
@Override
public void render(Map model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
System.out.println(Utils.getRealPath(request));
ResourceBundle rb = ResourceBundle.getBundle("view");/* Se lee el archivo view.properties*/
ReportesDAOJDBC reporte = (ReportesDAOJDBC)model.get("OBJETO_CONEXION");/* Se obtiene el objeto de conexion */
Connection con = reporte.getConexion();/* Se genera la conexion a la base de datos*/
String jasperFilePath = Utils.getRealPath(request) + rb.getString( JASPER_URL );/* Se obtiene la ruta fisica del archivo .jasper a ejectuar*/
JasperReport jasperReport = (JasperReport)JRLoader.loadObject(new File(jasperFilePath));/* Se carga el reporte ya compilado*/
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, model, con);/* Se llena el reporte con datos del modelo y con la conexion a la BD*/
try{
OutputStream out = response.getOutputStream();
JasperExportManager.exportReportToPdfStream(jasperPrint, out);/* Se manda el contenido a la salida estandar*/
out.flush();
out.close();
}catch(Exception e){
e.printStackTrace();
}
reporte.closeConecction(con);/* Cierro la conexion a la base de datos para liberar el pool*/
}
}
正如您所看到的,我使用ResourceBoundle类读取view.properties文件以获取要加载的.jasper文件的路径和名称。还要注意我不编译.jrxml文件我只是加载编译后的文件,我用IREPORTS编译jrxml。此外,我还有一个实用程序来获取我的.jasper文件的路径,如果您不知道如何操作,这里是代码。
public static String getRealPath(HttpServletRequest req) {
ServletContext context = req.getSession().getServletContext();
String path = context.getRealPath("/");
if (path != null) {
if (!path.endsWith(File.separator)) {
path += File.separator;
}
}
return path;
}
通过这个实现,我可以控制打开关闭连接的位置,我也尊重SPRING的MVC模型,并且我仍然使用view.properties。
在这一点上,也许你问我能做什么,如果我想要一个EXCEL文件,嗯,我也实现了一个XlsView,(Ajuuaaaa !!!)。这是代码:
import java.io.File;
import java.io.OutputStream;
import java.sql.Connection;
import java.util.Map;
import java.util.ResourceBundle;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import mx.com.mexican.leinsky.dao.jdbc.ReportesDAOJDBC;
import mx.com.mexican.leinksy.utils.Utils;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.export.JRXlsExporter;
import net.sf.jasperreports.engine.export.JRXlsExporterParameter;
import net.sf.jasperreports.engine.util.JRLoader;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.ResourceBundleViewResolver;
public class XlsView implements View {
private static final String CONTENT_TYPE = "application/vnd.ms-excel";
private String JASPER_URL;
private String FILE_NAME = "XLSFile";
public XlsView(String jasperUrl){
this.JASPER_URL = jasperUrl+".url";
}
@Override
public String getContentType() {
return CONTENT_TYPE;
}
@Override
public void render(Map model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if(model.get("FILE_NAME")!=null){
this.FILE_NAME = model.get("FILE_NAME").toString();
}
ResourceBundle rb = ResourceBundle.getBundle("view");/* Se lee el archivo view.properties*/
ReportesDAOJDBC reporte = (ReportesDAOJDBC)model.get("OBJETO_CONEXION");/* Se obtiene el objeto de conexion */
Connection con = reporte.getConexion();/* Se genera la conexion a la base de datos*/
String jasperFilePath = Utils.getRealPath(request) + rb.getString( JASPER_URL );/* Se obtiene la ruta fisica del archivo .jasper a ejectuar*/
JasperReport jasperReport = (JasperReport)JRLoader.loadObject(new File(jasperFilePath));/* Se carga el reporte ya compilado*/
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, model, con);/* Se llena el reporte con datos del modelo y con la conexion a la BD*/
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition","attachment; filename=\""+FILE_NAME+".xls\"");
response.setHeader("Pragma", "No-cache");
response.setDateHeader("Expires", 1);
try{
OutputStream out = response.getOutputStream();
JRXlsExporter exporterXLS = new JRXlsExporter();
exporterXLS.setParameter(JRXlsExporterParameter.JASPER_PRINT,jasperPrint);
exporterXLS.setParameter(JRXlsExporterParameter.OUTPUT_STREAM,out);
exporterXLS.exportReport();
out.flush();
out.close();
}catch(Exception e){
e.printStackTrace();
}
reporte.closeConecction(con);/* Cierro la conexion a la base de datos para liberar el pool*/
}
}
所以这是我的解决方案,希望它有所帮助!