SpringBoot笔记:SpringBoot 集成 Dataway 多数据源配置(二)

文章目录

前言

之前简单介绍了一下 Dataway 使用,本文继续介绍一下它的多数据源配置和使用。

核心代码和配置

yml 配置

yml 复制代码
# springboot多环境配置
#端口,项目上下文
server:
  port: 8080
  servlet:
    context-path: /springboot-dataway
    # 全局服务编码设置
    encoding:
      charset: utf-8
      enabled: true
      force: true


# mysql 连接信息配置
spring:
  # mysql 数据库连接信息,本地使用 mysql 服务版本为:8.0.28
  datasource:
    username: root
    password: 6tojyh*A3eQ6
    url: jdbc:mysql://localhost:3306/dataway?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    # druid 数据连接池配置
    druid:
      initial-size: 3
      min-idle: 3
      max-active: 10
      max-wait: 6000
      # 配置druid监控页
      aop-patterns: com.demo.* #监控springBean
      stat-view-servlet: # 配置监控页功能
        enabled: true # 默认开启,这里显示说明
        login-username: admin # 登录名
        login-password: 6tojyh*A3eQ6 # 登录密码
        reset-enable: false # 禁用重置按钮
      web-stat-filter: # 监控 web
        enabled: true
        url-pattern: /* # 监控所有
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'  #放行
      filter:
        stat: # 对上面 filters 里的 stat 的详细配置
          slow-sql-millis: 1000 # 慢 sql 时间是毫秒单位的,执行时间 1 秒以上的为慢 SQL
          log-slow-sql: true # 日志记录
          enabled: true
        wall:
          enabled: true
          config:
            drop-table-allow: false # 禁用删除表的 sql
        # 除主数据源外,第1个数据源
    db1:
      username: root
      password: 6tojyh*A3eQ6
      url: jdbc:mysql://localhost:3306/console?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
      driver-class-name: com.mysql.cj.jdbc.Driver
    # 除主数据源外,第 2 个数据源
    db2:
      username: postgres
      password: UFWOd75qD7
      url: jdbc:postgresql://localhost:32770/postgres?binaryTransfer=false&forceBinary=false&reWriteBatchedInserts=true
      driver-class-name: org.postgresql.Driver

# mdataway 配置
# 是否启用 Dataway 功能(必选:默认false)
HASOR_DATAQL_DATAWAY: true
# 开启 ui 管理功能(注意生产环境必须要设置为 false,否则会造成严重的生产安全事故)
HASOR_DATAQL_DATAWAY_ADMIN: true
# dataway  API工作路径(可选,默认:/api/)
HASOR_DATAQL_DATAWAY_API_URL: /api/
# dataway-ui 的工作路径(可选,默认:/interface-ui/)
HASOR_DATAQL_DATAWAY_UI_URL: /interface-ui/
# SQL执行器方言设置(可选,建议设置)
HASOR_DATAQL_FX_PAGE_DIALECT: mysql
# 登陆认证方式在 basic 模式下的时候,配置的登陆账号
HASOR_DATAQL_DATAWAY_AUTHORIZATION_USERNAME: admin
# 登陆认证方式在 basic 模式下的时候,配置的登陆密码,默认密码admin
HASOR_DATAQL_DATAWAY_AUTHORIZATION_PASSWORD: 6tojyh*A3eQ6

# 日志输出配置
logging:
  level:
    root: debug
    org:
      springframework:
        security: WARN
        web: ERROR
    # 设置自己的 com.demo.mapper 目录 输出sql日志
    com.demo.mapper: debug
  file:
    path: ./logs
    name: './logs/springboot-dataway.log'
  pattern:
    file: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n'
    console: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n'

注入多数据源

java 复制代码
package com.demo.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.demo.chain.*;
import net.hasor.core.ApiBinder;
import net.hasor.core.DimModule;
import net.hasor.core.TypeSupplier;
import net.hasor.dataql.Finder;
import net.hasor.dataql.QueryApiBinder;
import net.hasor.dataql.fx.db.FxSqlCheckChainSpi;
import net.hasor.dataway.spi.LoginPerformChainSpi;
import net.hasor.dataway.spi.LoginTokenChainSpi;
import net.hasor.dataway.spi.PreExecuteChainSpi;
import net.hasor.dataway.spi.ResultProcessChainSpi;
import net.hasor.db.JdbcModule;
import net.hasor.db.Level;
import net.hasor.spring.SpringModule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;


/**
 * @Classname DatawayModule
 * @Description 将Hasor模块注入spring,并注入数据源
 * @Date 2023/7/28 11:43
 * @Created by Leo825
 */
@DimModule  // Hasor 中的标签,表明是一个Hasor的model
@Component  // Spring 中的标签,表明是一个组件
public class DatawayModule implements SpringModule,TypeSupplier {
    // 默认数据源
    @Autowired
    private DataSource dataSource;
    // 数据源1的配置信息
    @Value("${spring.datasource.db1.url}")
    private String jdbcUrl1;
    @Value("${spring.datasource.db1.driver-class-name}")
    private String driver1;
    @Value("${spring.datasource.db1.username}")
    private String username1;
    @Value("${spring.datasource.db1.password}")
    private String password1;

    // 数据源2的配置信息
    @Value("${spring.datasource.db2.url}")
    private String jdbcUrl2;
    @Value("${spring.datasource.db2.driver-class-name}")
    private String driver2;
    @Value("${spring.datasource.db2.username}")
    private String username2;
    @Value("${spring.datasource.db2.password}")
    private String password2;

    @Resource
    private ApplicationContext applicationContext;

    @Override
    public <T> T get(Class<? extends T> targetType) {
        return applicationContext.getBean(targetType);
    }

    @Override
    public <T> boolean test(Class<? extends T> targetType) {
        return applicationContext.getBeanNamesForType(targetType).length > 0;
    }

    @Override
    public void loadModule(ApiBinder apiBinder) throws Throwable {
        // .DataSource form Spring boot into Hasor
        apiBinder.installModule(new JdbcModule(Level.Full, this.dataSource));
        // 注入数据源
        apiBinder.installModule(new JdbcModule(Level.Full, this.dataSource));
        apiBinder.installModule(new JdbcModule(Level.Full, "dataSource1", getDataSource(jdbcUrl1, driver1, username1, password1)));
        apiBinder.installModule(new JdbcModule(Level.Full, "dataSource2", getDataSource(jdbcUrl2, driver2, username2, password2)));

        // 打印sql日志
        apiBinder.bindSpiListener(FxSqlCheckChainSpi.class, FxSqlCheckChain.getInstance());
        // 数据权限参数
        apiBinder.bindSpiListener(PreExecuteChainSpi.class, PreExecuteChain.getInstance());
        // 返回结果
        apiBinder.bindSpiListener(ResultProcessChainSpi.class, ResultProcessChain.getInstance());
//        // 登录过滤
//        apiBinder.bindSpiListener(LoginPerformChainSpi.class, LoginPerformChain.getInstance());
        // 登录 token 过滤
//        apiBinder.bindSpiListener(LoginTokenChainSpi.class, LoginTokenChain.getInstance());

        // udf/udfSource/import 指令 的类型创建委托给 spring
        QueryApiBinder queryBinder = apiBinder.tryCast(QueryApiBinder.class);
        queryBinder.bindFinder(Finder.TYPE_SUPPLIER.apply(this));
    }


    /**
     * 注入数据源
     *
     * @param
     * @param driver
     * @param username
     * @param password
     * @return
     */
    private DruidDataSource getDataSource(String jdbcUrl, String driver, String username, String password) {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(jdbcUrl);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(driver);
        // 用来检测连接是否有效
        dataSource.setValidationQuery("SELECT 1");
        // 借用连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
        dataSource.setTestOnBorrow(false);
        // 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
        dataSource.setTestOnReturn(false);
        // 连接空闲时检测,如果连接空闲时间大于timeBetweenEvictionRunsMillis指定的毫秒,
        // 执行validationQuery指定的SQL来检测连接是否有效
        // 如果检测失败,则连接将被从池中去除
        dataSource.setTestWhileIdle(true);
        dataSource.setTimeBetweenEvictionRunsMillis(60000);//1分钟
        dataSource.setMaxActive(20);
        dataSource.setInitialSize(5);
        return dataSource;
    }
}

常用Spi实现

FxSqlCheckChainSpi,这里主要用来打印 sql

java 复制代码
package com.demo.chain;

import lombok.extern.slf4j.Slf4j;
import net.hasor.dataql.fx.db.FxSqlCheckChainSpi;
import net.hasor.utils.StringUtils;

/**
 * @Classname FxSqlCheckChain
 * @Description 打印sql
 * @Date 2023/8/3 20:35
 * @Created by Leo825
 */
@Slf4j
public class FxSqlCheckChain implements FxSqlCheckChainSpi {

    public static FxSqlCheckChain getInstance() {
        return new FxSqlCheckChain();
    }

    @Override
    public int doCheck(FxSqlInfo fxSqlInfo) throws Throwable {
        String sourceName = fxSqlInfo.getSourceName();
        if (StringUtils.isNotEmpty(sourceName)) {
            log.info("【dataway】dataSource ==>:{}", sourceName);
        }
        log.info("【dataway】sql ==>:{}", fxSqlInfo.getQueryString().trim());
        log.info("【dataway】params ==>: {}", fxSqlInfo.getQueryParams());
        // 如果存在后续,那么继续执行检查,否则使用 EXIT 常量控制退出后续的检查。
        return FxSqlCheckChainSpi.NEXT;
    }
}

PreExecuteChainSpi,这里主要是在接口执行前进行一些权限参数控制

java 复制代码
package com.demo.chain;

import lombok.extern.slf4j.Slf4j;
import net.hasor.dataway.spi.ApiInfo;
import net.hasor.dataway.spi.PreExecuteChainSpi;
import net.hasor.utils.future.BasicFuture;
import org.apache.commons.collections4.MapUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * @Classname PreExecuteChain
 * @Description 接口执行前
 * @Date 2023/8/3 20:37
 * @Created by Leo825
 */
@Slf4j
public class PreExecuteChain implements PreExecuteChainSpi {

    public static PreExecuteChain getInstance() {
        return new PreExecuteChain();
    }

    /**
     * sql 直接前
     * @param apiInfo
     * @param basicFuture
     */
    @Override
    public void preExecute(ApiInfo apiInfo, BasicFuture<Object> basicFuture) {
        Map<String, Object> parameter = apiInfo.getParameterMap();

        // 注入用户权限参数
        parameter.putAll(MapUtils.emptyIfNull(loginUserAuthParams()));
    }


    /**
     * 获取登录用户权限参数
     * @return
     */
    private Map<String, Object> loginUserAuthParams () {
        Map<String, Object> authParams = new HashMap<>();
        // todo 注入登录用户 权限参数
        return authParams;
    }
}

ResultProcessChainSpi,主要是返回值进行一些封装

java 复制代码
package com.demo.chain;
import com.demo.common.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import net.hasor.dataway.spi.ApiInfo;
import net.hasor.dataway.spi.ResultProcessChainSpi;
/**
 * @Classname ReturnProcessChain
 * @Description 兼
 * 容 4.1.5之前 dataway版本 dataway 执行结果处理, 让 structure 配置仅使用于   dataway页面调试使用
 * @Date 2023/8/3 20:47
 * @Created by Leo825
 */
@Slf4j
public class ResultProcessChain implements ResultProcessChainSpi {

    public static ResultProcessChain getInstance() {
        return new ResultProcessChain();
    }


    /**
     * 对返回结果进行处理
     * @param formPre
     * @param apiInfo
     * @param result
     * @return
     */
    @Override
    public Object callAfter(boolean formPre, ApiInfo apiInfo, Object result) {
        // apiInfo.isPerform() 为 true 表示,API 调用是从 UI 界面发起的。
        if (apiInfo.isPerform() || apiInfo.getParameterMap().containsKey("SELF_CALL")) {
            return result;
        }
        apiInfo.getOptionMap().put("resultStructure", false);
        return AjaxResult.success(result);
    }

    /**
     * 异常
     * @param formPre
     * @param apiInfo
     * @param e
     * @return
     */
    @Override
    public Object callError(boolean formPre, ApiInfo apiInfo, Throwable e) {
        if (apiInfo.isPerform()) {
            return ResultProcessChainSpi.super.callError(formPre, apiInfo, e);
        }
        apiInfo.getOptionMap().put("resultStructure", false);
        return AjaxResult.error( "系统繁忙");
    }
}

swagger 配置

java 复制代码
package com.demo.config;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

import java.util.ArrayList;
import java.util.List;

/**
 * swagger配置
 */
@Component
@Primary
public class SwaggerProvider implements SwaggerResourcesProvider {
    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resources = new ArrayList<>();
        resources.add(swaggerResource("应用接口", "/v2/api-docs", "1.0"));
        resources.add(swaggerResource("Dataway接口", "/interface-ui/api/docs/swagger2.json", "1.0"));
        return resources;
    }

    private SwaggerResource swaggerResource(String name, String location, String version) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion(version);
        return swaggerResource;
    }
}

将 Dataway 里面的接口发布到 swagger里面

java 复制代码
package com.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * swagger配置
 */
@EnableSwagger2
@Configuration()
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)//
                .apiInfo(apiInfo())//
                .select()//
                .apis(RequestHandlerSelectors.basePackage("com.demo"))//
                .paths(PathSelectors.any())//
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()//
                .title("Spring Boot中使用Swagger2构建RESTful APIs")//
                .description("参考手册:https://www.hasor.net/doc/display/dataql/")//
                .termsOfServiceUrl("https://www.hasor.net/doc/display/dataql/")//
                .contact("[email protected]").version("1.0")//
                .build();
    }
}

自定义 Udf

可以使用自定义udf,并且在管理页面可以直接访问

java 复制代码
package com.demo.udf;

import com.demo.config.SpringContextUtil;
import com.demo.service.MyUdfService;
import lombok.extern.slf4j.Slf4j;
import net.hasor.dataql.DimUdf;
import net.hasor.dataql.Hints;
import net.hasor.dataql.Udf;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * 自定义 udf,需要在 DatawayModule 添加以下配置代码,否则会出现注入为 null 问题
 * // udf/udfSource/import 指令 的类型创建委托给 spring
 * QueryApiBinder queryBinder = apiBinder.tryCast(QueryApiBinder.class);
 * queryBinder.bindFinder(Finder.TYPE_SUPPLIER.apply(this));
 */
@Slf4j
@DimUdf("myNameUdf")
@Service
public class MyNameUdf implements Udf {

    @Resource
    private MyUdfService myUdfService;

    @Override
    public Object call(Hints readOnly, Object... params) {
        log.info("获取当前服务信息: " + myUdfService.myName());
        return "张三";
    }
}

web管理页面调用方式如下:

java 复制代码
import 'com.demo.udf.MyNameUdf' as myNameUdf;
return myNameUdf();

据官方文档说可以通过 import 'bean' 方式引入,但是尝试了没有成功。

指定数据源进行查询

主要通过 FRAGMENT_SQL_DATA_SOURCE 来指定数据源:

java 复制代码
// springboot 整合Dataway 多数据源配置。使用示例:
hint FRAGMENT_SQL_DATA_SOURCE = "dataSource2";
var query = @@sql()<%
    select * from subjects
%>
return query()
相关推荐
光而不耀@lgy12 分钟前
C++初登门槛
linux·开发语言·网络·c++·后端
方圆想当图灵31 分钟前
由 Mybatis 源码畅谈软件设计(七):SQL “染色” 拦截器实战
后端·mybatis·代码规范
晓数1 小时前
【硬核干货】JetBrains AI Assistant 干货笔记
人工智能·笔记·jetbrains·ai assistant
毅航1 小时前
MyBatis 事务管理:一文掌握Mybatis事务管理核心逻辑
java·后端·mybatis
我的golang之路果然有问题1 小时前
速成GO访问sql,个人笔记
经验分享·笔记·后端·sql·golang·go·database
lwewan1 小时前
26考研——存储系统(3)
c语言·笔记·考研
柏油1 小时前
MySql InnoDB 事务实现之 undo log 日志
数据库·后端·mysql
来自星星的猫教授1 小时前
spring,spring boot, spring cloud三者区别
spring boot·spring·spring cloud
搞机小能手2 小时前
六个能够白嫖学习资料的网站
笔记·学习·分类
nongcunqq2 小时前
爬虫练习 js 逆向
笔记·爬虫