mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-08 01:28:24 +08:00
add multi datasource support
This commit is contained in:
parent
c1340f256b
commit
3006ab30da
@ -0,0 +1,140 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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<String, String> 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<String, String> dataSourceProperties;
|
||||||
|
|
||||||
|
public DataSourceBuilder(Map<String, String> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,70 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
|
||||||
* <p>
|
|
||||||
* 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
|
|
||||||
* <p>
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* <p>
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -15,13 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.mybatisflex.spring.boot;
|
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.datasource.RoutingDataSource;
|
||||||
import com.mybatisflex.core.util.StringUtil;
|
|
||||||
import com.mybatisflex.spring.FlexSqlSessionFactoryBean;
|
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.mapping.DatabaseIdProvider;
|
||||||
import org.apache.ibatis.plugin.Interceptor;
|
import org.apache.ibatis.plugin.Interceptor;
|
||||||
import org.apache.ibatis.scripting.LanguageDriver;
|
import org.apache.ibatis.scripting.LanguageDriver;
|
||||||
@ -31,8 +26,10 @@ import org.mybatis.spring.SqlSessionFactoryBean;
|
|||||||
import org.springframework.beans.BeanWrapperImpl;
|
import org.springframework.beans.BeanWrapperImpl;
|
||||||
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
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.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
@ -42,7 +39,10 @@ import org.springframework.util.StringUtils;
|
|||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.beans.PropertyDescriptor;
|
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.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@ -55,25 +55,13 @@ import java.util.stream.Stream;
|
|||||||
@ConditionalOnPropertyEmpty("spring.datasource.url")
|
@ConditionalOnPropertyEmpty("spring.datasource.url")
|
||||||
@EnableConfigurationProperties(MybatisFlexProperties.class)
|
@EnableConfigurationProperties(MybatisFlexProperties.class)
|
||||||
@AutoConfigureAfter({MybatisLanguageDriverAutoConfiguration.class})
|
@AutoConfigureAfter({MybatisLanguageDriverAutoConfiguration.class})
|
||||||
|
@AutoConfigureBefore({DataSourceAutoConfiguration.class})
|
||||||
public class MultiDataSourceAutoConfiguration extends MybatisFlexAutoConfiguration {
|
public class MultiDataSourceAutoConfiguration extends MybatisFlexAutoConfiguration {
|
||||||
|
|
||||||
private List<SqlSessionFactory> sqlSessionFactories = new ArrayList<>();
|
private List<SqlSessionFactory> sqlSessionFactories = new ArrayList<>();
|
||||||
private List<DataSource> dataSources = new ArrayList<>();
|
private List<DataSource> dataSources = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
private static Map<String, String> 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
|
public MultiDataSourceAutoConfiguration(MybatisFlexProperties properties
|
||||||
, ObjectProvider<Interceptor[]> interceptorsProvider
|
, ObjectProvider<Interceptor[]> interceptorsProvider
|
||||||
, ObjectProvider<TypeHandler[]> typeHandlersProvider
|
, ObjectProvider<TypeHandler[]> typeHandlersProvider
|
||||||
@ -88,110 +76,14 @@ public class MultiDataSourceAutoConfiguration extends MybatisFlexAutoConfigurati
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void initDataSources(Map<String, DataSourceProperty> datasourceMap) {
|
private void initDataSources(Map<String, Map<String, String>> datasourceMap) {
|
||||||
datasourceMap.forEach((s, dsp) -> {
|
if (datasourceMap != null) {
|
||||||
SqlSessionFactory sqlSessionFactory = buildSqlSessionFactory(s, createDataSource(dsp));
|
datasourceMap.forEach((s, dsp) -> {
|
||||||
sqlSessionFactories.add(sqlSessionFactory);
|
DataSource dataSource = new DataSourceBuilder(dsp).build();
|
||||||
});
|
SqlSessionFactory sqlSessionFactory = buildSqlSessionFactory(s, dataSource);
|
||||||
}
|
sqlSessionFactories.add(sqlSessionFactory);
|
||||||
|
});
|
||||||
|
|
||||||
private DataSource createDataSource(DataSourceProperty dataSourceProperty) {
|
|
||||||
String type = dataSourceProperty.getType();
|
|
||||||
if (StringUtil.isBlank(type)) {
|
|
||||||
type = detectDataSourceClass();
|
|
||||||
}
|
}
|
||||||
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
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public SqlSessionFactory sqlSessionFactory() {
|
public SqlSessionFactory sqlSessionFactory() {
|
||||||
return sqlSessionFactories.get(0);
|
return sqlSessionFactories.isEmpty() ? null : sqlSessionFactories.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public DataSource dataSource() {
|
public DataSource dataSource() {
|
||||||
return dataSources.get(0);
|
return dataSources.isEmpty() ? null : dataSources.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,7 @@ public class MybatisFlexProperties {
|
|||||||
//多数据源的配置
|
//多数据源的配置
|
||||||
//mybatis-flex.datasource.ds1.url=***
|
//mybatis-flex.datasource.ds1.url=***
|
||||||
//mybatis-flex.datasource.ds2.url=***
|
//mybatis-flex.datasource.ds2.url=***
|
||||||
private Map<String, DataSourceProperty> datasource;
|
private Map<String, Map<String,String>> datasource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Location of MyBatis xml config file.
|
* Location of MyBatis xml config file.
|
||||||
@ -102,11 +102,11 @@ public class MybatisFlexProperties {
|
|||||||
*/
|
*/
|
||||||
private CoreConfiguration configuration;
|
private CoreConfiguration configuration;
|
||||||
|
|
||||||
public Map<String, DataSourceProperty> getDatasource() {
|
public Map<String, Map<String, String>> getDatasource() {
|
||||||
return datasource;
|
return datasource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDatasource(Map<String, DataSourceProperty> datasource) {
|
public void setDatasource(Map<String, Map<String, String>> datasource) {
|
||||||
this.datasource = datasource;
|
this.datasource = datasource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user