不同数据集的写日志需求

背景描述

         某客户要求记录访问报表查询的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();

       }

    }

}