`

让mybatis支持管理和操作多个不同的业务数据库实例

阅读更多

在微服务大行其道的今天,一个工程中同时操作多个不同的业务数据库这种情况已经很少见了,但并不意味不存在这样的需求。

 

MyBatis世界上流行最广泛的SQL映射框架,由ClintonBegin在2002年创建,其后,捐献给了Apache基金会,成立了iBatis项目。 2010年5月,将代码库迁致GoogleCode,并更名为MyBatis。但是Mybatis对多个不同业务数据库的支持并没有因为从ibatis升级到mybatis过程中很好的集成原来ibatis项目中对多个数据库的支持。相反,mybatis对多数据库的管理能力相对较弱,接下来,我将详细说明如何让你的mybatis项目支持我们的多个不同业务数据库。

 

背景说明:当下有两个数据库dinner、customer通过同一个工程分别存储不同的业务数据。

 

配置SqlSessionFactoryBean信息(META-INF/spring/myteay-common-dal.xml):

 

    <bean id="dinnerSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="myteayDinnerDataSource"></property>
        <property name="configLocation" value="classpath:/sqlmap/myteaydinner-sqlmap.xml" />
    </bean>

    <bean id="customerSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="myteayDataSource"></property>
        <property name="configLocation" value="classpath:/sqlmap/myteay-sqlmap.xml" />
    </bean>

 

 

通过org.mybatis.spring.SqlSessionFactoryBean对数据库和sqlmap的管理,最终SqlSessionFactoryBean会给我们一个org.apache.ibatis.session.defaults.DefaultSqlSessionFactory实例。

 

数据库操作过程中DAO通过集成org.mybatis.spring.support.SqlSessionDaoSupport类,利用对org.apache.ibatis.session.SqlSessionFactory实例,获取相应的DataSource实例进行对指定的数据库的数据管理工作。

 

问题也因此来了,在org.mybatis.spring.support.SqlSessionDaoSupport类中,org.apache.ibatis.session.SqlSessionFactory实例是作为类成员属性存在的,而打开org.mybatis.spring.support.SqlSessionDaoSupport类的源码分析发现,SqlSessionDaoSupport对通过SqlSessionFactory类构建的org.apache.ibatis.session.SqlSession实例管理提供了仅此一份的类实例,并未提供通过特定配置指定org.apache.ibatis.session.SqlSessionFactory实例的处理逻辑。

 

如果强行指定org.apache.ibatis.session.SqlSessionFactory实例未dinner库配置,这时由于customer数据库的配置也是通过org.mybatis.spring.support.SqlSessionDaoSupport类获取org.apache.ibatis.session.SqlSessionFactory实例的,这样就会导致customer数据库操作异常。

 

要想让SqlSessionDaoSupport类同时支持两种数据库实例,就需要为其补充进来一个切换策略。

 

SqlSessionFactory切换实现逻辑如下:

 

/**
 * Danlley Wei (mailto://danlley@126.com)
 * Copyright (c) 2005-2017 All Rights Reserved.
 */
package com.myteay.common.dal.utils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.springframework.util.CollectionUtils;

import com.myteay.common.util.comm.StringUtils;

/**
 * SqlSessionFactory会话实例管理组件
 * 
 * @author danlley(danlley@126.com)
 * @version $Id: SqlSession.java, v 0.1 2017年5月7日 上午12:40:16 danlley(danlley@126.com) Exp $
 */
public class SqlSessionSwitcher {

    /** 用于指定DefaultSqlSessionFactory,线程安全 */
    private Map<String, DefaultSqlSessionFactory> switcherBeans = new ConcurrentHashMap<String, DefaultSqlSessionFactory>();

    /**
     * 获取指定的SqlSessionFactory
     * 
     * @param key
     * @return
     */
    public SqlSessionFactory getSqlSessionFactory(String key) {
        if (StringUtils.isBlank(key) || CollectionUtils.isEmpty(switcherBeans) || !switcherBeans.containsKey(key)) {
            throw new IllegalArgumentException("SqlSessionSwitcher初始化失败,无法得到可用的SqlSessionFactory实例 key=" + key);
        }

        return switcherBeans.get(key);
    }

    /**
     * Setter method for property <tt>switcherBeans</tt>.
     * 
     * @param switcherBeans value to be assigned to property switcherBeans
     */
    public void setSwitcherBeans(Map<String, DefaultSqlSessionFactory> switcherBeans) {
        this.switcherBeans = switcherBeans;
    }
}

 

 

对应配置信息如下(META-INF/spring/myteay-common-dal.xml):

 

    <bean id="switcher" class="com.myteay.common.dal.utils.SqlSessionSwitcher">
    	<property name="switcherBeans">
    		<map>
    			<entry key="dinnerSqlSessionFactory" value-ref="dinnerSqlSessionFactory"></entry>
    			<entry key="customerSqlSessionFactory" value-ref="customerSqlSessionFactory"></entry>
    		</map>
    	</property>
    </bean>

 

 

通过上面的处理,我们首先得到了一个针对不同数据库的完整SqlSessionFactory实例集合。

 

为了能够让每个DAO实现类能够识别获取指定的SqlSessionFactory实例,我们定义一个常量接口,方便DAO接口继承:

 

/**
 * Danlley Wei (mailto://danlley@126.com)
 * Copyright (c) 2005-2017 All Rights Reserved.
 */
package com.myteay.common.dal.utils;

/**
 * 各个DAO类继承此类,以便能够得到指定的数据库
 * 
 * @author danlley(danlley@126.com)
 * @version $Id: MtDBKey.java, v 0.1 2017年5月7日 上午2:08:07 danlley(danlley@126.com) Exp $
 */
public interface MtDBKey {

    /** dinner库标识 */
    public final static String dinner   = "dinnerSqlSessionFactory";

    /** customer库标识 */
    public final static String customer = "customerSqlSessionFactory";
}

 

 

继承示例代码如下:

 

/**
 * Myteay.com Inc.
 * Copyright (c) 2015-2015 All Rights Reserved.
 */
package com.myteay.common.dal.daointerface;

import com.myteay.common.dal.dataobject.UsersInfoDO;
import com.myteay.common.dal.utils.MtDBKey;

/**
 * 用户基本信息操作DAO
 * 
 * @author Administrator
 * @version $Id: UsersInfoDAO.java, v 0.1 2015年11月15日 下午4:37:30 Administrator Exp $
 */
public interface UsersInfoDAO extends MtDBKey {

    /**
     * 通过userid查找用户信息
     * 
     * @param userID    会员ID
     * @return
     */
    UsersInfoDO getById(String userId);
}

 

 

为了能够让DAO实现类获取操作特定数据库的能力,我们对org.mybatis.spring.support.SqlSessionDaoSupport类的功能进行重新实现:

 

/**
 * Danlley Wei (mailto://danlley@126.com)
 * Copyright (c) 2005-2017 All Rights Reserved.
 */
package com.myteay.common.dal.utils;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.support.DaoSupport;

/**
 * 本类参考了org.mybatis.spring.support.SqlSessionDaoSupport类的实现,但解决了多数据源管理的问题
 * 
 * @see org.mybatis.spring.support.SqlSessionDaoSupport
 * @author danlley(danlley@126.com)
 * @version $Id: MtSqlSessionDaoSupport.java, v 0.1 2017年5月7日 上午12:35:28 danlley(danlley@126.com) Exp $
 */
public class MtSqlSessionDaoSupport extends DaoSupport {

    /** 数据库切换管理器 */
    @Autowired
    private SqlSessionSwitcher switcher;

    /** SQL会话 */
    private SqlSession         sqlSession;

    /** 外部SQL会话标识 */
    private boolean            externalSqlSession = false;

    /**
     * 初始化SQL会话
     * 
     * @param sqlSessionFactory
     */
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        if (!this.externalSqlSession) {
            //this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
        }
    }

    /**
     * SQL会话
     * 
     * @param sqlSessionTemplate
     */
    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        //this.sqlSession = sqlSessionTemplate;
        // this.externalSqlSession = true;
    }

    /**
     * Users should use this method to get a SqlSession to call its statement methods
     * This is SqlSession is managed by spring. Users should not commit/rollback/close it
     * because it will be automatically done.
     *
     * @return Spring managed thread safe SqlSession
     * @throws Exception 
     */
    public SqlSession getSqlSession(String key) {

        if (sqlSession != null) {
            return this.sqlSession;
        }

        //防止线程多次对实例进行不必要的初始化
        if (switcher == null || switcher.getSqlSessionFactory(key) == null) {
            throw new IllegalArgumentException(
                "Property 'sqlSessionFactory' or 'sqlSessionTemplate' or 'switcher' are required");
        }

        sqlSession = new SqlSessionTemplate(switcher.getSqlSessionFactory(key));

        return this.sqlSession;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void checkDaoConfig() {
        //notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
    }

    /**
     * Setter method for property <tt>switcher</tt>.
     * 
     * @param switcher value to be assigned to property switcher
     */
    public void setSwitcher(SqlSessionSwitcher switcher) {
        this.switcher = switcher;
    }

}

 

 

 

接下来,DAO接口的实现类不再需要继承org.mybatis.spring.support.SqlSessionDaoSupport类,转而继承我们重新改写的类com.myteay.common.dal.utils.MtSqlSessionDaoSupport。示例代码如下:

/**
 * Myteay.com Inc.
 * Copyright (c) 2015-2015 All Rights Reserved.
 */
package com.myteay.common.dal.ibatis;

import com.myteay.common.dal.daointerface.UsersInfoDAO;
import com.myteay.common.dal.dataobject.UsersInfoDO;
import com.myteay.common.dal.utils.MtSqlSessionDaoSupport;

/**
 * 用户基本信息操作DAO
 * 
 * @author Administrator
 * @version $Id: IbatisUsersInfoDAO.java, v 0.1 2015年11月15日 下午4:37:30 Administrator Exp $
 */
public class IbatisUsersInfoDAO extends MtSqlSessionDaoSupport implements UsersInfoDAO {

    /** 
     * @see com.myteay.common.dal.daointerface.UsersInfoDAO#getById(java.lang.String)
     */
    @Override
    public UsersInfoDO getById(String userId) {
        return (UsersInfoDO) this.getSqlSession(customer).selectOne("MS-MT-USER-INFO-GET-BY-ID", userId);
    }

}

 

最后,不失完整性,贴出整体的配置信息(META-INF/spring/myteay-common-dal.xml):

<?xml version="1.0" encoding="GBK"?>

<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"  
    xsi:schemaLocation="  
        http://www.springframework.org/schema/beans  
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
        http://www.springframework.org/schema/context  
        http://www.springframework.org/schema/context/spring-context-4.0.xsd  
        http://www.springframework.org/schema/tx  
        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"  
default-autowire="byName">
	<!-- ============================================================================ -->
	<!-- ============================  DataSource配置   =============================== -->
	<!-- ============================================================================ -->
	<!-- 会员数据库 -->
	<bean id="myteayDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName">
			<value>org.gjt.mm.mysql.Driver</value>
		</property>
		<property name="url">
			<value>jdbc:mysql://192.168.56.101:3306/customers?useUnicode=true&amp;characterEncoding=gbk</value>
		</property>
		<property name="username">
			<value>customers</value>
		</property>
		<property name="password">
			<value>*******</value>
		</property>
	</bean>
	<!-- 业务配置库 -->
    <bean id="myteayDinnerDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName">
            <value>org.gjt.mm.mysql.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:mysql://192.168.56.101:3306/dinner?useUnicode=true&amp;characterEncoding=gbk</value>
        </property>
        <property name="username">
            <value>dinner</value>
        </property>
        <property name="password">
            <value>*******</value>
        </property>
    </bean>
	<!-- ============================================================================ -->
	<!-- ===================       SqlSessionFactoryBean配置           ===================== -->
	<!-- ============================================================================ -->
    <bean id="dinnerSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="myteayDinnerDataSource"></property>
        <property name="configLocation" value="classpath:/sqlmap/myteaydinner-sqlmap.xml" />
    </bean>

    <bean id="customerSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="myteayDataSource"></property>
        <property name="configLocation" value="classpath:/sqlmap/myteay-sqlmap.xml" />
    </bean>


	<!-- ============================================================================ -->
	<!-- ================== TransactionTemplate和TransactionManager配置 ============== -->
	<!-- ============================================================================ -->
    <!-- transactionManager -->
    <bean id="dinnerTxManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="myteayDinnerDataSource"></property>
    </bean>
    <tx:annotation-driven transaction-manager="dinnerTxManager" />

    <!-- transactionManager -->
    <bean id="customerTxManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="myteayDataSource"></property>
    </bean>
    <tx:annotation-driven transaction-manager="customerTxManager" />
    
	<bean id="myteayDinnerTransactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
		<property name="transactionManager">
			<ref bean="dinnerTxManager"/>
		</property>
	</bean>
	<bean id="myteayCustomerTransactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
		<property name="transactionManager">
			<ref bean="customerTxManager"/>
		</property>
	</bean>
    <bean id="switcher" class="com.myteay.common.dal.utils.SqlSessionSwitcher">
    	<property name="switcherBeans">
    		<map>
    			<entry key="dinnerSqlSessionFactory" value-ref="dinnerSqlSessionFactory"></entry>
    			<entry key="customerSqlSessionFactory" value-ref="customerSqlSessionFactory"></entry>
    		</map>
    	</property>
    </bean>
</beans>

 

至此,针对mybatis的多个业务库管理支持的改造工作结束!

 

0
1
分享到:
评论

相关推荐

    mybatis-multi-database:springboot mybatis多数据库(实例)配置扩展插件

    多数据库实例扩展插件支持通过一个基础通用的Mapper,通过配置创建不同项目的Mapper,操作在同一个数据库系统中不同项目的数据库实例,避免创建多个项目相同的Mapper文件。目前版本只支持springboot框架,通过使用...

    mybatis实例教程代码

    2019年最新mybatis的学习教程加代码测试实例。由浅入深分别演示:1、最初基本实现原理代码...每个实例均附说明与mysql数据库表sql。项目工程倒入myeclipse即可运行。(注意更换configuration.xml中的绝对路径。(多处))

    Mina+Mongodb+Mybatis+数据库连接池实例

    这是一个简单的基于mina框架的实现增删改查的工程,融合了mongodb mybatis proxool等多个技术。

    springmybatis

    MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(Plan ...

    mybatis中文版教程

    MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除  了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML  或注解用于配置和原始映射,将接口和 Java ...

    mybatis mysql分页实例(不能用找我)

    亲测可用mysql+mybatis分页 自己建一个数据库 名字test 表test 三列:包括id text1 text2 多添加几条记录 设置不同的page值就可以见到分页效果

    spring mybatis多数据源实例详解

    本文主要介绍sping mybatis多数据源处理,在开发过程中经常会遇到多个数据库,这里给大家举例说明如何处理,希望能帮助有需要的小伙伴

    Mybatis现学现用

    很全很全:并且有项目实例 例如:mybatis中的#和$的区别? 1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by "111",...

    已通过运行的mybatis小例子

    这是一个mybatis的一个小例子,实现了新增、一对多查询和删除操作。数据库采用的是mysql8.0.23,附件含建库和建表相关语句。开发环境为eclipse2021,jdk版本为jdk-17_windows-x64_bin。已通过运行。

    MyBatis3.2.3帮助文档(中文版).zip

    XML 配置文件(configuration XML)中包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务作用域和控制方式的事务管理器(TransactionManager)。XML 配置文件的详细内容后面...

    Mybatis2.3和Spring3.2.8整合(带注解)

    并且附带百度云盘的4种整合的项目实例,相信能够帮助很多正在学习mybatis却苦于没有一个从头到尾的例子可以看,4种项目全部都可以运行,里面有创建表的语句,大家可以根据需要创建Mysql或者Oracle的数据库中的表。...

    SpringBoot+mybatisPlus+atomikos+druid.zip

    Spring Boot:mybatis-plus + atomikos + druid 实现不同实例数据库的多数据源配置和分布式事务管理(demo项目),想到工作上可能会用到多数据源,但是自己在这方面并不是很熟悉,于是在网上查阅了很多文章,结果...

    springboot+mybatis-plus+shardingsphere 实现读写分离

    提高访问性能和系统可扩展性。通过将读取操作分配给专门负责读取的从库,可以将读取操作的负载分散到多个数据库实例,从而提升读取性能和系统的可扩展性

    Spring mvc mybatis web整合案例(包含相关jar包)

    要么就是还要我自己做太多修改才能用的,我就自己从项目里面抽了一个出来,可能jar包有多余的,但是肯定能用,mysql的运行文件在webroot下面的document里面(运行了就有相关的数据库数据),运行我采用的是navicat...

    jeesite1.2.7 springMVC+mybatis 开源下载

    JeeSite是基于多个优秀的开源项目,高度整合封装而成的高效,高性能,强安全性的开源Java EE快速开发平台。 JeeSite本身是以Spring Framework为核心容器,Spring MVC为模型视图控制器,MyBatis为数据访问层, Apache...

    一个基于spring boot、spring oauth2.0、mybatis、redis的轻量级、前后端分离的商城项目

    一个基于spring boot、spring oauth2.0、mybatis、redis的轻量级、前后端分离、防范xss攻击、拥有分布式锁,为生产环境多实例完全准备,数据库为b2b2c设计,拥有完整sku和下单流程的完全开源商城 前言 Mall4j项目...

    Java开发面试题整理含答案(计网、Java、操作系统、数据库、框架).zip

    代码和项目实例:提供了多个Java项目的源代码,方便学习者参考和实践。 学习笔记和心得:记录了学习过程中的重点难点和心得体会,有助于学习者更好地理解和掌握知识。 二、适用人群 本资源适用于即将毕业或已经毕业...

    轻量级、前后端分离、防范xss攻击、拥有分布式锁,数据库为b2b2c设计,拥有完整sku和下单流程的完全开源商城

    一个基于spring boot、spring oauth2.0、mybatis、redis的轻量级、前后端分离、防范xss攻击、拥有分布式锁,为生产环境多实例完全准备,数据库为b2b2c设计,拥有完整sku和下单流程的完全开源商城。Mall4j项目致力于...

    常规Java工具,算法,加密,数据库,面试题,源代码分析,解决方案.zip

    代码和项目实例:提供了多个Java项目的源代码,方便学习者参考和实践。 学习笔记和心得:记录了学习过程中的重点难点和心得体会,有助于学习者更好地理解和掌握知识。 二、适用人群 本资源适用于即将毕业或已经毕业...

    一个基于spring boot的轻量级、前后端分离、防范xss攻击、拥有分布式锁的商城系统Mall4j

    一个基于spring boot、spring oauth2.0、mybatis、redis的轻量级、前后端分离、防范xss攻击、拥有分布式锁,为生产环境多实例完全准备,数据库为b2b2c设计,拥有完整sku和下单流程的完全开源商城。Mall4j项目致力于...

Global site tag (gtag.js) - Google Analytics