在SpringBoot中配置MyBatis多数据源需要准备以下几个文件:
为了图省事,就都放config目录下了
下面分别对各个文件做简单说明:
1.DatabaseContextHolder:
public class DatabaseContextHolder {
private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();
public static DatabaseType getDatabaseType(){
return contextHolder.get();
}
public static void setDatabaseType(DatabaseType type){
contextHolder.set(type);
}
public static void clear() {
contextHolder.remove();
}
}
DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法
2.DatabaseType:
public enum DatabaseType {
database
}
这里枚举了需要连接的数据库:database(可配置多个)
3.DynamicDataSource:
public class DynamicDataSource extends AbstractRoutingDataSource {
protected Object determineCurrentLookupKey(){
return DatabaseContextHolder.getDatabaseType();
}
}
DynamicDataSource继承AbstractRoutingDataSource并重写其中的方法determineCurrentLookupKey(),在该方法中使用DatabaseContextHolder获取当前线程的DatabaseType
4.MyBatisConfig:
@Configuration
@MapperScan(basePackages="com.xxx.xxxx.testservice.mapper")
public class MyBatisConfig {
private static final Logger log = LoggerFactory.getLogger(MyBatisConfig.class);
@Autowired
private Environment env;
/**
* 数据库 mysql
*/
@Bean
public DataSource databaseDataSource() throws Exception {
Properties props = new Properties();
props.put("driverClassName", env.getProperty("jdbc.driverClassName"));
props.put("url", env.getProperty("jdbc.url"));
props.put("username", env.getProperty("jdbc.username"));
props.put("password", env.getProperty("jdbc.password"));
props.put("type", env.getProperty("jdbc.type"));
this.setCommonJDBCProperties(props);
return DruidDataSourceFactory.createDataSource(props);
}
/**
* @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
* @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
*/
@Bean
@Primary
public DynamicDataSource dataSource(
@Qualifier("databaseDataSource") DataSource databaseDataSource
) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.database, databaseDataSource);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
dataSource.setDefaultTargetDataSource(databaseDataSource);// 默认的datasource设置为databaseDataSource
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource dataSource) throws Exception{
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(dataSource);
fb.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));
fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));
return fb.getObject();
}
/**
* 配置事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
/**
* 设置共用的jdbc属性
* @param props
* @author 10148712
*/
private void setCommonJDBCProperties(Properties props)
{
props.put("initialSize", env.getProperty("jdbc.initialSize"));
props.put("minIdle", env.getProperty("jdbc.minIdle"));
props.put("maxActive", env.getProperty("jdbc.maxActive"));
props.put("maxWait", env.getProperty("jdbc.maxWait"));
props.put("validationQuery", env.getProperty("jdbc.validationQuery"));
props.put("testOnBorrow", env.getProperty("jdbc.testOnBorrow"));
props.put("testOnReturn", env.getProperty("jdbc.testOnReturn"));
props.put("testWhileIdle", env.getProperty("jdbc.testWhileIdle"));
props.put("timeBetweenEvictionRunsMillis", env.getProperty("jdbc.timeBetweenEvictionRunsMillis"));
props.put("minEvictableIdleTimeMillis", env.getProperty("jdbc.minEvictableIdleTimeMillis"));
props.put("removeAbandoned", env.getProperty("jdbc.removeAbandoned"));
props.put("removeAbandonedTimeout", env.getProperty("jdbc.removeAbandonedTimeout"));
props.put("logAbandoned", env.getProperty("jdbc.logAbandoned"));
props.put("poolPreparedStatements", env.getProperty("jdbc.poolPreparedStatements"));
props.put("maxPoolPreparedStatementPerConnectionSize", env.getProperty("jdbc.maxPoolPreparedStatementPerConnectionSize"));
props.put("filters", env.getProperty("jdbc.filters"));
}
}
application.yml文件中需要有如下配置:
jdbc:
driverClassName: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://数据库地址:端口号/数据库名称?allowMultiQueries=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull
username: 数据库账号
password: 数据库密码
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x' from dual
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
removeAbandoned: true
removeAbandonedTimeout: 300
poolPreparedStatements: true
maxOpenPreparedStatements: 20
maxPoolPreparedStatementPerConnectionSize: 20
logAbandoned: true
mybatis:
mapperLocations: classpath:mapper/mysql/*.xml
typeAliasesPackage: com.xxx.xxxx.testservice.model
实践过程中,遇到一些问题和解决办法如下:
启动服务时报错:Requested bean is currently in creation: Is there an unresolvable circular reference?
解决方案:springboot 配置文件中添加spring.datasource.initialize=false即可
查询数据时报错:java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
解决方案:pom中引入mysql-connector-java依赖(与plugin中的依赖无关)