通过本教程,您将了解到什么是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>它们用于向用户生成和显示如下所示的各种图表:
<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)维。
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,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,proName) values(2,'电视机') insert into product(proId,proName) values(3,2,'mp3') insert into product(proId,proName) values(4,'mp4') insert into product(proId,proName) values(5,'数码相机') insert into product(proId,proName) values(6,3,'椅子') insert into product(proId,proName) values(7,'桌子') insert into sale(saleId,proId,cusId,unitPrice,number) values(1,340.34,2) insert into sale(saleId,number) values(2,140.34,1) insert into sale(saleId,number) values(3,240.34,3) insert into sale(saleId,number) values(4,4,540.34,4) insert into sale(saleId,number) values(5,80.34,5) insert into sale(saleId,number) values(6,5,90.34,26) insert into sale(saleId,number) values(7,6,7) insert into sale(saleId,number) values(8,7,640.34,28) insert into sale(saleId,number) values(9,29) insert into sale(saleId,number) values(10,740.34,number) values(11,30.34,number) values(12,1240.34,72) insert into sale(saleId,number) values(13,314.34,27) insert into sale(saleId,number) values(14,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的维:
添加产品维(因为产品维由两个表连接而成,因此比客户维复杂些):
<?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查询结束。