Skip to main content

Multiple DataSources

In Jimmer, each data source corresponds to a sqlClient object (whose type is JSqlClient or KSqlClient).

For the most classic single data source case, one sqlClient object is sufficient. Even the Jimmer Spring Boot Starter can automatically create a single sqlClient object.

For multiple data sources, developers need to manually create multiple sqlClient objects.

For the Jimmer Spring Boot Starter, the support for multiple data sources falls into two cases:

  • Distributed Transaction Mode
  • Local Transaction Mode

Distributed Transaction Mode

Here, distributed transactions refer to using Spring's support for JTA transactions.

In this mode, simply create multiple sqlClient objects.

import org.springframework.beans.factory.annotation.Qualifier;
import org.babyfish.jimmer.spring.SpringClients;

@configuration
public class SqlClientConfig {

@Bean
public JSqlClient sqlClient1(
ApplicationContext ctx,
@Qualifier("ds1") DataSource dataSource
) {
return SqlClients.java(ctx, dataSource, null);
}

@Bean
public JSqlClient sqlClient2(
ApplicationContext ctx,
@Qualifier("ds2") DataSource dataSource
) {
return SqlClients.java(ctx, dataSource, null);
}
}

Then, developers can freely use any of the sqlClient objects. Even if two data sources are operated within a transaction, the JTA transaction will ensure consistency between them.

Local Transaction Mode

Local transactions are slightly different, let's first look at the code:

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.support.PlatformTransactionManager;
import org.babyfish.jimmer.spring.SpringClients;
import org.babyfish.jimmer.spring.transaction.JimmerTransactionManager;
import org.babyfish.jimmer.spring.transaction.TransactionalSqlClients;

@configuration
public class SqlClientConfig {

@Bean
public PlatformTransactionManager tm1(
ApplicationContext ctx,
@Qualifier("ds1") DataSource dataSource
) {
return new JimmerTransactionManager(
SqlClients.java(ctx, dataSource, null)
);
}

@Bean
public PlatformTransactionManager tm2(
ApplicationContext ctx,
@Qualifier("ds2") DataSource dataSource
) {
return new JimmerTransactionManager(
SqlClients.java(ctx, dataSource, null)
);
}

@Bean
public JSqlClient sqlClient() {
return TransactionalSqlClients.java();
}
}
  • ❶ Create the first transaction manager based on the first DataSource, note that:

    • The transaction manager type is org.babyfish.jimmer.spring.transaction.JimmerTransactionManager.

    • Although the internal code creates a sqlClient object, it is not exposed to the Spring context, but is held and hidden by the transaction manager.

  • ❷ Create the second transaction manager based on the second DataSource, same as ❶, no need to repeat.

  • ❸ Create a sqlClient proxy and expose it to the Spring context for developers to inject and use.

    • For any business method that needs to operate Jimmer, the annotation @Transactional("tm1") or @Transactional("tm2") must be used, so that Jimmer can be told which JimmerTransactionManager the current business method is using. Otherwise, using the ❸ sqlClient proxy will result in the following exception:

      The transactional sql client is used, 
      however, there is no AOP transaction, or the transaction manager is not
      "org.babyfish.jimmer.spring.transaction.JimmerTransactionManager"
    • Once Jimmer understands that the current thread is using one of the transaction managers from ❶ and ❷, the ❸ sqlClient proxy will use the internal sqlClient of the corresponding JimmerTransactionManager from ❶ or ❷ to provide services for the user.

That is, in the local transaction mode, although multiple sqlClient objects can be created, the business method must explicitly specify which data source to use through the Spring annotation @Transactional(transactionManagerRef).