【问题标题】:Equivalent of MyBatis XML multiple environments in MyBatis GuiceMyBatis Guice 中 MyBatis XML 多环境的等价物
【发布时间】:2016-02-28 06:41:34
【问题描述】:

我正在编写一个需要根据上下文使用不同数据库的服务(一个简单的字符串标签)。每个数据库都有完全相同的模式。数据库列表是动态的。

查看MyBatis-Guice documentation on multiple data sources,该示例是预先知道数据源列表的地方,并且每个数据源都有不同的映射器。同样,找到here on SO 的问题也假设了相同的要求。

如上所述,我的要求更加动态和流畅。这个想法是将所有当前已知的数据库(及其连接信息)放在一个配置中,并在服务启动时对其进行解析。然后,根据任何传入请求的上下文,代码应该为正确的数据库拉取 SqlSessionFactory。使用该 SqlSessionFactory 的所有下游代码完全相同 - 即不依赖于请求上下文。这意味着无论需要什么数据库,都使用相同的映射器。

我的 MyBatis 和 Guice 知识无疑是非常新的和有限的。但是,我无法谷歌任何显示 MyBatis-Guice 等同于 MyBatis 的 multiple environment approach supported by the XML configuration 的内容。

【问题讨论】:

  • hk2 (hk2.java.net) 有很多基于xml配置动态添加服务的支持。你可能想检查一下

标签: java guice mybatis


【解决方案1】:

我想出了一个适合我的解决方案,所以我想在这里分享一下。使用 Guice 的决定已经做出,所以没有回旋的余地。

首先,我编写了一个 MyBatis Guice 模块来注册单个数据源。它是一个PrivateModule,这样所有为一个数据源注册的 MyBatis 类不会与其他数据源的其他注册冲突。它使用了内部的 MyBatisModule 实现,因为 Java 不支持多重继承。这意味着我们不能做public class MyMyBatisModule extends PrivateModule, MyBatisModule {...}。

public class MyMyBatisModule extends PrivateModule {

    private final String     datasourceLabel;
    private final Properies  datasourceProperties;
    private       List< Key<?> > exposedKeys = new ArrayList< Key<?> >();

    public MyMyBatisModule( String datasourceLabel, Properties datasourceProperties ) {

        this.datasourceLabel = datasourceLabel;
        this.datasourceProperties = datasourceProperties;
    }

    @Override
    protected void configure() {

        install( new InternalMyMyBatisModule( ) );

        for( Key<?> key: keys ) {
            expose( key );
        }
    }

    private class InternalMyMyBatisModule extends MyBatisModule {

        @Override
        protected void initialize( ) {

            environmentId( datasourceLabel );
            Names.bindProperties( binder(), properties );

            install( JdbcHelper.MySQL ); // See JDBC Helper commentary below

            bindDataSourceProviderType( C3p0DataSourceProvider.class ); // Choose whichever one you want
            bindTransactionFactoryType( JdbcTransactionFactory.class );

            // Register your mapper classes here.  These mapper classes will have their
            // keys exposed from the PrivateModule
            //
            // i.e.
            // 
            // keys.add( registerMapper( FredMapper.class );
            // kets.add( registerMapper( GingerMapper.class );
        }

        private <T> Key<T> registerMapper( Class<T> mapperClass ) {
            Key<T> key = Key.get( mapperClass, Names.named( datasourceLabel ) );
            bind( key ).to( mapperClass );
            addMapperClass( mapperClass );
            return key;
        }
    }
}

JdbcHeler.MySQL:我使用JdbcHelper.MySQL 作为将属性映射到连接字符串的快捷方式,并使用com.mysql.jdbc.Driver 作为 JDBC 驱动程序。它被声明为:

MySQL("jdbc:mysql://${JDBC.host|localhost}:${JDBC.port|3306}/${JDBC.schema}", "com.mysql.jdbc.Driver"),

现在是注册所有数据源的时候了。 MyBatisModules 为我们处理这个问题。它需要一个 datasourceLabel 到 jdbc 属性的映射。

public class MyBatisModules extends AbstractModule {

    private Map< String, Properties > connectionsProperties;

    public MyBatisModules( Map< String, Properties > = new HashMap< String, Properties > connectionsProperties ) {
        this.connectionsProperties = connectionsProperties; // consider deep copy if appropriate
    }

    @Override
    protected void configure( ) {

        for( Entry< String, Properties > datasourceConnectionProperties : this.connectionsProperties.entrySet() ) {
            install( new MyMyBatisModule( datasourceConnectionProperties.getKey(), datasourceConnectionProperties.getValue() ) );
        }

        bind( MapperRetriever.class ); // See MapperRetriever later

        // bind your DAO classes here.  By wrapping MyBatis Mapper use in DAO implementations, theoretically we
        // can fairly easily change from MyBatis to any other database library just by changing the DAO implementation.
        // The rest of our codebase would remain the same.
        //
        // i.e.
        //
        // bind( FredDao.class ).to( FredDaoMyBatis.class );
        // bind( GingerDao.class).to( GingerDaoMyBatis.class );
    }
}

现在我们只需要某种方法来获取正确的 Mapper 类(它本身与正确的数据源相关联)。为此,我们实际上需要调用 Guice Injector 上的方法。我不太喜欢传递它的想法,所以我将它包裹在MapperRetriever 中。您需要为每个Mappers 实现一个检索方法。

public class MapperRetriever {

    private final Injector injector;

    @Inject
    public MapperRetriver( Injector injector ) {
        this.injector = injector;
    }

    // The follwing two methods use the example Mappers referenced in the MyMyBatisModule implementation above

    public FredMapper getFredMapper( String datasourceLabel ) {
        return this.injector.getInstance( Key.get( FredMapper.class, Names.named( datasourceLabel ) ) );
    }

    public GingerMapper getGingerMapper( String datasourceLabel ) {
        return this.injector.getInstance( Key.get( GingerMapper.class, Names.named( datasourceLabel ) ) );
    }
}

还有一个示例 DAO 实现 ...

public interface FredDao {
    Fred selectFred( String datasourceLable, String fredId );
}    

public class FredDaoMyBatis implements FredDao {

    private MapperRetriever mapperRetriever;

    @Inject
    public FredDaoMyBatis( MapperRetriever mapperRetriever ) {
        this.mapperRetriever = mapperRetriever;
    }

    @Override
    public Fred selectFred( String datasourceLabel, String fredId ) {
        FredMapper fredMapper = this.mapperRetriever.getFredMapper( datasourceLabel );
        return fredMapper.getFred( fredId );
    }
}

【讨论】:

    【解决方案2】:

    您还可以创建一个自定义 SqlSessionFactoryProvider,它返回一个委托给正确 DataSource 的 SqlSessionFactory 的 SqlSessionFactory。使用 ThreadLocal 来确定底层的 SqlSessionFactory。

    public class DelegatingSqlSessionFactory implements SqlSessionFactory {
        private final Map<String, SqlSessionFactory> factories = new HashMap<>();
    
        public DelegatingSqlSessionFactory(Map<String, DataSource> dataSources) throws ClassNotFoundException {
            dataSources.forEach((key, ds) -> {
                factories.put(key, createSqlSessionFactory(ds));
            });
        }
    
        private SqlSessionFactory delegate() {
            // Read from a ThreadLocal to determine correct SqlSessionFactory key
            String key = findKey();
            return factories.get(key);
        }
    
        @Override
        public SqlSession openSession() {
            return delegate().openSession();
        }
    
        @Override
        public SqlSession openSession(boolean autoCommit) {
            return delegate().openSession(autoCommit);
        }
    
        @Override
        public SqlSession openSession(Connection connection) {
            return delegate().openSession(connection);
        }
    
        @Override
        public SqlSession openSession(TransactionIsolationLevel level) {
            return delegate().openSession(level);
        }
    
        @Override
        public SqlSession openSession(ExecutorType execType) {
            return delegate().openSession(execType);
        }
    
        @Override
        public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
            return delegate().openSession(execType, autoCommit);
        }
    
        @Override
        public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
            return delegate().openSession(execType, level);
        }
    
        @Override
        public SqlSession openSession(ExecutorType execType, Connection connection) {
            return delegate().openSession(execType, connection);
        }
    
        @Override
        public Configuration getConfiguration() {
            return delegate().getConfiguration();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-08
      • 1970-01-01
      • 2016-08-24
      • 2020-07-23
      • 2011-07-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多