Ruoyi implements dynamic data source switching from the database

One of the requirements of the current project is that the data can be dynamically queried, the data source can be added in the front-end page configuration, and then the data source can be bound to the business. When querying data, the configuration in the yml configuration file is not fixed. The database configuration is based on the connection based on the data source stored in the database table bound to the business.

 Although Ruoyi has encapsulated data source switching and specified the data source through AOP, Ruoyi's original method can only switch the existing data source in the configuration file, which means that if I want to add a new The data source needs to be added to the configuration file, and the same goes for modifications, which is obviously very troublesome. Doesn't meet our needs.

 

/**
 * 自定义多数据源切换注解
 *
 * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
 *
 * @author ruoyi
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{
    /**
     * 切换数据源名称
     */
    public DataSourceType value() default DataSourceType.MASTER;
}

Then my idea is to add a layer of processing based on Ruoyi's original one, comment out the surrounding notification originally written by Ruoyi, and use @Before() pre-notification for processing

 Add a new enumeration OTHER to the data source enumeration to identify the switching data source.

/**
 * 数据源
 * 
 * @author ruoyi
 */
public enum DataSourceType
{
    /**
     * 主库
     */
    MASTER,

    /**
     * 从库
     */
    SLAVE,

    /**
     * 其他
     */
    OTHER
}

 The specified type is judged in the aspect pre-notification. If it is master, the default data source is used, slave uses the slave library configured in the configuration file, and other means using the data source existing in the database table, and then based on the request The parameters of the method obtain the business ID and then go to the database to query the data source information bound to it, and then set it as the current data source. After use, the data source is cleared through the post notification @After.

/**
 * 多数据源处理
 * 
 * @author ruoyi
 */
@Log4j2
@Aspect
@Order(1)
@Component
public class DataSourceAspect
{
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Resource
    private ISysDataSourceService sysDataSourceService;
    @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"
            + "|| @within(com.ruoyi.common.annotation.DataSource)")
    public void dsPointCut()
    {

    }

    @Before("dsPointCut()")
    public void doBefore(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        logger.info("方法名称: " + method.getName());
        DataSource dataSource = method.getAnnotation(DataSource.class);
        DruidConfig dynamicDataSourceConfig = SpringContextUtils.getBean(DruidConfig.class);
        Map<Object, Object> map = dynamicDataSourceConfig.getTargetDataSources();
        log.info("TargetDataSources==="+map.toString());

        // 通过判断 DataSource 中的值来判断当前方法应用哪个数据源
        String value = String.valueOf(dataSource.value());
        boolean flag;
        String name = "";
        switch (value) {
            case "MASTER":
                flag = true;
                name = DataSourceType.MASTER.name();
                break;
            case "SLAVE":
                flag = true;
                name = DataSourceType.SLAVE.name();
                break;
            case "OTHER":
                flag = true;
               
                name = "OTHER";
                if (map.get(name) != null) {
                    break;
                } else {
                    //获取传入参数
                    Object[] args = joinPoint.getArgs();
                    JSONObject json = (JSONObject) args[0];
                    Long dataSourceId = json.getLong("dataSourceId");
                    log.info("获取的数据源ID=="+dataSourceId);
                    //从传入参数获取业务ID查找数据源信息,设置数据源信息
                    SysDataSource source = sysDataSourceService.getById(dataSourceId);
                    String url = source.getDbUrl();
                    String username = source.getDbUsername();
                    //密码解密
                    String password = SecurityUtil.jiemi(source.getDbPwd());
                    log.info("解密后密码=="+password);
                    log.info("数据源切换:" + url);

                    DruidDataSource s = dynamicDataSourceConfig.createDataSource(name, url, username,
                            password, source.getDbDriver());
                }
                break;
            default:
                flag = false;
                break;
        }

        if (!flag) {
            logger.error("************注意************");
            name = DataSourceType.MASTER.name();
            logger.info("加载到未知数据源,系统自动设置数据源为默认数据源!");
        }
        DynamicDataSource.setDataSource(name);
        //设置成数据源
        DynamicDataSourceContextHolder.setDataSourceType(name);
        logger.info("当前数据源: " + name);
        logger.info("当前数据源: " + ((DruidDataSource) map.get(name)).getUrl());
        logger.info("set datasource is " + name);
    }


    @After("dsPointCut()")
    public void doAfter() {
        logger.info("*********准备清除数据源*********");
        DynamicDataSource.clearDataSource();
        DynamicDataSourceContextHolder.clearDataSourceType();
        logger.info("*********数据源清除成功*********");
    }

It should be noted that the newly added enumeration type is added to the druidConfig configuration file.

 When you need to switch data sources, just add annotations to the class or method

@DataSource(value = DataSourceType.OTHER)

Guess you like

Origin blog.csdn.net/weixin_53160419/article/details/131373123