自定义动态数据源

2023-11-05

准备

  • mysql
  • spring boot
  • 配置依赖

在这里插入图片描述

  • 配置文件
# 数据源配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    ds:
      # 主库数据源
      master:
        url: jdbc:mysql://192.168.10.10:3306/test08?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
        username: root
        password: root
      # 从库数据源
      slave:
        # 从数据源开关/默认关闭
        url: jdbc:mysql://192.168.10.10:3306/test09?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
        username: root
        password: root
      # 初始连接数
    initialSize: 5
    # 最小连接池数量
    minIdle: 10
    # 最大连接池数量
    maxActive: 20
    # 配置获取连接等待超时的时间
    maxWait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 300000
    # 配置一个连接在池中最大生存的时间,单位是毫秒
    maxEvictableIdleTimeMillis: 900000
    # 配置检测连接是否有效
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    webStatFilter:
      enabled: true
    statViewServlet:
      enabled: true
      # 设置白名单,不填则允许所有访问
      allow:
      url-pattern: /druid/*
      # 控制台管理用户名和密码
      login-username: tienchin
      login-password: 123456
    filter:
      stat:
        enabled: true
        # 慢SQL记录
        log-slow-sql: true
        slow-sql-millis: 1000
        merge-sql: true
      wall:
        config:
          multi-statement-allow: true

在这里插入图片描述

业务流程分析

  1. 自定义一个注解 @DataSource,将来可能加在 service 方法或者是该类上,表示方法或者类上的所有方法都是用某一个数据源。

  2. 对于第一步,如果某个方法上面有 @DataSource 注解,那么就将该方法需要使用的数据源名称存入到 ThreadLocal 当中。

  3. 自定义切面,在切面中去解析 @DataSource 注解,当一个方法或者类上面有 @DataSource 注解的时候,将 @DataSource 注解所标记的数据源存入 ThreadLocal 中。

  4. 最后,当 Mapper 执行的时候,需要 DataSource,他会自动去 AbstractRoutingDataSource 类中去查找需要的数据源,我们只需要在 AbstractRoutingDataSource 中返回 ThreadLocal 中的值即可。


代码实现

 

定义注解:@DataSource


package org.javaboy.dd;

import org.javaboy.dd.dataSource.DataSourceType;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author: yueLQ
 * @date: 2022-08-14 21:14
 *  这个注解,将来可也加载到某一个 service 类或者方法上,通过 value 属性来指定类或者方法应该使用那个数据源
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface DataSource {

    /**
     *  如果一个方法上,加了 @DataSource 注解,但是没有指定数据源的名称,那么默认就是用 master 数据源
     * @return
     */
    String value() default DataSourceType.DEFAULT_DS_NAME;
}

 

定义一个 contextHolder 来存储当前线程使用的数据源:

package org.javaboy.dd.dataSource;

/**
 * @author: yueLQ
 * @date: 2022-08-14 21:20
 *
 * 这个类用来存储当前线程所使用的的数据源名称
 */
public class DynamicDataSourceContextHolder {

    private static ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal();

    public static void setDataSourceType(String dsType){
        CONTEXT_HOLDER.set(dsType);
    }

    public static String getDataSourceType(){
      return   CONTEXT_HOLDER.get();
    }

    /**
     *  清除
     */
    public static void clearDataSourceType(){
        CONTEXT_HOLDER.remove();
    }
}


定义一个切面,动态的读取业务层注解中的值:

package org.javaboy.dd.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.javaboy.dd.DataSource;
import org.javaboy.dd.dataSource.DynamicDataSourceContextHolder;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

/**
 * @author: yueLQ
 * @date: 2022-08-14 21:26
 */
@Component
@Aspect
@Order(11)
public class DataSourceAspect {

    /**
     * @annotation(org.javaboy.dd.DatadSource) 表示方法上有 datasource 将方法拦截下来
     * @within(org.javaboy.dd.DatadSource) 表示类上有 datasource 将类中方法拦截下来
     */
    @Pointcut("@annotation(org.javaboy.dd.DataSource) || @within(org.javaboy.dd.DataSource)")
    public void pc(){

    }

    @Around("pc()")
    public Object around(ProceedingJoinPoint point){
        DataSource dataSource = getDatadSource(point);
        if (dataSource !=null){
            // 获取直接中数据源的名称
            String value = dataSource.value();
            DynamicDataSourceContextHolder.setDataSourceType(value);
        }

        try {
            return point.proceed();
        }catch (Throwable e){
            e.printStackTrace();
        }finally {
            DynamicDataSourceContextHolder.clearDataSourceType();
        }

        return null;
    }


    private DataSource getDatadSource(ProceedingJoinPoint point) {
        // 获取方法上的有效注解
        MethodSignature signature = (MethodSignature) point.getSignature();
        // 查找方法上的注解
        DataSource annotation = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        // 如果不为空,则方法上有该注解
        if (!ObjectUtils.isEmpty(annotation)) {
            return annotation;
        }
        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }

}

读取配置文件的数据源的值:

package org.javaboy.dd.dataSource;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.sql.DataSource;
import java.util.Map;

/**
 * @author: yueLQ
 * @date: 2022-08-15 19:07
 */
@ConfigurationProperties(prefix = "spring.datasource")
public class DruidProperties {

    private String type;

    private String driverClassName;

    private Map<String, Map<String, String>> ds;

    private Integer initialSize;

    private Integer minIdle;

    private Integer maxActive;

    private Integer maxWait;

    /**
     * 一会在外部构造好一个 dataSource 对象,但是对象只包含三个核心属性,url,username,password
     * 在这个方法中,给这个对象设置公共的属性
     *
     * @param dataSource
     * @return
     */
    public DataSource dataSource(DruidDataSource dataSource) {
        dataSource.setInitialSize(initialSize);
        dataSource.setMinIdle(minIdle);
        dataSource.setMaxActive(maxActive);
        dataSource.setMaxWait(maxWait);
        return dataSource;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getDriverClassName() {
        return driverClassName;
    }

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public Map<String, Map<String, String>> getDs() {
        return ds;
    }

    public void setDs(Map<String, Map<String, String>> ds) {
        this.ds = ds;
    }

    public Integer getInitialSize() {
        return initialSize;
    }

    public void setInitialSize(Integer initialSize) {
        this.initialSize = initialSize;
    }

    public Integer getMaxActive() {
        return maxActive;
    }

    public void setMaxActive(Integer maxActive) {
        this.maxActive = maxActive;
    }

    public Integer getMinIdle() {
        return minIdle;
    }

    public void setMinIdle(Integer minIdle) {
        this.minIdle = minIdle;
    }

    public Integer getMaxWait() {
        return maxWait;
    }

    public void setMaxWait(Integer maxWait) {
        this.maxWait = maxWait;
    }
}


加载数据源的配置:

package org.javaboy.dd.dataSource;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author: yueLQ
 * @date: 2022-08-15 19:20
 *  加载数据源的配置
 */
@Component
@EnableConfigurationProperties(DruidProperties.class)
public class LoadDataSource {

    @Autowired
    private DruidProperties druidProperties;

    /**
    * 将数据源数据存入 map 集合中
    */
    public Map<String, DataSource> loadAllDataSource() {
        HashMap<String, DataSource> map = new HashMap<>();
        Map<String, Map<String, String>> ds = druidProperties.getDs();
        Set<String> keySet = ds.keySet();
        try {
            for (String key : keySet) {
                map.put(key, druidProperties.dataSource((DruidDataSource) DruidDataSourceFactory.createDataSource(ds.get(key))));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }
}

继承 AbstractRoutingDataSource 抽象类,重写方法:

package org.javaboy.dd.dataSource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: yueLQ
 * @date: 2022-08-15 19:29
 */
@Component
public class DynamicDataSource extends AbstractRoutingDataSource {

    /**
     *  拿到数据源之后,我们要告诉该类去找 LoadDataSource 中的 loadAllDataSource() 方法,查找信息
     */
    public DynamicDataSource(LoadDataSource loadDataSource) {
        // 1. 设置所有的数据源
        Map<String, DataSource> allDs = loadDataSource.loadAllDataSource();
        super.setTargetDataSources(new HashMap<>(allDs));
        // 2,设置默认的数据源
        // 将来,并不是所有的方法上都有 @dataSource 的注解,对于那些没有 @DataSource 注解的方法,该使用那个数据源
        super.setDefaultTargetDataSource(allDs.get(DataSourceType.DEFAULT_DS_NAME));
        // 3. 调用父类中的方法
        super.afterPropertiesSet();
    }

    /**
     * 这个方法用来返回数据源名称,当系统需要获取数据源的时候,会自动调用该方法获取数据源的名称
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}


定义一个常量类:

package org.javaboy.dd.dataSource;

/**
 * @author: yueLQ
 * @date: 2022-08-15 19:40
 */
public interface DataSourceType {

    /**
     *  默认的数据源
     */
    String DEFAULT_DS_NAME = "master";

    /**
     *  session key
     */
    String DS_SESSION_KEY = "ds_session_key";
}


创建数据库,model mapper service controller

/*
 Navicat Premium Data Transfer

 Source Server         : TienChin
 Source Server Type    : MariaDB
 Source Server Version : 100311
 Source Host           : 192.168.10.10:3306
 Source Schema         : test08

 Target Server Type    : MariaDB
 Target Server Version : 100311
 File Encoding         : 65001

 Date: 16/08/2022 20:19:17
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(32) DEFAULT NULL,
  `name` varchar(64) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
  `age` int(10) DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'zhangsan', 18);

SET FOREIGN_KEY_CHECKS = 1;

--------------------------------

/*
 Navicat Premium Data Transfer

 Source Server         : TienChin
 Source Server Type    : MariaDB
 Source Server Version : 100311
 Source Host           : 192.168.10.10:3306
 Source Schema         : test09

 Target Server Type    : MariaDB
 Target Server Version : 100311
 File Encoding         : 65001

 Date: 16/08/2022 20:20:30
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(32) DEFAULT NULL,
  `name` varchar(64) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
  `age` int(10) DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (2, 'lisi', 99);

SET FOREIGN_KEY_CHECKS = 1;



package org.javaboy.dd.model;

/**
 * @author: yueLQ
 * @date: 2022-08-15 19:56
 */

public class User {

    private Integer  id;

    private String name;

    private Integer age;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}


package org.javaboy.dd.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.javaboy.dd.model.User;

import java.util.List;

/**
 * @author: yueLQ
 * @date: 2022-08-15 19:59
 */
@Mapper
public interface UserMapper {

    @Select("select * from user")
    List<User> getAllUsers();
}


package org.javaboy.dd.service;

import org.javaboy.dd.DataSource;
import org.javaboy.dd.mapper.UserMapper;
import org.javaboy.dd.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author: yueLQ
 * @date: 2022-08-15 20:01
 */
@Service
@DataSource()
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public List<User> getAllUser(){
        return userMapper.getAllUsers();
    }
}


package org.javaboy.dd.controller;

import org.javaboy.dd.model.User;
import org.javaboy.dd.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author: yueLQ
 * @date: 2022-08-15 20:03
 */
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("hello")
    public List<User> hello(){
        return userService.getAllUser();
    }
}


网页上切换数据源


定义接口,传入参数,切换数据源:

package org.javaboy.dd.controller;

import org.javaboy.dd.dataSource.DataSourceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

/**
 * @author: yueLQ
 * @date: 2022-08-16 19:05
 */
@RestController
public class DataSourceController {

    private static  final Logger logger = LoggerFactory.getLogger(DataSourceController.class);

    /**
     * 修改数据源的接口
     * 数据源的修改就存储到 httpSession 中去, 跟当前回话绑定
     * @param dsType
     */
    @PostMapping("/dsType")
    public void setDsType(String dsType, HttpSession session) {
        session.setAttribute(DataSourceType.DS_SESSION_KEY,dsType);
        logger.info("数据源切换为:{}",dsType);

    }
}


定义全局切面,切换数据源:

package org.javaboy.dd.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.javaboy.dd.dataSource.DataSourceType;
import org.javaboy.dd.dataSource.DynamicDataSourceContextHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;

/**
 * @author: yueLQ
 * @date: 2022-08-16 19:13
 * <p>
 * 该切面去管理,我们传入的数据源,接收到数据源之后,全局所有的 service 方法,使用该数据源
 * <p>
 * 当我们配置了全局的数据源切换和注解的数据源 @DataSource() 切换时
 * 有 @DataSource 注解,则使用注解,没有注解的使用全局数据源,使用 @Order() 注解
 * 业务上优先级高,代码上的优先级要低,让带有 @dataSource() 的注解后执行
 */
@Component
@Aspect
@Order(10)
public class GloalDataSourceAspect {

    @Autowired
    private HttpSession httpSession;

    /**
     * 所有的 service 方法都切入
     */
    @Pointcut("execution(* org.javaboy.dd.service.*.*(..))")
    public void pc() {
    }

    /**
     * 环绕通知
     *
     * @param point
     */
    @Around("pc()")
    public Object aroud(ProceedingJoinPoint point) {
        // 从 session 中,把我们的数据源的名称读出来。出入到 contextHolder 中去
        DynamicDataSourceContextHolder.setDataSourceType((String) httpSession.getAttribute(DataSourceType.DS_SESSION_KEY));

        try {
            return point.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        } finally {
            // 清空
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
        return null;
    }
}

定义前台页面,引入 jquery

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jquery-3.4.1.js"></script>
</head>
<body>
<div>
    请选择数据源:
    <select name="" id="" onchange="dsChange(this.options[this.options.selectedIndex].value)">
        <option value="请选择">请选择</option>
        <option value="master">master</option>
        <option value="slave">slave</option>
    </select>
</div>
<div id="result">

</div>
<button onclick="loadData()">加载数据</button>
<script>
    function loadData(){
        $.get("/hello",function (data){
            $("#result").html(JSON.stringify(data))
        })
    }
     function dsChange(val){
         $.post("/dsType",{dsType:val})
     }
</script>
</body>
</html>

在这里插入图片描述
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自定义动态数据源 的相关文章

随机推荐

  • 通过Idea或命令将本地项目上传至git

    通过Idea或命令将本地项目上传至git 一 Git创建仓库 1 登录Gitee账号 点击新建 2 填写如下相关信息 点击创建 3 在此处可以复制项目链接 二 Idea配置和解绑git 提交项目 1 idea打开项目 操作如下 2 在弹框里
  • 关于getComputedStyle的第二个参数的解释

    最近别人问了我一下getComputedStyle的封装过程为什么要写if currentStyle 我当时也没反应过来 毕竟好长时间没有用了 因此决定来探究一下这个getComputedStyle 函数 即是温习也是和大家分享 如果有不对
  • Java Thread类的静态Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()方法(带示例)...

    线程类静态Thread UncaughtExceptionHandler getDefaultUncaughtExceptionHandler Thread Class static Thread UncaughtExceptionHand
  • 程序“[1344] MyProject2.exe: 本机”已退出,返回值为 0 (0x0) 错误解决方法

    笔记本用的是win7 64位的系统 没法再用以前的VC6 0了 所以就用了VS2010 在编写窗口程序的时候 运行的时候总是一闪而过 并输出以下的信息 还真是麻烦呀 MyProject2 exe 已加载 D vc练习 MyProject2
  • 海湾主机汉字注释表打字出_海湾消防主机字根表-海湾消防主机

    1洁2964金2980 津2982紧2984锦2985仅2986进2988禁2991荆3003 京3009精3011经3013井3014警3015镜3021九3037 酒3038救3040旧3041居3051菊3053局3054巨3062
  • 性能测试之压力测试

    文章目录 一 基本介绍 二 性能指标 三 下载安装JMeter 1 下载安装包 2 启动JMeter 四 使用JMeter 1 模拟用户请求 2 填写测试地址 3 接收测试结果 4 结果解释 一 基本介绍 压力测试考察当前软硬件条件下系统所
  • 学爬虫之前必须先了解的基础

    爬虫的基础 1 先介绍一下啥是爬虫 在这我也就不扯啥嘴皮子了 简单讲 爬虫就是将前端网页上的数据通过一定的方式爬取下来 一般爬虫可以分为 通用爬虫 和 聚焦爬虫 两种 通用爬虫 通用网络爬虫 是 捜索引擎抓取系统 Baidu Google
  • 【Android-JetpackCompose】13、实战在线课程 App

    文章目录 一 BottomNavigation 底部导航 1 1 底部导航栏的布局 点击 1 2 设置 bottomBar 的颜色 1 3 设置顶部 actionBar 的颜色 二 主页 StudyScreen 2 1 顶部状态栏 2 2
  • HTML表格(table)实例

    实例1 课程表 table border 1 width 60 cellpadding 2 caption 课程表 caption tr align center td 时间 日期 td td 一 td tr table
  • 附近的商店

    看着每天的感染数据在下降 上海解封的日子快到了 打开美团看看附近店铺有没有好吃 准备解封大吃特吃一顿 排序按照距离优先 还有附近几公里之内的店铺 想了解这个功能怎么实现的 查了网上资料 得到的常用的算法是 geohash 和 S2 Geoh
  • Reverse题常规步骤

    Reverse题常规步骤 1 查壳 一般难度的题目都是加了壳的 都需要进行壳分析 2 脱壳 有壳就脱壳 3 IDA逆向 脱完壳以后用IDA进行逆向分析
  • CentOS7.x环境下安装Docker以及常用命令

    Docker官方建议在Ubuntu中安装 因为Docker是基于Ubuntu发布的 而且一般Docker出现的问题Ubuntu是最先更新或者打补丁的 在很多版本的CentOS中是不支持更新最新的一些补丁包的 我这里将Docker安装到阿里云
  • 人脸识别损失函数综述(附开源地址)

    原文 人脸识别损失函数综述 附开源地址 原文地址 https zhuanlan zhihu com p 51324547 常用的度量学习损失方法有对比损失 Contrastive loss 三元组损失 Triplet loss 四元组损失
  • Android 蓝牙串口通信Demo

    目录 一 前言 二 效果图 三 源码 带注释 1 布局代码 2 Java代码 3 权限声明 四 注意 五 项目源码下载 一 前言 在学校和机电的同学组队准备做一个智能小车去参加比赛 需要我开发一个小车的控制app 所以我开始学习蓝牙串口通信
  • 一文带你深入了解HTTP

    http的发展史 在学习网络之前 了解它的历史能够帮助我明白为何它会发展为如今这个样子 能让我有探究它的兴趣 下面的这张图片就展示了 互联网 诞生至今的发展历程 http是什么 HyperTextTransferProtocol 直译为 超
  • sql代码转换小工具(delphi)

    1 下载 2 粘贴sql语句 3 msql转换为delphi sql语句
  • shell脚本基础3——正则表达式

    文章目录 一 基本了解 二 基本正则表达式 2 1 字符匹配 2 2 位置锚定 2 3 匹配次数 2 4 分组 三 扩展正则表达式 3 1 字符匹配 3 2 位置锚定 3 3 匹配次数 3 4 分组 3 5 或者 一 基本了解 正则表达式分
  • 对表的简单查询

    SQL命令对表进行查询 目录 SQL命令对表进行查询 1 无条件查询 2 条件查询 3 统计汇总查询 4 分组查询 5 对查询结果排序 1 无条件查询 无条件查询指只包含 SELECT FROM 的查询 这种查询最简单 例如 在S表中查询所
  • Android Studio安装中的问题及第一次运行hello world程序

    总结一下 在安装Android Studio中可能出现和遇到的问题 并给出解决方案 1 AS在安卓官网可以直接下载完整的安装包 2 SDK找不到的情况 可以自己下载 参考 https www jianshu com p 4ac1313b50
  • 自定义动态数据源

    文章目录 准备 业务流程分析 代码实现 网页上切换数据源 准备 mysql spring boot 配置依赖 配置文件 数据源配置 spring datasource type com alibaba druid pool DruidDat