Fescar - RM UpdateExecutor介绍

简介: 开篇 这篇文章的目的是讲解RM Executor模块当中一些通用的方法,这些方法在各个Executor的父类当中实现的,各个子类Executor模块都会复用,因此抽取出来统一的进行讲解。 个人是认为抽取通用的内容放在一篇文章讲解完后可以针对每类Executor讲解特有的功能,这样能够有更好的理解。

开篇

 这篇文章的目的是讲解RM Executor模块当中一些通用的方法,这些方法在各个Executor的父类当中实现的,各个子类Executor模块都会复用,因此抽取出来统一的进行讲解。

 个人是认为抽取通用的内容放在一篇文章讲解完后可以针对每类Executor讲解特有的功能,这样能够有更好的理解。这篇文章讲解Executor的实现类UpdateExecutor。


类依赖图


说明:

  • 着重讲解UpdateExecutor实现类。


UpdateExecutor方法介绍

public class UpdateExecutor<T, S extends Statement> extends AbstractDMLBaseExecutor<T, S> {

    public UpdateExecutor(StatementProxy statementProxy, StatementCallback statementCallback, SQLRecognizer sqlRecognizer) {
        super(statementProxy, statementCallback, sqlRecognizer);
    }

    @Override
    protected TableRecords beforeImage() throws SQLException {
        SQLUpdateRecognizer recognizer = (SQLUpdateRecognizer)sqlRecognizer;

        TableMeta tmeta = getTableMeta();
        List<String> updateColumns = recognizer.getUpdateColumns();

        StringBuffer selectSQLAppender = new StringBuffer("SELECT ");
        if (!tmeta.containsPK(updateColumns)) {
            // PK should be included.
            selectSQLAppender.append(getColumnNameInSQL(tmeta.getPkName()) + ", ");
        }
        for (int i = 0; i < updateColumns.size(); i++) {
            selectSQLAppender.append(updateColumns.get(i));
            if (i < (updateColumns.size() - 1)) {
                selectSQLAppender.append(", ");
            }
        }
        String whereCondition = null;
        ArrayList<Object> paramAppender = new ArrayList<>();
        if (statementProxy instanceof ParametersHolder) {
            whereCondition = recognizer.getWhereCondition((ParametersHolder)statementProxy, paramAppender);
        } else {
            whereCondition = recognizer.getWhereCondition();
        }
        selectSQLAppender.append(" FROM " + getFromTableInSQL() + " WHERE " + whereCondition + " FOR UPDATE");
        String selectSQL = selectSQLAppender.toString();

        TableRecords beforeImage = null;
        PreparedStatement ps = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            if (paramAppender.isEmpty()) {
                st = statementProxy.getConnection().createStatement();
                rs = st.executeQuery(selectSQL);
            } else {
                ps = statementProxy.getConnection().prepareStatement(selectSQL);
                for (int i = 0; i< paramAppender.size(); i++) {
                    ps.setObject(i + 1, paramAppender.get(i));
                }
                rs = ps.executeQuery();
            }
            beforeImage = TableRecords.buildRecords(tmeta, rs);

        } finally {
            if (rs != null) {
                rs.close();
            }
            if (st != null) {
                st.close();
            }
            if (ps != null) {
                ps.close();
            }
        }
        return beforeImage;
    }
}

说明:

  • UpdateExecutor需要保存SQL执行前的镜像。
  • 执行前镜像的准备整体思路是按照按照update的更新条件逆向拼接正向查询SQL。
  • 查询字段当中必须包含主键原因是因为afterImage()操作当中需要根据主键进行查询。原因猜测因为update会把where条件中字段的值变更,保存主键最安全。
  • 查询的SQL的select的字段从update字段中解析并加上主键列
  • 查询的SQL的条件通过解析update的条件生成。
  • 根据TableRecords.buildRecords生成执行前镜像。


public class UpdateExecutor<T, S extends Statement> extends AbstractDMLBaseExecutor<T, S> {

    public UpdateExecutor(StatementProxy statementProxy, StatementCallback statementCallback, SQLRecognizer sqlRecognizer) {
        super(statementProxy, statementCallback, sqlRecognizer);
    }


    @Override
    protected TableRecords afterImage(TableRecords beforeImage) throws SQLException {
        SQLUpdateRecognizer recognizer = (SQLUpdateRecognizer)sqlRecognizer;

        TableMeta tmeta = getTableMeta();
        if (beforeImage == null || beforeImage.size() == 0) {
            return TableRecords.empty(getTableMeta());
        }
        List<String> updateColumns = recognizer.getUpdateColumns();

        StringBuffer selectSQLAppender = new StringBuffer("SELECT ");
        if (!tmeta.containsPK(updateColumns)) {
            // PK should be included.
            selectSQLAppender.append(getColumnNameInSQL(tmeta.getPkName()) + ", ");
        }
        for (int i = 0; i < updateColumns.size(); i++) {
            selectSQLAppender.append(updateColumns.get(i));
            if (i < (updateColumns.size() - 1)) {
                selectSQLAppender.append(", ");
            }
        }
        List<Field> pkRows = beforeImage.pkRows();
        selectSQLAppender.append(
            " FROM " + getFromTableInSQL() + " WHERE " + buildWhereConditionByPKs(pkRows) + " FOR UPDATE");
        String selectSQL = selectSQLAppender.toString();

        TableRecords afterImage = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        try {
            pst = statementProxy.getConnection().prepareStatement(selectSQL);
            int index = 0;
            for (Field pkField : pkRows) {
                index++;
                pst.setObject(index, pkField.getValue(), pkField.getType());
            }
            rs = pst.executeQuery();
            afterImage = TableRecords.buildRecords(tmeta, rs);

        } finally {
            if (rs != null) {
                rs.close();
            }
            if (pst != null) {
                pst.close();
            }
        }
        return afterImage;
    }
}

说明:

  • UpdateExecutor需要保存SQL执行后的镜像。
  • UpdateExecutor执行后的镜像通过查询更新的字段字段生成的。
  • 查询更新后镜像的SQL的select的字段从update字段中解析并加上主键列。
  • 查询更新后镜像的SQL的条件是beforeImage()查询得到的主键key。
  • 根据TableRecords.buildRecords生成执行前镜像。


期待

  UpdateExecutor生成执行前后镜像的过程分析完后,接下去会分析生成镜像日志的流程。

目录
相关文章
|
存储 Ubuntu 应用服务中间件
【Nginx】centos和Ubuntu操作系统下载Nginx配置文件并启动Nginx服务详解
【Nginx】centos和Ubuntu操作系统下载Nginx配置文件并启动Nginx服务详解
401 1
|
开发工具 git
vscode 查看git提交日志
vscode 查看git提交日志
2357 0
|
安全 Linux 数据安全/隐私保护
国内外四款强大的远控使用体验:ToDesk、向日葵、AnyDesk、Microsoft 远程桌面横向比较
国内外四款强大的远控使用体验:ToDesk、向日葵、AnyDesk、Microsoft 远程桌面横向比较
2937 0
|
SQL 自然语言处理 数据库
XiYan-SQL:一种多生成器集成的Text-to-SQL框架
XiYan-SQL 是一种创新的多生成器集成Text-to-SQL框架,通过M-Schema增强模型对数据库结构的理解,结合ICL与SFT方法提升SQL生成质量和多样性,经实验证明在多个数据集上表现优异,特别是在Spider和SQL-Eval上取得了领先成绩。
2870 7
|
网络协议 安全 文件存储
Potplayer通过公网访问群晖WebDav,快速搭建远程办公环境
Potplayer通过公网访问群晖WebDav,快速搭建远程办公环境
622 0
Potplayer通过公网访问群晖WebDav,快速搭建远程办公环境
|
SQL 存储 容灾
关于主从延迟,一篇文章给你讲明白了!
在实际的生产环境中,由单台MySQL作为独立的数据库是完全不能满足实际需求的,无论是在安全性,高可用性以及高并发等各个方面 因此,一般来说都是通过集群主从复制(Master-Slave)的方式来同步数据,再通过读写分离(MySQL-Proxy)来提升数据库的并发负载能力进行部署与实施总结MySQL主从集群带来的作用是:提高数据库负载能力,主库执行读写任务(增删改),备库仅做查询。提高系统读写性能、可扩展性和高可用性。数据备份与容灾,备库在异地,主库不存在了,备库可以立即接管,无须恢复时间。用于记录数据库执行的写入性操作(不包括查询)信息,以二进制的形式保存在磁盘中。可以简单理解为记录的就是sq
2729 0
|
网络协议 Java 应用服务中间件
解决java.net.ConnectException: Connection refused:connect报错
解决java.net.ConnectException: Connection refused:connect报错
8025 1
|
缓存 NoSQL Java
惊!Spring Boot遇上Redis,竟开启了一场缓存实战的革命!
【8月更文挑战第29天】在互联网时代,数据的高速读写至关重要。Spring Boot凭借简洁高效的特点广受开发者喜爱,而Redis作为高性能内存数据库,在缓存和消息队列领域表现出色。本文通过电商平台商品推荐系统的实战案例,详细介绍如何在Spring Boot项目中整合Redis,提升系统响应速度和用户体验。
336 0
|
存储 Ubuntu 安全
最佳镜像搬运工 Skopeo 指南(1)
最佳镜像搬运工 Skopeo 指南(1)
最佳镜像搬运工 Skopeo 指南(1)