Mybatis source code modification-realize flexible switching of data sources

1. Realize flexible switching of data sources

Mybatis currently only supports single data source configuration. If you want to switch the data source, you cannot operate flexibly. Therefore, if you want to modify the mybatis source code, it supports flexible switching of data sources. The final use method is as follows. Specify the corresponding data source. Mybatis automatically supports it. Query under the database under the specified data source, and also supports different database connection pools.

1. Use case

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. Configuration example

The mybatis configuration file supports multiple data sources through the following configuration

<!-- 数据库环境 -->
    <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. Results display

Result: The first data source test database does not have this data and this record information is not printed. The second data source test1 database has this record and prints this data, realizing the goal of dynamic and flexible switching of data sources

------------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]

Second, the realization principle

1. Modify the mybatis-3-config.dtd file to add dataSources element information, and modify the environment element at the upper level of dataSources

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

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

2. When parsing environment configuration, add the logic of parsing dataSources

The dataSourceMap attribute is added to the Environment class. When parsing the configuration file, the data source id and the corresponding data source are loaded into the dataSourceMap. When the Environment is built, the builder mode is used

private final Map<String, DataSource> dataSourceMap;

Modify when XMLConfigBuilder parses

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;
    }

After parsing, the datasources are loaded into the Environment

3. Refactor the method of obtaining connection

It is obtained through the getConnection method of JdbcTransaction when the connection is created and obtained, so the method of opening and obtaining the connection needs to be reconstructed.

First increase the familiarity of dataSourceMap, and load the dataSourceMap when instantiating the JdbcTransaction object

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. Reproduce database query and update operations

Query and update methods pass in a dbkey to specify which data source to query from; reconstruction is very simple, just pass in the specified dbkey, and the code is omitted.

Guess you like

Origin blog.csdn.net/khuangliang/article/details/108248905