本⽂实现案例场景:
某系统除了需要从⾃⼰的主要数据库上读取和管理数据外,还有⼀部分业务涉及到其他多个数据库,要求可以在任何⽅法上可以灵活指定具体要操作的数据库。
为了在开发中以最简单的⽅法使⽤,本⽂基于注解和AOP的⽅法实现,在spring boot框架的项⽬中,添加本⽂实现的代码类后,只需要配置好数据源就可以直接通过注解使⽤,简单⽅便。⼀配置⼆使⽤
1. 启动类注册动态数据源 2. 配置⽂件中配置多个数据源 3. 在需要的⽅法上使⽤注解指定数据源
1、在启动类添加 @Import({DynamicDataSourceRegister.class, MProxyTransactionManagementConfiguration.class})
@SpringBootApplication
@Import({DynamicDataSourceRegister.class}) // 注册动态多数据源public class SpringBootSampleApplication { // 省略其他代码}
2、配置⽂件配置内容为: (不包括项⽬中的其他配置,这⾥只是数据源相关的)
# 主数据源,默认的
spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/testspring.datasource.username=rootspring.datasource.password=123456
# 更多数据源
custom.datasource.names=ds1,ds2
custom.datasource.ds1.driver-class-name=com.mysql.jdbc.Drivercustom.datasource.ds1.url=jdbc:mysql://localhost:3306/test1custom.datasource.ds1.username=rootcustom.datasource.ds1.password=123456
custom.datasource.ds2.driver-class-name=com.mysql.jdbc.Drivercustom.datasource.ds2.url=jdbc:mysql://localhost:3306/test2custom.datasource.ds2.username=rootcustom.datasource.ds2.password=123456
3、使⽤⽅法
package org.springboot.sample.service;import java.sql.ResultSet;
import java.sql.SQLException;import java.util.List;
import org.springboot.sample.datasource.TargetDataSource;import org.springboot.sample.entity.Student;
import org.springboot.sample.mapper.StudentMapper;
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import org.springframework.stereotype.Service;/**
* Student Service *
* @author 单红宇(365384722)
* @myblog http://blog.csdn.net/catoop/ * @create 2016年1⽉12⽇ */
@Service
public class StudentService {
@Autowired
private JdbcTemplate jdbcTemplate; // MyBatis的Mapper⽅法定义接⼝ @Autowired
private StudentMapper studentMapper; @TargetDataSource(name=\"ds2\")
public List public List /** * 不指定数据源使⽤默认数据源 * * @return * @author SHANHY * @create 2016年1⽉24⽇ */ public List String sql = \"SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT\"; return (List 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; } }); } /** * 指定数据源 * * @return * @author SHANHY * @create 2016年1⽉24⽇ */ @TargetDataSource(name=\"ds1\") public List String sql = \"SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT\"; return (List 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; } }); } /** * 指定数据源 * * @return * @author SHANHY * @create 2016年1⽉24⽇ */ @TargetDataSource(name=\"ds2\") public List String sql = \"SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE FROM STUDENT\"; return (List 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为接⼝,代码如下: package org.springboot.sample.mapper;import java.util.List; import org.springboot.sample.entity.Student; /** * StudentMapper,映射SQL语句的接⼝,⽆逻辑实现 * * @author 单红宇(365384722) * @myblog http://blog.csdn.net/catoop/ * @create 2016年1⽉20⽇ */ public interface StudentMapper { // 注解 @TargetDataSource 不可以在这⾥使⽤ List 请将下⾯⼏个类放到Spring Boot项⽬中。 DynamicDataSource.Java DynamicDataSourceAspect.java DynamicDataSourceContextHolder.java DynamicDataSourceRegister.java TargetDataSource.java package org.springboot.sample.datasource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/** * 动态数据源 * * @author 单红宇(365384722) * @create 2016年1⽉22⽇ */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); }} package org.springboot.sample.datasource;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;/** * 切换数据源Advice * * @author 单红宇(365384722) * @create 2016年1⽉23⽇ */ @Aspect @Order(-1)// 保证该AOP在@Transactional之前执⾏@Component public 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(\"数据源[{}]不存在,使⽤默认数据源 > {}\ } else { logger.debug(\"Use DataSource : {} > {}\ DynamicDataSourceContextHolder.setDataSourceType(ds.name()); } } @After(\"@annotation(ds)\") public void restoreDataSource(JoinPoint point, TargetDataSource ds) { logger.debug(\"Revert DataSource : {} > {}\ DynamicDataSourceContextHolder.clearDataSourceType(); }} package org.springboot.sample.datasource;import java.util.ArrayList;import java.util.List; public class DynamicDataSourceContextHolder { private static final ThreadLocal public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } /** * 判断指定DataSrouce当前是否存在 * * @param dataSourceId * @return * @author SHANHY * @create 2016年1⽉24⽇ */ public static boolean containsDataSource(String dataSourceId){ return dataSourceIds.contains(dataSourceId); }} package org.springboot.sample.datasource;import java.util.HashMap;import java.util.Map;import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.MutablePropertyValues;import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.beans.factory.support.GenericBeanDefinition;import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;import org.springframework.boot.bind.RelaxedDataBinder; import org.springframework.boot.bind.RelaxedPropertyResolver;import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService;import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotationMetadata; /** * 动态数据源注册 * 启动动态数据源请在启动类中(如SpringBootSampleApplication) * 添加 @Import(DynamicDataSourceRegister.class) * * @author 单红宇(365384722) * @create 2016年1⽉24⽇ */ 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 Map @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { Map
因篇幅问题不能全部显示,请点此查看更多更全内容