-->

MyBatis-guice 3.3 + Multiple datasources + propert

2020-07-23 04:44发布

问题:

I'm using MyBatis-guice 3.3 to connect to a first database using a java Properties object and a ScriptRunner to run few scripts:

Environment environment = injector.getInstance(SqlSessionFactory.class).getConfiguration().getEnvironment();
DataSource source = environment.getDataSource();
ScriptRunner runner = new ScriptRunner(source.getConnection());
runner.setLogWriter(null);
runner.setStopOnError(true);
runner.runScript(Resources.getResourceAsReader(properties.getProperty("script.dbA.create.schema")));

Now I would like to add a second datasource (dbB) using the same approach. Following the MyBatis-guice reference guide I have to use 2 PrivateModule. This part works fine.

But then how should I call my ScriptRunner to run some scripts for dbA and some others for dbB?

回答1:

Create 2 qualifier annotations @DbA and @DbB or similar.

Now each of the private modules will call (via the MyBatisModule)

binder().bind(SqlSessionFactory.class).toProvider(SqlSessionFactoryProvider.class).in(Scopes.SINGLETON);

that means it is not possible to do

expose(SqlSessionFactory.class).annotatedWith(DbA.class);

that would require

binder().bind(SqlSessionFactory.class).annotatedWith(DbA.class).toProvider(SqlSessionFactoryProvider.class).in(Scopes.SINGLETON);

Instead you need to provide an intermediate class that gets injected with SqlSessionFactory and is exposed with qualifier annotation.

And in each private module do something in the lines of

bind(MyBatisEnv.class).to(MyBatisImpl.class).annotatedWith(DbX.class);
expose(MyBatisEnv.class).annotatedWith(DbX.class);


回答2:

Here is a solution.

Injector injector = Guice.createInjector(
    new PrivateModule() {
        @Override protected void configure() {
            install(new MyBatisModule() {
                @Override protected void initialize() {
                     bindDataSourceProviderType(BasicDataSourceProvider.class);
                     bindTransactionFactoryType(JdbcTransactionFactory.class);
                     // add all your mappers here nowhere else
                     addMapperClass(MapperA1.class);
                }
            });
            Names.bindProperties(binder(), createProperties("dbA"));
            // expose all your mappers here
            expose(MapperA1.class);

            // bind&expose all db specific stuff here (SessionFactory, SessionManager, etc.)
            bind(SqlSessionFactory.class).annotatedWith(DbA.class).to(SqlSessionFactory.class);
            expose(SqlSessionFactory.class).annotatedWith(DbA.class);
    }},
    new PrivateModule() {
        @Override protected void configure() {
            install(new MyBatisModule() {
                @Override protected void initialize() {
                     bindDataSourceProviderType(BasicDataSourceProvider.class);
                     bindTransactionFactoryType(JdbcTransactionFactory.class);
                     // add all your mappers here nowhere else
                     addMapperClass(MapperB1.class);
                }
            });
            Names.bindProperties(binder(), createProperties("dbB"));
            // expose all your mappers here
            expose(MapperB1.class);

            // bind&expose all db specific stuff here (SessionFactory, SessionManager, etc.)
            bind(SqlSessionFactory.class).annotatedWith(DbB.class).to(SqlSessionFactory.class);
            expose(SqlSessionFactory.class).annotatedWith(DbB.class);
    }}
);
DataSource dbA dataSource = injector.getInstance(Key.get(SqlSessionFactory.class), DbA.class).getConfiguration().getEnvironment().getDataSource();
ScriptRunner runner = new ScriptRunner(source.getConnection());
runner.runScript(Resources.getResourceAsReader("dbA/path/create_db.sql"));
runner.closeConnection();

private static Properties createDbProperties(String schema) {
    final Properties p = new Properties();
    p.setProperty("mybatis.environment.id", "test");
    p.setProperty("JDBC.driver", "org.hsqldb.jdbcDriver");
    p.setProperty("JDBC.url", "jdbc:hsqldb:mem" + schema + ";sql.syntax_ora=true");
    p.setProperty("JDBC.username", "sa");
    p.setProperty("JDBC.password", "");
    return p;
}


回答3:

If anyone is looking for a solution in GWT Platform environment, here is the configuration from an working environment. Hope this is useful.

// mybatis-dbA-config.xml
<configuration>

  <settings>
    <setting name="cacheEnabled" value="false" />
    <setting name="useGeneratedKeys" value="true" />
    <setting name="defaultExecutorType" value="REUSE" />
  </settings>

  <typeAliases>
    <package name="com.example.domain" />
  </typeAliases>

  <environments default="test">
    <environment id="test">
      <transactionManager type="JDBC" />
      <dataSource type="POOLED">
        <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@//localhost:1521/SchemaName" />
        <property name="username" value="user" />
        <property name="password" value="pwd" />
      </dataSource>
    </environment>
  </environments>

  <mappers>
    <mapper resource="com/example/mapper/DomainAMapper.xml" />
  </mappers>

</configuration>

...

//dbA Dao implementation
public class DbADaoImpl implements DbADao {
    @Inject
    @Named("DbA")
    private SqlSession sqlSession;

    public List<DomainA> listDbADomains() {
        return this.sqlSession.selectList("com.example.mapper.DomainAMapper.listDomainAs");
    }
...

//dbB Dao implementation
public class DbBDaoImpl implements DbBDao {
    @Inject
    @Named("DbB")
    private SqlSession sqlSession;

    public List<DomainB> listDbBDomains() {
        return this.sqlSession.selectList("com.example.mapper.DomainBMapper.listDomainBs");
    }
...

// Service implementation
public class MyServiceImpl implements MyService {
  @Inject
  private DbADao aDao;
  @Inject
  private DbBDao bDao;

  @Transactional(isolation = Isolation.SERIALIZABLE)
  @Override
  public Boolean doBusinessStuff() {
      // use aDao.
      // use bDao.
  }
...

// DbA Module
public class DbAModule extends PrivateModule {
    @Override
    protected void configure() {
        install(new XMLMyBatisModule() {
            @Override
            protected void initialize() {
                setEnvironmentId("test");
                setClassPathResource("mybatis-dbA-config.xml");

                useJdbcDriverClassLoaderoracle.jdbc.OracleDriver.class.getClassLoader());
                addProperties(createTestProperties());
            }
        });

        bind(SqlSession.class).annotatedWith(Names.named("DbA")).toProvider(SqlSessionManagerProvider.class).in(Scopes.SINGLETON);
        expose(SqlSession.class).annotatedWith(Names.named("DbA"));
    }

    protected static Properties createTestProperties() {
        final Properties myBatisProperties = new Properties();
        myBatisProperties.setProperty("mybatis.environment.id", "test");
        myBatisProperties.setProperty("JDBC.autoCommit", "false");
        return myBatisProperties;
    }
}

...
// DbB Module
// identical to the above replacing DbA with DbB

...

//
// GWTP Configurations
//

//
public class DaoServiceModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(Service.class).to(ServiceImpl.class);
    bind(DbADao.class).to(DbADaoImpl.class);
    bind(DbBDao.class).to(DbBDaoImpl.class);
  }

public class GuiceServletConfig extends GuiceServletContextListener {
  @Override
  protected Injector getInjector() {
    return Guice.createInjector(new ServerModule(),
        new DispatchServletModule(),
        new DbAModule(), 
        new DbAModule(),
        new DaoServiceModule());
  }