开源olap:mondrian/jpivot 【持续更新】

上周花了好些时间去研究这两个玩意,了解了OLAP的基本原理,学习了一下OLAP查询语言MDX标准,下面是关于MDX的官方解释:

MDX was introduced by Microsoft with Microsoft SQL Server OLAP Services in around 1998, as the language component of the OLE DB for OLAP API. More recently, MDX has appeared as part of the XML for Analysis API. Microsoft proposed MDX as a standard, and its adoption among application writers and other OLAP providers is steadily increasing.

最终把mondrian/jpivot集成到了我们项目中,在此列出遇到的几个困难:

1、如何统一数据源集成。 因为官方demo数据源的指定有两种方式,一种是基于JDNI,需要在tomcat里面配置,另外一种就是要在jsp里面指定,每次都要指定,对于项目,这种做法肯定是不可取的,后来我通过修改 jp:mondrianQuery标签的定义类解决了此问题, 关于数据库的一系列配置,可以通过ServetContext来保存传递。

2、可定制性报表界面展示。场景描述:当一个用户登录,用户会点开一些节点查看数据,如果发现此展示视图符合自己的要求,用户可以保存此视图,下次登录后打开此报表,会直接看到当时所保存的视图。当然这样对一些一般不发生变化维度数据是可取的,如果经常变化,还是另想办法。至于怎么做的就是存储MDX语句就ok,+ 主要要修改jpivot这一层(方法MondrianModel.getResult())。

3、mondrian缓存清理。mondrian的缓存机制相当了得,具体可以看下面的官方文档,为了提高查询效率,所以将所有数据都加载到缓存里面,但这个给调试带来了不便,如果修改了事实表和维表的数据,查询依然还是之前的数据,大家可以通过CacheControl这个类来控制,我做了个缓存清理功能,主要为了生产环境调试用,但只能对于新连接管用,用户必须关闭浏览器重新建立连接才得行,更加的复杂的控制我们可一起探讨 O(∩_∩)O~

4、jpivot钻取数据的格式化。在利用jpivot钻取的时候,发现出来的数字数据都精确到了小数点后两位,我可不想所有数字都这么精确,连带出来的id值也被这样格式化了,悲惨!查阅了官方文档,实现了Level标签中的formatter属性,但是依然没用,后来发现这么一个文件:config.xml, 其实通过配置这个文件可以对某些数据进行格式化定义,貌似国际化没有中文,大家改成修改默认配置就ok。

5、定制jpivot钻取的数据并导出。在利用jpivot钻取的时候,发现钻取的明细数据都是设置好的Level值,但是我想显示一些非Level的值并到出到excel,因为每次点钻取明细数据的时候,其实Jivot都会递交一条SQL去后台查询,我最终的做法是截获此SQL的条件,然后自己拼装好需要查出来的字段,完美解决了! 截获SQL的类在DrillThroughUI.DrilThroughHandler.request , MondrianDrillThrough.drillThrough(Cell),有了SQL,至于导出你自己另外做了!

6、定制mondrian数据权限。情景:我希望olap报表对于admin,可以查看所有地区的销售额,但对于北方区的用户lily则只能查看北方区的销售业绩,这就需要数据级的权限控制了。 mondrian的权限控制有2种,一种是固定的配置,另外一种可以通过编程动态指定,即动态赋予用户某些权限,具体需要修改的地方是:MondrianModel.initialize()里面设置角色的地方。 怎么动态设置角色,可以查看mondrian源代码里面的方法:AccessControlTest.getRestrictedConnection(boolean restrictCustomers),还是给个例子吧:

    //ahuoo
    RoleImpl role = new RoleImpl();
    Schema schema = monConnection.getSchema();
    final boolean fail = true;
    Cube salesCube = schema.lookupCube("Sales", fail);
    final SchemaReader schemaReader = salesCube.getSchemaReader(null);

    Hierarchy storeHierarchy = salesCube.lookupHierarchy(
             new Id.Segment("Customers", Id.Quoting.UNQUOTED), false);
    role.grant(schema, Access.ALL_DIMENSIONS);
    role.grant(salesCube, Access.ALL);

    Level nationLevel = Util.lookupHierarchyLevel(storeHierarchy, "Country");
    Role.RollupPolicy rollupPolicy = Role.RollupPolicy.HIDDEN;

    role.grant(storeHierarchy, Access.CUSTOM, nationLevel, null, rollupPolicy);    

    role.grant(schemaReader.getMemberByUniqueName(
                   Util.parseIdentifier("[Customers].[USA].[WA]"), fail), Access.ALL);

    //使Member   [Customers].[USA].[WA].[Anacortes]无法访问
    role.grant(schemaReader.getMemberByUniqueName(
                 Util.parseIdentifier("[Customers].[USA].[WA].[Anacortes]"), fail), Access.NONE);

    //Prevents any further modifications
    role.makeImmutable();

    //设置角色
    monConnection.setRole(role);

7、关于Hierarchy中hasAll属性的说明。一般情况下,我们设置为true即可,如果设置了属性allMemberName=”所有性别”,则olap就会显示“所有性别”这个名字作为其顶层,否则mondrian会帮起个ALL hierarchyName的名字; 如果我们设置成了false,表示我们不要显示这个顶层,直接显示某个级别下的成员,可由属性defaultMember指定具体的成员,不指定默认显示第一个成员。

8、关于聚集表agg的建立。聚集表有助于提高数据查询的性能,其原理可以与oracle中的物化视图类似,只是mondrian在程序级做了查询选取策略。具体建立可以参看官方文档,下面给个小例子:

<Table name=”T_FCT_KUAIDI_QUERY” >
<AggName name=”MV_AGG_FCT_KUAIDI_QUERY”>
<AggFactCount column=”FFACTCOUNT”/>

<AggForeignKey factColumn=”FCOMID” aggColumn=”FCOMID”/>
<AggForeignKey factColumn=”FQUERYRESOURCE” aggColumn=”FQUERYRESOURCE”/>

<AggMeasure name=”[Measures].[查询次数]” column=”FQUERYCOUNT” />
<AggMeasure name=”[Measures].[失败次数]” column=”FFAILCOUNT” />
<AggLevel name=”[日期].[月份]” column=”FMONTH” />
<AggLevel name=”[日期].[年份]” column=”FYEAR” />
</AggName>
</Table>

其中要注意的是 AggFactCount必须要有,对于某些连接到维表的字段, 必须配置AggForeignKey,不然出来的结果有问题,但不会报任何错,%>_<%, 可以通过配置log4j.logger.mondrian.rolap.agg.AggregationManager=DEBUG来跟踪真正执行的SQL,验证Agg是否可用.

9、关于mondrian的性能优化。mondrian性能的优化在其官方文档中有很大篇幅提到,主要是从两点入手:

  • agg聚集表的建立:如上面的第8点所示,mondrian官方文档中提到了两种agg表建立的方案:  lost和collapsed,就是丢掉一些维,聚合一些维,用起来感觉还是不能随心所欲,对于collapsed,测试发现只可容纳一个维度的聚集。
  • 对应数据库索引的建立:不同数据库有不同的优化方案,对于oracle,文档中有提到用星型转换,本人测试其实在事实表外键上建立bitmap通常比较简单耐用。

下面是引用文档中提到的优化策略:

Performance tuning is an iterative process. The steps are something like this:

  1. Choose a few queries which are typical for those the end-users will be executing.
  2. Run your set of sample queries, and note how long they take. Now the cache has been primed, run the queries again: has performance improved?
  3. Is the performance good enough? If it is, stop tuning now! If your data set isn’t very large, you probably don’t need any aggregate tables.
  4. Decide which aggregate tables to create. If you turn on SQL tracing, looking at the GROUP BY clauses of the long-running SQL statements will be a big clue here.
  5. Register the aggregate tables in your catalog, create the tables in the database, populate the tables, and add indexes.
  6. Restart Mondrian, to flush the cache and re-read the schema, then go to step 2 to see if things have improved.

对于新手,可能不知道mondrian jpivot到底是什么?我这里解释一下:
mondrian :是olap服务器,负责olap模型到关系数据库的映射,简单的说就是实现了MDX标准,可以将MDX语句转换成关系数据库中的一套。

jpivot: 简单的说就是个展示工具,把mondrian传来的xml数据渲染成我们熟悉的html,对于层次性很强的报表,xml渲染的确有他的魅力,免去了繁杂的js痛苦,画个整个数据流程图吧

oracle/mysql ———->mondrian———->jpivot

这里给个官方的学习文档地址:
http://mondrian.pentaho.org/documentation/olap.php

已有22 条评论

    • 在包的com.tonbeller.wcf.format这个目录下面有个配置文件config.xml,因为没有中文版,所以读取的是默认配置,你把默认的pattern改哈就可以了

  1. 我报了一个异常,Caused by: org.apache.commons.beanutils.NestedNullException: Null property value for ‘propertyConfig’
    at org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(PropertyUtilsBean.java:669)
    at org.apache.commons.beanutils.PropertyUtilsBean.getProperty(PropertyUtilsBean.java:715)
    at org.apache.commons.beanutils.PropertyUtils.getProperty(PropertyUtils.java:290)
    at com.tonbeller.wcf.expr.ExprUtils.getModelReference(ExprUtils.java:87)
    at com.tonbeller.wcf.controller.RequestContextImpl.getModelReference(RequestContextImpl.java:106)
    at com.tonbeller.wcf.toolbar.ScriptButtonModel.isPressed(ScriptButtonModel.java:38)
    at com.tonbeller.wcf.toolbar.ToolButton.render(ToolButton.java:114)
    at com.tonbeller.wcf.toolbar.ToolBar.render(ToolBar.java:77)
    at com.tonbeller.wcf.component.NestableComponentSupport.render(NestableComponentSupport.java:39)
    at com.tonbeller.wcf.component.RendererTag.doEndTag(RendererTag.java:137)
    at org.apache.jsp.testpage_jsp._jspx_meth_wcf_005frender_005f0(testpage_jsp.java:1186)
    at org.apache.jsp.testpage_jsp._jspService(testpage_jsp.java:185)
    … 69 more

    不知道这是不是哪儿没有配置对还是因为我使用了spring scurity的原因,麻烦您指点一下啊,谢谢

    • 把你的testpage.jsp里面的这一句给去掉试试

      wcf:scriptbutton id=”propertiesButton” tooltip=”toolb.properties” img=”properties” model=”#{table01.rowAxisBuilder.axisConfig.propertyConfig.showProperties}”

    • for(Iterator itSchemas = RolapSchema.getRolapSchemas(); itSchemas.hasNext();)
      {
      RolapSchema schema1 = itSchemas.next();
      RolapConnection con = schema1.getInternalConnection();
      CacheControl cc = con.getCacheControl(null);
      cc.flushSchemaCache(); //刷新mondrian缓存,注意,只对新连接有用,旧连接会依旧用缓存

      }

    • 当然可以结合你当前的业务系统使用,但是你必须要改动一些jpivot的源代码才行,你可以先调试调试mondrian原有的权限例子,然后在我上面提到的地方打断点测试!

  2. 谢谢回复,能否告知一下你的联系方式交流一下吗? 现在遇到一个问题,同时打开几个多维分析页面时,会出现页面混淆的情况,也就是都变成了一个多维分析页面了, <jp:mondrianQuery id="query01" id并不一样啊?另外我的mdx查询并不是写在一个jsp页面上,每个多维分析页面对应一个mdx查询页面,请指教,谢谢。。。

  3. //可以这么获取SQL
    int reportType = (Integer)session.getAttribute(“reportType”);
    String sql = ((MondrianCell) cell).getMonCell().getDrillThroughSQL(true);
    session.setAttribute(“drillSql_”+reportType, sql);

  4. ahuoooo,请教个问题。
    我的schema中有

    现在有数据,中国-山东-济南,但钻取的时候点中国就出不来了,是不是汉字钻取会出现问题?按英文名字钻取是的可以。

  5. 你好,想请教您一下,我下载了mondrian按照网上的方式把他部署到了MyEclipse上,在新建的jsp上代码:

    select {[Measures].[zxse]} ON columns,{([Cplb].[alllb],[Yhxb].[allxb])} ON rows from Sales

    然后运行改jsp,报错:org.apache.jasper.JasperException: javax.servlet.ServletException: javax.servlet.jsp.JspException: java.lang.NullPointerException
    我刚学这方面,实在找不出原因,请您帮忙看一下,麻烦了,感谢

糖果进行回复 取消回复

电子邮件地址不会被公开。 必填项已用*标注

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>