mondrian使用测试

     mondrian是一个OLAP引擎,和OLTP不同的是,OLAP是建立在物理表以上的一个逻辑模型,称为一个立方体,这个立方体的建立是通过指定可选的维度和度量,每一个维度有一个维度表,维度有层次信息。OLAP的查找是通过一种MDX表中实现的,类似于OLTP的sql语法,MDX有自己的语法,不过它只规范了查询语句,因为OLAP中不会对数据进行修改,因此OLAP执行的过程中有两个方面,首先就是对模型的定义,在mondrian中,逻辑模型的定义是通过xml的方式交给mondrian解析的,对于这个模型的查询是通过MDX格式的语句完成的,下面通过一个实例调用mondrian接口看一下mondrian的使用。
package mondrianTest;

import java.io.PrintWriter;

import mondrian.olap.Connection;
import mondrian.olap.DriverManager;
import mondrian.olap.Query;
import mondrian.olap.Result;

public class TestMDX {
public void testQuery(){
       Connection connection = DriverManager.getConnection("Provider=mondrian;" + 
            "Jdbc=jdbc:mysql://10.241.20.157:3306/foodmart?user=root&password=root;" +
            "Catalog=C:\\Users\\Administrator\\Desktop\\nrtp\\FoodMart.xml;",
           null, false);
       Query query = connection.parseQuery("select {[Measures].[业务量]} on columns from MsgBusi");
       Result result = connection.execute(query);
       PrintWriter pw = new PrintWriter(System.out);
       result.print(pw);
       pw.flush();

   }

   public static void main(String[] args) {
       TestMDX a =  new TestMDX();
       System.out.println("调用mondrian api进行查询");
       a.testQuery();
   }
}

这段代码是自己从网上down下来的,要想运行它需要加一些执行过程中需要的jar包,至少需要以下几个包:
mondrian使用测试_第1张图片

接着运行出现如下错误:
Exception in thread "main" mondrian.olap.MondrianException: Mondrian Error:Internal error: while parsing catalog C:\Users\Administrator\Desktop\nrtp\FoodMart.xml
at mondrian.resource.MondrianResource$_Def0.ex(MondrianResource.java:755)
at mondrian.olap.Util.newInternal(Util.java:1083)
at mondrian.olap.Util.newError(Util.java:1099)
at mondrian.rolap.RolapSchema.load(RolapSchema.java:303)
at mondrian.rolap.RolapSchema.<init>(RolapSchema.java:213)
at mondrian.rolap.RolapSchema.<init>(RolapSchema.java:76)
at mondrian.rolap.RolapSchema$Pool.get(RolapSchema.java:835)
at mondrian.rolap.RolapSchema$Pool.get(RolapSchema.java:657)
at mondrian.rolap.RolapConnection.<init>(RolapConnection.java:148)
at mondrian.rolap.RolapConnection.<init>(RolapConnection.java:79)
at mondrian.olap.DriverManager.getConnection(DriverManager.java:122)
at mondrian.olap.DriverManager.getConnection(DriverManager.java:87)
at mondrian.olap.DriverManager.getConnection(DriverManager.java:55)
at mondrianTest.TestMDX.testQuery(TestMDX.java:13)
at mondrianTest.TestMDX.main(TestMDX.java:41)
Caused by: org.eigenbase.xom.XOMException: In Schema: In Cube: In Measure: In MeasureExpression: In SQL: Value 'infobright' of attribute 'dialect' has illegal value 'infobright'.  Legal values: {generic, access, db2, derby, firebird, hsqldb, mssql, mysql, oracle, postgres, sybase, teradata, ingres, luciddb}
at mondrian.olap.MondrianDef$Schema.<init>(MondrianDef.java:134)
at mondrian.rolap.RolapSchema.load(RolapSchema.java:289)
... 11 more

这一步是解析模式定义的xml文件出现了错误,根据提示,查看了一下infobright是什么发现它也是一种数据仓库的解决方案,可能当前版本的mondrian不支持它,所以将出现他的xml标签都注释掉算了,反正我们这里只使用mysql(要确保mysql的连接驱动的jar包已经加入到工程的classpath下),不关心其他的。
接着再次运行出现如下错误:

Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:408)
at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1137)
at com.mysql.jdbc.MysqlIO.<init>(MysqlIO.java:355)
at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2490)
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2527)
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2309)
at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:834)
at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:46)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:408)
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:419)
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:344)
at java.sql.DriverManager.getConnection(Unknown Source)
at java.sql.DriverManager.getConnection(Unknown Source)
at mondrianTest.TestMysql.main(TestMysql.java:17)
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
at java.net.PlainSocketImpl.connect(Unknown Source)
at java.net.SocksSocketImpl.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at java.net.Socket.<init>(Unknown Source)
at com.mysql.jdbc.StandardSocketFactory.connect(StandardSocketFactory.java:258)
at com.mysql.jdbc.MysqlIO.<init>(MysqlIO.java:305)
... 15 more

这个错误从堆栈上看是connect引起的,通过http://stackoverflow.com/questions/2983248/com-mysql-jdbc-exceptions-jdbc4-communicationsexception-communications-link-fai的解析发现时由于没连上服务器导致的,但是明明服务器的3306端口已经打开了啊,而且在服务器上使用mysql客户端就可以连接上去啊。这是怎么回事,使用tcpdump抓包发现每一次SYN请求都会被服务器无情的回复RST报文,这说明该端口确实没有打开,使用netstat查看该端口发现的确:



原来服务器只接收本地的连接,怪不得我在windows上一直连不上呢,解决的方法就是修改mysql的配置文件my.conf,把其中的bind-address从原来的127.0.0.1修改为0.0.0.0,这样就可以从远程连接到mysql了。
接着重新运行又出现这样的错误:
Exception in thread "main" mondrian.olap.MondrianException: Mondrian Error:Failed to parse query 'select {[Measures].[业务量]} on columns from MsgBusi'
at mondrian.resource.MondrianResource$_Def0.ex(MondrianResource.java:755)
at mondrian.olap.ConnectionBase.parseQuery(ConnectionBase.java:77)
at mondrian.olap.ConnectionBase.parseQuery(ConnectionBase.java:59)
at mondrianTest.TestMDX.testQuery(TestMDX.java:18)
at mondrianTest.TestMDX.main(TestMDX.java:37)
Caused by: mondrian.olap.MondrianException: Mondrian Error:Error while parsing MDX statement 'select {[Measures].[业务量]} on columns from MsgBusi'
at mondrian.resource.MondrianResource$_Def0.ex(MondrianResource.java:755)
at mondrian.olap.Parser.parseInternal(Parser.java:760)
at mondrian.olap.ConnectionBase.parseQuery(ConnectionBase.java:74)
... 3 more
Caused by: mondrian.olap.MondrianException: Mondrian Error:MDX cube 'MsgBusi' not found or not processed
at mondrian.resource.MondrianResource$_Def0.ex(MondrianResource.java:752)
at mondrian.rolap.RolapSchema.lookupCube(RolapSchema.java:1067)
at mondrian.olap.Query.<init>(Query.java:187)
at mondrian.olap.Parser.makeQuery(Parser.java:851)
at mondrian.olap.CUP$Parser$actions.CUP$Parser$do_action(Parser.java:1700)
at mondrian.olap.Parser.do_action(Parser.java:685)
at java_cup.runtime.lr_parser.parse(lr_parser.java:569)
at mondrian.olap.Parser.parseInternal(Parser.java:755)
... 4 more

这个是查询MDX中出现的错误了,这说明前面的连接mysql,解析xml文件的操作都执行成功了,根据错误提示发现MsgBusi不存在,查看xml文件发现这个MsgBusi的确不存在,这才想起来,原来down下来的代码和我用的逻辑模型的定义不是同一个,我这里用的是mondrian提供的一个示例foodmart,所以我应该用我这里的逻辑模型定义的立方体和维度等进行查询。所以需要修改一下MDX语句,经过多次测试之后发现这样的语句能够在当前的配置中跑出结果:SELECT  { [Measures].[Unit Sales] } on columns,{ [Time].[Year].[1997] } on rows FROM Sales  WHERE ([Customers].[State Province].[CA]),跑出的结果如下:
Axis #0:
{[Customers].[USA].[CA]}
Axis #1:
{[Measures].[Unit Sales]}
Axis #2:
{[Time].[1997]}
Row #0: 74,748

但是我发现mondrian的版本很多,当前最新的版本已经是4.0版本的了,而能够刚才使用的版本还是2.x版本的,然后发现在较新的版本中都在使用olap4j这样的接口进行访问,这个接口类似于jdbc访问数据库引擎,是一个java的接口规范,用于访问ROLAP引擎 ,使用mdx语句,于是就换成当前最新的稳定版本3.9进行测试,换了之后发现出错了:
Exception in thread "main" java.lang.NoSuchMethodError: mondrian.resource.MondrianResource.getThreadOrDefaultLocale()Ljava/util/Locale;
at mondrian.resource.MondrianResource.instance(MondrianResource.java:29)
at mondrian.rolap.aggmatcher.AggTableManager.<clinit>(AggTableManager.java:54)
at mondrian.rolap.RolapSchema.<init>(RolapSchema.java:194)
at mondrian.rolap.RolapSchema.<init>(RolapSchema.java:216)
at mondrian.rolap.RolapSchemaPool.get(RolapSchemaPool.java:214)
at mondrian.rolap.RolapSchemaPool.get(RolapSchemaPool.java:66)
at mondrian.rolap.RolapConnection.<init>(RolapConnection.java:160)
at mondrian.rolap.RolapConnection.<init>(RolapConnection.java:90)
at mondrian.olap.DriverManager.getConnection(DriverManager.java:112)
at mondrian.olap.DriverManager.getConnection(DriverManager.java:68)
at mondrian.olap.DriverManager.getConnection(DriverManager.java:50)
at mondrianTest.TestMDX.testQuery(TestMDX.java:13)
at mondrianTest.TestMDX.main(TestMDX.java:38)

妈的,找不到这个函数,真是奇怪了,这个包也存在,包里面也有这个类,为什么偏偏就没有这个函数呢?!这就是java比较烦人的地方,在C/C++里面编译期间必须能够找到所有的函数定义,虽然有些函数的实现没有找到,在装载的时候也会首先进行动态链接,而不会在执行的时候再进行,但是JAVA这种只有在使用的时候再进行load的方式就很可能出现这种动态的错误,例如找不到class、找不到method等等,这个问题查了半天也没有能够找出错误,最后是在没有办法了采取最后的方法:查看源码。幸亏有grepCode这样的网站,真的是让查看java源代码方便了不知道多少个数量级, 俗话说:源码在手,了无秘密。有了源码,什么问题都不在是问题了,在grepcode中查找mondrian.resource.MondrianResource这个类,发现在mondrian中只有3.6.7之前的版本才有,所以又不得不切换回3.6.x版本,这里我用的是3.6.1版本,换了jar包之后发现这个错误还是存在,好吧,看下代码吧。
     在代码中,的确是MondrianResource的instance方法中(29)行调用了getThreadOrDefaultLocale函数,而在当前的类中没有定义这个函数,那么它一定是继承另外一个类,在父类中实现的,果然,它继承的是org.eigenbase.resgen.ShadowResourceBundle类,再查看一下这个类,我擦,在codegrep上根本搜不到这个类的源码,这时候请教google吧,搜出来这个网页:http://www.java2s.com/Code/Jar/e/Downloadeigenbaseresgenjar.htm,我勒个咔擦,我之前下载的eigenbase-resgen.jar文件不就是在这里下载的吗?!而这个jar包中分明有org.eigenbase.resgen.ShadowResourceBundle类啊(要不然我的代码编译都不会通过的),一切又绕回来了,这时候该怎么办?
     这时候我想起来了,既然mondrian自己能够编译通过,顺利执行,那么无论是在mondrian的maven依赖包中,还是在saifu中都应该有这个包的依赖,而且这个包应该就是可以使用的,而之前的eigenbase-resgen.jar不能使用可能是因为版本问题,查看了mondrian的pom.xml文件,发现它使用的是如下的版本:

<dependency>
      <groupId>eigenbase</groupId>
      <artifactId>eigenbase-resgen</artifactId>
      <version>1.3.1</version>
    </dependency>

查看了saiku的pom.xml文件,它使用的是如下的版本:

<dependency>
               <groupId>eigenbase</groupId>
                <artifactId>eigenbase-resgen</artifactId>
                <version>1.3.0.11873</version> 
</dependency> 

虽然这两个版本不一样,但是我想应该任何一个都可以吧,于是下了1.3.1版本的,将之前的jar包换了一下之后测试一下,OK了,得到了同样的结果。
虽然得到了相同的结果,但是这里还是使用mondrian之前的execute接口来执行MDX语句的,而不是通过olap4j的方式,代码中可以看到execute接口已经被抛弃了,查看mondrian文档看到这样的注释:
Deprecated. This method is deprecated and will be removed in mondrian-4.0. It operates by internally creating a statement. Better to use olap4j and explicitly create a statement.
这里说这个接口将在mondrian 4.0以后被移除了,建议使用olap4j接口创建statement的方式代替,于是又在网上搜到一片mondrian教程( http://alenzhai.iteye.com/blog/2158953),这里使用的就是olap4j的方式访问的,把代码copy下来之后进行修改,保持connection的参数和mdx语句不变,运行过之后能够得到相同的结果,只不过这种访问方式更加规范。附上源代码:

package mondrianTest;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import org.olap4j.Cell;
import org.olap4j.CellSet;
import org.olap4j.OlapConnection;
import org.olap4j.OlapException;
import org.olap4j.OlapStatement;
import org.olap4j.OlapWrapper;
import org.olap4j.Position;
import org.olap4j.metadata.Member;

public class Olap4jTest {		
	/**
	 * 获取连接Olap的连接
	 * @param url  连接Olap的URL
	 * @return
	 * @throws ClassNotFoundException
	 * @throws SQLException
	 */
	public static OlapConnection getConnection(String url) throws ClassNotFoundException, SQLException{
		Class.forName("mondrian.olap4j.MondrianOlap4jDriver");
		Connection connection = DriverManager.getConnection(url);
		OlapConnection olapConnection = connection.unwrap(OlapConnection.class);
		return olapConnection;
	}
	
	/**
	 * 获取查询的结构结果集
	 * @param mdx  mdx查询语句
	 * @param conn Olap连接
	 * @return
	 * @throws OlapException
	 */
	public static CellSet getResultSet(String mdx,OlapConnection conn) throws OlapException{
		OlapStatement statement = conn.createStatement();
		CellSet cellSet = statement.executeOlapQuery(mdx);
		return cellSet;
	}
	
	 public void testQuery(){			 	
		 OlapConnection connection = null;
		try {
			connection = getConnection("jdbc:mondrian:" + 
			        		"Jdbc=jdbc:mysql://10.241.20.157:3306/foodmart?user=root&password=root;" +
			        		"Catalog=C:\\Users\\Administrator\\Desktop\\nrtp\\FoodMart.xml;");
		} catch (ClassNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (SQLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

        String query = "SELECT  { [Measures].[Unit Sales] } on columns,{ [Time].[Year].[1997] } on rows FROM Sales  WHERE ([Customers].[State Province].[CA])";

        //获取查询结果  
        CellSet cs = null;
		try {
			cs = getResultSet(query, connection);
		} catch (OlapException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 

        PrintWriter pw = new PrintWriter(System.out);

        //处理返回数据
		if(cs.getAxes().size()>1){
			for (Position row : cs.getAxes().get(1)) {
			    for (Position column : cs.getAxes().get(0)) {
			        for (Member member : row.getMembers()) {
			            System.out.println("rows:"+member.getUniqueName());
			        }
			        for (Member member : column.getMembers()) {
			            System.out.println("columns:"+member.getUniqueName());
			        }
			        final Cell cell = cs.getCell(column, row);
			        System.out.println("values:"+cell.getValue());
			        System.out.println();
			    }
			    }
		}else{
			for(Position column:cs.getAxes().get(0))
			{
				for(Member member:column.getMembers()){
					System.out.println("columns:"+member.getUniqueName());
				}
				Cell cell=cs.getCell(column);
				System.out.print("values:"+cell.getValue());
				System.out.println();
			}
		}

	}

    public static void main(String[] args) {
    	Olap4jTest a =  new Olap4jTest();
        System.out.println("调用mondrian api进行查询");
        a.testQuery();
    }
}

执行的输出结果如下:
rows:[Time].[1997]
columns:[Measures].[Unit Sales]
values:74748.0

最后看一下使用的jar包的信息:
mondrian使用测试_第2张图片

ok,测试完成了。虽然过程很曲折,但是也学到了一些新知识,这里只是对mondrian做了一个简单的demo,证明了通过api调用的方式使用olap4j可以使用后端的mysql进行olap操作,接下来还需要了解mondrian对于hive和sql on hbase的支持,今天又发现一个新的开源OLAP引擎:Kylin,这个是eBay开源的,它是一个MOLAP引擎,和mondrian(ROLAP引擎)不同的是它会在创建Cube的时候将需要展示的维度和度量聚合到一个表中,并且将这个表进行物化,保存在HBASE中,之后的查询操作不是通过MDX标准来完成的,而是对物化表的SQL查询,这样的优点就是查询速度非常快,但是物化的过程需要一定的时间,并且物化表并不一定能够完全支持所有的cude操作,如果不能支持的话还是通过ROLAP的方式到hive表中动态查找(类似于mondrian),而mondrian不需要保存任何数据(除了mondrian的元数据),所有的MDX查询操作都是动态的生成sql,交由后端的数据库执行完成的,这些sql很可能是多表的join操作,对于hive而言可能性能上是一个很大的问题。
      我觉得mondrian的学习主要在于两个方面:cude的定义和MDX查看,所以接下来还需要学习一个mondrian中生成cube的xml文件的格式,如何定义一个cube、MDX的语法等,先做一个后端连接hive的demo吧。


你可能感兴趣的:(olap,数据仓库,mondrian,ROLAP)