(十一)Spring Boot整合Mybatis使用druid实现多数据源自动切换
2017-08-28 10:13
1406 查看
demo源代码下载:点此下载
为了在开发中以最简单的方法使用,本文基于注解和AOP的方法实现,在spring boot框架的项目中,添加本文实现的代码类后,只需要配置好数据源就可以直接通过注解使用,简单方便。
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次插入操作
后续再看一下,多数据源的事务问题。
相关文章推荐
- Spring Boot 整合 Mybatis 实现 Druid 多数据源详解
- Spring Boot 整合mybatis 使用多数据源的实现方法
- Spring Boot 整合 Mybatis 实现 Druid 多数据源详解
- Spring Boot 整合 Mybatis 实现 Druid 多数据源详解
- Springboot整合Mybatis实现Druid多数据源
- Spring Boot 整合 Mybatis 实现 Druid 多数据源
- Spring Boot 整合 Mybatis 实现 Druid 多数据源详解
- Spring Boot 整合 Mybatis 实现 Druid 多数据源详解
- Spring Boot 整合 Mybatis 实现 Druid 多数据源详解
- SpringMVC+ Mybatis 配置多数据源 + 自动数据源切换 + 实现数据库读写分离
- spring-boot | 使员Druid 整合Mybatis 最简配置多数据源
- spring Boot 整合mybatis 如何使用多数据源
- 详解Spring Boot整合Mybatis实现 Druid多数据源配置
- Springboot整合Mybatis分页使用Druid监控SQL日志
- spring boot整合mybatis使用c3p0数据源连接mysql
- SpringBoot学习:整合MyBatis,使用Druid连接池
- SpringBoot + Mybatis plus 实现多数据源整合
- Springboot+Mybatis+Druid 实现多数据源,与事务管理
- SpringBoot整合Mybatis时使用Druid数据库连接池