springboot 整合 pagehelper + tk-mybatis 多資料來源問題
閒暇之餘,寫點最近的收穫,寫一點心得,便於以後參考方便,另外可以幫助有這樣需求的人少走彎路。
整合多個數據源,很多部落格會提到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<SqlSessionFactory>這樣就可以動態配置了。實現如下:
@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中關閉(這裡的配置優先順序最高)。至此技術分享結束。
心得有二,第一看懂原始碼很重要,第二和同事聊聊可能會有心得思路,到了最後一步真不知道怎麼弄了,問了下同事,豁然開朗,還是年輕、年輕.......