<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hskw</groupId>
<artifactId>shard</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shard</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.14</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
slave:
datasource:
# 数据源基本配置
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.xxx.xxx:3306/sjk1
type: com.alibaba.druid.pool.DruidDataSource
# 数据源其他配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
master:
datasource:
# 数据源基本配置
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.xxx.xxx:3306/sjk2
type: com.alibaba.druid.pool.DruidDataSource
# 数据源其他配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
package com.luban.sharding.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyDataSource {
String value() default "master";
}
package com.luban.sharding.utils;
public class DbUtil {
public static String master="master";
public static String slave="slave";
private static ThreadLocal<String> threadLocal=new ThreadLocal();
public static void setDb(String db){
threadLocal.set(db);
}
public static String getDb(){
return threadLocal.get();
}
}
package com.luban.sharding.factorybean;
import com.luban.sharding.utils.DbUtil;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
public class DbFactoryBean implements FactoryBean<AbstractRoutingDataSource> {
@Autowired
@Qualifier("masterDataSource")
private DataSource masterDataSource;
@Qualifier("slaveDataSource")
@Autowired
private DataSource slaveDataSource;
@Nullable
@Override
public AbstractRoutingDataSource getObject() throws Exception {
AbstractRoutingDataSource abstractRoutingDataSource=new AbstractRoutingDataSource() {
@Nullable
@Override
protected Object determineCurrentLookupKey() {
System.out.println(DbUtil.getDb());
return DbUtil.getDb();
}
};
abstractRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
Map<Object,Object> map=new HashMap<>();
map.put("master",masterDataSource);
map.put("slave",slaveDataSource);
abstractRoutingDataSource.setTargetDataSources(map);
abstractRoutingDataSource.afterPropertiesSet();
return abstractRoutingDataSource;
}
@Nullable
@Override
public Class<?> getObjectType() {
return AbstractRoutingDataSource.class;
}
}
package com.luban.sharding.config;
import com.luban.sharding.factorybean.DbFactoryBean;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class DbConfig implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata,
BeanDefinitionRegistry beanDefinitionRegistry) {
//注册MyMapperFactoryBean
//为什么不用其他方式注册MyMapperFactoryBean呢 因为这里是代码形式,可以循环,产生多个
BeanDefinitionBuilder beanDefinitionBuilder =
BeanDefinitionBuilder.genericBeanDefinition(DbFactoryBean.class);
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
//注册
beanDefinitionRegistry.registerBeanDefinition("dbFactoryBean",beanDefinition);
}
}
package com.luban.sharding.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.luban.sharding.factorybean.DbFactoryBean;
import com.luban.sharding.utils.DbUtil;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Import(DbConfig.class)
@Configuration
@MapperScan("com.luban.sharding.mapper")
public class DruidConfig {
@ConfigurationProperties(prefix = "master.datasource")
@Bean
public DataSource masterDataSource(){
return new DruidDataSource();
}
/**
* 事物管理器,单纯的读是不用开启事物的,
* 写数据源需要开启事务
* @param dbFactoryBean
* @return
*/
@Bean
public PlatformTransactionManager txManager(DataSource dbFactoryBean) {
return new DataSourceTransactionManager(dbFactoryBean);
}
@ConfigurationProperties(prefix = "slave.datasource")
@Bean
public DataSource slaveDataSource(){
return new DruidDataSource();
}
// @Bean
// public DynamicDataSource dynamicDataSource(){
// DynamicDataSource dynamicDataSource=new DynamicDataSource();
// Map<Object,Object> map=new HashMap<>();
// map.put(DbUtil.master,masterDataSource());
// map.put(DbUtil.slave,slaveDataSource());
// dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
// dynamicDataSource.setTargetDataSources(map);
// return dynamicDataSource;
// }
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dbFactoryBean){
SqlSessionFactoryBean sqlSessionFactory=new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dbFactoryBean);
sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/*.xml"));
return sqlSessionFactory;
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactoryBean sqlSessionFactoryBean) throws Exception {
SqlSessionTemplate sqlSessionTemplate=new SqlSessionTemplate(sqlSessionFactoryBean.getObject());
return sqlSessionTemplate;
}
}
package com.luban.sharding.aop;
import com.luban.sharding.annotation.MyDataSource;
import com.luban.sharding.utils.DbUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
@Aspect
public class DbAop {
@Before(value = "execution(* com.luban.sharding.mapper..*.*(..))")
public void before(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
MyDataSource annotation = method.getAnnotation(MyDataSource.class);
if(annotation==null){
DbUtil.setDb(DbUtil.master);
return;
}
System.out.println(annotation.value()+"---------------");
DbUtil.setDb(annotation.value());
}
}
package com.luban.sharding;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@EnableAspectJAutoProxy(proxyTargetClass = true)
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class ShardingApplication {
public static void main(String[] args) {
SpringApplication.run(ShardingApplication.class, args);
}
}
package com.luban.sharding.mapper;
import com.luban.sharding.annotation.MyDataSource;
import com.luban.sharding.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
@Insert({"insert into user(id,name) values(#{sid},#{sname})"})
Integer insertUser(User user);
@MyDataSource("slave")
@Select("select id as sid,name as sname from user")
List<User> selectUser();
}
到此,想要切换数据源,就在mapper方法上加上自定义注解,指定数据源的值,因为指定的AOP扫描mapper文件。
这是利用FactoryBean的方式,然后import引入。还有一种就是用@Bean的方式,两种的区别就是,@Bean走完整的Spring的生命周期流程,FactoryBean特殊处理,这个得去看Spring源码。
去掉FacoryBean和DbConfig类,然后去掉DruidConfig类上的@Import注解,新建DynamicDataSource类,打开DruidConfig类中
注释的DynamicDataSource这个@Bean。
package com.luban.sharding.config;
import com.luban.sharding.utils.DbUtil;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.lang.Nullable;
public class DynamicDataSource extends AbstractRoutingDataSource{
@Nullable
@Override
protected Object determineCurrentLookupKey() {
System.out.println(DbUtil.getDb());
return DbUtil.getDb();
}
}
org. Apache. ibatis. binding. BindingException:Invalid bound statement (not found)
这个错,首先是看就网上其他博客说的那样,你有没有拼错、不对应、全路径报名等。我的不是,拼错这些除了新手一般不会这样,我的是这么回事,mapper和xml不在同一文件夹下,所以配置文件中指定了mybatis.mapper-locations=classpath:mybatis/mapper/*.xml,但是动态数据源,SpringBoot的自动配置会失效,所以报找不到mapper也就是上面那个错,你得利用Java代码配置,上面有我就不在贴一次代码了。还有就是new PathMatchingResourcePatternResolver()的时候,打了前几个字母,Idea不提示,你不用管,继续敲到最后几个的时候就会提示。