通过本教程,您将了解到什么是Mondiran,及如何将mondrian支持添加到您的Java Web项目中。
在阅读本教程之前,您可能需要掌握以下概念:
OLAP(联机分析处理On-Line Analytical Processing),您可以通过阅读ROLAP的概念.pptx来了解OLAP
MDX多维表达式,您可以通过阅读MDX的基本语法及概念.pptx来了解MDX
1. Mondrian是什么?
Mondrian是一个开源项目。一个用Java写成的OLAP引擎。它用MDX语言实现查询,从关系数据库(RDBMS)中读取数据。然后经过Java API以多维的方式对结果进行展示。
Mondrian的使用方式同JDBC驱动类似。可以非常方便的与现有的Web项目集成
1.1 Mondrian的体系结构(Architecture)
Mondrian OLAP 系统由四个层组成; 从最终用户到数据中心, 顺序为:
1.1.1 表现层(the presentation layer)
1.1.2 维度层(the dimensional layer)
1.1.3 集合层(the star layer)
1.1.4 存储层(the storage layer)
结构图如下:
1.1.1 表现层(the presentation layer)
表现层决定了最终用户将在他们的显示器上看到什么, 及他们如何同系统产生交互。
有许多方法可以用来向用户显示多维数据集, 有 pivot 表 (一种交互式的表), pie, line 和图表(bar charts)。它们可以用Swing 或 JSP来实现。
表现层以多维"文法(grammar)(维、度量、单元)”的形式发出查询,然后OLAP服务器返回结果。
1.1.1.1 Jpivot表现层
JPivot 是Mondrian的表现层TagLib,一直保持着良好的开发进度。
您可以通过访问jpivot的官方网站http://jpivot.sourceforge.net/以获得更多的帮助及支持
jpivot使用XML/ XSLT渲染OLAP报表:
JPivot 使用 WCF (Web Component Framework) ,基于XML/XSLT来渲染Web UI组件。这使它显得十分另类。不过,OLAP报表这种非常复杂但又有规律可循的东西,最适合使用XSLT来渲染。
jpivot完全基于JSP+TagLib:
JPivot另外一个可能使人不惯的地方是它完全基于taglib而不是大家熟悉的MVC模式。
但它可以很方便的将多维数据展示给最终用户,如下表格:
jpivot其实是一个自定义jsp的标签库。它基于XML/XSLT配置来生成相应的html。所幸的是,我们并不需要了解太多关于这方面的内容,我们只要掌握相应jsp标签的使用即可。
在本教程的实例中,我们将会对一些常用到的jpivot标签进行讲解。
您还可以通过汉化WEB-INF/jpivot下的xml文件来完成对jpivot的汉化工作
1.1.2 维度层(the dimensional layer)
维度层用来解析、验证和执行MDX查询要求。
一个MDX查询要通过几个阶段来完成:首先是计算坐标轴(axes),再者计算坐标轴axes 中cell的值。
为了提高效率,维度层把要求查询的单元成批发送到集合层,查询转换器接受操作现有查询的请求,而不是对每个请求都建立一个MDX 声明。
1.1.3 集合层(the star layer)
集合层负责维护和创建集合缓存,一个集合是在内存中缓存一组单元值, 这些单元值由一组维的值来确定。
维度层对这些单元发出查询请求,如果所查询的单元值不在缓存中,则集合管理器(aggregation manager)会向存储层发出查询请求
1.1.4 存储层(the storage layer)
存储层是一个关系型数据库(RDBMS)。它负责创建集合的单元数据,和提供维表的成员。
1.2 API
Mondrian 为客户端提供一个用于查询的API
因为到目前为止,并没有一个通用的用于OLAP查询的API,因此Mondrian提供了它私有的API.
尽管如此,一个常使用JDBC的人将同样发现它很熟悉.不同之处仅在于它使用的是MDX查询语言,而非SQL
下面的java片段展示了如何连接到Mondrian,然后执行一个查询,最后打印结果.
- import mondrian.olap.*;
- import java.io.PrintWriter;
- Connection connection = DriverManager.getConnection("Provider=mondrian;"
- +"Jdbc=jdbc:odbc:MondrianFoodMart;"
- +"Catalog=/WEB-INF/FoodMart.xml;",null,false);
- Query query = connection.parseQuery("SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} on columns,"
- +" {[Product].children} on rows "
- +"FROM [Sales] " +"WHERE ([Time].[1997].[Q1], [Store].[CA].[San Francisco])");
- Result result = connection.execute(query);
- result.print(new PrintWriter(System.out));
与JDBC类似,一个Connection由DriverManager创建,Query 对象类似于JDBC 的Statement,它通过传递一个MDX语句来创建.Result对象类似于JDBC的ResultSet,只不过它里面保存的是多维数据
您可以通过查看Mondrian帮助文档里的javadoc来获取更多关于Mondrian API的资料
通过上面的介绍,您应该对mondrian的体系有一个基本的了解。
下面我们将通过一个简单的例子来加深您的理解。
2. 一个简单的Mondrian例子
现在让我们用一个简单的例子来说明将Mondrian支持添加到您java web的具体步骤。
2.1 准备开发工具及环境
本测试需要的环境:
操作系统:Windows 2000;
Web服务器:tomcat6.0;
关系数据库:sql server 2000;
开发工具:eclipse + myeclipse;
JDBC驱动:jtds-1.2.2;
您可以在http://tomcat.apache.org/上下载到tomcat的最新版本及帮助;
您可以在http://www.myeclipseide.com/上下载到myeclipse的最新版本及相应的eclipse开发平台版本
2.2 准备Mondrian资源:
从http://sourceforge.net/projects/mondrian/下载Mondrian的最新版本(目前版本为3.0,大约有50M+大小)。
2.3 创建项目
启动eclipse。
在eclipse中新创建一个web项目,名为Tezz。注意需要加入JSTL支持。
具体步骤如下:
2.3.1 打开新建web项目对话框
一个新项目Tezz的文件结构如下:
2.4 添加必须的文件
将下载的压缩包进行解压。完成后,进入文件夹可以看到如下目录结构。双击进入lib文件夹。
Lib文件夹有如下内容:注意到这里的mondrian.war文件是一个可直接布署的项目,我们需要将它解压,然后从中取出我们所需要的文件。(建议将其扩展名改成zip,然后直接右键解压)
进入解压后的文件夹,选中jpivot、wcf二个文件夹及busy.jsp、error.jsp、testpage.jsp三个文件,我们需要将这些资源复制到我们测试项目的WebRoot文件夹中。按ctrl+C键复制。
注:jpivot、wcf这两个文件夹包含mondrian使用的图像和css文件。Busy.jsp显示等待页面、error.jsp显示出错页面、testpage.jsp这文件的用处将在后面介绍。
切换到eclipse界面,在我们的Tezz项目的WebRoot文件夹处右击鼠标,在弹出的菜单中选择Paste(粘贴)即可
粘贴完成后的项目结构如下
注意:因为我们还未将所有资料复制到项目中,因此eclipse会显示错误图标
最后进入WEB-INF文件夹(在上面步骤中解压的项目文件mondrian.war里),选中jpivot、lib、wcf这三个文件夹,同样需要复制它们到测试项目的WEB-INF文件夹中。
Jpivot、wcf这两个文件夹包含jpivot和wcf用于生成用户界面的配置文件(*.xml、*.xsl)及标签文件(*.tld)的定义。Lib文件夹包含的是mondrian所要用的java包。
切换到eclipse界面,在我们的Tezz项目的WebRoot文件夹处右击鼠标,在弹出的菜单中选择Paste(粘贴)
至此Mondrian的支持添加完毕,下面我们将配置web.xml,让我们的项目能够使用到mondrian的功能。
2.5 配置web.xml
用eclipse打开我们在上面解压的布署项目的WEB-INF/web.xml文件
过滤器(filter)
复制如下所示的xml代码到我们测试项目Tezz的web.xml文件中。
作用:这个过滤器在访问/testpage.jsp前被调用。它被设计成jpivot的前端控制器,用于判断并将用户的请求发送到某个页面。
注:在实际项目中可以使用您自己定义的servlet或使用其他技术来替代它以提供更多的功能
- <filter>
- <filter-name>JPivotController</filter-name>
- <filter-class>com.tonbeller.wcf.controller.RequestFilter</filter-class>
- <init-param>
- <param-name>indexJSP</param-name>
- <param-value>/index.html</param-value>
- <description>如果这是一个新的会话,则转到此页面</description>
- </init-param>
- <init-param>
- <param-name>errorJSP</param-name>
- <param-value>/error.jsp</param-value>
- <description>出错时显示的页面</description>
- </init-param>
- <init-param>
- <param-name>busyJSP</param-name>
- <param-value>/busy.jsp</param-value>
- <description>这个页面用于当用户点击一个查询时,在这个查询还未将结果还回给用户时所显示的界面</description>
- </init-param>
- </filter>
-
- <filter-mapping>
- <filter-name>JPivotController</filter-name>
- <url-pattern>/testpage.jsp</url-pattern>
- </filter-mapping>
复制下面的listener到我们的web.xml文件中(用于初始化一些资源)
- <listener>
- <listener-class>mondrian.web.taglib.Listener</listener-class>
- </listener>
-
- <!– 资源初始化-->
- <listener>
- <listener-class>com.tonbeller.tbutils.res.ResourcesFactoryContextListener</listener-class>
- </listener>
Print servlet,该servlet用于将数据生成Excel文件或pdf文件并返回给用户,如果您需要用到该功能,则需要将其copy到您项目的web.xml文件中
- <servlet>
- <servlet-name>Print</servlet-name>
- <display-name>Print</display-name>
- <description>Default configuration created for servlet.</description>
- <servlet-class>com.tonbeller.jpivot.print.PrintServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>Print</servlet-name>
- <url-pattern>/Print</url-pattern>
- </servlet-mapping>
MDXQueryServlet用于接受并执行一个MDX查询,然后将该查询以Html表格的形式返回。其中的参数connectString用于指定连接到数据库的字符串,例如使用jtds驱动连接到sql server 2000的字符串如下:
Provider=mondrian;Jdbc=jdbc:jtds:sqlserver://localhost/Tezz;user=sa;password=123456;Catalog=/WEB-INF/queries/tezz.xml;JdbcDrivers=net.sourceforge.jtds.jdbc.Driver;
如果您需要用到该功能,则需要将其copy到您项目的web.xml文件中。
- <servlet>
- <servlet-name>MDXQueryServlet</servlet-name>
- <servlet-class>mondrian.web.servlet.MDXQueryServlet</servlet-class>
- <init-param>
- <param-name>connectString</param-name>
- <param-value>@mondrian.webapp.connectString@</param-value>
- </init-param>
- </servlet>
- <servlet-mapping>
- <servlet-name>MDXQueryServlet</servlet-name>
- <url-pattern>/mdxquery</url-pattern>
- </servlet-mapping>
DisplayChart 和GetChart 这两个Servlet 用于生成图表和将其显示给最终用户,如果您需要用到该功能,则需要将其copy到您项目的web.xml文件中。
- <!-- jfreechart provided servlet -->
- <servlet>
- <servlet-name>DisplayChart</servlet-name>
- <servlet-class>org.jfree.chart.servlet.DisplayChart</servlet-class>
- </servlet>
- <!-- jfreechart provided servlet -->
- <servlet>
- <servlet-name>GetChart</servlet-name>
- <display-name>GetChart</display-name>
- <description>Default configuration created for servlet.</description>
- <servlet-class>com.tonbeller.jpivot.chart.GetChart</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>DisplayChart</servlet-name>
- <url-pattern>/DisplayChart</url-pattern>
- </servlet-mapping>
- <servlet-mapping>
- <servlet-name>GetChart</servlet-name>
- <url-pattern>/GetChart</url-pattern>
- </servlet-mapping>
它们用于向用户生成和显示如下所示的各种图表:
最后添加以下标签库到我们的web.xml项目中即可
- <taglib>
- <taglib-uri>http://www.tonbeller.com/wcf</taglib-uri>
- <taglib-location>/WEB-INF/wcf/wcf-tags.tld</taglib-location>
- </taglib>
-
- <taglib>
- <taglib-uri>http://www.tonbeller.com/jpivot</taglib-uri>
- <taglib-location>/WEB-INF/jpivot/jpivot-tags.tld</taglib-location>
- </taglib>
到这里,您应该对mondrian在web.xml的配置有一定的了解,并可按需要添加相应的功能。
接下来我们将要创建本例子所要用到的表格及数据。
2.6 准备测试用表
本例使用的表结构如下所示:
Sale是事实表,它有两个维:客户(customer)维和由两个表组成的产品(Product)维。
表格的创建很简单,您只需要将下面的sql语句导入数据库即可
2.6.1 使用以下sql语句创建表
- /**销售表*/
- create table Sale (
- saleId int not null,
- proId int null,
- cusId int null,
- unitPrice float null, --单价
- number int null, --数量
- constraint PK_SALE primary key (saleId)
- )
- /**用户表*/
- create table Customer (
- cusId int not null,
- gender char(1) null, --性别
- constraint PK_CUSTOMER primary key (cusId)
- )
- /**产品表*/
- create table Product (
- proId int not null,
- proTypeId int null,
- proName varchar(32) null,
- constraint PK_PRODUCT primary key (proId)
- )
- /**产品类别表*/
- create table ProductType (
- proTypeId int not null,
- proTypeName varchar(32) null,
- constraint PK_PRODUCTTYPE primary key (proTypeId)
- )
2.6.2 使用以下sql语句导入数据
- insert into Customer(cusId,gender) values(1,'F')
- insert into Customer(cusId,gender) values(2,'M')
- insert into Customer(cusId,gender) values(3,'M')
- insert into Customer(cusId,gender) values(4,'F')
- insert into producttype(proTypeId,proTypeName) values(1,'电器')
- insert into producttype(proTypeId,proTypeName) values(2,'数码')
- insert into producttype(proTypeId,proTypeName) values(3,'家具')
- insert into product(proId,proTypeId,proName) values(1,1,'洗衣机')
- insert into product(proId,proTypeId,proName) values(2,1,'电视机')
- insert into product(proId,proTypeId,proName) values(3,2,'mp3')
- insert into product(proId,proTypeId,proName) values(4,2,'mp4')
- insert into product(proId,proTypeId,proName) values(5,2,'数码相机')
- insert into product(proId,proTypeId,proName) values(6,3,'椅子')
- insert into product(proId,proTypeId,proName) values(7,3,'桌子')
- insert into sale(saleId,proId,cusId,unitPrice,number) values(1,1,1,340.34,2)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(2,1,2,140.34,1)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(3,2,3,240.34,3)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(4,3,4,540.34,4)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(5,4,1,80.34,5)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(6,5,2,90.34,26)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(7,6,3,140.34,7)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(8,7,4,640.34,28)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(9,6,1,140.34,29)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(10,7,2,740.34,29)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(11,5,3,30.34,28)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(12,4,4,1240.34,72)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(13,3,1,314.34,27)
- insert into sale(saleId,proId,cusId,unitPrice,number) values(14,3,2,45.34,27)
2.7 建立模式(schema)文件
一个模式定义了一个多维数据库. 它包含一个逻辑模型(logical model)、一组数据立方(consisting of cubes)、层次(hierarchies)、和成员(members), 并映射到物理模型(关系数据库)上。
简单的说,配置一个模式就是配置一个关系数据结构到多维数据结构的映射。
注:关于mondrian的模式及模式的配置,您可以通过阅读mondrian的基本模式.pptx来了解。这里我们只对其进行了简单介绍。
2.7.1 创建模式文件:
模式文件的创建很简单。首先在WEB-INF下新建一个queries的文件夹,然后在该文件夹下创建一个名为tezz.xml的文件。再按下面的步骤将xml元素添加入即可。
2.7.2 配置模式文件:
2.7.2.1 添加数据立方Sales:
2.7.2.2 添加数据立方Sales的维:
添加产品维(因为产品维由两个表连接而成,因此比客户维复杂些):
添加度量(共有三个度量:数量、平均单价和总销售额):
最后生成的tezz.xml文件内容如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <Schema name="tezz">
- <Cube name="Sales">
- <!-- 事实表(fact table) -->
- <Table name="sale" />
- <!-- 客户维 -->
- <Dimension name="客户性别" foreignKey="cusId">
- <Hierarchy hasAll="true" allMemberName="所有性别" primaryKey="cusId">
- <Table name="Customer"></Table>
- <Level name="gender" column="gender"></Level>
- </Hierarchy>
- </Dimension>
- <!-- 产品类别维 -->
- <Dimension name="产品类别" foreignKey="proId">
- <Hierarchy hasAll="true" allMemberName="所有产品" primaryKey="proId" primaryKeyTable="product">
- <join leftKey="proTypeId" rightKey="proTypeId">
- <Table name="product" />
- <Table name="producttype"></Table>
- </join>
- <Level name="proTypeId" column="proTypeId"
- nameColumn="proTypeName" uniqueMembers="true" table="producttype" />
- <Level name="proId" column="proId" nameColumn="proName"
- uniqueMembers="true" table="product" />
- </Hierarchy>
- </Dimension>
- <Measure name="数量" column="number" aggregator="sum" datatype="Numeric" />
- <Measure name="总销售额" aggregator="sum" formatString="¥#,##0.00">
- <!-- unitPrice*number所得值的列 -->
- <MeasureExpression>
- <SQL dialect="generic">(unitPrice*number)</SQL>
- </MeasureExpression>
- </Measure>
- <CalculatedMember name="平均单价" dimension="Measures">
- <Formula>[Measures].[总销售额] / [Measures].[数量]</Formula>
- <CalculatedMemberProperty name="FORMAT_STRING" value="¥#,##0.00" />
- </CalculatedMember>
- </Cube>
- </Schema>
2.8 编写MDX查询语句
在模式文件定义完成之后,我们就可以根据它来编写相应MDX查询语句了。
本例所用的MDX语句如下:
2.9 创建查询文件
现在我们将创建一个jsp文件,该jsp使用jpivot的mondrianQuery标签来完成查询。
该文件最后将被testpage.jsp使用。
在/WEB-INF/queries文件夹下面创建一名为tezz的jsp文件。该jsp包含如下内容:
2.10 布署项目
至此我们已经全部配置完成,文件结构如下:
布署项目,启动Tomcat,在浏览器上输入http://localhost:8080/Tezz/testpage.jsp?query=tezz即可看到如下结果:
注:testpage.jsp?query=tezz,这里的tezz即刚我们创建的用于查询jsp文件名称
3. testpage.jsp的流程
testpage.jsp文件用于发出查询及将结果转换成html格式。它使用一组jsp标签来完成这些复杂的工作。
在本教程的最后一章里,我们对testpage.jsp的流程及用到的主要标签进行简单介绍。
3.1 wcf:include标签:
3.2 jp:table标签:
<jp:table id="table01" query="#{query01}"/>
jp:table根据query01中保存的结果(领域数据)准备显示OLAP表格所需的数据(显示数据)
<wcf:render ref="table01" xslUri="/WEB-INF/jpivot/table/mdxtable.xsl"/>
根据table01的结果,使用mdxtable.xsl中的配置,渲染出OLAP表格。
3.3 其他jp、wcf标签
同样,其他jp标签,如<jp:chart id=“chart01“ ---/>等标签准备待渲染的数据,再由相应的<wcf:render ref=“chart01” ---/>标签将它们渲染成html格式。
这样,用户将在浏览器上看到最终的结果。
至此,一个完整的mondrian查询结束。