Mondiran创建连接

以前使用jdbc创建连接的时候使用的url是这样的形式:jdbc:mysql://hostname:port/database?key1=value1&key2=value2,在URL需要以"jabc:mysql"开头的,其中需要声明数据库服务器的地址和端口、数据库名还有一些其它的属性,以"?"分割,每一个属性使用"&"字符分割,但是mondrian作为OLAP服务器和mysql这类的关系型数据库还是有所区别的,毕竟它本身不保存任何数据,它更像是一个工具类的东西(类似于hive),能够把MDX翻译成一串SQL语句再交由关系数据库执行,所以它不能独立得称为一个服务器(在我的理解中,对于服务器的使用只需要知道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函数,参数为用户名和密码),创建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:这种情况下会根据是否指定用户名和密码判断是否创建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的密码,hive的jdbc连接的url中)则需要将值通过引号括起来作为一个完整的值,所有的参数都保存在RolapConnectionProperties类中,包括:

1、Provider:在mondrian中它的值要么不指定要么指定为mondrian

2、Jdbc:指定数据源的jdbc的url信息。

3、JdbcDrivers:指定数据源的driver类,可以指定多个,通过‘,‘分割。

4、JdbcUser:连接数据源的用户名

5、JdbcPassword:连接数据源的密码

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的参数名,值为参数值。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-02 05:01:08

Mondiran创建连接的相关文章

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

安装好Oracle之后,有几种方式可以来管理Oracle中的数据库,首先就是登陆网页版的界面:https://localhost:1158/em,这种方式管理的东西太多,使用起来有点不方便,第二种方式在cmd上使用sqlplus输入用户名密码登陆,这种方式需要记忆sql语句用起来才方便,有点不好,比如找一个查询某个表中有哪些约束条件,这条语句我搜索半天都没找到,而在mysql只要使用show create table <tablename>,习惯了mysql的show命令,在用这个sqlplu

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

HSTS(HTTP Strict Transport Security)国际互联网工程组织IETE正在推行一种新的Web安全协议 HSTS的作用是强制客户端(如浏览器)使用HTTPS与服务器创建连接.其实HSTS的最大作用是防止302 HTTP劫持(中间人).HSTS的缺点是浏览器支持率不高,另外配置HSTS后HTTPS很难实时降级成HTTP. 采用HSTS协议的网站将保证浏览器始终连接到该网站的HTTPS加密版本,不需要用户手动在URL地址栏中输入加密地址.该协议将帮助网站采用全局加密,用户看

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

LINQ--语言集成查询(Language Integrated Query)是一组用于c#和Visual Basic语言的扩展.它允许编写C#或者Visual Basic代码以查询数据库相同的方式操作内存数据. 创建连接: 添加新项→LinQ to sql类→重命名为需要用的数据库名→服务器资源管理器→连接到数据库→填写服务器名.用户名密码.连接到数据库的名称→从数据连接中要到要引用的表拖进 数据库名.dbml 中 增删改查表达式 查询: 新建类:数据访问类,添加方法: Data0617Dat

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

原文:ASP.NET MVC 5 - 创建连接字符串(Connection String)并使用SQL Server LocalDB 您创建的MovieDBContext类负责处理连接到数据库,并将Movie对象映射到数据库记录的任务中.你可能会问一个问题,如何指定它将连接到数据库? 实际上,确实没有指定要使用的数据库,Entity Framework将预设值使用的LocalDB. 在本节中,我们将显式地在Web.config文件中,添加应用程序的连接字符串(connection string)

使用代理创建连接池 proxyPool

配置文件properties 1 url=jdbc:mysql://127.0.0.1:3306/mine?characterEncoding=UTF-8 2 user=root 3 password=1234 4 driverClass=com.mysql.jdbc.Driver 主要代码 1 package JDBCUtils; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.lang.r

spring创建连接池的几种方式

spring使用连接池有很多种方式,jdbc(不使用连接池),c3p0,dbcp,jndi,下面将分别贴代码介绍这几种:  1.jdbc方式 使用的是DriverManagerDataSource,DriverManagerDataSource建立连接是只要有连接就新建一个connection,  根本没有连接池的作用 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManag

nginx源码分析--监听套接字的创建 套接字的监听 HTTP请求创建连接

作为一个web服务器,那么肯定是有监听套接字的,这个监听套接字是用于接收HTTP请求的,这个监听套接字的创建是根据配置文件的内容来创建的,在nginx.conf文件中有多少个地址就需要创建多少个监听套接字.这里不说各个结构体的构造 只说大体情况! 1).首先在main函数中调用了ngx_init_cycle()函数,在这个函数的最后调用了ngx_open_listening_sockets函数,这个函数负责将创建的监听套接字进行套接字选项的设置(比如非阻塞.接受发送的缓冲区.绑定.监听处理) 2

JDBC 创建连接对象的三种方式

创建连接对象的三种方式 //第一种方式 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?user=root&password=root") ; //第二种方式 //读取properties文件 Properties pro = new Properties() ; InputStream in = JdbcDemo3.class.getClassLoader().ge

[Kali_Metasploit]db_connect创建连接时无法连接的解决方案

问题1复现路径: 1. 终端输入msfconsole,进入msf命令行模式,需要等待若干分钟: 2. 输入db_status,查看连接状态,两种结果:一,默认连接msf3:二,没有连接,显示postgresql selected, no connection 3. 创建连接数据库msfbook,输入 db_connect postgres:[email protected]/msfbook 现象: 没有显示任何信息,输入db_status查看,仍没有连接 分析: 没有相关账号和密码,所以无法连接