通过注解和AOP,实现主从数据源的切换。
首先项目布局:
@Data
@Builder
public class UserBean {
private Long id;
private String name;
private String password;
}
@Repository
public interface UserDao {
//新增
int insertUser(UserBean userBean);
//查询
List
}
dao接口
<!-- 查询全部用户 -->
<select id="getAllUser" resultType="com.wht.springdynamicdatasource.entity.UserBean">
select \* from users
</select>
mapper.xml
public interface UserService {
int insertUser(UserBean user);
List<UserBean> getAllUser();
}
service接口
@Service
@Transactional
// @DataSource("SLAVE_DATASOURCE")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
// @DataSource("MASTER\_DATASOURCE")
public int insertUser(UserBean user) {
return userDao.insertUser(user);
}
@Override
@DataSource("SLAVE\_DATASOURCE")
public List<UserBean> getAllUser() {
return userDao.getAllUser();
}
}
service实现类
@Target(value = {ElementType.METHOD,ElementType.FIELD,ElementType.PARAMETER,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value() default "MASTER-DATASOURCE";
}
@Aspect
@Order(1)
@Component
public class DataSourceAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Pointcut(value = "execution(\* com.wht.springdynamicdatasource.service.\*.\*(..))")
public void dataPointCut(){
};
@Before("dataPointCut()")
public void before(JoinPoint joinPoint){
Class<?> aClass = joinPoint.getTarget().getClass();
// 获取类级别注解
DataSource classAnnotation = aClass.getAnnotation(DataSource.class);
if (classAnnotation != null){
String dataSource = classAnnotation.value();
logger.info("this is datasource: "+ dataSource);
DynamicDataSource.putDataSourceKey(dataSource);
}else {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
DataSource methodAnnotation = method.getAnnotation(DataSource.class);
if (methodAnnotation != null){
String dataSource = methodAnnotation.value();
logger.info("this is dataSource: "+ dataSource);
DynamicDataSource.putDataSourceKey(dataSource);
}
}
}
@After("dataPointCut()")
public void after(JoinPoint joinPoint){
logger.info("执行完毕!");
DynamicDataSource.removeDataSourceKey();
}
}
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);
//数据源key值
private static final ThreadLocal
//设置数据源
public static void putDataSourceKey(String key){
log.info("切换数据源到{}", key);
holder.set(key);
}
//获取数据源
public static String getDataSourceKey(){
return holder.get();
}
//移除
public static void removeDataSourceKey(){
holder.remove();
}
// 该方法决定使用哪个数据源
@Override
protected Object determineCurrentLookupKey() {
Object o = getDataSourceKey();
log.info("当前数据源为:===", getDataSourceKey());
return getDataSourceKey();
}
}
@Getter
public enum DataSourceName {
MASTER("MASTER\_DATASOURCE", 1),
SLAVE("SLAVE\_DATASOURCE", 2);
private String name;
private int index;
DataSourceName(String name, int index) {
this.name = name;
this.index = index;
}
}
/**
* @createtime 2019/9/3
* @description 根据配置文件生成多个DataSource实例,将其注入自定义DynamicDataSource中,
* 该类继承了AbstractRoutingDataSource,进行路由管理。由sqlSessionFactory扫描mybatis相关配置,
* 生成对应工厂实例。
*/
@Configuration
@EnableTransactionManagement
//指定dao的扫描位置和sqlSessionFactoryRef的引用实例。
@MapperScan(basePackages = {"com.wht.springdynamicdatasource.dao"}, sqlSessionFactoryRef = "sqlSessionFactory")
public class DataSourceConfig {
//yml中配置的mapper扫描路径
@Value("${mybatis.mapperLocations}")
private String mapperLocations;
//yml中配置的mybatis配置文件位置
@Value("${mybatis.configLocation}")
private String configLocation;
/\*\*
\* step1:通过指定配置文件前缀,生成主、从两个DataSource实例
\* @return
\*/
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource(){
DruidDataSource masterDataSource = DataSourceBuilder.create().type(DruidDataSource.class).build();
masterDataSource.setName("masterDataSource");
return masterDataSource;
}
//slave数据源实例
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource(){
DruidDataSource slaveDataSource = DataSourceBuilder.create().type(DruidDataSource.class).build();
slaveDataSource.setName("slaveDataSource");
return slaveDataSource;
}
/\*\*
\* step: 通过DynamicDataSource继承Spring提供的数据库路由管理
\* 接口AbstractRoutingDataSource,将多个数据源实例注入其中。
\* @return
\*/
@Bean(name = "dataSource")
public DataSource dataSource(){
DynamicDataSource dynamicDataSource = new DynamicDataSource();
//默认数据源
dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
//多数据源
Map<Object,Object> dataSourceMaps = new HashMap<>(2);
//构建数据源映射map
dataSourceMaps.put(DataSourceName.MASTER.getName(), masterDataSource());
dataSourceMaps.put(DataSourceName.SLAVE.getName(), slaveDataSource());
dynamicDataSource.setTargetDataSources(dataSourceMaps);
return dynamicDataSource;
}
/\*\*
\* step3:通过上一步的自定义多数据源路由Bean,指定mybatis配置文件位置和mapper.xml的位置。
\* 通过SqlSessionFactoryBean,生成SqlSessionFactory;
\* @param dynamicDataSource
\* @return
\* @throws Exception
\*/
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource);
//扫描mapper配置
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
//扫描mybatis配置文件
sqlSessionFactoryBean.setConfigLocation(new ClassPathResource(configLocation));
return sqlSessionFactoryBean.getObject();
}
/\*\*
\* step4:通过上一步的SqlSessionFactory,生成线程安全的SqlSessionTemplate,
\* 该Bean为mybatis管理数据库连接的最核心类;
\* @param sqlSessionFactory
\* @return
\* @throws Exception
\*/
@Bean
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
/\* 使用上面配置的Factory,SqlSessionTemplate是线程安全的,是mybatis的管理数据库的最主要的一个类\*/
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory);
return template;
}
/\*\*
\* 事务管理PlatformTransactionManager是Spring提供的事务管理的顶层抽象,
\* 通过手动配置,来代替默认的自动拾物配置,
\* 这样就可以在方法中直接使用注解@Transactional时,指定事务管理的数据源。
\* @param dataSource
\* @return
\*/
@Bean
public PlatformTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class SpringDynamicDatasourceApplication {
public static void main(String\[\] args) {
SpringApplication.run(SpringDynamicDatasourceApplication.class, args);
}
}
mybatis的配置文件
spring的启动配置文件
#logging日志配置
logging:
level:
root: info
org:
springframework:
web: DEBUG
##指向mapper的xml文件位置
mybatis:
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: mybatis/mapper/*.xml
# 加载全局的配置文件
configLocation: mybatis/mybatis-config.xml
spring:
datasource:
## master 数据源配置
master:
url: jdbc:mysql://localhost:3306/mytest?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8
username: root
password: ????
driverClassName: com.mysql.cj.jdbc.Driver
## cluster 数据源配置
slave:
url: jdbc:mysql://localhost:3306/mytest1?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8
username: root
password: ????
driverClassName: com.mysql.cj.jdbc.Driver
手机扫一扫
移动阅读更方便
你可能感兴趣的文章