通过本教E,(zhn)将?jin)解C么是Mondiran,?qing)如何将mondrian支持d到?zhn)的Java Web目中?/p>
在阅L教程之前Q?zhn)可能需要掌握以下概念:(x)
OLAP(联机分析处理On-Line Analytical Processing)Q?zhn)可以通过阅读ROLAP的概?pptx来了(jin)解OLAP
MDX多维表达式,(zhn)可以通过阅读MDX的基本语法及(qing)概念.pptx来了(jin)解MDX
1. Mondrian是什么?
Mondrian是一个开源项目。一个用Java写成的OLAP引擎。它用MDX语言实现查询Q从关系数据?RDBMS)中读取数据。然后经qJava API以多l的方式对结果进行展C?/p>
Mondrian的用方式同JDBC驱动cM。可以非常方便的与现有的Web目集成
1.1 Mondrian的体pȝ?Architecture)
Mondrian OLAP pȝ由四个层l成; 从最l用户到数据中心(j), 序?
1.1.1 表现?the presentation layer)
1.1.2 l度?the dimensional layer)
1.1.3 集合?the star layer)
1.1.4 存储?the storage layer)
l构囑֦?

1.1.1 表现?the presentation layer)
表现层决定了(jin)最l用户将在他们的昄器上看到什? ?qing)他们如何同pȝ产生交互?/p>
有许多方法可以用来向用户昄多维数据? ?pivot ?(一U交互式的表), pie, line 和图?bar charts)。它们可以用Swing ?JSP来实现?/p>
表现层以多维"文法(grammar)(l、度量、单?”的Ş式发出查询,然后OLAP服务器返回结果?/p>
1.1.1.1 Jpivot表现?br />
JPivot 是Mondrian的表现层TagLibQ一直保持着良好的开发进度?
(zhn)可以通过讉Kjpivot的官方网?a target="_blank" >http://jpivot.sourceforge.net/以获得更多的帮助?qing)支?/p>
jpivot使用XML/ XSLT渲染OLAP报表:
JPivot 使用 WCF (Web Component Framework) Q基于XML/XSLT来渲染Web UIlg。这使它昑־十分另类。不q,OLAP报表q种非常复杂但又有规律可循的东西Q最适合使用XSLT来渲染?br />
jpivot完全ZJSP+TagLibQ?br />
JPivot另外一个可能Z惯的地方是它完全Ztaglib而不是大家熟(zhn)的MVC模式?/p>
但它可以很方便的多l数据展C给最l用P如下表格Q?br />

jpivot其实是一个自定义jsp的标{ֺ。它ZXML/XSLT配置来生成相应的html。所q的是,我们q不需要了(jin)解太多关于这斚w的内容,我们只要掌握相应jsp标签的用即可?/p>
在本教程的实例中Q我们将?x)对一些常用到的jpivot标签q行讲解?/p>
(zhn)还可以通过汉化WEB-INF/jpivot下的xml文g来完成对jpivot的汉化工?/p>
1.1.2 l度?the dimensional layer)
l度层用来解析、验证和执行MDX查询要求?/p>
一个MDX查询要通过几个阶段来完成:(x)首先是计坐标uQaxesQ,再者计坐标uaxes 中cell的倹{?/p>
Z(jin)提高效率Q维度层把要求查询的单元成批发送到集合层,查询转换器接受操作现有查询的hQ而不是对每个h都徏立一个MDX 声明?br />
1.1.3 集合?the star layer)
集合层负责维护和创徏集合~存Q一个集合是在内存(sh)~存?sh)l单元| q些单元值由一l维的值来定?/p>
l度层对q些单元发出查询hQ如果所查询的单元g在缓存(sh)Q则集合理?aggregation manager)?x)向存储层发出查询请?br />
1.1.4 存储?the storage layer)
存储层是一个关pd数据?RDBMS)。它负责创徏集合的单元数据,和提供维表的成员?br />
1.2 API
Mondrian 为客L(fng)提供一个用于查询的API
因ؓ(f)到目前ؓ(f)?q没有一个通用的用于OLAP查询的API,因此Mondrian提供?jin)它U有的API.
管如此,一个常使用JDBC的h同样发现它很熟(zhn)?不同之处仅在于它使用的是MDX查询语言,而非SQL
下面的java片段展示?jin)如何连接到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));
与JDBCcM,一个Connection由DriverManager创徏,Query 对象cM于JDBC 的Statement,它通过传递一个MDX语句来创?Result对象cM于JDBC的ResultSet,只不q它里面保存的是多维数据
(zhn)可以通过查看Mondrian帮助文档里的javadoc来获取更多关于Mondrian API的资?br />
通过上面的介l,(zhn)应该对mondrian的体pL一个基本的?jin)解?/p>
下面我们通过一个简单的例子来加深?zhn)的理解?br />
2. 一个简单的Mondrian例子
现在让我们用一个简单的例子来说明将Mondrian支持d到?zhn)java web的具体步骤?br />
2.1 准备开发工具及(qing)环境
本测试需要的环境Q?br /> 操作pȝQWindows 2000;
Web服务器:(x)tomcat6.0Q?br /> 关系数据库:(x)sql server 2000Q?br /> 开发工P(x)eclipse + myeclipse;
JDBC驱动Qjtds-1.2.2;
(zhn)可以在http://tomcat.apache.org/上下载到tomcat的最新版本及(qing)帮助Q?br /> (zhn)可以在http://www.myeclipseide.com/上下载到myeclipse的最新版本及(qing)相应的eclipse开发^台版?br />
2.2 准备Mondrian资源Q?/h2>
?a target="_blank" >http://sourceforge.net/projects/mondrian/下蝲Mondrian的最新版?目前版本?.0Q大U有50MQ大??br />

2.3 创徏目
启动eclipse?/p>
在eclipse中新创徏一个web目Q名为Tezz。注意需要加入JSTL支持?/p>
具体步骤如下Q?/p>
2.3.1 打开新徏web目对话?/h3>



一个新目Tezz的文件结构如?


2.4 d必须的文?/h2>
下载的压羃包进行解压。完成后Q进入文件夹可以看到如下目录l构。双击进入lib文g夏V?br />


Lib文gҎ(gu)如下内容Q注意到q里的mondrian.war文g是一个可直接布v的项目,我们需要将它解压,然后从中取出我们所需要的文g?其扩展名改成zipQ然后直接右键解?


q入解压后的文g夹,选中jpivot、wcf二个文g夹及(qing)busy.jsp、error.jsp、testpage.jsp三个文gQ我们需要将q些资源复制到我们测试项目的WebRoot文g夹中。按ctrl+C键复制?br />


注:(x)jpivot、wcfq两个文件夹包含mondrian使用的图像和css文g。Busy.jsp昄{待面、error.jsp昄出错面、testpage.jspq文件的用处在后面介绍?/p>
切换到eclipse界面Q在我们的Tezz目的WebRoot文g夹处叛_鼠标Q在弹出的菜单中选择Paste(_脓(chung))卛_


_脓(chung)完成后的目l构如下


注意Q因为我们还未将所有资料复制到目中,因此eclipse?x)显C错误图?br />
最后进入WEB-INF文g?在上面步骤中解压的项目文件mondrian.war?Q选中jpivot、lib、wcfq三个文件夹Q同样需要复制它们到试目的WEB-INF文g夹中?br />


Jpivot、wcfq两个文件夹包含jpivot和wcf用于生成用户界面的配|文?*.xml?.xsl)?qing)标{文?*.tld)的定义。Lib文g夹包含的是mondrian所要用的java包?br />
切换到eclipse界面Q在我们的Tezz目的WebRoot文g夹处叛_鼠标Q在弹出的菜单中选择Paste(_脓(chung))


x(chng)Mondrian的支持添加完毕,下面我们配|web.xmlQ让我们的项目能够用到mondrian的功能?br />
2.5 配置web.xml
用eclipse打开我们在上面解压的布v目的WEB-INF/web.xml文g


qo(h)?filter)
复制如下所C的xml代码到我们测试项目Tezz的web.xml文g中?/p>
作用Q这个过滤器在访?testpage.jsp前被调用。它被设计成jpivot的前端控制器Q用于判断ƈ用L(fng)h发送到某个面?/p>
注:(x)在实际项目中可以使用(zhn)自己定义的servlet或用其他技术来替代它以提供更多的功?br />
- <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>如果q是一个新的会(x)话,则{到此面</description>
- </init-param>
- <init-param>
- <param-name>errorJSP</param-name>
- <param-value>/error.jsp</param-value>
- <description>出错时显C的面</description>
- </init-param>
- <init-param>
- <param-name>busyJSP</param-name>
- <param-value>/busy.jsp</param-value>
- <description>q个面用于当用L(fng)M个查询时Q在q个查询q未结果还回给用户时所昄的界?lt;/description>
- </init-param>
- </filter>
-
- <filter-mapping>
- <filter-name>JPivotController</filter-name>
- <url-pattern>/testpage.jsp</url-pattern>
- </filter-mapping>
复制下面的listener到我们的web.xml文g?用于初始化一些资?
- <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文g或pdf文gq返回给用户Q如果?zhn)需要用到该功能Q则需要将其copy到?zhn)目的web.xml文g?
- <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用于接受q执行一个MDX查询Q然后将该查询以Html表格的Ş式返回。其中的参数connectString用于指定q接到数据库的字W串Q例如用jtds驱动q接到sql server 2000的字W串如下Q?
Provider=mondrian;Jdbc=jdbc:jtds:sqlserver://localhost/Tezz;user=sa;password=123456;Catalog=/WEB-INF/queries/tezz.xml;JdbcDrivers=net.sourceforge.jtds.jdbc.Driver;
如果(zhn)需要用到该功能Q则需要将其copy到?zhn)目的web.xml文g中?/p>
- <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 q两个Servlet 用于生成图表和将其显C给最l用P如果(zhn)需要用到该功能Q则需要将其copy到?zhn)目的web.xml文g中?
- <!-- 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>
它们用于向用L(fng)成和昄如下所C的各种图表Q?


最后添加以下标{ֺ到我们的web.xml目中即?/p>
- <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>
到这里,(zhn)应该对mondrian在web.xml的配|有一定的?jin)解Qƈ可按需要添加相应的功能?
接下来我们将要创建本例子所要用到的表格?qing)数据?/p>
2.6 准备试用表
本例使用的表l构如下所C:(x)


Sale是事实表Q它有两个维Q客?customer)l和׃个表l成的?Product)l?/p>
表格的创建很单,(zhn)只需要将下面的sql语句导入数据库即?/p>
2.6.1 使用以下sql语句创徏?/h3> - /**销售表*/
- create table Sale (
- saleId int not null,
- proId int null,
- cusId int null,
- unitPrice float null, --单h(hun)
- 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)
- )
- /**产品cd?/
- 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,'?sh)?)
- insert into producttype(proTypeId,proTypeName) values(2,'数码')
- insert into producttype(proTypeId,proTypeName) values(3,'家具')
- insert into product(proId,proTypeId,proName) values(1,1,'z衣?)
- insert into product(proId,proTypeId,proName) values(2,1,'?sh)视?)
- 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)文g
一个模式定义了(jin)一个多l数据库. 它包含一个逻辑模型(logical model)、一l数据立?consisting of cubes)、层?hierarchies)、和成员(members), q映到物理模型(关系数据?上?/p>
单的_(d)配置一个模式就是配|一个关pL据结构到多维数据l构的映?/p>
注:(x)关于mondrian的模式及(qing)模式的配|,(zhn)可以通过阅读mondrian的基本模?pptx来了(jin)解。这里我们只对其q行?jin)简单介l?/p>
2.7.1 创徏模式文gQ?/h3>
模式文g的创建很单。首先在WEB-INF下新Z个queries的文件夹Q然后在该文件夹下创Z个名为tezz.xml的文件。再按下面的步骤xml元素d入即可?/p>

2.7.2 配置模式文gQ?/h3> 2.7.2.1 d数据立方Sales:

2.7.2.2 d数据立方Sales的维:

d产品l?因ؓ(f)产品l由两个表连接而成Q因此比客户l复杂些)Q?br />

d度量(共有三个度量:数量、^均单价和总销售额)Q?br />

最后生成的tezz.xml文g内容如下Q?br />
- <?xml version="1.0" encoding="UTF-8"?>
- <Schema name="tezz">
- <Cube name="Sales">
- <!-- 事实?fact table) -->
- <Table name="sale" />
- <!-- 客户l?nbsp;-->
- <Dimension name="客户性别" foreignKey="cusId">
- <Hierarchy hasAll="true" allMemberName="所有性别" primaryKey="cusId">
- <Table name="Customer"></Table>
- <Level name="gender" column="gender"></Level>
- </Hierarchy>
- </Dimension>
- <!-- 产品cdl?nbsp;-->
- <Dimension name="产品cd" 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="K?,##0.00">
- <!-- unitPrice*number所得值的?nbsp;-->
- <MeasureExpression>
- <SQL dialect="generic">(unitPrice*number)</SQL>
- </MeasureExpression>
- </Measure>
- <CalculatedMember name="q_单h(hun)" dimension="Measures">
- <Formula>[Measures].[总销售额] / [Measures].[数量]</Formula>
- <CalculatedMemberProperty name="FORMAT_STRING" value="K?,##0.00" />
- </CalculatedMember>
- </Cube>
- </Schema>
2.8 ~写MDX查询语句
在模式文件定义完成之后,我们可以根据它来编写相应MDX查询语句?jin)?/p>
本例所用的MDX语句如下Q?br /> 
2.9 创徏查询文g
现在我们创Z个jsp文gQ该jsp使用jpivot的mondrianQuery标签来完成查询?/p>
该文件最后将被testpage.jsp使用?/p>
?WEB-INF/queries文g夹下面创Z名ؓ(f)tezz的jsp文g。该jsp包含如下内容Q?br />

2.10 布v目
x(chng)我们已经全部配置完成Q文件结构如下:(x)

布v目Q启动TomcatQ在览器上输入http://localhost:8080/Tezz/testpage.jsp?query=tezz卛_看到如下l果:

?testpage.jsp?query=tezzQ这里的tezz卛_我们创徏的用于查询jsp文g名称
3. testpage.jsp的流E?/h1>
testpage.jsp文g用于发出查询?qing)将l果转换成html格式。它使用一ljsp标签来完成这些复杂的工作?/p>
在本教程的最后一章里Q我们对testpage.jsp的流E及(qing)用到的主要标{进行简单介l?/p>
3.1 wcf:include标签Q?/h2>

3.2 jp:table标签Q?/h2>
<jp:table id="table01" query="#{query01}"/>
jp:tableҎ(gu)query01中保存的l果Q领域数据)(j)准备昄OLAP表格所需的数据(昄数据Q?br />
<wcf:render ref="table01" xslUri="/WEB-INF/jpivot/table/mdxtable.xsl"/>
Ҏ(gu)table01的结果,使用mdxtable.xsl中的配置Q渲染出OLAP表格?br />
3.3 其他jp、wcf标签
同样Q其他jp标签Q如<jp:chart id=“chart01“ ---/>{标{և备待渲染的数据,再由相应?lt;wcf:render ref=“chart01” ---/>标签它们渲染成html格式?/p>
q样Q用户将在浏览器上看到最l的l果?/p>
x(chng)Q一个完整的mondrian查询l束?/p>