mondrian使用测试
生活随笔
收集整理的這篇文章主要介紹了
mondrian使用测试
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
? ? ?mondrian是一個OLAP引擎,和OLTP不同的是,OLAP是建立在物理表以上的一個邏輯模型,稱為一個立方體,這個立方體的建立是通過指定可選的維度和度量,每一個維度有一個維度表,維度有層次信息。OLAP的查找是通過一種MDX表中實現(xiàn)的,類似于OLTP的sql語法,MDX有自己的語法,不過它只規(guī)范了查詢語句,因為OLAP中不會對數(shù)據(jù)進行修改,因此OLAP執(zhí)行的過程中有兩個方面,首先就是對模型的定義,在mondrian中,邏輯模型的定義是通過xml的方式交給mondrian解析的,對于這個模型的查詢是通過MDX格式的語句完成的,下面通過一個實例調(diào)用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].[業(yè)務量]} 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("調(diào)用mondrian api進行查詢");a.testQuery();}
}
這段代碼是自己從網(wǎng)上down下來的,要想運行它需要加一些執(zhí)行過程中需要的jar包,至少需要以下幾個包:
接著運行出現(xiàn)如下錯誤: 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文件出現(xiàn)了錯誤,根據(jù)提示,查看了一下infobright是什么發(fā)現(xiàn)它也是一種數(shù)據(jù)倉庫的解決方案,可能當前版本的mondrian不支持它,所以將出現(xiàn)他的xml標簽都注釋掉算了,反正我們這里只使用mysql(要確保mysql的連接驅(qū)動的jar包已經(jīng)加入到工程的classpath下),不關心其他的。 接著再次運行出現(xiàn)如下錯誤:
Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failureThe 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的解析發(fā)現(xiàn)時由于沒連上服務器導致的,但是明明服務器的3306端口已經(jīng)打開了啊,而且在服務器上使用mysql客戶端就可以連接上去啊。這是怎么回事,使用tcpdump抓包發(fā)現(xiàn)每一次SYN請求都會被服務器無情的回復RST報文,這說明該端口確實沒有打開,使用netstat查看該端口發(fā)現(xiàn)的確:
原來服務器只接收本地的連接,怪不得我在windows上一直連不上呢,解決的方法就是修改mysql的配置文件my.conf,把其中的bind-address從原來的127.0.0.1修改為0.0.0.0,這樣就可以從遠程連接到mysql了。 接著重新運行又出現(xiàn)這樣的錯誤: Exception in thread "main" mondrian.olap.MondrianException: Mondrian Error:Failed to parse query 'select {[Measures].[業(yè)務量]} 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].[業(yè)務量]} 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中出現(xiàn)的錯誤了,這說明前面的連接mysql,解析xml文件的操作都執(zhí)行成功了,根據(jù)錯誤提示發(fā)現(xiàn)MsgBusi不存在,查看xml文件發(fā)現(xiàn)這個MsgBusi的確不存在,這才想起來,原來down下來的代碼和我用的邏輯模型的定義不是同一個,我這里用的是mondrian提供的一個示例foodmart,所以我應該用我這里的邏輯模型定義的立方體和維度等進行查詢。所以需要修改一下MDX語句,經(jīng)過多次測試之后發(fā)現(xiàn)這樣的語句能夠在當前的配置中跑出結果: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
但是我發(fā)現(xiàn)mondrian的版本很多,當前最新的版本已經(jīng)是4.0版本的了,而能夠剛才使用的版本還是2.x版本的,然后發(fā)現(xiàn)在較新的版本中都在使用olap4j這樣的接口進行訪問,這個接口類似于jdbc訪問數(shù)據(jù)庫引擎,是一個java的接口規(guī)范,用于訪問ROLAP引擎,使用mdx語句,于是就換成當前最新的穩(wěn)定版本3.9進行測試,換了之后發(fā)現(xiàn)出錯了: 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)
媽的,找不到這個函數(shù),真是奇怪了,這個包也存在,包里面也有這個類,為什么偏偏就沒有這個函數(shù)呢?!這就是java比較煩人的地方,在C/C++里面編譯期間必須能夠找到所有的函數(shù)定義,雖然有些函數(shù)的實現(xiàn)沒有找到,在裝載的時候也會首先進行動態(tài)鏈接,而不會在執(zhí)行的時候再進行,但是JAVA這種只有在使用的時候再進行l(wèi)oad的方式就很可能出現(xiàn)這種動態(tài)的錯誤,例如找不到class、找不到method等等,這個問題查了半天也沒有能夠找出錯誤,最后是在沒有辦法了采取最后的方法:查看源碼。幸虧有grepCode這樣的網(wǎng)站,真的是讓查看java源代碼方便了不知道多少個數(shù)量級,俗話說:源碼在手,了無秘密。有了源碼,什么問題都不在是問題了,在grepcode中查找mondrian.resource.MondrianResource這個類,發(fā)現(xiàn)在mondrian中只有3.6.7之前的版本才有,所以又不得不切換回3.6.x版本,這里我用的是3.6.1版本,換了jar包之后發(fā)現(xiàn)這個錯誤還是存在,好吧,看下代碼吧。 在代碼中,的確是MondrianResource的instance方法中(29)行調(diào)用了getThreadOrDefaultLocale函數(shù),而在當前的類中沒有定義這個函數(shù),那么它一定是繼承另外一個類,在父類中實現(xiàn)的,果然,它繼承的是org.eigenbase.resgen.ShadowResourceBundle類,再查看一下這個類,我擦,在codegrep上根本搜不到這個類的源碼,這時候請教google吧,搜出來這個網(wǎng)頁:http://www.java2s.com/Code/Jar/e/Downloadeigenbaseresgenjar.htm,我勒個咔擦,我之前下載的eigenbase-resgen.jar文件不就是在這里下載的嗎?!而這個jar包中分明有org.eigenbase.resgen.ShadowResourceBundle類啊(要不然我的代碼編譯都不會通過的),一切又繞回來了,這時候該怎么辦? 這時候我想起來了,既然mondrian自己能夠編譯通過,順利執(zhí)行,那么無論是在mondrian的maven依賴包中,還是在saifu中都應該有這個包的依賴,而且這個包應該就是可以使用的,而之前的eigenbase-resgen.jar不能使用可能是因為版本問題,查看了mondrian的pom.xml文件,發(fā)現(xiàn)它使用的是如下的版本:
<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接口來執(zhí)行MDX語句的,而不是通過olap4j的方式,代碼中可以看到execute接口已經(jīng)被拋棄了,查看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接口創(chuàng)建statement的方式代替,于是又在網(wǎng)上搜到一片mondrian教程(http://alenzhai.iteye.com/blog/2158953),這里使用的就是olap4j的方式訪問的,把代碼copy下來之后進行修改,保持connection的參數(shù)和mdx語句不變,運行過之后能夠得到相同的結果,只不過這種訪問方式更加規(guī)范。附上源代碼:
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 blocke1.printStackTrace();} catch (SQLException e1) {// TODO Auto-generated catch blocke1.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 blocke.printStackTrace();} PrintWriter pw = new PrintWriter(System.out);//處理返回數(shù)據(jù)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("調(diào)用mondrian api進行查詢");a.testQuery();} }
執(zhí)行的輸出結果如下: rows:[Time].[1997] columns:[Measures].[Unit Sales] values:74748.0
最后看一下使用的jar包的信息:
ok,測試完成了。雖然過程很曲折,但是也學到了一些新知識,這里只是對mondrian做了一個簡單的demo,證明了通過api調(diào)用的方式使用olap4j可以使用后端的mysql進行olap操作,接下來還需要了解mondrian對于hive和sql on hbase的支持,今天又發(fā)現(xiàn)一個新的開源OLAP引擎:Kylin,這個是eBay開源的,它是一個MOLAP引擎,和mondrian(ROLAP引擎)不同的是它會在創(chuàng)建Cube的時候?qū)⑿枰故镜木S度和度量聚合到一個表中,并且將這個表進行物化,保存在HBASE中,之后的查詢操作不是通過MDX標準來完成的,而是對物化表的SQL查詢,這樣的優(yōu)點就是查詢速度非常快,但是物化的過程需要一定的時間,并且物化表并不一定能夠完全支持所有的cude操作,如果不能支持的話還是通過ROLAP的方式到hive表中動態(tài)查找(類似于mondrian),而mondrian不需要保存任何數(shù)據(jù)(除了mondrian的元數(shù)據(jù)),所有的MDX查詢操作都是動態(tài)的生成sql,交由后端的數(shù)據(jù)庫執(zhí)行完成的,這些sql很可能是多表的join操作,對于hive而言可能性能上是一個很大的問題。 我覺得mondrian的學習主要在于兩個方面:cude的定義和MDX查看,所以接下來還需要學習一個mondrian中生成cube的xml文件的格式,如何定義一個cube、MDX的語法等,先做一個后端連接hive的demo吧。
這段代碼是自己從網(wǎng)上down下來的,要想運行它需要加一些執(zhí)行過程中需要的jar包,至少需要以下幾個包:
接著運行出現(xiàn)如下錯誤: 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文件出現(xiàn)了錯誤,根據(jù)提示,查看了一下infobright是什么發(fā)現(xiàn)它也是一種數(shù)據(jù)倉庫的解決方案,可能當前版本的mondrian不支持它,所以將出現(xiàn)他的xml標簽都注釋掉算了,反正我們這里只使用mysql(要確保mysql的連接驅(qū)動的jar包已經(jīng)加入到工程的classpath下),不關心其他的。 接著再次運行出現(xiàn)如下錯誤:
Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failureThe 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的解析發(fā)現(xiàn)時由于沒連上服務器導致的,但是明明服務器的3306端口已經(jīng)打開了啊,而且在服務器上使用mysql客戶端就可以連接上去啊。這是怎么回事,使用tcpdump抓包發(fā)現(xiàn)每一次SYN請求都會被服務器無情的回復RST報文,這說明該端口確實沒有打開,使用netstat查看該端口發(fā)現(xiàn)的確:
原來服務器只接收本地的連接,怪不得我在windows上一直連不上呢,解決的方法就是修改mysql的配置文件my.conf,把其中的bind-address從原來的127.0.0.1修改為0.0.0.0,這樣就可以從遠程連接到mysql了。 接著重新運行又出現(xiàn)這樣的錯誤: Exception in thread "main" mondrian.olap.MondrianException: Mondrian Error:Failed to parse query 'select {[Measures].[業(yè)務量]} 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].[業(yè)務量]} 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中出現(xiàn)的錯誤了,這說明前面的連接mysql,解析xml文件的操作都執(zhí)行成功了,根據(jù)錯誤提示發(fā)現(xiàn)MsgBusi不存在,查看xml文件發(fā)現(xiàn)這個MsgBusi的確不存在,這才想起來,原來down下來的代碼和我用的邏輯模型的定義不是同一個,我這里用的是mondrian提供的一個示例foodmart,所以我應該用我這里的邏輯模型定義的立方體和維度等進行查詢。所以需要修改一下MDX語句,經(jīng)過多次測試之后發(fā)現(xiàn)這樣的語句能夠在當前的配置中跑出結果: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
但是我發(fā)現(xiàn)mondrian的版本很多,當前最新的版本已經(jīng)是4.0版本的了,而能夠剛才使用的版本還是2.x版本的,然后發(fā)現(xiàn)在較新的版本中都在使用olap4j這樣的接口進行訪問,這個接口類似于jdbc訪問數(shù)據(jù)庫引擎,是一個java的接口規(guī)范,用于訪問ROLAP引擎,使用mdx語句,于是就換成當前最新的穩(wěn)定版本3.9進行測試,換了之后發(fā)現(xiàn)出錯了: 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)
媽的,找不到這個函數(shù),真是奇怪了,這個包也存在,包里面也有這個類,為什么偏偏就沒有這個函數(shù)呢?!這就是java比較煩人的地方,在C/C++里面編譯期間必須能夠找到所有的函數(shù)定義,雖然有些函數(shù)的實現(xiàn)沒有找到,在裝載的時候也會首先進行動態(tài)鏈接,而不會在執(zhí)行的時候再進行,但是JAVA這種只有在使用的時候再進行l(wèi)oad的方式就很可能出現(xiàn)這種動態(tài)的錯誤,例如找不到class、找不到method等等,這個問題查了半天也沒有能夠找出錯誤,最后是在沒有辦法了采取最后的方法:查看源碼。幸虧有grepCode這樣的網(wǎng)站,真的是讓查看java源代碼方便了不知道多少個數(shù)量級,俗話說:源碼在手,了無秘密。有了源碼,什么問題都不在是問題了,在grepcode中查找mondrian.resource.MondrianResource這個類,發(fā)現(xiàn)在mondrian中只有3.6.7之前的版本才有,所以又不得不切換回3.6.x版本,這里我用的是3.6.1版本,換了jar包之后發(fā)現(xiàn)這個錯誤還是存在,好吧,看下代碼吧。 在代碼中,的確是MondrianResource的instance方法中(29)行調(diào)用了getThreadOrDefaultLocale函數(shù),而在當前的類中沒有定義這個函數(shù),那么它一定是繼承另外一個類,在父類中實現(xiàn)的,果然,它繼承的是org.eigenbase.resgen.ShadowResourceBundle類,再查看一下這個類,我擦,在codegrep上根本搜不到這個類的源碼,這時候請教google吧,搜出來這個網(wǎng)頁:http://www.java2s.com/Code/Jar/e/Downloadeigenbaseresgenjar.htm,我勒個咔擦,我之前下載的eigenbase-resgen.jar文件不就是在這里下載的嗎?!而這個jar包中分明有org.eigenbase.resgen.ShadowResourceBundle類啊(要不然我的代碼編譯都不會通過的),一切又繞回來了,這時候該怎么辦? 這時候我想起來了,既然mondrian自己能夠編譯通過,順利執(zhí)行,那么無論是在mondrian的maven依賴包中,還是在saifu中都應該有這個包的依賴,而且這個包應該就是可以使用的,而之前的eigenbase-resgen.jar不能使用可能是因為版本問題,查看了mondrian的pom.xml文件,發(fā)現(xiàn)它使用的是如下的版本:
<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接口來執(zhí)行MDX語句的,而不是通過olap4j的方式,代碼中可以看到execute接口已經(jīng)被拋棄了,查看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接口創(chuàng)建statement的方式代替,于是又在網(wǎng)上搜到一片mondrian教程(http://alenzhai.iteye.com/blog/2158953),這里使用的就是olap4j的方式訪問的,把代碼copy下來之后進行修改,保持connection的參數(shù)和mdx語句不變,運行過之后能夠得到相同的結果,只不過這種訪問方式更加規(guī)范。附上源代碼:
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 blocke1.printStackTrace();} catch (SQLException e1) {// TODO Auto-generated catch blocke1.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 blocke.printStackTrace();} PrintWriter pw = new PrintWriter(System.out);//處理返回數(shù)據(jù)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("調(diào)用mondrian api進行查詢");a.testQuery();} }
執(zhí)行的輸出結果如下: rows:[Time].[1997] columns:[Measures].[Unit Sales] values:74748.0
最后看一下使用的jar包的信息:
ok,測試完成了。雖然過程很曲折,但是也學到了一些新知識,這里只是對mondrian做了一個簡單的demo,證明了通過api調(diào)用的方式使用olap4j可以使用后端的mysql進行olap操作,接下來還需要了解mondrian對于hive和sql on hbase的支持,今天又發(fā)現(xiàn)一個新的開源OLAP引擎:Kylin,這個是eBay開源的,它是一個MOLAP引擎,和mondrian(ROLAP引擎)不同的是它會在創(chuàng)建Cube的時候?qū)⑿枰故镜木S度和度量聚合到一個表中,并且將這個表進行物化,保存在HBASE中,之后的查詢操作不是通過MDX標準來完成的,而是對物化表的SQL查詢,這樣的優(yōu)點就是查詢速度非常快,但是物化的過程需要一定的時間,并且物化表并不一定能夠完全支持所有的cude操作,如果不能支持的話還是通過ROLAP的方式到hive表中動態(tài)查找(類似于mondrian),而mondrian不需要保存任何數(shù)據(jù)(除了mondrian的元數(shù)據(jù)),所有的MDX查詢操作都是動態(tài)的生成sql,交由后端的數(shù)據(jù)庫執(zhí)行完成的,這些sql很可能是多表的join操作,對于hive而言可能性能上是一個很大的問題。 我覺得mondrian的學習主要在于兩個方面:cude的定義和MDX查看,所以接下來還需要學習一個mondrian中生成cube的xml文件的格式,如何定義一個cube、MDX的語法等,先做一個后端連接hive的demo吧。
總結
以上是生活随笔為你收集整理的mondrian使用测试的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: infiniband 网络优化
- 下一篇: c语言中fopen的作用,c语言fope