快逸报表将报表sql和参数输出到xml文件中
需求背景
客户要求能够将报表名称、报表数据集sql、报表参数值、访问报表时间、登录人信息等信息输出到xml文件中,以对该xml文件进行分析。
要求:1、sql是完整的sql语句,即可以直接拿到数据库端执行。 2、文件需进行大小控制,可分时间段存储xml。
实现思路
鉴于最终的参数值可以在报表计算后取到,该需求可以通过报表计算监听类实现。所有信息可以通过dom4j写入xml文件。
难点在于我们直接获取的数据集sql是带有问号的原始sql,需要将指定位置的问号替换成对应的参数值,替换的时候要考虑到类型转换。
程序介绍
先准备带有参数模板的报表,在发布报表的jsp中指定计算监听类,如:calculateListener=”listener.CalcListenerWriteSql2″
CalcListenerWriteSql2.java介绍:
1、 public void beforeCalculate()
计算监听类计算后方法,首先初始化一个map,用于存储数据集名和数据集sql(完整不带问号的sql)。
Map
再在该方法中获取数据集元对象以及数据集个数,通过循环数据集个数获取数据集名和通过getDsSql()方法获取数据集sql。
DataSetMetaData dsmd = report.getDataSetMetaData(); // 获取数据集元对象
int dsNum = dsmd.getDataSetConfigCount(); // 获取数据集个数
最后map及必要信息传给操作xml的方法,本例中xml文件都是以当前年月命名,若无该该文件则创建,否则修改,以达到每月创建使用一个xml的目的。
createXML(dsMap,raqName,userID,now,currYM);//创建xml
modifyXML(dsMap,raqName,userID,now,currYM);//修改xml
2、 public String getDsSql(SQLDataSetConfig sdsc)
根据传递过来的SQLDataSetConfig对象获取对应数据集sql(原始sql,带问号的),该方法通过getParam()将sql中问号与参数值对应,最后返回一个完整的sql字符串。
3、 public Map
本方法先判断报表数据集的参数类型,经Expression计算参数表达式值,最后将参数位置和参数值存到map中返回。
Expression exp = new Expression(context, paramExp);
Object afterCalcObj = exp.calculate(context, false);
4、 public void createXML(Map
创建xml方法(本例使用dom4j操作xml,使用时需导入dom4j的包)。其中创建时需考虑到xml文件编码、汉字乱码以及特殊符号转码等问题。
5、 public void modifyXML(Map
修改xml方法。实现类似创建,注意同4。
附Java程序源码:
package listener;
import java.sql.Date;
import java.sql.Time;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.*;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import com.runqian.report4.model.expression.Expression;
import com.runqian.report4.usermodel.AbstractCalculateListener;
import com.runqian.report4.usermodel.DataSetConfig;
import com.runqian.report4.usermodel.DataSetMetaData;
import com.runqian.report4.usermodel.SQLDataSetConfig;
import com.sun.jmx.snmp.Timestamp;
public class CalcListenerWriteSql2 extends AbstractCalculateListener {
public void beforeCalculate() throws Exception {
}
public void afterCalculate() throws Exception {
/**获取数据集名称和sql部分**/
Map
DataSetMetaData dsmd = report.getDataSetMetaData(); // 获取数据集元对象
int dsNum = dsmd.getDataSetConfigCount(); // 获取数据集个数
DataSetConfig dsc = null;
String dsName = “”;
SQLDataSetConfig sdsc = null;
for (int i = 0; i < dsNum; i++) {
String newSql = "";// 不带问号的sql
dsc = dsmd.getDataSetConfig(i);
dsName = dsc.getName();// 取数据集名字
sdsc = (SQLDataSetConfig) dsmd.getDataSetConfig(i);
newSql = getDsSql(sdsc);
//Map中数据格式:key=数据集名;value=完整sql
dsMap.put(dsName, newSql);
}
/**获取报表名称,登录人ID,访问报表时间等信息**/
String raqName = (String)request.getAttribute("raqName");//报表名称
String userID = (String)request.getAttribute("userID");//用户ID
DateFormat df_now = DateFormat.getDateTimeInstance();
String now = df_now.format(new Date(System.currentTimeMillis()));//访问该报表时间
//*****************【可能需要修改部分开始】--------------------------//
/**判断是要新生成xml,还是读取本月的**/
String currDate = "";//当前年月日
String firstDay = "";//每月第一天
String currYM = "";//当前月份
//判断当前日期是不是每月一号
String[] temp = now.split(" ");//空格分割出日期、时间
String[] date = temp[0].split("-");//-号分隔出年月日
for(int i=0;i
currDate += (date[i]+"-");
firstDay += (date[i]+"-");
}
currDate += date[date.length-1];
firstDay += "1";
currYM += (date[0]+"-");
currYM +=(date[1]);
String xmlPath = request.getRealPath("\\")+"reportInfo"+File.separator+currYM+".xml";
File f = new File(xmlPath);
if(!f.exists()){//以年月命名的xml文件不存在,需要创建XML
System.out.println(currYM+".xml不存在,正在创建……");
createXML(dsMap,raqName,userID,now,currYM);//创建xml
System.out.println("创建XML结束!");
}else{//否则,以年月命名的xml文件存在,读取xml追加信息
System.out.println(currYM+".xml已存在,正在修改……");
modifyXML(dsMap,raqName,userID,now,currYM);//修改xml
//createXML(dsMap,raqName,userID,now,currYM);
System.out.println("修改XML结束!");
}
//----------------【可能需要修改部分结束】************************/
}
/**
* 获取处理后sql语句,即不带问号的完整sql
* @param sdsc
* @return
* @throws Exception
*/
public String getDsSql(SQLDataSetConfig sdsc) throws Exception{
String finalSql = "";//处理好的sql,返回
Map
String sql = sdsc.getSQL();// 取数据集sql
String[] sqlArr = sql.split(“\\u003F”);// split问号,问号需要转义
for(int j=0;j
finalSql = finalSql+sqlArr[j]+paramMap.get(j);
}
finalSql = finalSql + sqlArr[sqlArr.length-1];
return finalSql;
}
/**
* 此方法用于获取该数据集的所有参数,将其位置和值存在map中
* @param sdsc
* @return
* @throws Exception
*/
public Map
Map
int paramNum = sdsc.getParamCount();// 取每个数据集中参数个数
for (int j = 0; j < paramNum; j++) {
String paramExp = sdsc.getParamExp(j); // 取每个数据集中参数表达式
//System.out.println("第" + j + "个参数:" + paramExp);
Expression exp = new Expression(context, paramExp);
Object afterCalcObj = exp.calculate(context, false);
/** 判断参数类型* */
// 字符串
if (afterCalcObj instanceof String) {
paramMap.put(j, "'" + afterCalcObj.toString() + "'");// 字符串则拼单引号和值进去
// 以下是数值型
} else if (afterCalcObj instanceof Integer) {
paramMap.put(j, afterCalcObj.toString());//
} else if (afterCalcObj instanceof Short) {
paramMap.put(j, afterCalcObj.toString());//
} else if (afterCalcObj instanceof Long) {
paramMap.put(j, afterCalcObj.toString());//
} else if (afterCalcObj instanceof BigInteger) {
paramMap.put(j, afterCalcObj.toString());//
} else if (afterCalcObj instanceof Float) {
} else if (afterCalcObj instanceof Double) {
paramMap.put(j, afterCalcObj.toString());//
} else if (afterCalcObj instanceof BigDecimal) {
paramMap.put(j, afterCalcObj.toString());//
// 以下是日期型
// ******************【可能要修改的部分开始】--------------------------------
// 此处采用oracle的to_date函数将字符串转为日期类型,使用者需根据自身数据库情况采用对应的函数和日期格式
} else if (afterCalcObj instanceof Date) {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String paramMapValue = "to_date('"
+ df.format(afterCalcObj) + "','yyyy-MM-dd')";// 需要修改行
paramMap.put(j, paramMapValue);
} else if (afterCalcObj instanceof Time) {
DateFormat df = new SimpleDateFormat("hh:mm:ss");
String paramMapValue = "to_date('"
+ df.format(afterCalcObj) + "','hh:mm:ss')";// 需要修改行
paramMap.put(j, paramMapValue);
} else if (afterCalcObj instanceof Timestamp) {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String paramMapValue = "to_date('"
+ df.format(afterCalcObj)
+ "','yyyy-MM-dd hh:mm:ss')";// 需要修改行
paramMap.put(j, paramMapValue);
// ------------------【可能要修改的部分结束】********************************
// 布尔值
} else if (afterCalcObj instanceof Boolean) {
System.out.println("参数类型为布尔值,若使用请修改程序!");
// 组类型
} else if (afterCalcObj instanceof ArrayList) {
ArrayList al_afterCalcObj = (ArrayList)afterCalcObj;
Object element = al_afterCalcObj.get(0);//取ArrayList里一个元素,看是什么类型
if(element instanceof String){//字符串
String paramMapValue="";
for(int i=0;i
paramMapValue = "'"+al_afterCalcObj.get(i)+"','";
}
paramMapValue = paramMapValue +al_afterCalcObj.get(al_afterCalcObj.size()-1)+"'";
paramMap.put(j, paramMapValue);
}else{//其他类型,不需要加单引号的,由于日期组没有意义,所以不做判断
String paramMapValue="";
for(int i=0;i
paramMapValue = al_afterCalcObj.get(i)+",";
}
paramMapValue = paramMapValue +al_afterCalcObj.get(al_afterCalcObj.size()-1);
paramMap.put(j, paramMapValue);
}
}
}
return paramMap;
}
/**
* 根据相关信息创建XML文件,文件名为yyyy-MM.xml
* @param map 存放数据集名和sql
* @param raqName 报表名
* @param userID 用户ID
* @param now 当前日期时间
* @param currYM 当前年月
*/
public void createXML(Map
Document document = DocumentHelper.createDocument(); //创建文档实例
document.addComment(“create time:”+now);//添加注释:文档创建时间
Element root = document.addElement(“messageRoot”);//创建根元素messageRoot
//catalogElement.addProcessingInstruction(“target”,”text”);
Element message = root.addElement(“message”);//根节点上添加message元素
Element time=message.addElement(“time”);//message节点上添加time元素
time.setText(now);//设置time元素文本
Element user_id = message.addElement(“userID”);
user_id.setText(userID);
Element raq_name = message.addElement(“raqName”);
raq_name.setText(raqName);
//遍历HashMap,取key-数据集名;value-sql
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
String key = entry.getKey().toString();
String value = entry.getValue().toString();
Element sql = message.addElement(“sql”);//message上添加sql元素
sql.addAttribute(“dsName”,key);//数据集名放到 sql节点的dsName属性中
sql.setText(value);//sql内容放到sql节点内容中
}
try{
String realPath = request.getRealPath(“”);
if(!”\\”.equals(realPath.trim().charAt(realPath.length()-1))){
realPath += “\\”;
}
//***************【可能需要修改部分开始】—————–//
//xml文件名当前年月;文件目录在应用根目录的reportInfo下,可修改
String xmlPath = realPath +”reportInfo\\”+currYM+”.xml”;
new File(realPath+”reportInfo”).mkdir();//创建reportInfo目录
//—————【可能需要修改部分结束】*****************//
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding(“utf-8″);//设置xml编码
/**重要:此处需使用FileOutputStream防止中文乱码*/
//XMLWriter output = new XMLWriter(new FileWriter( new File(xmlPath)),format);
XMLWriter output = new XMLWriter(new FileOutputStream( new File(xmlPath)),format);
/**重要:避免xml内容被转义*/
//output.setEscapeText(false);
output.write( document );
output.close();
}
catch(IOException e){System.out.println(e.getMessage());}
}
/**
* 用于向现有xml文件中追加信息
* @param map
* @param raqName
* @param userID
* @param now
* @param currYM
*/
public void modifyXML(Map
//取应用真实路径
String realPath = request.getRealPath(“”);
if(!”\\”.equals(realPath.trim().charAt(realPath.length()-1))){
realPath += “\\”;
}
//***************【可能需要修改部分开始】—————–//
//xml文件名当前年月;文件目录在应用根目录的reportInfo下,可修改
String inputXml = realPath +”reportInfo\\”+currYM+”.xml”;
//—————【可能需要修改部分结束】*****************//
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(inputXml);//读入xml
Element root = document.getRootElement();//取根节点
Element message = root.addElement(“message”);//根节点上添加message元素
Element time=message.addElement(“time”);//message节点上添加time元素
time.setText(now);//设置time元素文本
Element user_id = message.addElement(“userID”);
user_id.setText(userID);
Element raq_name = message.addElement(“raqName”);
raq_name.setText(raqName);
//遍历HashMap,取key-数据集名;value-sql
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
String key = entry.getKey().toString();
String value = entry.getValue().toString();
Element sql = message.addElement(“sql”);//message上添加sql元素
sql.addAttribute(“dsName”,key);//数据集名放到 sql节点的dsName属性中
sql.setText(value);//sql内容放到sql节点内容中
}
OutputFormat format = OutputFormat.createPrettyPrint();
/**重要:此处需使用FileOutputStream防止中文乱码*/
//XMLWriter output = new XMLWriter(new FileWriter( new File(xmlPath)),format);
XMLWriter output = new XMLWriter(new FileOutputStream( new File(inputXml)),format);
/**重要:避免xml内容被转义*/
//output.setEscapeText(false);
output.write( document );
output.close();
} catch (DocumentException de) {
de.printStackTrace();
} catch(IOException ioe){
ioe.printStackTrace();
}
}
}