曾经使用jdbc创建连接的时候使用的url是这种形式:jdbc:mysql://hostname:port/database?key1=value1&key2=value2,在URL须要以"jabc:mysql"开头的,当中须要声明数据库server的地址和端口、数据库名另一些其他的属性,以"?"切割,每个属性使用"&"字符切割,可是mondrian作为OLAPserver和mysql这类的关系型数据库还是有所差别的,毕竟它本身不保存不论什么数据。它更像是一个工具类的东西(类似于hive)。可以把MDX翻译成一串SQL语句再交由关系数据库运行,所以它不能独立得称为一个server(在我的理解中,对于server的使用仅仅须要知道IP和port以及一些其他的參数就足够了)。
     为了可以使用java自带的DriverManager创建connection。mondrian做了这个兼容。在使用mysql这种数据库的时候我们会首先运行例如以下的操作:
Class. forName(driver);
connection = DriverManager. getConnection(url , username ,password );
getConnection函数返回的是java.sql.Connection对象。这个Connection是通用的数据库连接。可是mondrian也是用了同样的方式创建到OLAP引擎的连接,它的driver为mondrian.olap4j.MondrianOlap4jDriver,它的url有自己独特的结构,以下就看一下在mondrian中是怎样创建连接的。除了之上的两部。还须要再进行其它的操作:
Class. forName(driver);
connection = DriverManager. getConnection(url , username ,password );
OlapConnection olapConnection = connection.unwrap(OlapConnection.class);

     在mondrian中的url格式例如以下:jdbc:mondrian:Jdbc=jdbc:mysql://10.241.20.157:3306/foodmart?

user=root&password=root;Catalog=C:\\Users\\Administrator\\Desktop\\nrtp\\FoodMart.xml;能够看出url中的每一项是通过";"切割的,类似于mysql的开头是"jdbc:mysql",mondrian连接的url的开头必须是"jdbc:mondrian:",另外还包含"Jdbc"和"Catalog"属性字段。在DriverManager的getConnection静态方法中运行例如以下操作:

    public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}

这个函数是全部的数据库连接创建的方式。事实上就是创建一个Properties对象,然后加上user和password属性。然后调用其他的getConnection方法:
    private static Connection getConnection(
String url, java.util.Properties info, Class<? > caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized (DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
} if(url == null) {
throw new SQLException("The url cannot be null", "08001");
} println("DriverManager.getConnection(\"" + url + "\")"); // Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null; for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
} }
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
} println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
     整了这么多事实上就是那个for语句运行真正的创建连接,前面仅仅是设置classloader。由于在调用getConnection之前我们使用Class.forName函数仅仅给了一个driver的类名。这里势必要使用反射创建一个对象,可是在这个for循环中遍历了registeredDrivers对象。这是DriverManager类的一个static的List对象,可是我们在调用getConnection之前并没有对DriverManager类做不论什么操作,这个对象是什么时候放入数据的呢,这时候就要看一下在运行getConnection之前的Class.forName方法了,这种方法事实上就是使用当前的classLoader将指定的类载入进来,载入类的时候会初始化这个类(部署初始化不论什么对象),包含初始化一些static代码区,果然。在mondrian.olap4j.MondrianOlap4jDriver类中有这样一段static代码区:
    static {
try {
register();
} catch (SQLException e) {
e.printStackTrace();
}
}
     这里register方法运行了这样一个语句:DriverManager.registerDriver(new MondrianOlap4jDriver());哈哈,正是在这里将这个driver注冊到DriverManager的,在registerDriver函数中,DriverManager将注冊的driver对象增加到registeredDrivers对象中的。这样也就解开了之前的疑惑,一直在疑惑这一句forName有什么作用。

我想其他的driver类(包含mysql、oracle等)都应该使用这样的方式吧。

     接续看getConnection方法,它遍历registeredDrivers列表中的每个成员,然后尝试用每个driver创建connection,直到遇到第一个可以创建成功的driver,假设都创建失败,reason中的错误信息是第一个driver创建connection的失败信息,真正创建connection的方法是Connection con = aDriver.driver.connect(url, info);。当中info是包括了user和password信息的properties。接下来就再次回到MondrianOlap4jDriver类中运行创建连接了。从这里可以看出DriverManager事实上就是一个工厂,须要创建connection的时候向它注冊一个driver,创建对象的时候使用每个详细的driver完毕。

MondrianOlap4jDriver类中创建connection的方法例如以下:

    public Connection connect(String url, Properties info) throws SQLException {
if (!MondrianOlap4jConnection.acceptsURL(url)) {
return null;
}
return factory.newConnection(this, url, info);
}
     这里的factory是一个driver的一个成员变量,它是在driver初始化的时候创建的,在mondrian中它是依据不同的jdbc版本号使用不同的factory。详细的策略是不同的版本号的jdbc中包括不同的class,然后通过Class.forName推断该class是否存在已决定当前的版本号(这个策略能够省去一些版本号配置的信息)。当前使用的factory是"mondrian.olap4j.FactoryJdbc41Impl",这个函数会创建并返回一个MondrianOlap4jConnectionJdbc41对象,这个对象实际创建的是他的父类对象MondrianOlap4jConnection,它的构造函数例如以下:
    MondrianOlap4jConnection( Factory factory, MondrianOlap4jDriver
driver,String url,Properties info) throws
SQLException
參数各自是上面提到的factory对象,driver对象。连接url和DriverManager传递过来的配置信息。我认为这个对象事实上就是为了实现olap4j接口在olap4j接口和mondrian真正的connection之间做的一层转换,创建connection主要是创建一个mondrian原生的connection,然后保存和这个connection相关的server、schema等信息。创建connection的语句例如以下:
        this.mondrianConnection = (RolapConnection) mondrian.olap.DriverManager .getConnection(list, null);

     创建mondrian连接的是通过mondrian的DriverManager实现的,当中list为解析之后的url中的key-value对和info中的配置信息,第二个參数是CatalogLocator对象,这里为null,实际上并没有什么卵用。

     创建mondrian连接的第一步就是创建指定的server,在mondrian中server是用来维护和管理connection的。每次连接创建的时候会依据URL中指定的"Instance"配置决定使用哪个server(为什么做成多个server后期再研究),假设没有指定这个配置则使用默认的server。在mondrian中全部的server是由MondrianServerRegistry这个全局对象维护的。可是在依据Instance查找server的时候假设找不到则会抛出异常,而不会创建新的server。通过跟踪调用逻辑发现仅仅有在使用XMLA服务的时候才会创建server,这里暂不讨论,假设全部的URL中都不包括Instance配置。创建server的入口为MondrianServerRegistry.createWithRepository,它须要两个參数,分别为RepositoryContentFinder对象和CatalogLocator对象,默认的server也都是用默认的这两个对象,当中CatalogLocator对象用来处理URL中指定的Catalog信息,默认不做不论什么处理。

     接着创建一个RolapConnection对象,这个对象就是mondrian内部的连接,它的构造函数为:RolapConnection(MondrianServer
server,Util.PropertyList connectInfo,RolapSchema schema,DataSource
dataSource),參数分别为指定的server、URL的配置信息,使用的Schema对象,这里为null,指定的datasource信息,这里为null。

     每个连接会被赋予一个全局唯一的ID,它是递增的,然后推断Provider配置,这个配置要么在URL中指定为mondrian要么不指定。接着分别提取"Catalog"、“JdbcUser”、"Jdbc"和"DataSource"配置的值。然后依据URL创建一个底层数据员的dataSource对象(DataSource是一个接口,实现了定义了getConnection函数。參数为username和password),创建dataSource对象是依据URL中指定的Jdbc、JdbcUser和JdbcPassword配置完毕的。另外这时还会获取URL中指定的JdbcDrivers配置载入这里指定的全部的driver(每个driver使用,切割),接着再载入全部默认的drivers,包含"mondrian.jdbcDrivers",
"sun.jdbc.odbc.JdbcOdbcDriver,org.hsqldb.jdbcDriver,oracle.jdbc.OracleDriver,com.mysql.jdbc.Driver"(load之前会推断是否已经载入),接着再从URL中查找全部数据源jdbc连接的參数。这些參数是通过jdbc.xxx=yyy指定的。当中xxx为參数名。yyy为參数值。

接着再依据URL中指定的"PoolNeeded"參数已决定是否创建一个datasource池,默认情况下是对于使用指定Jdbc创建的数据源会创建这个池子,而对于通过DataSource參数指定的数据源则不创建。这时候创建的DataSource对象分成了四种情况(当Jdbc和DataSource同一时候指定的时候使用Jdbc):

1、在mondrian连接的URL中指定了Jdbc而且没有指定PoolNeeded:这样的情况下会RolapConnectionPool中依据jdbc的url和jdbc的配置信息从池子中获取DataSource对象(假设是mysql会加上autoReconnect=true),实际返回的是dbcp中提供的PoolingDataSource类型对象。
2、在mondrian连接的URL中指定了Jdbc同一时候指定PoolNeeded=false:这样的情况下mondrian会觉得你不须要进行缓存。因此会创建一个新的DataSource对象,类型为DriverManagerDataSource,每次getConnection的时候都是使用java提供的DriverManager老老实实地创建一个connection。

3、在mondrian连接的URL中指定了DataSource并没有指定PoolNeeded:这样的情况下相同也会使用RolapConnectionPool创建一个缓存的DataSource对象。
4、在mondrian连接的URL中指定了DataSource并指定PoolNeeded=true:这样的情况下会依据是否指定username和password推断是否创建UserPasswordDataSource对象(这玩意事实上是一个代理),假设都没指定则依据DataSource參数指定的类使用该类的默认构造函数创建一个dataSource对象。

     上面无论是使用哪种方式创建DataSource都将会被保存在mondrian的connection内部成员。每次创建向数据源的连接时都是用getConnection获取。

     创建完DataSource会将当前的mondrian的connection增加到server中,由一个map保存,当中key为connection分配的id。value为connection对象。
     接着是依据schema參数是否为null以运行不同的操作,当前schema等于null。则会创建一个statement,并增加到Locus(这个东东在mondrian中经经常使用到,后面再详述)中。接着创建一个Schema对象。这个对象是这里的重点。解析并保存了xml文件里定义的全部cube的信息,等下再看。先看下创建完schema之后会解析URL中的"Role"參数,并处理当前connection的role。默认情况下使用schema的默认role(ALL)。
     至此mondrian的connection就创建完毕了。基本的操作就是创建DataSource对象和Schema对象,后者的创建全是通过RolapSchemaPool提高的get接口完毕,在创建的时候会依据"UseSchemaPool"參数以决定是否使用schema池,默觉得true,假设指定了false则创建一个新的Schema对象。否则须要依据"JdbcConnectionUuid"參数、jdbc连接的url信息、dataSource參数以及读取的catalog所有内容的md5值作为key从schemaPool中查找。除此之外。假设在URL中指定了"UseContentChecksum"參数,并依据该參数推断是否仅仅依据catalog的所有内容的md5值作为key查找(也就是不必使用jdbc的url作为key的一部分了),当然不管使用哪种方式从池中获取schema。schema增加池子的时候都有一个过期时间,由"UseSchemaPool"參数指定,假设不指定则设为-1s(详细含义后面再讲)。

     创建schema的过程是比較复杂的。主要是要载入的schema文件的所有内容并做一些初始化操作。本文不作讲述,除此之外在创建schema的时候还会创建一个mondrian连接(事实上并没有什么用),这里递归的运行RolapConnection的构造函数(见上面),可是这时候schema已经不为null了,创建的时候会创建完DataSource之后创建一个connection,然后就返回了。而且schema中创建的这个InternalConnection并不会运行不论什么其它的操作,因此能够觉得这一步创建connection仅仅是为了測试数据源的连通性。

此外Schema构造函数还会创建AggTableManager对象和DataSourceChangeListener对象。

     创建完schema之后再从schema文件里(catalog内容)载入全部的cube以及相关信息。这一部相当复杂。在创建完mondrian连接之后还须要对olap4j连接进行一些额外的设置,以后用到的时候再讲。
     最后对全部创建connection的URL中出现的參数进行一个总结。參数之间通过';'进行切割,假设在某一个參数的值中须要出现';'(比如jdbc的password。hive的jdbc连接的url中)则须要将值通过引號括起来作为一个完整的值,全部的參数都保存在RolapConnectionProperties类中。包含:
1、Provider:在mondrian中它的值要么不指定要么指定为mondrian
2、Jdbc:指定数据源的jdbc的url信息。
3、JdbcDrivers:指定数据源的driver类,能够指定多个,通过','切割。
4、JdbcUser:连接数据源的username
5、JdbcPassword:连接数据源的password
6、Catalog:schema的地址。能够是HTTP的URL或本地文件等。

7、CatalogContent:schema的所有内容,一个xml文件。
8、CatalogName:schema的名字,当前未使用。
9、DataSource:指定的DataSource类型,必须实现javax.sql.DataSource接口,在URL中必须指定Jdbc或者DataSource
10、PoolNeeded:是否缓存DataSource对象。

11、Role:当前连接使用的权限信息。

12、UseContentChecksum:是否仅仅是用schema文件的内容作为key查找schema。

13、UseSchemaPool:是否使用schema池,假设指定则依据前一项推断依据什么来作为查找schema的key。假设为false则每次都创建一个新的schema。

14、DynamicSchemaProcessor:能够指定一个类动态的改动schema内容。
15、Locale:设置的本地化信息,默认使用系统的locale
16、DataSourceChangeListener:能够设置一个实现mondrian.spi.DataSourceChangeListener接口的类,用于推断数据源是否发生改变,假设发生改变则须要更新缓存。
17、Ignore:配置是否忽略载入schema时的非致命错误和警告。
18、Instance:指定创建connection所在的server,不指定则使用默认的server
19、JdbcConnectionUuid:标识jdbc连接的id,假设指定则能够通过该配置推断两个jdbc的连接是否同样。
20、PinSchemaTimeout:缓存schema的过期时间,假设指定为正数则表示缓存schema强引用的过期时间,假设过期则会被垃圾回收,假设指定为0则表示永只是期,假设指定为负数则表示将schema保存为一个软引用,它的绝对值为该软引用的过期时间,这里的时间单元能够设置为d,
h, m, s, ms。
21、jdbc.:jdbc參数信息,后面指定jdbc的參数名,值为參数值。

Mondiran创建连接的更多相关文章

  1. ASP.NET MVC 5 - 创建连接字符串(Connection String)并使用SQL Server LocalDB

    您创建的MovieDBContext类负责处理连接到数据库,并将Movie对象映射到数据库记录的任务中.你可能会问一个问题,如何指定它将连接到数据库? 实际上,确实没有指定要使用的数据库,Entity ...

  2. 《Entity Framework 6 Recipes》中文翻译系列 (38) ------ 第七章 使用对象服务之动态创建连接字符串和从数据库读取模型

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第七章 使用对象服务 本章篇幅适中,对真实应用中的常见问题提供了切实可行的解决方案. ...

  3. HTTPS-HSTS协议(强制客户端使用HTTPS与服务器创建连接)

    HSTS(HTTP Strict Transport Security)国际互联网工程组织IETE正在推行一种新的Web安全协议 HSTS的作用是强制客户端(如浏览器)使用HTTPS与服务器创建连接. ...

  4. JDBC 创建连接对象的三种方式 、 properties文件的建立、编辑和信息获取

    创建连接对象的三种方式 //第一种方式 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ ...

  5. Oracle--SQL Developer创建连接及使用

    安装好Oracle之后,有几种方式可以来管理Oracle中的数据库,首先就是登陆网页版的界面:https://localhost:1158/em,这种方式管理的东西太多,使用起来有点不方便,第二种方式 ...

  6. ASP.NET MVC 5 学习教程:创建连接字符串

    原文 ASP.NET MVC 5 学习教程:创建连接字符串 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 ...

  7. ADO.NET 1创建连接、执行命令

    一无参构造函数的形式: 创建连接.创建命令.执行命令: string connstr = @"server=.;database=TestDataBase;uid=sa;pwd=130988 ...

  8. 【译】ASP.NET MVC 5 教程 - 5:使用 SQL 服务器 LocalDB 创建连接字符串

    原文:[译]ASP.NET MVC 5 教程 - 5:使用 SQL 服务器 LocalDB 创建连接字符串 在上一节中,我们创建了MovieDBContext 类来连接数据库.处理Movie 对象和数 ...

  9. LinQ 创建连接、简单增删改查

    LINQ--语言集成查询(Language Integrated Query)是一组用于c#和Visual Basic语言的扩展.它允许编写C#或者Visual Basic代码以查询数据库相同的方式操 ...

随机推荐

  1. apache php 多站点配置 重新整理

    需要下载的东东:apache_2.0.59-win32-x86-no_ssl.msi  (服务器软件,用来编译PHP的) php-5.1.5-Win32.zip  (PHP的主文件) 第一步:1.安装 ...

  2. ThreeJs 3D 全景项目开发总结

    本文来自网易云社区 作者:唐钊 项目背景 那是在一个毫无征兆的下午,我还沉浸在 vue 的世界中,突然编辑跑过来说N的新官网想做一些3D全景的东西,一开始其实我的内心是拒绝的,一是没怎么实质性做过 W ...

  3. mysql 索引和查询优化

    对于任何DBMS,索引都是进行优化的最主要的因素.对于少量的数据,没有合适的索引影响不是很大,但是,当随着数据量的增加,性能会急剧下降.如果对多列进行索引(组合索引),列的顺序非常重要,MySQL仅能 ...

  4. Java多线程框架源码阅读之---ReentrantLock

    ReentrantLock基于Sync内部类来完成锁.Sync有两个不同的子类NonfairSync和FairSync.Sync继承于AbstractQueuedSynchronizer. Reent ...

  5. Convolutional Networks for Image Semantic Segmentation

    本系列文章由 @yhl_leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/52857657 把前段时间自己整理的一个 ...

  6. html 文本标签

    文本格式化标签 标签 描述 <b> 定义粗体文本. <big> 定义大号字. <em> 定义着重文字. <i> 定义斜体字. <small> ...

  7. Rust 内存管理

    Rust 内存管理 Rust 与其他编程语言相比,最大的亮点就是引入了一套在编译期间,通过静态分析的方式,确定所有对象的作用域与生命周期,从而可以精确的在某个对象不再被使用时,将其销毁,并且不引入任何 ...

  8. 【CCF】无线网络 搜索+思维

    #include<iostream> #include<cstdio> #include<cstring> #include<string> #incl ...

  9. java中String初始化的两种方式

    转自:http://www.diybl.com/course/3_program/java/javajs/2007104/75886.html       字符串可能是任何程序语言中都会出现的对象,j ...

  10. winform控件大小改变是防止背景重绘导致的闪烁(转载)

    在工作中需要做一个伸缩控件,这个自定义控件继承于Panel.这个伸缩控件分为两个部分,头部是一个自定义组件,伸缩控件的背景为灰色,头部背景要求白色.伸缩控件在点击按钮时会重绘,同时他内部的控件也会重绘 ...