#spring-mvc #jasper-reports
#spring-mvc #jasper-отчеты
Вопрос:
Я создаю прототип веб-приложения с использованием Spring MVC 3.0 с JasperReports. Я уже создавал приложения для создания отчетов с использованием Spring Jfreechart iText Apache POI и смог успешно использовать соответствующие классы просмотра, предоставляемые Spring, для потоковой передачи PDF-файлов, XLS и изображений.
На этот раз я хочу попробовать использовать JasperReports, чтобы я мог создавать свои PDF-файлы вне приложения и не беспокоиться о знании базового API (будь то jfreechart, itext или poi).
Проблема
У меня есть файл report1.jrxml, который содержит тег queryString с моим запросом с двумя параметрами даты. Когда я тестирую отчет через iReport, он компилируется и успешно запускается. Здесь проблем нет.
Теперь я читаю раздел JasperReports из следующей документации Spring 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=useramp;password=secret");
JasperRunManager.runReportToPdfStream(reportStream,
servletOutputStream, new HashMap(), connection);
connection.close();
servletOutputStream.flush();
servletOutputStream.close();
Что мне нужно
Мне нужно выполнить то, что указано в коде выше, использующем классы Spring, такие как JasperReportsPdfView, JasperReportsXlsView или, что еще лучше, JasperReportsMultiFormatView
Итак, в итоге мне нужно иметь возможность передавать следующее из моего контроллера в отчет jasper:
- Параметры
- Информация о подключении к БД, чтобы строка запроса внутри 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);
}
}
Ответ №1:
Я нашел ответ на свой вопрос. Я изменил свой контроллер выше на этот:
@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. как вы получили источник данных — это компонент?
Ответ №2:
Я нашел другой способ передать соединение в качестве параметра, а затем закрыть его.
ПРОБЛЕМА:
Я реализую решение выше, проблема заключалась в том, что каждый раз, когда я вызываю 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 (отчеты) в качестве параметра HashMap, имя параметра — «OBJETO_CONEXION». Вот код для отчетов DAOJDBC:
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*/
}
}
Как вы можете видеть, я прочитал файл view.properties, используя класс ResourceBoundle, чтобы получить путь и имя файла .jasper для загрузки, также обратите внимание, что я не компилирую файл .jrxml, я просто загружаю скомпилированный файл, я компилирую jrxml с IREPORTS. Также у меня есть утилита для получения пути к моему файлу .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*/
}
}
Итак, это мое решение, надеюсь, оно поможет!!!