(十一)Spring Boot整合Mybatis使用druid实现多数据源自动切换
druid
阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池
项目地址:https://gitcode.com/gh_mirrors/druid/druid
免费下载资源
·
demo源代码下载:点此下载
1.启动类注册动态数据源
2.需要切换数据源的方法上添加注解
3.在一个事务中切换数据源是无效的
我的测试方法:测试两个数据源,在数据源1中 执行插入操作, 然后操作数据源2再次进行插入操作, 操作数据源中的表结构 和sql 都是相同的,然后数据源1 执行了2次插入操作
后续再看一下,多数据源的事务问题。
为了在开发中以最简单的方法使用,本文基于注解和AOP的方法实现,在spring boot框架的项目中,添加本文实现的代码类后,只需要配置好数据源就可以直接通过注解使用,简单方便。
配置过程
添加aop依赖
<!-- Spring AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
application.properties
jdbc.datasources=ds,ds1
jdbc.ds.driverClassName=com.mysql.jdbc.Driver
jdbc.ds.url=jdbc\:mysql\://localhost\:3306/ryx?characterEncoding\=UTF-8
jdbc.ds.username=root
jdbc.ds.password=123456
jdbc.ds1.driverClassName=com.mysql.jdbc.Driver
jdbc.ds1.url=jdbc\:mysql\://172.20.1.121\:3306/ryx?characterEncoding\=UTF-8
jdbc.ds1.username=root
jdbc.ds1.password=root
#druid监控
druid.username=root
druid.password=Ruyixing2017
SpringMVC.java
@Configuration
public class SpringMVC extends WebMvcConfigurerAdapter {
@Value("${druid.username}")
private String druidUsername;
@Value("${druid.password}")
private String druidPassword;
@Bean
public HttpMessageConverter<String> responseBodyConverter() {
StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
return converter;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
converters.add(responseBodyConverter());
}
//资源映射
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/mystatic/**")
.addResourceLocations("classpath:/mystatic/");
}
/**
* druid监控
* @return
*/
@Bean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
reg.addInitParameter("loginUsername", druidUsername);
reg.addInitParameter("loginPassword", druidPassword);
return reg;
}
/**
* druid监控过滤
* @return
*/
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}
DynamicDataSource.java
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
@Override
protected Object determineCurrentLookupKey() {
return getDataSourceType();
}
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
}
DataSourceAspect.java
@Aspect
@Order(-1)//在@Transactional事务标注的方法内切换数据源需要设置
@Component
public class DataSourceAspect {
private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);
@Before("@annotation(dataTest)")
public void setDataSourceKey(JoinPoint point,TargetDataSource dataTest) throws Throwable{
//根据连接点所属的类实例,动态切换数据源
logger.info("切换数据源为:{}",dataTest.value());
DynamicDataSource.setDataSourceType(dataTest.value());
}
@After("@annotation(dataTest)")
public void clearDataSourceType(JoinPoint point,TargetDataSource dataTest) {
DynamicDataSource.clearDataSourceType();
}
}
DynamicDataSourceRegister.java
/**
* 动态数据源注册
* @author xiyan
*/
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
// 数据源配置信息
private PropertyValues dataSourcePropertyValues;
// 默认数据源
private DataSource defaultDataSource;
// 动态数据源
private Map<String, DataSource> dynamicDataSources = new HashMap<>();
/**
* 加载多数据源配置
* Environment 是用来读取application.properties中的内容
*/
@Override
public void setEnvironment(Environment env) {
//读取application.properties中以jdbc.开头的属性
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "jdbc.");
String dsPrefixs = propertyResolver.getProperty("datasources");
for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
Map<String, Object> map = propertyResolver.getSubProperties(dsPrefix + ".");
DataSource ds = initDataSource(map);
// 设置默认数据源
if ("ds".equals(dsPrefix)) {
defaultDataSource = ds;
} else {
dynamicDataSources.put(dsPrefix, ds);
}
dataBinder(ds, env);
}
}
/**
* 初始化数据源
* @param map
* @return
*/
public DataSource initDataSource(Map<String, Object> map) {
String driverClassName = map.get("driverClassName").toString();
String url = map.get("url").toString();
String username = map.get("username").toString();
String password = map.get("password").toString();
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
//配置最大连接
dataSource.setMaxActive(300);
//配置初始连接
dataSource.setInitialSize(20);
//配置最小连接
dataSource.setMinIdle(10);
//连接等待超时时间
dataSource.setMaxWait(60000);
//间隔多久进行检测,关闭空闲连接
dataSource.setTimeBetweenEvictionRunsMillis(60000);
//一个连接最小生存时间
dataSource.setMinEvictableIdleTimeMillis(300000);
//连接等待超时时间 单位为毫秒 缺省启用公平锁,
//并发效率会有所下降, 如果需要可以通过配置useUnfairLock属性为true使用非公平锁
dataSource.setUseUnfairLock(true);
//用来检测是否有效的sql
dataSource.setValidationQuery("select 'x'");
dataSource.setTestWhileIdle(true);
//申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能
dataSource.setTestOnBorrow(false);
//归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能
dataSource.setTestOnReturn(false);
//打开PSCache,并指定每个连接的PSCache大小启用poolPreparedStatements后,
//PreparedStatements 和CallableStatements 都会被缓存起来复用,
//即相同逻辑的SQL可以复用一个游标,这样可以减少创建游标的数量。
dataSource.setPoolPreparedStatements(true);
dataSource.setMaxOpenPreparedStatements(20);
try {
//配置sql监控的filter
dataSource.setFilters("stat,wall,log4j");
dataSource.init();
} catch (SQLException e) {
throw new RuntimeException("druid datasource init fail");
}
/*String dsType = map.get("dsType").toString();
Class<DataSource> dataSourceType;
DataSource dataSource = null;
try {
dataSourceType = (Class<DataSource>) Class.forName(dsType);
dataSource = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
.username(username).password(password).type(dataSourceType).build();;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}*/
return dataSource;
}
/**
* 加载数据源配置信息
* @param dataSource
* @param env
*/
private void dataBinder(DataSource dataSource, Environment env) {
RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
dataBinder.setIgnoreNestedProperties(false);// false
dataBinder.setIgnoreInvalidFields(false);// false
dataBinder.setIgnoreUnknownFields(true);// true
if (dataSourcePropertyValues == null) {
Map<String, Object> values = new RelaxedPropertyResolver(env, "datasource").getSubProperties(".");
dataSourcePropertyValues = new MutablePropertyValues(values);
}
dataBinder.bind(dataSourcePropertyValues);
}
/**
* 注册数据源been
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
// 将主数据源添加到更多数据源中
targetDataSources.put("dataSource", defaultDataSource);
// 添加更多数据源
targetDataSources.putAll(dynamicDataSources);
// 创建DynamicDataSource
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DynamicDataSource.class);
beanDefinition.setSynthetic(true);
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
mpv.addPropertyValue("targetDataSources", targetDataSources);
registry.registerBeanDefinition("dataSource", beanDefinition);
logger.info("多数据源注册成功");
}
}
TargetDataSource.java
/**
* 在方法上使用,用于指定使用哪个数据源
* @author xiyan
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value();
}
HelloApplication.java
@SpringBootApplication
@PropertySource(value={"classpath:config/path.properties"},ignoreResourceNotFound=true,encoding="utf-8")
@Import(DynamicDataSourceRegister.class)
public class HelloApplication {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
}
StudentServiceImpl.java
//切换为第二个数据库
@TargetDataSource("ds1")
public Student getStudentById(Integer id) {
return studentMapper.getStudentById(id);
}
注意:
1.启动类注册动态数据源
2.需要切换数据源的方法上添加注解
3.在一个事务中切换数据源是无效的
我的测试方法:测试两个数据源,在数据源1中 执行插入操作, 然后操作数据源2再次进行插入操作, 操作数据源中的表结构 和sql 都是相同的,然后数据源1 执行了2次插入操作
后续再看一下,多数据源的事务问题。
GitHub 加速计划 / druid / druid
27.83 K
8.56 K
下载
阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池
最近提交(Master分支:3 个月前 )
f060c270 - 8 天前
1613a765
* Improve gaussdb ddl parser
* fix temp table 10 天前
更多推荐
已为社区贡献8条内容
所有评论(0)