帆软报表控件嵌入式部署+MongoDB程序数据源

来源:互联网 发布:手机卫星电视软件 编辑:程序博客网 时间:2024/05/21 09:00

帆软报表控件

这里写图片描述

官方嵌入式部署流程

嵌入式部署流程

拷贝文件

  • 简单粗暴式的话,直接将相关目录全部拷贝到自己项目的WEB-INF目录下;看了一下,差不多有两三百兆的样子;
  • 精细化操作的话,选择性拷贝;
  • 拷贝的jar包包括6个fr的jar包,1个fr的plugin的jar包;(与官方文档有不相符的地方);
  • 拷贝的目录包括plugins目录(报表控件的插件目录),reportlets目录(将来存放报表的目录),resources目录(资源文件目录);

web.xml配置

在项目的web.xml中配置报表控件的servlet及servlet-mapping。

部署过程遇到的问题

  • 组内的项目是Maven项目,是通过pom.xml管理项目的jar包依赖的,也就是没有在项目目录下保留lib目录;这样就导致报表控件所需的jar包无法直接拷贝到程序目录下,而创建lib的话与maven项目会有所冲突;
  • 目录拷贝的选择性;帆软官方文档让把reportlets、resources、plugins目录完整拷贝到项目目录下。查看了一下对应目录,reportlets目录下有很多的演示报表cpt文件,plugins是安装的插件文件,resources下面有很多配置文件以及地图文件……
  • 项目使用SpringMVC,直接使用前置的Dispatcher进行所有请求的分发;这样的话,报表控件的servlet就会被忽略掉;
  • ……

部署问题处理

  • 关于项目依赖jar包的处理,采用的方式是搭建maven私服,使用的是nexus;这样仅需要在组内项目中配置maven仓库地址即可,对项目的冲击最小。(在Nexus中进行第三方jar包的管理);
  • 从报表控件要拷贝的目录中删除项目不需要的文件,reportlets下的示例报表全部清理掉,plugins下目前仅保留与web页面自适应相关的插件,resources目录下把地图相关的资源全部清理掉,别的配置文件目前没有深究暂且保留;
  • 在web.xml中目前先把报表控件的ReportServer的servlet前置,后面再研究相关的处理吧;

程序数据源

之所以要用到程序数据源,是因为组内的项目使用的数据库是mongodb,而帆软的mongodb插件支持的查询深度还不够,所以采用程序数据源的方式来处理。
帆软报表控件程序数据源的写法说白了比较简单,就是实现AbstractTableData这个抽象类,然后实现其中的4个抽象方法即可。
但是使用起来不是很顺畅,原因在于:
- 首先要在在IDE中写程序数据源的代码;
- 然后编译对应的代码生成class文件;
- 将class文件拷贝到帆软报表设计器对应的classes目录下;
- 打开报表设计器选择程序数据源(class文件)进行报表设计;
- 设计完成的报表拷贝到项目的reportlets目录下;
可以看到,需要不停地在IDE和报表设计器之间切换,还需要在资源管理器中移动文件。可能这是目前嵌入式部署不得不面对的问题,如果是独立部署的话,可能就仅仅需要将classes文件拷贝到帆软报表设计器对应目录下即可。

测试程序数据源可行性

在项目中写了一个数据源,如下:

public class TestDemo extends AbstractTableData {    private String[] columnNames;    private Object[][] rowData;    public TestDemo() {        String[] columnNames = {"Name", "Score"};        Object[][] datas = {{"Alex", new Integer(15)}, {"Helly", new Integer(22)}, {"Bobby", new Integer(99)}};        this.columnNames = columnNames;        this.rowData = datas;    }    @Override    public int getColumnCount() throws TableDataException {        return columnNames.length;    }    @Override    public String getColumnName(int columnIndex) throws TableDataException {        return columnNames[columnIndex];    }    @Override    public int getRowCount() throws TableDataException {        return rowData.length;    }    @Override    public Object getValueAt(int rowIndex, int columnIndex) {        return rowData[rowIndex][columnIndex];    }}

不涉及业务,不涉及mongodb数据库,仅仅测试一下可行性。
按照上面的步骤设计完报表之后,将报表拷贝回项目reportlets目录,运行:
这里写图片描述
可以看到,程序运行OK,报表也能正常显示。

MongoDB程序数据源

由于写了程序数据源之后,是要放到报表设计器目录下进行报表设计才能看到结果的,这样给调试带来了一些难度。
解决办法就是:
- main方法进行跟踪调试;
- 单元测试;

    // 数据源一共有多少列(多少个字段)    public abstract int getColumnCount() throws TableDataException;    // 对应index的那一列的列名(字段名)是什么    public abstract String getColumnName(int index) throws TableDataException;    // 数据源一共有多少行(多少条记录)    public abstract int getRowCount() throws TableDataException;    // 对应rowIndex的那一行的第columnIndex列的数值    public abstract Object getValueAt(int rowIndex, int columnIndex);

从抽象类接口可以看出,四个抽象方法意义很明确,使用main方法或者单元测试来进行验证的话,跟踪调试查看的数据没有问题,那么报表中的数据应该就没有问题。

关于数据库连接的问题

按照从前的思维,希望能够使用数据库连接池,复用数据库连接。
然后发现MongoDB的MongoClient和关系型数据库的连接还是有一些区别的,可能开销没有那么大。因此在数据源中直接new新的MongoClient来使用。
再后来仔细想想,我们的程序数据源不是运行在我们的Web容器中,不是使用Spring进行管理,而是编译成为class文件之后拷贝到帆软的设计器目录下使用的,那个时候,怎么使用数据库连接池中的连接资源呢?
所以,目前还是在每个数据源中直接new连接的方式来操作,有没有更好的办法抽空问问帆软的技术支持。

数据源Demo

按照业务,从我们自己的mongo数据库中获取数据,写了如下一个数据源:

public class BQItemDataSource extends AbstractTableData {    private enum Field_Name {        CODE("code"), DESCRIPTION("description"), SPEC("spec"), UNIT("unit"), JLGZ("jlgz"),        TYPE("type"), QUANTITY("quantity");        private String fieldName;        private Field_Name(String fieldName) {            this.fieldName = fieldName;        }        @Override        public String toString() {            return fieldName;        }    }    private String[] columnNames;    private ArrayList<LinkedHashMap<String, Object>> rowData;    public BQItemDataSource() {//        setDefaultParameters(new Parameter[] {new Parameter("ObjectId")});        columnNames = new String[Field_Name.values().length];        int i = 0;        for (Field_Name fieldName : Field_Name.values()) {            columnNames[i++] = fieldName.toString();        }    }    @Override    public int getColumnCount() throws TableDataException {        return columnNames.length;    }    @Override    public String getColumnName(int columnIndex) throws TableDataException {        return columnNames[columnIndex];    }    @Override    public int getRowCount() throws TableDataException {        init();        return rowData.size();    }    @Override    public Object getValueAt(int rowIndex, int columnIndex) {        init();        if (columnIndex >= columnNames.length) {            return null;        }        if (rowIndex >= rowData.size()) {            return null;        }        return rowData.get(rowIndex).get(columnNames[columnIndex]);    }    public void init() {        if (rowData != null) {            return;        }        rowData = new ArrayList<LinkedHashMap<String, Object>>();//        String objectId = parameters[0].getValue().toString();        String objectId = "5959b01b9107541bbc5cb98b";        JSONArray dataArray = getMongoTableData();        for (int i = 0; i < dataArray.size(); i++) {            JSONObject dataObj = dataArray.getJSONObject(i);            LinkedHashMap<String, Object> row = new LinkedHashMap<String, Object>();            if (dataObj.containsKey(Field_Name.CODE.toString())) {                row.put(Field_Name.CODE.toString(), dataObj.getString(Field_Name.CODE.toString()));            } else {                row.put(Field_Name.CODE.toString(), null);            }            if (dataObj.containsKey(Field_Name.DESCRIPTION.toString())) {                row.put(Field_Name.DESCRIPTION.toString(), dataObj.getString(Field_Name.DESCRIPTION.toString()));            } else {                row.put(Field_Name.DESCRIPTION.toString(), null);            }            if (dataObj.containsKey(Field_Name.SPEC.toString())) {                row.put(Field_Name.SPEC.toString(), dataObj.getString(Field_Name.SPEC.toString()));            } else {                row.put(Field_Name.SPEC.toString(), null);            }            if (dataObj.containsKey(Field_Name.UNIT.toString())) {                row.put(Field_Name.UNIT.toString(), dataObj.getString(Field_Name.UNIT.toString()));            } else {                row.put(Field_Name.UNIT.toString(), null);            }            if (dataObj.containsKey(Field_Name.JLGZ.toString())) {                row.put(Field_Name.JLGZ.toString(), dataObj.getString(Field_Name.JLGZ.toString()));            } else {                row.put(Field_Name.JLGZ.toString(), null);            }            if (dataObj.containsKey(Field_Name.TYPE.toString())) {                row.put(Field_Name.TYPE.toString(), dataObj.getString(Field_Name.TYPE.toString()));            } else {                row.put(Field_Name.TYPE.toString(), null);            }            if (dataObj.containsKey(Field_Name.QUANTITY.toString())) {                row.put(Field_Name.QUANTITY.toString(), dataObj.getDouble(Field_Name.QUANTITY.toString()));            } else {                row.put(Field_Name.QUANTITY.toString(), null);            }            rowData.add(row);        }    }    private JSONArray getMongoTableData() {        MongoClient mongoClient=new MongoClient( "localhost" , 27017 );        MongoDatabase db = mongoClient.getDatabase("gbq");        MongoCollection<Document> collection = db.getCollection("project");        Document doc= collection.find(eq("_id", new ObjectId("5959b01b9107541bbc5cb98b"))).first();        if(doc!=null){            JSONObject data = JSONObject.fromObject(doc.get("data"));            return data.getJSONArray("bqItem");        }else{            return new JSONArray();        }    }    public void release() throws Exception {        super.release();        this.rowData = null;    }//    public static void main(String args[]) {//        BQItemDataSource dataSource = new BQItemDataSource();//        dataSource.init();//        try {//            System.out.println(dataSource.getColumnCount());//            for (int i = 0; i < dataSource.getColumnCount(); i++) {//                System.out.println(dataSource.getColumnName(i));//            }//            for (int i = 0; i < dataSource.getRowCount(); i++) {//                for (int j = 0; j < dataSource.getColumnCount(); j++) {//                    System.out.print(dataSource.getValueAt(i, j) + "@");//                }//                System.out.println();//            }//        } catch (Exception e) {//            e.printStackTrace();//        }//    }}

如上的数据源中,没有使用传参方式,而是直接在代码中把参数写死了。实际上可以采用传参的方式,在帆软报表设计器中可以设定参数。
但是,上面的数据源class拷贝到帆软设计器目录下的时候,字段是可以获取到的,但是数据是获取不到的。
这里写图片描述
预览报表的时候页面是空的。

依赖的问题

想了很久,没有得到答案。周末的时候突然灵光一闪,想到一个可能的原因:
- 我们的程序数据源中使用了JSON和MongoClient,这两个东西是依赖于某些jar包的;
- 程序数据源的class文件拷贝到帆软目录下,在帆软的内置服务器中运行,而这个内置服务器未必引用了对应的jar包;
- 那为什么这个class数据源还是可以引用,并且能看到字段呢?因为帆软中引用的是class文件,而不是源代码,用到字段的时候调用getColumnCount和getColumnName这两个方法,是不受影响的;而显示数据的时候,调用getRowCount和getValueAt两个方法,内部使用了JSON和MongoClient相关的接口,可能内部已经报异常了。
解决办法:
- 查看程序数据源的jar包依赖(使用IDEA的Maven插件查看依赖关系图):
这里写图片描述
- 将所依赖的jar包拷贝到帆软的lib目录下:
从上图可以看出,直接依赖的包有json-lib和mongodb-driver;
因mongdodb而间接依赖的包有mongodb-driver-core和bson;
因json而间接依赖的包有commons-beanutils、commons-logging、commons-lang、commons-collections、ezmorph。
完成以上步骤之后,在帆软报表设计器中设计完报表点击预览,可以看到报表的内容:
这里写图片描述
至此,程序数据源的路已经走通,可以尽情发挥业务编程能力,编写数据源了!

原创粉丝点击