闲暇之余,写点最近的收获,写一点心得,便于以后参考方便,另外可以帮助有这样需求的人少走弯路。
整合多个数据源,很多博客会提到springboot整合jdbcTemplete为例子,网上有很多,今天主要推荐的是整合 pagehelper + tk-mybatis 多数据源问题。
其实很简单,我的整合思路分为以下几个步骤。
一、创建java配置类,这个配置类里面主要内容有dataSource、sqlSessionFactory、transactionManager、sqlSessionTemplete、具体实现如下:
@Configuration @MapperScan(/*basePackages = "com.batman.bysj.common.dao.mapper",*/ sqlSessionTemplateRef = "bysjSqlSessionTemplate", basePackageClasses = TestMapper.class) @EnableConfigurationProperties(value = BysjDruidDBProperties.class) public class BysjDruidDBAutoConfiguration { private Logger logger = LoggerFactory.getLogger(BysjDruidDBAutoConfiguration.class); private final BysjDruidDBProperties bysjDruidDBProperties; @Autowired public BysjDruidDBAutoConfiguration(BysjDruidDBProperties bysjDruidDBProperties) { this.bysjDruidDBProperties = bysjDruidDBProperties; } @Bean(name = "bysjDataSource") @Primary //在同样的DataSource中,首先使用被标注的DataSource public DataSource bysjDataSource() { DruidDataSource datasource = new DruidDataSource(); datasource.setUrl(bysjDruidDBProperties.getUrl()); datasource.setUsername(bysjDruidDBProperties.getUsername()); datasource.setPassword(bysjDruidDBProperties.getPassword()); datasource.setDriverClassName(bysjDruidDBProperties.getDriverClassName()); //configuration datasource.setInitialSize(bysjDruidDBProperties.getInitialSize()); datasource.setMinIdle(bysjDruidDBProperties.getMinIdle()); datasource.setMaxActive(bysjDruidDBProperties.getMaxActive()); datasource.setMaxWait(bysjDruidDBProperties.getMaxWait() == 0 ? bysjDruidDBProperties.getMaxWait() : 6000); datasource.setTimeBetweenEvictionRunsMillis(bysjDruidDBProperties.getTimeBetweenEvictionRunsMillis()); datasource.setMinEvictableIdleTimeMillis(bysjDruidDBProperties.getMinEvictableIdleTimeMillis()); datasource.setValidationQuery(bysjDruidDBProperties.getValidationQuery()); datasource.setTestWhileIdle(bysjDruidDBProperties.isTestWhileIdle()); datasource.setTestOnBorrow(bysjDruidDBProperties.isTestOnBorrow()); datasource.setTestOnReturn(bysjDruidDBProperties.isTestOnReturn()); datasource.setPoolPreparedStatements(bysjDruidDBProperties.isPoolPreparedStatements()); datasource.setMaxPoolPreparedStatementPerConnectionSize(bysjDruidDBProperties.getMaxPoolPreparedStatementPerConnectionSize()); try { datasource.setFilters(bysjDruidDBProperties.getFilters()); } catch (SQLException e) { logger.error("druid configuration initialization filter", e); } datasource.setConnectionProperties(bysjDruidDBProperties.getConnectionProperties()); return datasource; } @Bean(name = "bysjSqlSessionFactory") @Primary public SqlSessionFactory bysjSqlSessionFactory(@Qualifier("bysjDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources( "classpath:mapper/bysj/*.xml")); return bean.getObject(); } @Bean(name = "bysjTransactionManager") @Primary public DataSourceTransactionManager bysjTransactionManager(@Qualifier("bysjDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "bysjSqlSessionTemplate") @Primary public SqlSessionTemplate bysjSqlSessionTemplate(@Qualifier("bysjSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
需要注意的是需要标记@Primary注解,spring需要知道当前主要加载哪一个,上面的注解有MapperScan指定mybatis扫描的mapper文件位置以及所需要sqlSessionFactory的id,事务管理器可以配也可以不配(需要事务就配上),sqlSessionTemplete也可配可不配(可以从sqlSessionFactory中获取)。@EnableConfigurationProperties这样就可以springboot启动时自动扫描了,另外需要在spring.factories中配置,具体这点会在写下博客说明springboot中的自动配置是怎么实现的。
二、接下来给出properties文件。
@ConfigurationProperties(prefix = "spring.datasource") public class BysjDruidDBProperties { private String url; private String username; private String password; private String driverClassName; private int initialSize; private int minIdle; private int maxActive; private long maxWait; private long timeBetweenEvictionRunsMillis; private long minEvictableIdleTimeMillis; private String validationQuery; private boolean testWhileIdle; private boolean testOnBorrow; private boolean testOnReturn; private boolean poolPreparedStatements; private int maxPoolPreparedStatementPerConnectionSize; private String filters; private String connectionProperties; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public int getInitialSize() { return initialSize; } public void setInitialSize(int initialSize) { this.initialSize = initialSize; } public int getMinIdle() { return minIdle; } public void setMinIdle(int minIdle) { this.minIdle = minIdle; } public int getMaxActive() { return maxActive; } public void setMaxActive(int maxActive) { this.maxActive = maxActive; } public long getMaxWait() { return maxWait; } public void setMaxWait(long maxWait) { this.maxWait = maxWait; } public long getTimeBetweenEvictionRunsMillis() { return timeBetweenEvictionRunsMillis; } public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) { this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; } public long getMinEvictableIdleTimeMillis() { return minEvictableIdleTimeMillis; } public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) { this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; } public String getValidationQuery() { return validationQuery; } public void setValidationQuery(String validationQuery) { this.validationQuery = validationQuery; } public boolean isTestWhileIdle() { return testWhileIdle; } public void setTestWhileIdle(boolean testWhileIdle) { this.testWhileIdle = testWhileIdle; } public boolean isTestOnBorrow() { return testOnBorrow; } public void setTestOnBorrow(boolean testOnBorrow) { this.testOnBorrow = testOnBorrow; } public boolean isTestOnReturn() { return testOnReturn; } public void setTestOnReturn(boolean testOnReturn) { this.testOnReturn = testOnReturn; } public boolean isPoolPreparedStatements() { return poolPreparedStatements; } public void setPoolPreparedStatements(boolean poolPreparedStatements) { this.poolPreparedStatements = poolPreparedStatements; } public int getMaxPoolPreparedStatementPerConnectionSize() { return maxPoolPreparedStatementPerConnectionSize; } public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) { this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize; } public String getFilters() { return filters; } public void setFilters(String filters) { this.filters = filters; } public String getConnectionProperties() { return connectionProperties; } public void setConnectionProperties(String connectionProperties) { this.connectionProperties = connectionProperties; } }
三、接下来就是配置application.yml文件了,和平常配置数据库一样按照提示即可,网上可以搜到很多。
四、要配置另一个数据源就在走一遍一、二、三,不过要注意,给的bean的名称不能重复,你懂得。
五、多谢读者能看到最后,最大的坑就在于通用mapper,当我们所有的数据源都配置好了之后,当我们调用通用的时候会出现sqlSessionFactory只会是primary的那个,另一个不起作用,这是因为MapperAutoConfiguration中只配置了一个sqlSesisonFactory,这样就会出现自动切换数据源,需要添加一个List
@Configuration @ConditionalOnBean(SqlSessionFactory.class) @EnableConfigurationProperties(MapperProperties.class) @AutoConfigureAfter(MybatisAutoConfiguration.class) public class CustomizedMapperAutoConfiguration { private final List<SqlSessionFactory> sqlSessionFactoryList; @Autowired private MapperProperties properties; public CustomizedMapperAutoConfiguration(List<SqlSessionFactory> sqlSessionFactoryList) { this.sqlSessionFactoryList = sqlSessionFactoryList; } @PostConstruct public void addPageInterceptor() { MapperHelper mapperHelper = new MapperHelper(); mapperHelper.setConfig(properties); if (properties.getMappers().size() > 0) { for (Class mapper : properties.getMappers()) { mapperHelper.registerMapper(mapper); } } else { mapperHelper.registerMapper(Mapper.class); } for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) { mapperHelper.processConfiguration(sqlSessionFactory.getConfiguration()); } } }此外,我们需要把那个默认的MapperAutoConfiguration关闭,在springboot启动中的@SpringBootApplication中关闭(这里的配置优先级最高)。至此技术分享结束。
心得有二,第一看懂源码很重要,第二和同事聊聊可能会有心得思路,到了最后一步真不知道怎么弄了,问了下同事,豁然开朗,还是年轻、年轻.......