From 3006ab30da5ab595de2b2e847ff561dcf01d1ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 31 Mar 2023 09:37:29 +0800 Subject: [PATCH] add multi datasource support --- .../spring/boot/DataSourceBuilder.java | 140 ++++++++++++++++++ .../spring/boot/DataSourceProperty.java | 70 --------- .../MultiDataSourceAutoConfiguration.java | 140 ++---------------- .../spring/boot/MybatisFlexProperties.java | 6 +- 4 files changed, 159 insertions(+), 197 deletions(-) create mode 100644 mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/DataSourceBuilder.java delete mode 100644 mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/DataSourceProperty.java diff --git a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/DataSourceBuilder.java b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/DataSourceBuilder.java new file mode 100644 index 00000000..aae36102 --- /dev/null +++ b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/DataSourceBuilder.java @@ -0,0 +1,140 @@ +/** + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.mybatisflex.spring.boot; + +import com.mybatisflex.core.table.ConvertUtil; +import com.mybatisflex.core.util.StringUtil; +import org.apache.ibatis.reflection.Reflector; +import org.apache.ibatis.reflection.invoker.Invoker; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +public class DataSourceBuilder { + + private static Map dataSourceAlias = new HashMap<>(); + + static { + dataSourceAlias.put("druid", "com.alibaba.druid.pool.DruidDataSource"); + dataSourceAlias.put("hikari", "com.zaxxer.hikari.HikariDataSource"); + dataSourceAlias.put("hikaricp", "com.zaxxer.hikari.HikariDataSource"); + dataSourceAlias.put("bee", "cn.beecp.BeeDataSource"); + dataSourceAlias.put("beecp", "cn.beecp.BeeDataSource"); + dataSourceAlias.put("dbcp", "org.apache.commons.dbcp2.BasicDataSource"); + dataSourceAlias.put("dbcp2", "org.apache.commons.dbcp2.BasicDataSource"); + } + + private Map dataSourceProperties; + + public DataSourceBuilder(Map dataSourceProperties) { + this.dataSourceProperties = dataSourceProperties; + } + + public DataSource build() { + String dataSourceClassName = null; + String type = dataSourceProperties.get("type"); + if (StringUtil.isNotBlank(type)) { + if (dataSourceAlias.containsKey(type)) { + dataSourceClassName = dataSourceAlias.get(type); + } else { + dataSourceClassName = type; + } + } else { + dataSourceClassName = detectDataSourceClass(); + } + + if (StringUtil.isBlank(dataSourceClassName)) { + throw new IllegalArgumentException("Cannot find the dataSource type: " + type); + } + + try { + Class dataSourceClass = Class.forName(dataSourceClassName); + Object dataSourceObject = dataSourceClass.newInstance(); + setDataSourceProperties(dataSourceObject); + return (DataSource) dataSourceObject; + } catch (Exception e) { + throw new RuntimeException("Cannot new instance dataSource by class: " + dataSourceClassName); + } + } + + private void setDataSourceProperties(Object dataSourceObject) throws Exception { + Reflector reflector = new Reflector(dataSourceObject.getClass()); + for (String attr : dataSourceProperties.keySet()) { + String value = dataSourceProperties.get(attr); + String camelAttr = attrToCamel(attr); + if ("url".equals(camelAttr) || "jdbcUrl".equals(camelAttr)) { + if (reflector.hasSetter("url")) { + reflector.getSetInvoker("url").invoke(dataSourceObject, new Object[]{value}); + } else if (reflector.hasSetter("jdbcUrl")) { + reflector.getSetInvoker("jdbcUrl").invoke(dataSourceObject, new Object[]{value}); + } + } else { + if (reflector.hasSetter(camelAttr)) { + Invoker setInvoker = reflector.getSetInvoker(camelAttr); + setInvoker.invoke(dataSourceObject, new Object[]{ConvertUtil.convert(value, setInvoker.getType())}); + } + } + } + } + + + public static String attrToCamel(String string) { + String temp = string.toLowerCase(); + int strLen = temp.length(); + StringBuilder sb = new StringBuilder(strLen); + for (int i = 0; i < strLen; i++) { + char c = temp.charAt(i); + if (c == '-') { + if (++i < strLen) { + sb.append(Character.toUpperCase(temp.charAt(i))); + } + } else { + sb.append(c); + } + } + return sb.toString(); + } + + + private String detectDataSourceClass() { + String[] detectClassNames = new String[]{ + "com.alibaba.druid.pool.DruidDataSource", + "com.zaxxer.hikari.HikariDataSource", + "cn.beecp.BeeDataSource", + "org.apache.commons.dbcp2.BasicDataSource", + }; + + for (String detectClassName : detectClassNames) { + String result = doDetectDataSourceClass(detectClassName); + if (result != null) { + return result; + } + } + + return null; + } + + + private String doDetectDataSourceClass(String className) { + try { + Class.forName(className); + return className; + } catch (ClassNotFoundException e) { + return null; + } + } +} diff --git a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/DataSourceProperty.java b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/DataSourceProperty.java deleted file mode 100644 index bea74a3b..00000000 --- a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/DataSourceProperty.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.mybatisflex.spring.boot; - -/** - * 参考 {@link org.springframework.boot.autoconfigure.jdbc.DataSourceProperties} - * 自定义 DataSourceProperty,在未来可以提供更多的扩展 - */ -public class DataSourceProperty { - - private String type; - private String driverClassName; - private String url; - private String username; - private String password; - - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getDriverClassName() { - return driverClassName; - } - - public void setDriverClassName(String driverClassName) { - this.driverClassName = driverClassName; - } - - 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; - } -} diff --git a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MultiDataSourceAutoConfiguration.java b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MultiDataSourceAutoConfiguration.java index 6b6c291b..ddebe444 100644 --- a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MultiDataSourceAutoConfiguration.java +++ b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MultiDataSourceAutoConfiguration.java @@ -15,13 +15,8 @@ */ package com.mybatisflex.spring.boot; -import cn.beecp.BeeDataSource; -import com.alibaba.druid.pool.DruidDataSource; import com.mybatisflex.core.datasource.RoutingDataSource; -import com.mybatisflex.core.util.StringUtil; import com.mybatisflex.spring.FlexSqlSessionFactoryBean; -import com.zaxxer.hikari.HikariDataSource; -import org.apache.commons.dbcp2.BasicDataSource; import org.apache.ibatis.mapping.DatabaseIdProvider; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.scripting.LanguageDriver; @@ -31,8 +26,10 @@ import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.core.io.Resource; @@ -42,7 +39,10 @@ import org.springframework.util.StringUtils; import javax.sql.DataSource; import java.beans.PropertyDescriptor; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -55,25 +55,13 @@ import java.util.stream.Stream; @ConditionalOnPropertyEmpty("spring.datasource.url") @EnableConfigurationProperties(MybatisFlexProperties.class) @AutoConfigureAfter({MybatisLanguageDriverAutoConfiguration.class}) +@AutoConfigureBefore({DataSourceAutoConfiguration.class}) public class MultiDataSourceAutoConfiguration extends MybatisFlexAutoConfiguration { private List sqlSessionFactories = new ArrayList<>(); private List dataSources = new ArrayList<>(); - private static Map dataSourceAlias = new HashMap<>(); - - static { - dataSourceAlias.put("druid", "com.alibaba.druid.pool.DruidDataSource"); - dataSourceAlias.put("hikari", "com.zaxxer.hikari.HikariDataSource"); - dataSourceAlias.put("hikaricp", "com.zaxxer.hikari.HikariDataSource"); - dataSourceAlias.put("bee", "cn.beecp.BeeDataSource"); - dataSourceAlias.put("beecp", "cn.beecp.BeeDataSource"); - dataSourceAlias.put("dbcp", "org.apache.commons.dbcp2.BasicDataSource"); - dataSourceAlias.put("dbcp2", "org.apache.commons.dbcp2.BasicDataSource"); - } - - public MultiDataSourceAutoConfiguration(MybatisFlexProperties properties , ObjectProvider interceptorsProvider , ObjectProvider typeHandlersProvider @@ -88,110 +76,14 @@ public class MultiDataSourceAutoConfiguration extends MybatisFlexAutoConfigurati } - private void initDataSources(Map datasourceMap) { - datasourceMap.forEach((s, dsp) -> { - SqlSessionFactory sqlSessionFactory = buildSqlSessionFactory(s, createDataSource(dsp)); - sqlSessionFactories.add(sqlSessionFactory); - }); - } - - - private DataSource createDataSource(DataSourceProperty dataSourceProperty) { - String type = dataSourceProperty.getType(); - if (StringUtil.isBlank(type)) { - type = detectDataSourceClass(); + private void initDataSources(Map> datasourceMap) { + if (datasourceMap != null) { + datasourceMap.forEach((s, dsp) -> { + DataSource dataSource = new DataSourceBuilder(dsp).build(); + SqlSessionFactory sqlSessionFactory = buildSqlSessionFactory(s, dataSource); + sqlSessionFactories.add(sqlSessionFactory); + }); } - if (StringUtil.isBlank(type)) { - throw new IllegalArgumentException("The dataSource cannot be null or empty."); - } - - switch (type) { - case "druid": - case "com.alibaba.druid.pool.DruidDataSource": - return createDruidDataSource(dataSourceProperty); - case "hikari": - case "hikaricp": - case "com.zaxxer.hikari.HikariDataSource": - return createHikariDataSource(dataSourceProperty); - case "bee": - case "beecp": - case "cn.beecp.BeeDataSource": - return createBeeDataSource(dataSourceProperty); - case "dbcp": - case "dbcp2": - case "org.apache.commons.dbcp2.BasicDataSource": - return createDbcpDataSource(dataSourceProperty); - default: - throw new IllegalArgumentException("Cannot Support the dataSource type:" + dataSourceProperty.getType()); - } - } - - - private String detectDataSourceClass() { - String[] detectClassNames = new String[]{ - "com.alibaba.druid.pool.DruidDataSource", - "com.zaxxer.hikari.HikariDataSource", - "cn.beecp.BeeDataSource", - "org.apache.commons.dbcp2.BasicDataSource", - }; - - for (String detectClassName : detectClassNames) { - String result = doDetectDataSourceClass(detectClassName); - if (result != null) { - return result; - } - } - - return null; - } - - private String doDetectDataSourceClass(String className) { - try { - Class.forName(className); - return className; - } catch (ClassNotFoundException e) { - return null; - } - } - - private DataSource createDbcpDataSource(DataSourceProperty dataSourceProperty) { - BasicDataSource ds = new BasicDataSource(); - ds.setUrl(dataSourceProperty.getUrl()); - ds.setUsername(dataSourceProperty.getUsername()); - ds.setPassword(dataSourceProperty.getPassword()); - ds.setDriverClassName(dataSourceProperty.getDriverClassName()); - return ds; - } - - private DataSource createBeeDataSource(DataSourceProperty dataSourceProperty) { - BeeDataSource ds = new BeeDataSource(); - ds.setJdbcUrl(dataSourceProperty.getUrl()); - ds.setUsername(dataSourceProperty.getUsername()); - ds.setPassword(dataSourceProperty.getPassword()); - ds.setDriverClassName(dataSourceProperty.getDriverClassName()); - return ds; - } - - - private DataSource createDruidDataSource(DataSourceProperty dataSourceProperty) { - DruidDataSource ds = new DruidDataSource(); - ds.setUrl(dataSourceProperty.getUrl()); - ds.setUsername(dataSourceProperty.getUsername()); - ds.setPassword(dataSourceProperty.getPassword()); - ds.setDriverClassName(dataSourceProperty.getDriverClassName()); - return ds; - } - - - private DataSource createHikariDataSource(DataSourceProperty dataSourceProperty) { - HikariDataSource ds = new HikariDataSource(); - ds.setJdbcUrl(dataSourceProperty.getUrl()); - ds.setUsername(dataSourceProperty.getUsername()); - ds.setPassword(dataSourceProperty.getPassword()); - if (StringUtil.isNotBlank(dataSourceProperty.getDriverClassName())) { - ds.setDriverClassName(dataSourceProperty.getDriverClassName()); - } - return ds; } @@ -264,14 +156,14 @@ public class MultiDataSourceAutoConfiguration extends MybatisFlexAutoConfigurati @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory() { - return sqlSessionFactories.get(0); + return sqlSessionFactories.isEmpty() ? null : sqlSessionFactories.get(0); } @Bean @ConditionalOnMissingBean public DataSource dataSource() { - return dataSources.get(0); + return dataSources.isEmpty() ? null : dataSources.get(0); } diff --git a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MybatisFlexProperties.java b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MybatisFlexProperties.java index 97cd24bd..4aadcc9d 100644 --- a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MybatisFlexProperties.java +++ b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MybatisFlexProperties.java @@ -48,7 +48,7 @@ public class MybatisFlexProperties { //多数据源的配置 //mybatis-flex.datasource.ds1.url=*** //mybatis-flex.datasource.ds2.url=*** - private Map datasource; + private Map> datasource; /** * Location of MyBatis xml config file. @@ -102,11 +102,11 @@ public class MybatisFlexProperties { */ private CoreConfiguration configuration; - public Map getDatasource() { + public Map> getDatasource() { return datasource; } - public void setDatasource(Map datasource) { + public void setDatasource(Map> datasource) { this.datasource = datasource; }