mybatis源码修改-实现数据源灵活切换

一、实现灵活切换数据源

mybatis目前只支持单数据源配置,如果想要切换数据源的话不能够灵活的操作,因此想要修改mybatis源码已支持数据源灵活切换,最终使用方式如下,指定对应的数据源,mybatis自动支持去指定的数据源下面的数据库下查询,同时也支持不同的数据库连接池。

1、使用实例

private static void dbkeyTest() {
     
    long start = System.currentTimeMillis();
    final SqlSession session = sqlSessionFactory.openSession();
    // 指定特定的dbkey数据源查询
    Student student = session.selectOne("getStudent", 1, "db1");
    if (null != student) {
     
        System.out.println(student.toString());
    }

    final SqlSession session1 = sqlSessionFactory.openSession();
    Student student1 = session1.selectOne("getStudent", 1, "db2");
    if (null != student1) {
     
        System.out.println(student1.toString());
    }
    long end = System.currentTimeMillis();
    System.out.println("程序执行时间为" + (end - start));
}

2、配置实例

mybatis配置文件通过如下的配置来支持多数据源


    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            
            <dataSource type="maintest.C3P0DataSourceFactory">
                <property name="driverClass" value="com.mysql.jdbc.Driver"/>
                <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"/>
                <property name="user" value="root"/>
                <property name="password" value="root"/>
            dataSource>
            
            <dataSources>
                
                <dataSource type="POOLED" id="db1">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                dataSource>
                
                <dataSource type="POOLED" id="db2">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/test1?characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                dataSource>
            dataSources>
        environment>
    environments>

3、结果展示

结果:第一个数据源test数据库中没有这条数据没打印这条记录信息,第二个数据源test1数据库中有这条记录打印出这条数据,实现了动态灵活切换数据源的目标

------------dbkey is = db1 
DEBUG [main] - Opening JDBC Connection, dbKey=db1
DEBUG [main] - Created connection 2006034581.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7791a895]
DEBUG [main] - ==>  Preparing: select * from student where id= ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 0
------------dbkey is = db2
DEBUG [main] - Opening JDBC Connection, dbKey=db2
DEBUG [main] - Created connection 1427810650.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@551aa95a]
DEBUG [main] - ==>  Preparing: select * from student where id= ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
[id=1, studentID=2, name=aaa]

二、实现原理

1、mybatis-3-config.dtd 文件修改增加dataSources元素信息,并对dataSources上层的environment元素进行修改

<!ELEMENT environment (transactionManager,dataSource,dataSources*)>
<!ATTLIST environment
id CDATA #REQUIRED
>

<!ELEMENT dataSources (dataSource+)>
<!ATTLIST dataSources
id CDATA #IMPLIED
>

2、解析环境配置的时候,增加解析dataSources的逻辑

Environment类中增加dataSourceMap属性,解析配置文件的时候将数据源id和对应的数据源加载到dataSourceMap中,Environment构建的时候用的是builder模式

private final Map<String, DataSource> dataSourceMap;

XMLConfigBuilder 解析的时候修改

private void environmentsElement(XNode context) throws Exception {
     
    if (context != null) {
     
        if (environment == null) {
     
            environment = context.getStringAttribute("default");
        }
        for (XNode child : context.getChildren()) {
     
            String id = child.getStringAttribute("id");
            if (isSpecifiedEnvironment(id)) {
     
                TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
                // 创建dataSourceFactory工厂
                DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                DataSource dataSource = dsFactory.getDataSource();
                // 增加解析datasource的逻辑
                Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory)
                   .dataSource(dataSource).dataSourceMap(dataSourceElementMap(child.evalNode("dataSources")));

                configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }
}

/**
     * 解析datasources节点
     * 
     * @param context
     * @return
     * @throws Exception
     */
    private Map<String, DataSource> dataSourceElementMap(XNode context) throws Exception {
     
        if (context != null) {
     
            Map<String, DataSource> dataSourceMap = new HashMap<String, DataSource>();
            for (XNode child : context.getChildren()) {
     
                DataSourceFactory dsFactory = dataSourceElement(child);
                DataSource dataSource = dsFactory.getDataSource();
                String id = child.getStringAttribute("id");
                dataSourceMap.put(id, dataSource);
            }
            return dataSourceMap;
        }
        return null;
    }

解析完后datasources就加载到Environment中了

3、重构获取connection的方法

创建和获取连接时通过JdbcTransaction的getConnection方法获取的,因此需要重构打开和获取连接的方法。

首先增加dataSourceMap熟悉,在实例化JdbcTransaction对象的时候将dataSourceMap加载进来

protected Map<String, DataSource> dataSourceMap;

public Connection getConnection(String dbKey) throws SQLException {
     
        if (connection == null) {
     
            openConnection(dbKey);
        }
        return connection;
    }

protected void openConnection(String dbKey) throws SQLException {
     
        if (log.isDebugEnabled()) {
     
            log.debug("Opening JDBC Connection, dbKey=" + dbKey);
        }
        DataSource dataSource = dataSourceMap.get(dbKey);
        connection = dataSource.getConnection();
        if (level != null) {
     
            connection.setTransactionIsolation(level.getLevel());
        }
        setDesiredAutoCommit(autoCommmit);
    }

4、重现数据库查询,更新操作

查询,更新的方法传入一个dbkey用来指定从哪个数据源进行查询;重构非常简单,将指定的dbkey传递进来即可,代码省略。

你可能感兴趣的:(mybatis,mybatis)