- Spring Framework 4.2.5.RELEASE
- spring-jdbc (for JdbcTemplate)
- tomcat-jdbc 8.0.18 (for org.apache.tomcat.jdbc.pool.DataSource)
The key to making this all work is to to extend Spring's DelegatingDataSource and override getConnection() to set the catalog you want:
public class CatalogSpecificDataSource extends DelegatingDataSource { private final String catalog; public CatalogSpecificDataSource(DataSource orig, String catalog) { super(orig); this.catalog = catalog; } @Override public Connection getConnection() throws SQLException { Connection conn = super.getConnection(); conn.setCatalog(catalog); return conn; } }
The next problem is cleanup: how do you ensure that the connection is reset to the default catalog once it's placed into the pool? The answer is in the tomcat connection pool configuration:
PoolProperties p = new PoolProperties(); p.setDefaultCatalog("customers"); /* * set your connection properties here - driver, connection string, etc. * then, when you're done with that, add the interceptors */ p.setJdbcInterceptors( org.apache.tomcat.jdbc.pool.interceptor.ConnectionState.class.getName() + ";" + org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer.class.getName()); DataSource datasource = new DataSource(); datasource.setPoolProperties(p);
The ConnectionState interceptor remembers if you changed the catalog and will set it back. When you create your JdbcTemplate you can now:
JdbcTemplate t = new CatalogDataSource(realDataSource, "someOtherCatalog");
or use Spring's idea of assisted injection. To do that:
Step 1. Create a factory interface
public interface CatalogSpecificDataSourceFactory { javax.sql.DataSource getDatasource(String catalog); }
Step 2. Create a spring bean configuration:
@Bean @Scope(BeanDefinition.SCOPE_PROTOTYPE) public CatalogSpecificDataSourceFactory getCatalogSpecificDatasourceFactory() { return catalog -> new CatalogSpecificDataSource(getCatalogSpecificDatasource(), catalog); }
Step 3. When you want to use it, inject a CatalogSpecificDataSourceFactory then use it to create your Datasource:
DataSource ds = catalogSpecificDataSoruceFactory.getCatalogSpecificDatasource("booger"); JdbcTemplate t = new JdbcTemplate(ds);
Or, if you want to get even fancier, set up another assisted factory. In my case, following the assisted injection pattern once again:
public interface BatchMissionWriterFactory { BatchMissionWriter create(String databaseName); } /* then, in the config: */ @Bean @Scope(BeanDefinition.SCOPE_PROTOTYPE) public BatchWidgetWriterFactory getBatchMissionWriterFactory() { return catalog -> new BatchWidgetWriterFactory (getCatalogDataSourceFactory().getCatalogSpecificDatasource(catalog)); }
As usual, hope this is helpful to someone - feedback welcome.