zoukankan      html  css  js  c++  java
  • mybatis支持多数据库切换(转)

    问题:


    使用mybatis时都是用的sqlmapper来做的数据库到java对象的映射,因此在针对一些特定数据库方言使用时无法在多个数据库上切换。

    解决方案:


    • mybatis篇

    思路:

    通过定义environment的id来指定使用不同的数据库映射文件,如下
    <!--WizRtf2Html Charset=0 -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    <environments default="mysql">
    <environment id="mysql">
    <transactionManager type="JDBC" />
    <dataSource type="POOLED">
    <property name="driver" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/test" />
    <property name="username" value="root" />
    <property name="password" value="pwd" />
    </dataSource>
    </environment>
    </environments>
    <mappers>
    <mapper resource="cn/dcr/mybatis/entity/UserMapper.xml" />
    </mappers>
    </configuration>
    environment的id是mysql映射文件是cn/dcr/mybatis/entity/UserMapper.xml
    那么mybatis实际是读取的mysql/cn/dcr/mybatis/entity/UserMapper.xml
     

    实现:

    以org.apache.ibatis.builder.xml.XMLConfigBuilder类为蓝本创建一个新类org.apache.ibatis.builder.xml.XMLConfigBuilderEx
    添加一个成员变量
    private String dialect;
    修改environmentsElement方法设置方言
    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");
    dialect = id.toLowerCase();//设置方言
    if (isSpecifiedEnvironment(id)) {
    TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
    DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
    Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dsFactory.getDataSource());
    configuration.setEnvironment(environmentBuilder.build());
    }
    }
    }
    }
    修改mapperElement方法
    private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
    for (XNode child : parent.getChildren()) {
    String resource = child.getStringAttribute("resource");
    String url = child.getStringAttribute("url");
    InputStream inputStream;
    if (resource != null && url == null) {
    if(dialect != null){
    resource = dialect + "/" + resource;//从方言指定位置查找
    }
    ErrorContext.instance().resource(resource);
    inputStream = Resources.getResourceAsStream(resource);
    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
    mapperParser.parse();
    } else if (url != null && resource == null) {
    if(dialect != null){
    url = dialect + "/" + url;//从方言指定位置查找
    }
    ErrorContext.instance().resource(url);
    inputStream = Resources.getUrlAsStream(url);
    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
    mapperParser.parse();
    } else {
    throw new BuilderException("A mapper element may only specify a url or resource, but not both.");
    }
    }
    }
    }
    继承org.apache.ibatis.session.SqlSessionFactoryBuilder类创建一个新类org.apache.ibatis.session.SqlSessionFactoryBuilderEx用来加载org.apache.ibatis.builder.xml.XMLConfigBuilderEx
    覆盖父类中的build方法
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties props) {
    try {
    XMLConfigBuilderEx parser = new XMLConfigBuilderEx(inputStream, environment, props);
    Configuration config = parser.parse();
    return build(config);
    } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
    ErrorContext.instance().reset();
    try {
    inputStream.close();
    } catch (IOException e) {
    // Intentionally ignore. Prefer previous error.
    }
    }
    }
    调用org.apache.ibatis.builder.xml.XMLConfigBuilderEx来加载配置文件
     
    • spring篇

    思路:

    自定义mybatis配置加载Bean在spring的配置文件中添加方言的配置让自定义Bean在加载mybatis的配置时可以使用不同的数据库映射文件,如下
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBeanEx">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:configuration.xml" />
    <property name="mapperLocations" value="classpath*:${jdbc.dialect}/mappers/*.xml" />
    </bean>
    ${jdbc.dialect}在配置文件中设置,如mysql,那么spring会把mysql/mappers下的所有映射文件加载进来

    实现:

    以org.mybatis.spring.SqlSessionFactoryBean为蓝本创建org.mybatis.spring.SqlSessionFactoryBeanEx类
    将成员变量
    private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    修改为
    private SqlSessionFactoryBuilderEx sqlSessionFactoryBuilderEx = new SqlSessionFactoryBuilderEx();
    并去掉setSqlSessionFactoryBuilder方法添加setSqlSessionFactoryBuilderEx方法
    覆盖buildSqlSessionFactory方法
    protected SqlSessionFactory buildSqlSessionFactory() throws IOException, IllegalAccessException, InstantiationException {

    XMLConfigBuilderEx xmlConfigBuilderEx;
    Configuration configuration;

    if (this.configLocation != null) {
    try {
    xmlConfigBuilderEx = new XMLConfigBuilderEx(this.configLocation.getInputStream(), null, this.configurationProperties);
    configuration = xmlConfigBuilderEx.parse();
    } catch (Exception ex) {
    throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
    } finally {
    ErrorContext.instance().reset();
    }

    if (this.logger.isDebugEnabled()) {
    this.logger.debug("Parsed configuration file: '" + this.configLocation + "'");
    }
    } else {
    if (this.logger.isDebugEnabled()) {
    this.logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
    }
    configuration = new Configuration();
    }

    if (this.transactionFactory == null) {
    this.transactionFactory = new SpringManagedTransactionFactory(this.dataSource);
    }

    Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);

    configuration.setEnvironment(environment);

    if (!ObjectUtils.isEmpty(this.mapperLocations)) {
    Map<String, XNode> sqlFragments = new HashMap<String, XNode>();

    for (Resource mapperLocation : this.mapperLocations) {
    if (mapperLocation == null) {
    continue;
    }

    // MyBatis holds a Map using "resource" name as a key.
    // If a mapper file is loaded, it searches for a mapper
    // interface type.
    // If the type is found then it tries to load the mapper file
    // again looking for this:
    //
    // String xmlResource = type.getName().replace('.', '/') +
    // ".xml";
    //
    // So if a mapper interface exists, resource cannot be an
    // absolute path.
    // Otherwise MyBatis will throw an exception because
    // it will load both a mapper interface and the mapper xml file,
    // and throw an exception telling that a mapperStatement cannot
    // be loaded twice.
    String path;
    if (mapperLocation instanceof ClassPathResource) {
    path = ((ClassPathResource) mapperLocation).getPath();
    } else {
    // this won't work if there is also a mapper interface in
    // classpath
    path = mapperLocation.toString();
    }

    try {
    XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, path, sqlFragments);
    xmlMapperBuilder.parse();
    } catch (Exception e) {
    throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
    } finally {
    ErrorContext.instance().reset();
    }

    if (this.logger.isDebugEnabled()) {
    this.logger.debug("Parsed mapper file: '" + mapperLocation + "'");
    }
    }
    } else {
    if (this.logger.isDebugEnabled()) {
    this.logger.debug("Property 'mapperLocations' was not specified, only MyBatis mapper files specified in the config xml were loaded");
    }
    }

    return this.sqlSessionFactoryBuilderEx.build(configuration);
    }
  • 相关阅读:
    进阶之路 | 奇妙的Drawable之旅
    进阶之路 | 奇妙的Animation之旅
    进阶之路 | 奇妙的四大组件之旅
    Laravel
    Laravel 入门
    面试:给我说说你平时是如何优化MySQL的?
    EXPLAIN 查看 SQL 执行计划
    常见的图文布局
    常见的图文布局
    CSS3 的 filter(滤镜) 属性
  • 原文地址:https://www.cnblogs.com/yasepix/p/7155407.html
Copyright © 2011-2022 走看看