我的编程空间,编程开发者的网络收藏夹
学习永远不晚

数据源管理 | 动态权限校验,表结构和数据迁移流程

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

数据源管理 | 动态权限校验,表结构和数据迁移流程

本文源码:GitHub·点这里 || GitEE·点这里

一、数据同步简介

1、场景描述

如果经常接触数据开发,会有这样一个场景,服务A提供一个数据源,假设称为动态数据源A,需要读取该数据源下的数据;服务B提供一个数据源,假设称为动态数据源B,需要写入数据到该数据源。这个场景通常描述为数据同步,或者数据搬运。

2、基本流程

数据源管理 | 动态权限校验,表结构和数据迁移流程

基于上述流程图,整体步骤如下:

  • 测试多个数据源是否连接成功,并动态管理;
  • 判断数据源提供的账号是否有操作权限,例如读写;
  • 读取数据源A的表结构,在数据源B创建表;
  • 数据读取或者分页读取,写入数据源B中;
  • 在不知道表结构情况下,还需要读取表结构,生成SQL;

3、JDBC基础API

  • Statement

Java中JDBC下执行数据库操作的一个重要接口,在已经建立数据库连接的基础上,向数据库发送要执行的SQL语句。

  • PreparedStatement

继承Statement接口,且实现SQL预编译,可以提高批量处理效率。常应用于批量数据写入场景。

  • ResultSet

存储JDBC查询结果集的对象,ResultSet接口提供从当前行检索列值的方法。

二、基础工具封装

1、数据源管理

提供一个数据源管理的Factory,当前场景下主要管理一个读库即数据源A,和一个写库即数据源B,数据源连接验证通过,放入容器中。

@Componentpublic class ConnectionFactory {    private volatile Map<String, Connection> connectionMap = new HashMap<>();    @Resource    private JdbcConfig jdbcConfig ;    @PostConstruct    public void init (){        ConnectionEntity read = new ConnectionEntity(        "MySql","jdbc:mysql://localhost:3306/data_read","user01","123");        if (jdbcConfig.getConnection(read) != null){            connectionMap.put(JdbcConstant.READ,jdbcConfig.getConnection(read));        }        ConnectionEntity write = new ConnectionEntity(        "MySql","jdbc:mysql://localhost:3306/data_write","user01","123");        if (jdbcConfig.getConnection(write) != null){            connectionMap.put(JdbcConstant.WRITE,jdbcConfig.getConnection(write));        }    }    public Connection getByKey (final String key){        return connectionMap.get(key) ;    }}

2、动态SQL拼接

基础SQL管理

主要提供SQL的基础模板,例如全表查,分页查,表结构查询。

public class BaseSql {    public static String READ_SQL = "SELECT * FROM %s LIMIT 1";    public static String WRITE_SQL = "INSERT INTO %s (SELECT * FROM %s WHERE 1=0)" ;    public static String CREATE_SQL = "SHOW CREATE TABLE %s" ;    public static String SELECT_SQL = "SELECT * FROM %s" ;    public static String COUNT_SQL = "SELECT COUNT(1) countNum FROM %s" ;    public static String PAGE_SQL = "SELECT * FROM %s LIMIT %s,%s" ;    public static String STRUCT_SQL (){        StringBuffer sql = new StringBuffer() ;        sql.append(" SELECT                     ");        sql.append("     COLUMN_NAME,           ");        sql.append("     IS_NULLABLE,           ");        sql.append("     COLUMN_TYPE,           ");        sql.append("     COLUMN_KEY,            ");        sql.append("     COLUMN_COMMENT         ");        sql.append(" FROM                       ");        sql.append(" information_schema.COLUMNS ");        sql.append(" WHERE                      ");        sql.append(" table_schema = '%s'        ");        sql.append(" AND table_name = '%s'      ");        return String.valueOf(sql) ;    }}

SQL参数拼接

根据SQL模板中缺失的参数,进行动态补全,生成完成SQL语句。

public class BuildSql {        public static String buildReadSql(String table) {        String readSql = null ;        if (StringUtils.isNotEmpty(table)){            readSql = String.format(BaseSql.READ_SQL, table);        }        return readSql;    }        public static String buildWriteSql(String table){        String writeSql = null ;        if (StringUtils.isNotEmpty(table)){            writeSql = String.format(BaseSql.WRITE_SQL, table,table);        }        return writeSql ;    }        public static String buildStructSql (String table){        String structSql = null ;        if (StringUtils.isNotEmpty(table)){            structSql = String.format(BaseSql.CREATE_SQL, table);        }        return structSql ;    }        public static String buildTableSql (String schema,String table){        String structSql = null ;        if (StringUtils.isNotEmpty(table)){            structSql = String.format(BaseSql.STRUCT_SQL(), schema,table);        }        return structSql ;    }        public static String buildSelectSql (String table){        String selectSql = null ;        if (StringUtils.isNotEmpty(table)){            selectSql = String.format(BaseSql.SELECT_SQL,table);        }        return selectSql ;    }        public static String buildCountSql (String table){        String countSql = null ;        if (StringUtils.isNotEmpty(table)){            countSql = String.format(BaseSql.COUNT_SQL,table);        }        return countSql ;    }        public static String buildPageSql (String table,int offset,int size){        String pageSql = null ;        if (StringUtils.isNotEmpty(table)){            pageSql = String.format(BaseSql.PAGE_SQL,table,offset,size);        }        return pageSql ;    }}

三、业务化流程

1、基础鉴权

读库尝试一次单条数据读取,写库尝试一次不成立条件的写入,如果没有权限,会抛出相应异常。

@RestControllerpublic class CheckController {    @Resource    private ConnectionFactory connectionFactory ;    // MySQLSyntaxErrorException: SELECT command denied to user    @GetMapping("/checkRead")    public String checkRead (){        try {            String sql = BuildSql.buildReadSql("rw_read") ;            ExecuteSqlUtil.query(connectionFactory.getByKey(JdbcConstant.READ),sql) ;            return "success" ;        } catch (SQLException e) {            e.printStackTrace();        }        return "fail" ;    }    // MySQLSyntaxErrorException: INSERT command denied to user    @GetMapping("/checkWrite")    public String checkWrite (){        try {            String sql = BuildSql.buildWriteSql("rw_read") ;            ExecuteSqlUtil.update(connectionFactory.getByKey(JdbcConstant.WRITE),sql) ;            return "success" ;        } catch (SQLException e) {            e.printStackTrace();        }        return "fail" ;    }}

2、同步表结构

这里执行最简单操作,把读库表创建语句查询出来,丢到写库中执行。

@RestControllerpublic class StructController {    @Resource    private ConnectionFactory connectionFactory ;    @GetMapping("/syncStruct")    public String syncStruct (){        try {            String sql = BuildSql.buildStructSql("rw_read") ;            ResultSet resultSet = ExecuteSqlUtil.query(connectionFactory.getByKey(JdbcConstant.READ),sql) ;            String createTableSql = null ;            while (resultSet.next()){                createTableSql = resultSet.getString("Create Table") ;            }            if (StringUtils.isNotEmpty(createTableSql)){                ExecuteSqlUtil.update(connectionFactory.getByKey(JdbcConstant.WRITE),createTableSql) ;            }            return "success" ;        } catch (SQLException e) {            e.printStackTrace();        }        return "fail" ;    }}

3、同步表数据

读库的表数据读取,批量放入写库中。这里特别说一个方法:statement.setObject();在不知道参数个数和类型时,自动适配数据类型。

@RestControllerpublic class DataSyncController {    @Resource    private ConnectionFactory connectionFactory ;    @GetMapping("/dataSync")    public List<RwReadEntity> dataSync (){        List<RwReadEntity> rwReadEntities = new ArrayList<>() ;        try {            Connection readConnection = connectionFactory.getByKey(JdbcConstant.READ) ;            String sql = BuildSql.buildSelectSql("rw_read") ;            ResultSet resultSet = ExecuteSqlUtil.query(readConnection,sql) ;            while (resultSet.next()){                RwReadEntity rwReadEntity = new RwReadEntity() ;                rwReadEntity.setId(resultSet.getInt("id"));                rwReadEntity.setSign(resultSet.getString("sign"));                rwReadEntities.add(rwReadEntity) ;            }            if (rwReadEntities.size() > 0){                Connection writeConnection = connectionFactory.getByKey(JdbcConstant.WRITE) ;                writeConnection.setAutoCommit(false);                PreparedStatement statement = writeConnection.prepareStatement("INSERT INTO rw_read VALUES(?,?)");                // 基于动态获取列,和statement.setObject();自动适配数据类型                for (int i = 0 ; i < rwReadEntities.size() ; i++){                    RwReadEntity rwReadEntity = rwReadEntities.get(i) ;                    statement.setInt(1,rwReadEntity.getId()) ;                    statement.setString(2,rwReadEntity.getSign()) ;                    statement.addBatch();                    if (i>0 && i%2==0){                        statement.executeBatch() ;                    }                }                // 处理最后一批数据                statement.executeBatch();                writeConnection.commit();            }            return rwReadEntities ;        } catch (SQLException e) {            e.printStackTrace();        }        return null ;    }}

4、分页查询

提供一个分页查询工具,在数据量大的情况下不能一次性读取大量的数据,避免资源占用过高。

public class PageUtilEntity {        public static PageHelperEntity<Object> pageResult (int total, int pageSize,int currentPage, List dataList){        PageHelperEntity<Object> pageBean = new PageHelperEntity<Object>();        // 总页数        int totalPage = PageHelperEntity.countTotalPage(pageSize,total) ;        // 分页列表        List<Integer> pageList = PageHelperEntity.pageList(currentPage,pageSize,total) ;        // 上一页        int prevPage = 0 ;        if (currentPage==1){            prevPage = currentPage ;        } else if (currentPage>1&¤tPage<=totalPage){            prevPage = currentPage -1 ;        }        // 下一页        int nextPage =0 ;        if (totalPage==1){            nextPage = currentPage ;        } else if (currentPage<=totalPage-1){            nextPage = currentPage+1 ;        }        pageBean.setDataList(dataList);        pageBean.setTotal(total);        pageBean.setPageSize(pageSize);        pageBean.setCurrentPage(currentPage);        pageBean.setTotalPage(totalPage);        pageBean.setPageList(pageList);        pageBean.setPrevPage(prevPage);        pageBean.setNextPage(nextPage);        pageBean.initjudge();        return  pageBean ;    }}

四、最后总结

很多复杂度偏高的业务,越是需要借助基础API解决,因为复杂度高,不容易抽象化统一封装,如果数据同步这块业务,可以适配多种数据库,完全可以独立封装为中间件,开源项目中关于多方数据同步或计算的中间件也有好多,可以自行了解下,增长眼界开阔思路。

五、源代码地址

GitHub·地址https://github.com/cicadasmile/data-manage-parentGitEE·地址https://gitee.com/cicadasmile/data-manage-parent
推荐相关阅读
数据源管理:主从库动态路由,AOP模式读写分离
数据源管理:基于JDBC模式,适配和管理动态数据源

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

数据源管理 | 动态权限校验,表结构和数据迁移流程

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

数据源管理 | 动态权限校验,表结构和数据迁移流程

本文源码:GitHub·点这里 || GitEE·点这里一、数据同步简介1、场景描述如果经常接触数据开发,会有这样一个场景,服务A提供一个数据源,假设称为动态数据源A,需要读取该数据源下的数据;服务B提供一个数据源,假设称为动态数据源B,
2023-06-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录