1、在启动类添加注解,注册动态多数据源:
@SpringBootApplication@Import({DynamicDataSourceRegister.class}) // 注册动态多数据源public class OrangeplusApplication extends WebMvcConfigurerAdapter { protected final static Logger logger = LoggerFactory.getLogger(OrangeplusApplication.class); public static void main(String[] args) { SpringApplication.run(OrangeplusApplication.class, args); logger.info("Application is success!"); }}
2、yml配置文件中数据源配置内容为:
spring: profiles: dev datasource: url: jdbc:mysql://127.0.0.1:3306/orangeplus?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver# 更多数据源custom: profiles: dev datasource: names: ds1,ds2 ds1: driver-class-name: oracle.jdbc.driver.OracleDriver url: jdbc:oracle:thin:@localhost:1521:oracle username: root password: orcl#ncu ds2: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/orangeplus2?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull username: root password: 123456
3、在需要的方法上使用注解指定数据源,简单代码示例:
@Servicepublic class StudentService { @Autowired private JdbcTemplate jdbcTemplate; // MyBatis的Mapper方法定义接口 @Autowired private StudentMapper studentMapper; @TargetDataSource(name="ds2") public ListlikeName(String name){ return studentMapper.likeName(name); } public List likeNameByDefaultDataSource(String name){ return studentMapper.likeName(name); } /** * 不指定数据源使用默认数据源 */ public List getList(){ String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT"; return (List ) jdbcTemplate.query(sql, new RowMapper (){ @Override public Student mapRow(ResultSet rs, int rowNum) throws SQLException { Student stu = new Student(); stu.setId(rs.getInt("ID")); stu.setAge(rs.getInt("AGE")); stu.setName(rs.getString("NAME")); stu.setSumScore(rs.getString("SCORE_SUM")); stu.setAvgScore(rs.getString("SCORE_AVG")); return stu; } }); } /** * 指定数据源 */ @TargetDataSource(name="ds1") public List getListByDs1(){ String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT"; return (List ) jdbcTemplate.query(sql, new RowMapper (){ @Override public Student mapRow(ResultSet rs, int rowNum) throws SQLException { Student stu = new Student(); stu.setId(rs.getInt("ID")); stu.setAge(rs.getInt("AGE")); stu.setName(rs.getString("NAME")); stu.setSumScore(rs.getString("SCORE_SUM")); stu.setAvgScore(rs.getString("SCORE_AVG")); return stu; } }); } /** * 指定数据源 */ @TargetDataSource(name="ds2") public List getListByDs2(){ String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT"; return (List ) jdbcTemplate.query(sql, new RowMapper (){ @Override public Student mapRow(ResultSet rs, int rowNum) throws SQLException { Student stu = new Student(); stu.setId(rs.getInt("ID")); stu.setAge(rs.getInt("AGE")); stu.setName(rs.getString("NAME")); stu.setSumScore(rs.getString("SCORE_SUM")); stu.setAvgScore(rs.getString("SCORE_AVG")); return stu; } }); }}
要注意的是,在使用MyBatis时,注解@TargetDataSource 不能直接在接口类Mapper上使用。
按上面的代码中StudentMapper为接口,实例代码如下:public interface StudentMapper { // 多数据源注解 @TargetDataSource 不可以在这里使用 ListlikeName(String name); Student getById(int id); String getNameById(int id);}
请将下面几个类放到Spring Boot项目中:
DynamicDataSource.javapublic class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); }}
DynamicDataSourceAspect.java
@Aspect@Order(-1)// 保证该AOP在@Transactional之前执行@Componentpublic class DynamicDataSourceAspect { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class); @Before("@annotation(ds)") public void changeDataSource(JoinPoint point, TargetDataSource ds) throws Throwable { String dsId = ds.name(); if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) { logger.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature()); } else { logger.debug("Use DataSource : {} > {}", ds.name(), point.getSignature()); DynamicDataSourceContextHolder.setDataSourceType(ds.name()); } } @After("@annotation(ds)") public void restoreDataSource(JoinPoint point, TargetDataSource ds) { logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature()); DynamicDataSourceContextHolder.clearDataSourceType(); }}
DynamicDataSourceContextHolder.java
public class DynamicDataSourceContextHolder { private static final ThreadLocalcontextHolder = new ThreadLocal (); public static List dataSourceIds = new ArrayList<>(); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } /** * 判断指定DataSrouce当前是否存在 * * @param dataSourceId */ public static boolean containsDataSource(String dataSourceId){ return dataSourceIds.contains(dataSourceId); }}
DynamicDataSourceRegister.java
/** * 动态数据源注册 * 启动动态数据源请在启动类中(如SpringBootSampleApplication) * 添加 @Import(DynamicDataSourceRegister.class) * */public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class); private ConversionService conversionService = new DefaultConversionService(); private PropertyValues dataSourcePropertyValues; // 如配置文件中未指定数据源类型,使用该默认值 private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource"; // private static final Object DATASOURCE_TYPE_DEFAULT = // "com.zaxxer.hikari.HikariDataSource"; // 数据源 private DataSource defaultDataSource; private MapcustomDataSources = new HashMap<>(); @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { Map
TargetDataSource.java
/** * 在方法上使用,用于指定使用哪个数据源 * */@Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface TargetDataSource { String name();}