不同数据集的写日志需求
背景描述
某客户要求记录访问报表时查询的sql语句、访问人、访问时间等信息。之前我给出过相应实现,但客户在使用过程中发现添加了其他数据集类型以后,该实现方式似乎无法达到需求效果。
本文将在《将报表sql和参数输出到xml文件中》基础上完成对使用不同数据集的报表写日志的需求。
实现
先准备带有参数模板的报表,在发布报表的jsp中指定计算监听类,如:calculateListener=”listener.CalcListenerWriteSql2″
CalcListenerWriteSql2.java介绍:
1、 public void afterCalculate()
(1) 计算监听类计算后方法,首先判断context中是否存有参数,因为在翻页的时候context会丢失,导致context找不到的错误。
Map map = context.getAllParamMap();
Iterator iter = map.entrySet().iterator();
if(!iter.hasNext()){
System.out.println(“context里啥都没有啦~~”);
}else{ //执行下面的写日志操作
(2) 初始化一个map,用于存储数据集名和数据集sql(完整不带问号的sql)。
Map<String, String> dsMap = new HashMap<String, String>();// 初始化存放数据集名,数据sql的map
(3) 再在该方法中获取数据集元对象以及数据集个数,通过循环数据集个数获取数据集名和通过getDsSql()方法获取数据集sql。
DataSetMetaData dsmd = report.getDataSetMetaData(); // 获取数据集元对象
int dsNum = dsmd.getDataSetConfigCount(); // 获取数据集个数
循环数据集个数,判断每个数据集的类型(这里只判断SQL和存储过程类型),对每种类型给予不同的处理
if(dsc instanceof SQLDataSetConfig){//sql数据集
System.out.println(“SQLDataSetConfig->”+dsName);
sdsc = (SQLDataSetConfig)dsc;
newSql = getDsSql(sdsc);
//Map中数据格式:key=数据集名;value=完整sql
dsMap.put(dsName, newSql);
}else if(dsc instanceof ProcDataSetConfig){//存储过程数据集
System.out.println(“ProcDataSetConfig->”+dsName);
pdsc = (ProcDataSetConfig)dsc;
dsMap.put(dsName, pdsc.getSQL());//直接将调用存储过程语句存入MAP
}
(4) 最后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<Integer,String> getParam(SQLDataSetConfig sdsc)
本方法先判断报表数据集的参数类型,经Expression计算参数表达式值,最后将参数位置和参数值存到map中返回。
Expression exp = new Expression(context, paramExp);
Object afterCalcObj = exp.calculate(context, false);
4、 public void createXML(Map<String,String> map,String raqName,String userID,String now,String currYM)
创建xml方法(本例使用dom4j操作xml,使用时需导入dom4j的包)。其中创建时需考虑到xml文件编码、汉字乱码以及特殊符号转码等问题。
5、 public void modifyXML(Map<String,String> map,String raqName,String userID,String now,String currYM)
修改xml方法。实现类似创建,注意同4。
附源程序:
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.ide.base.DataSetTypes;
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.ProcDataSetConfig;
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 {
Map map = context.getAllParamMap();
Iterator iter = map.entrySet().iterator();
if(!iter.hasNext()){
System.out.println(“context里啥都没有啦~~”);
}else{
/**获取数据集名称和sql部分**/
Map<String, String> dsMap = new HashMap<String, String>();// 初始化存放数据集名,数据sql的map
DataSetMetaData dsmd = report.getDataSetMetaData(); // 获取数据集元对象
int dsNum = dsmd.getDataSetConfigCount(); // 获取数据集个数
System.out.println(“ds个数—————->”+dsNum);
DataSetConfig dsc = null;
String dsName = “”;
SQLDataSetConfig sdsc = null;
ProcDataSetConfig pdsc = null;
for (int i = 0; i < dsNum; i++) {
String newSql = “”;// 不带问号的sql
dsc = dsmd.getDataSetConfig(i);
dsName = dsc.getName();// 取数据集名字
if(dsc instanceof SQLDataSetConfig){//sql数据集
System.out.println(“SQLDataSetConfig->”+dsName);
sdsc = (SQLDataSetConfig)dsc;
newSql = getDsSql(sdsc);
//Map中数据格式:key=数据集名;value=完整sql
dsMap.put(dsName, newSql);
}else if(dsc instanceof ProcDataSetConfig){//存储过程数据集
System.out.println(“ProcDataSetConfig->”+dsName);
pdsc = (ProcDataSetConfig)dsc;
dsMap.put(dsName, pdsc.getSQL());
}
}
/**获取报表名称,登录人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<date.length-1;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<Integer, String> paramMap = getParam(sdsc);//取该数据集参数值
String sql = sdsc.getSQL();// 取数据集sql
String[] sqlArr = sql.split(“\\u003F”);// split问号,问号需要转义
for(int j=0;j<sqlArr.length-1;j++){
finalSql = finalSql+sqlArr[j]+paramMap.get(j);
}
finalSql = finalSql + sqlArr[sqlArr.length-1];
return finalSql;
}
/**
* 此方法用于获取该数据集的所有参数,将其位置和值存在map中
* @param sdsc
* @return
* @throws Exception
*/
public Map<Integer,String> getParam(SQLDataSetConfig sdsc)throws Exception{
Map<Integer, String> paramMap = new HashMap<Integer, String>();// 存放参数值和参数位置
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);
// System.out.println(“参数计算前!”);
Object afterCalcObj = exp.calculate(context, false);
// System.out.println(“参数计算后!”);
/** 判断参数类型* */
// 字符串
if (afterCalcObj instanceof String) {
// System.out.println(“第” + j + “个参数类型:字符串!”);
paramMap.put(j, “‘” + afterCalcObj.toString() + “‘”);// 字符串则拼单引号和值进去
// 以下是数值型
} else if (afterCalcObj instanceof Integer) {
// System.out.println(“第” + j + “个参数类型:整数!”);
paramMap.put(j, afterCalcObj.toString());//
} else if (afterCalcObj instanceof Short) {
// System.out.println(“第” + j + “个参数类型:短整数!”);
paramMap.put(j, afterCalcObj.toString());//
} else if (afterCalcObj instanceof Long) {
// System.out.println(“第” + j + “个参数类型:长整数!”);
paramMap.put(j, afterCalcObj.toString());//
} else if (afterCalcObj instanceof BigInteger) {
// System.out.println(“第” + j + “个参数类型:大整数!”);
paramMap.put(j, afterCalcObj.toString());//
} else if (afterCalcObj instanceof Float) {
// System.out.println(“第” + j + “个参数类型:单精度!”);
} else if (afterCalcObj instanceof Double) {
// System.out.println(“第” + j + “个参数类型:双精度!”);
paramMap.put(j, afterCalcObj.toString());//
} else if (afterCalcObj instanceof BigDecimal) {
// System.out.println(“第” + j + “个参数类型:数值!”);
paramMap.put(j, afterCalcObj.toString());//
// 以下是日期型
// ******************【可能要修改的部分开始】——————————–
// 此处采用oracle的to_date函数将字符串转为日期类型,使用者需根据自身数据库情况采用对应的函数和日期格式
} else if (afterCalcObj instanceof Date) {
// System.out.println(“第” + j + “个参数类型:日期!”);
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) {
// System.out.println(“第” + j + “个参数类型:时间!”);
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) {
// System.out.println(“第” + j + “个参数类型:日期时间!”);
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(“第” + j + “个参数类型:布尔值!”);
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<al_afterCalcObj.size()-1;i++){
paramMapValue = “‘”+al_afterCalcObj.get(i)+”‘,’”;
}
paramMapValue = paramMapValue +al_afterCalcObj.get(al_afterCalcObj.size()-1)+”‘”;
// System.out.println(“字符串组处理后:”+paramMapValue);
paramMap.put(j, paramMapValue);
}else{//其他类型,不需要加单引号的,由于日期组没有意义,所以不做判断
String paramMapValue=”";
for(int i=0;i<al_afterCalcObj.size()-1;i++){
paramMapValue = al_afterCalcObj.get(i)+”,”;
}
paramMapValue = paramMapValue +al_afterCalcObj.get(al_afterCalcObj.size()-1);
// System.out.println(“其他组类型处理后:”+paramMapValue);
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<String,String> map,String raqName,String userID,String now,String currYM){
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,String> map,String raqName,String userID,String now,String currYM){
//取应用真实路径
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();
}
}
}