MongoDB程序数据源+报表设计探路

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

测试程序数据源可行性
在项目中写了一个数据源,如下:
  1. 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方法进行跟踪调试;
- 单元测试;
  1.     // 数据源一共有多少列(多少个字段)    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数据库中获取数据,写了如下一个数据源:
  1. 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。
完成以上步骤之后,在帆软报表设计器中设计完报表点击预览,可以看到报表的内容:

至此,程序数据源的路已经走通,可以尽情发挥业务编程能力,编写数据源了!

分享扩散:
参与人数 +1 F豆 +66 理由
兔子酱 + 66 骚年,我看好你哦

查看全部评分

沙发
发表于 2017-8-7 12:49:44
楼主排版要加强呀,已经为楼主将代码部分修改为代码样式插入。

发帖编辑器的使用(这个很重要!!!)
http://bbs.fanruan.com/thread-63837-1-1.html
(出处: 帆软论坛)
板凳
发表于 2017-8-8 11:27:56
find没太大用,基本都用聚合
地板
发表于 2017-8-8 15:32:22
这个排版看得我无FUCK说~
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

4回帖数 1关注人数 2745浏览人数
最后回复于:2017-8-8 18:04

返回顶部 返回列表