手敲MyBatis(十二章)-ResultMap解析映射使用

news2024/9/30 19:28:38

1.前言

这一章节主要解决好几章之前留下的坑,需要根据XML配置的ReultMap进行解析映射成具体的PO供用户使用。

我们本章就来解决下在xml中配置了如下标红框的内容,怎么解析映射到具体的实体类中,如下就是将id为activityMap的resultMap怎么解析到type为Activity实体中,并将一个一个的redult的字段属性获取到以后映射到Activity的属性里,并在最终查询获取时用到了这个resultMap的结果进行属性值的反射就可以了,

下面这张图是xml与各属性之间对应的关系,我们需要根据这个关系的逻辑处理对应的映射。

首先需要有xml的resultMap标签,去定义标识id以及对应的PO实体type,然后对应的类resultMap去存储id以及type等信息,然后字段的就定义在result的column和property,主要一个是数据库字段属性,一个是实体里的属性,然后到时解析到resultMapping对应属性里,

解析完了以后执行sql语句时返回结果用的是resultMap,并把对应的resultMap类数据取出获得ResultMapping与数据库数据一一比对,匹配则进入反射并设置对应的数据库值即可。

代码还是蛮简单的,涉及的东西也不多,主要围绕着解析然后映射字段等处理来。

 2.UML类图

1.主要在XmlMapperBuilder里添加解析resultMap的方法,如resultMapElement方法,解析获取resultMap的id和type,并通过buildResultMappingFromContext方法解析resultMapping的属性和列,解析完毕构建存储到resultMapping,并最终存储到resultMap中,数据都处理好了,

2.resultMap数据都处理好了后,传递到ResultMapResolver里,最终由此调用MapperBuilderAssistant助手进行resultMap的构建。

3.Sql执行完处理结果时,添加新的映射结果集处理。

3.代码实现

3.1 XMLMapperBuilder

XMLMapperBuilder类修改如下:

在configurationElement方法里新增解析resultMap操作,新增resultMapElement方法用来解析resultMap,新增buildResultMappingFromContext方法来解析resultMapping,其实都不复杂就这点业务逻辑,打个断点可以看下

public class XMLMapperBuilder extends BaseBuilder {

// 省略其他

 private void configurationElement(Element element) {
        // 配置namespace   ,代码省略中
        

        // 2. 解析resultMap step-13 新增
        resultMapElements(element.elements("resultMap"));

        // select、insert、update、delete SQL解析
      
    }

 private void resultMapElements(List<Element> list) {
        for (Element element : list) {
            try {
                resultMapElement(element, Collections.emptyList());
            } catch (Exception ignore) {
            }
        }
    }


    /**
     * 解析resultMap
     * <resultMap id="activityMap" type="po.Activity">
     * <id column="id" property="id"/>
     * <result column="activity_id" property="activityId"/>
     * <result column="activity_name" property="activityName"/>
     * <result column="activity_desc" property="activityDesc"/>
     * <result column="create_time" property="createTime"/>
     * <result column="update_time" property="updateTime"/>
     * </resultMap>
     */
    private void resultMapElement(Element resultMapNode, List<ResultMapping> additionalResultMappings) {
        String id = resultMapNode.attributeValue("id");  // resultMap id
        String type = resultMapNode.attributeValue("type"); // resultMap type
        // 将resultMap的type转换Class
        Class<?> typeClass = resolveClass(type);

        List<ResultMapping> resultMappings = new ArrayList<>();
        resultMappings.addAll(additionalResultMappings);

        // resultMap所有的子result
        List<Element> resultChildren = resultMapNode.elements();
        for (Element resultChild : resultChildren) {
            List<ResultFlag> flags = new ArrayList();
            if ("id".equals(resultChild.getName())) {
                flags.add(ResultFlag.ID);
            }
            // ResultMapping添加到集合里
            resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
        }
        // 创建结果映射解析器
        ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, resultMappings);
        resultMapResolver.resolve();
    }

    /**
     * <id column="id" property="id"/>
     * <result column="activity_id" property="activityId"/>
     */
    private ResultMapping buildResultMappingFromContext(Element context, Class<?> resultType, List<ResultFlag> flags) {
        String property = context.attributeValue("property"); // 需要被映射的属性,如:activityId
        String column = context.attributeValue("column");    // 原列,如activity_id
        return builderAssistant.buildResultMapping(resultType, property, column, flags);
    }
}

 3.2 MapperBuilderAssistant

MapperBuilderAssistant主要添加了构建resultMapping方法操作,以及获取javaType下属性的set方法的参数类型方法处理。

public class MapperBuilderAssistant extends BaseBuilder {
// step-13 新增方法
    public ResultMapping buildResultMapping(
            Class<?> resultType,
            String property,
            String column,
            List<ResultFlag> flags) {
        // 获取resultType的里属性当前SET方法的参数类型
        Class<?> javaTypeClass = resolveResultJavaType(resultType, property, null);// 列的结果类型
        TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, null);

        // 构建ResultMapping
        ResultMapping.Builder builder = new ResultMapping.Builder(configuration, property, column, javaTypeClass);
        builder.typeHandler(typeHandlerInstance);
        builder.flags(flags);
        return builder.build();
    }

     
     // new add
     // 根据resultType获取javaType
    private Class<?> resolveResultJavaType(Class<?> resultType, String property, Class<?> javaType) {
        if (javaType == null && property != null) {
            try {
                MetaClass metaResultType = MetaClass.forClass(resultType);
                // 根据当前属性获取set参数类型
                javaType = metaResultType.getSetterType(property);

            } catch (Exception ignore) {
            }
        }
        if (javaType == null) {
            javaType = Object.class;
        }
        return javaType;
    }
}

3.3 BaseBuilder

BaseBuilder主要说新增两个从注册器获取类型操作,一个是resolveClass方法里从typeAliasRegistry获取,获取不到的化直接将当前类型反射成类(通过Resources.classForName(string)),

另一个是resolveTypeHandler方法里从typeHandlerRegistry里获取java类型,前提是typeHandlerType不为空,否则返回空,但是我们上边MapperBuilderAssistant类buildResultMapping()里传的是空,所以这里一点返回空,没关系后边会填这个坑

public class BaseBuilder {
// 省略其他...
 // 根据类型名称反射成Class 类型
    protected Class<?> resolveClass(String alias) {
        if (alias == null) {
            return null;
        }
        try {
            return resolveAlias(alias);
        } catch (Exception e) {
            throw new RuntimeException("Error resolving class. Cause: " + e, e);
        }
    }
 
    // new add  获取TypeHandler
    protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
        if (typeHandlerType == null) {
            return null;
        }
        return typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
    }
}

3.4. ResultMapResolver

ResultMapResolver新增的类,在这里主要的方法就是调用助手类构建resultMap,特意封装了一个ResultMapResolver类。

ublic class ResultMapResolver {
    private final MapperBuilderAssistant assistant;
    private String id;
    private Class<?> type;
    private List<ResultMapping> resultMappings;

    public ResultMapResolver(MapperBuilderAssistant assistant, String id, Class<?> type, List<ResultMapping> resultMappings) {
        this.assistant = assistant;
        this.id = id;
        this.type = type;
        this.resultMappings = resultMappings;
    }

    public ResultMap resolve() {
        // 通过助手将resultMapping放入resultMap里
        return assistant.addResultMap(this.id, this.type, this.resultMappings);
    }
}

3.5.ResultMapping

ResultMapping类的修改,新增了静态内部类帮忙构造ResultMapping所需要的属性,其中 resolveTypeHandler()方法就是解决上面说的typeHandler为空的问题,

public class ResultMapping {
    private Configuration configuration;
    private String property;
    private String column;
    private Class<?> javaType;
    private TypeHandler<?> typeHandler;
    private List<ResultFlag> flags;

    ResultMapping() {
    }

    public static class Builder {
        private ResultMapping resultMapping = new ResultMapping();

        public Builder(Configuration configuration, String property, String column,
                       Class<?> javaType) {
            resultMapping.configuration = configuration;
            resultMapping.property = property;
            resultMapping.column = column;
            resultMapping.javaType = javaType;
            resultMapping.flags = new ArrayList<>();
        }

        public Builder typeHandler(TypeHandler<?> typeHandler) {
            resultMapping.typeHandler = typeHandler;
            return this;
        }

        public Builder flags(List<ResultFlag> flags) {
            resultMapping.flags = flags;
            return this;
        }

        public ResultMapping build() {
            // 构建ResultMapping时TypeHandler为空,再次获取TypeHandler
            resolveTypeHandler();
            return resultMapping;
        }

        private void resolveTypeHandler() {
            if (resultMapping.typeHandler == null && resultMapping.javaType != null) {// javaType不为空,因为根据此来获取类型处理器
                Configuration configuration = resultMapping.configuration;
                TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
                resultMapping.typeHandler = typeHandlerRegistry.getTypeHandler(resultMapping.javaType, null);
            }
        }
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public String getProperty() {
        return property;
    }

    public void setProperty(String property) {
        this.property = property;
    }

    public String getColumn() {
        return column;
    }

    public Class<?> getJavaType() {
        return javaType;
    }

    public TypeHandler<?> getTypeHandler() {
        return typeHandler;
    }

    public List<ResultFlag> getFlags() {
        return flags;
    }

}

3.6.resultMap

resultMap就只是更改了在build方法时将resultMapping.getColumn()转为大写存入mappedColumns中。

public class ResultMap {
    private String id;
    private Class<?> type;
    private List<ResultMapping> resultMappings;
    private Set<String> mappedColumns;

    public ResultMap() {
    }

    public static class Builder {
        private ResultMap resultMap = new ResultMap();

        public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
            resultMap.id = id;
            resultMap.type = type;
            resultMap.resultMappings = resultMappings;
        }


        public ResultMap build() {
            resultMap.mappedColumns = new HashSet<>();
            // new add
            for (ResultMapping resultMapping : resultMap.resultMappings) {
                final String column = resultMapping.getColumn();
                if (column != null) {
                    // 填充到已映射的列转化为大写,后期和查询出来的属性对比
                    resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
                }
            }
            return resultMap;
        }
    }

    public String getId() {
        return id;
    }

    public Class<?> getType() {
        return type;
    }

    public List<ResultMapping> getResultMappings() {
        return resultMappings;
    }

    public Set<String> getMappedColumns() {
        return mappedColumns;
    }

    public List<ResultMapping> getPropertyResultMappings() {
        return resultMappings;
    }
}

3.7.Configuration

Configuration类中添加了resultMap的存储与获取

    // 结果映射,存在Map里-新增
    protected final Map<String, ResultMap> resultMaps = new HashMap<>();

    public ResultMap getResultMap(String id) {
        return resultMaps.get(id);
    }

    public void addResultMap(ResultMap resultMap) {
        resultMaps.put(resultMap.getId(), resultMap);
    }

3.8.DefaultResultSetHandler

前面是解析,到DefaultResultSetHandler就是反射和使用了,在getRowValue()方法里添加新的方法applyPropertyMappings()迎来处理resultMap映射,然后跟上节其他的自动映射的操作一样获取java类型,根据类型从结果集获取结果值,然后通过反射工具往对象里设置值。

 private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
        // 根据返回值类型,实例化对象
        Object resultObject = createResultObject(rsw, resultMap, null);
        if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
            final MetaObject metaObject = configuration.newMetaObject(resultObject);
            // 自动映射,把每列的值都赋给对应的字段上
            applyAutomaticMappings(rsw, resultMap, metaObject, null);
            // Map映射,根据映射类型赋值到字段
            applyPropertyMappings(rsw, resultMap, metaObject, null);
        }
        return resultObject;
    }


private boolean applyPropertyMappings(ResultSetWrapper rsw,
                                          ResultMap resultMap,
                                          MetaObject metaObject,
                                          String columnPrefix) throws SQLException {
        // 已映射的列名
        final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
        boolean foundValues = false;
        // 获取ResultMapping数据
        final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
        for (ResultMapping propertyMapping : propertyMappings) {
            final String column = propertyMapping.getColumn();
            if (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) {
                // 获取值
                final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
                // 根据对应列类型获取值
                Object value = typeHandler.getResult(rsw.getResultSet(), column);
                // 设置值
                final String property = propertyMapping.getProperty();
                if (value != NO_VALUE && property != null && value != null) {
                    // 通过反射工具类设置属性值
                    metaObject.setValue(property, value);
                    foundValues = true;
                }
            }
        }
        return foundValues;
    }

4.测试准备

添加新的xml,名字为:Activity_Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="df.middleware.mybatis.dao.IActivityDao">
    <resultMap id="activityMap" type="df.middleware.mybatis.po.Activity">
        <id column="id" property="id"/>
        <result column="activity_id" property="activityId"/>
        <result column="activity_name" property="activityName"/>
        <result column="activity_desc" property="activityDesc"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <select id="queryActivityById" parameterType="java.lang.Long" resultMap="activityMap">
        SELECT activity_id, activity_name, activity_desc, create_time, update_time
        FROM activity
        WHERE activity_id = #{activityId}
    </select>
</mapper>

 mybatis-config-datasource.xml,配置成Activity_Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="UNPOOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis_demo?useUnicode=true"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!-- XML 配置 -->
        <mapper resource="mapper/Activity_Mapper.xml"/>
    </mappers>

</configuration>

 单元测试

  private SqlSession sqlSession;

    @Before
    public void init() throws IOException {
        // 1. 从SqlSessionFactory中获取SqlSession
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));
        sqlSession = sqlSessionFactory.openSession();
    }


    // 单元
    @Test
    public void test_queryActivityById() {
        // 1. 获取映射器对象
        IActivityDao dao = sqlSession.getMapper(IActivityDao.class);
        // 2. 测试验证
        Activity res = dao.queryActivityById(100001L);
        System.out.println(res.getActivityName());

        //logger.info("测试结果:{}", JSON.toJSONString(res));
    }

建表Sql语句

DROP TABLE IF EXISTS `activity`;
CREATE TABLE `activity`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  `activity_id` bigint(20) NOT NULL COMMENT '活动ID',
  `activity_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '活动名称',
  `activity_desc` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '活动描述',
  `create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `unique_activity_id`(`activity_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '活动配置' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of activity
-- ----------------------------
INSERT INTO `activity` VALUES (1, 100001, '活动名', '测试活动', '2021-08-08 20:14:50', '2021-08-08 20:14:50');
INSERT INTO `activity` VALUES (3, 100002, '活动名', '测试活动', '2021-10-05 15:49:21', '2021-10-05 15:49:21');

 单元测试完毕,拿到了想要的值

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/687242.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

发送图文并茂的html格式的邮件

本文介绍如何生成和发送包含图表和表格的邮件&#xff0c;涉及echarts图表转换为图片、图片内嵌到html邮件内容中、html邮件内容生成、邮件发送方法等 一、图表处理 因为html格式的邮件不支持echarts,也不支持js执行&#xff0c;所以图表需要转换为图片内嵌在邮件内容中 因为平…

Sui Builder House京都站倒计时!

Sui Builder House京都站将于6月29日-30日举行&#xff0c;为世界各地的开发者提供身临其境地学习和交流的机会。查看Sui Builder House京都站的五个亮点 活动信息 &#x1f4c6; Builder House时间&#xff1a;2023年6月29日 — 6月30日 &#x1f4cd; 活动地址&#xff1a;…

NetSuite OAuth1.0中InvalidSignature问题

本周闭关写代码&#xff0c;用Java通过TBA方式访问NetSuite REST Webservices。由于是手生&#xff0c;卡在InvalidSignature报错上&#xff0c;在这个问题上被卡了一整天。 直到终于到来的Aha时刻。 在NetSuite中的样例代码是PHP的&#xff0c; 我平移到Java后&#xff0c;代码…

【云原生丶Docker】虚拟化技术简介

什么是虚拟化技术&#xff1f; Docker 是一款基于容器虚拟化技术构建的软件&#xff0c;那到底什么虚拟化技术呢&#xff1f;在学习 Docker 之前&#xff0c;先简单了解下虚拟化技术。 虚拟化是云原生的实现基础&#xff0c;它能够帮助我们更加有效地利用物理计算机硬件。 虚…

clip-interrogator本地部署

clip-interrogator本地部署 1. 克隆源码2. 创建虚拟环境及配置3. 下载远程相关文件3.1 下载Salesforce相关文件失败3.2 下载cache相关文件失败3.3 解决库不完整问题 4. 测试代码4.1 脚本一4.2 脚本二4.3 测试run_cli.py文件4.4 测试run_gradio.py文件 源码链接&#xff1a;http…

d3dcompiler_47.dll缺失怎么办?d3dcompiler_47.dll缺失修复方法(详解)

d3dcompiler_47.dll在Windows操作系统和 DirectX应用程序中使用此 DLL 文件,包括游戏&#xff0c;图形处理软件&#xff0c;CAD软件和其他3D应用程序中。如果电脑提示“由于找不到d3dcompiler_47.dll&#xff0c;无法继续执行此代码”&#xff0c;“d3dcompiler_47.dll缺失”&a…

Docker卷与持久化数据

数据主要分为两类&#xff0c;持久化的与非持久化的。 两者都很重要&#xff0c;并且Docker均有对应的支持方式。每个Docker容器都有自己的非持久化存储。非持久化存储自动创建&#xff0c;从属于容器&#xff0c;生命周期与容器相同。这意味着删除容器也会删除全部非持久化数…

网络维护岗位个人求职简历

网络维护岗位个人求职简历1 姓名&#xff1a; 国籍&#xff1a;中国 目前所在地&#xff1a;广州民族&#xff1a;汉族 户口所在地&#xff1a;湖南身材&#xff1a;170cm64kg 婚姻状况&#xff1a;未婚年龄&#xff1a;28 求职意向及工作经历 应聘职位&#xff1a; 工作年限&a…

day14_面向对象的三大特征之一(继承)

继承的概述 Java是对现实社会的模拟&#xff0c;现实中有儿子可以继承父亲的财产&#xff0c;因此有了富二代的。 java中多个类中存在相同属性和行为时&#xff0c;将这些内容抽取到单独一个类中&#xff0c;那么多个类中无需再定义这些属性和行为&#xff0c;只需要和抽取出来…

【Java-SpringBoot+Vue+MySql】项目开发杂记

目录 1、关闭eslint 2、 新建路由 3、安装jQuery依赖-在Vue使用JQuery语法 4、MySQL———数据全部清除&#xff0c;自增归零 5、前后端数据传递——增加功能 6、使用element-ui获取当前表格中的数据——删除功能 1、关闭eslint 2、 新建路由 {path: /user,component: Lay…

LLM - Hugging Face 工程 BERT base model (uncased) 配置

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/131400428 BERT是一个在大量英文数据上以自监督的方式预训练的变换器模型。这意味着它只是在原始文本上进行预训练&#xff0c;没有人以…

RabbitMQ实现延迟消息,RabbitMQ使用死信队列实现延迟消息,RabbitMQ延时队列插件

文章目录 一、什么是延迟消息1、队列的属性2、消息的属性3、什么是死信4、使用死信队列的缺点5、延时消息插件 二、JavaAPI利用死信队列实现RabbitMQ延迟消息1、代码实现2、基本流程 三、JavaAPI利用插件实现RabbitMQ延迟消息1、代码实现2、基本原理 四、Springboot利用死信队列…

5分钟自建可视化平台,在线拖拽组件也太方便了!

一、前言 我们在完成 C 端用户的业务需求时&#xff0c;发现大量电商推广页面的逻辑复杂度和交互程度都比较低&#xff0c;却要消耗大量的开发资源。同时在后台管理系统的开发过程中&#xff0c;我们也发现一些相同套路的页面在不同场景下需要重复开发&#xff0c;即使我们封装…

如何提升企业采购绩效?提高采购绩效的方法

采购是任何企业的一个重要方面&#xff0c;它在确保企业平稳高效运行方面发挥着重要作用。作为一名采购专业人员&#xff0c;你有责任确保你的企业以正确的质量、数量和价格获得所需的货物和服务。 本文将讨论一些最佳做法&#xff0c;这些做法可以帮助你提高采购专业人员的绩…

【032】C++高级开发之多态技术详解(虚函数最全讲解)

C的多态技术&#xff08;虚函数&#xff09;详解 引言一、多态的概念二、虚函数2.1、父类指针保存子类空间地址 带来的问题2.2、虚函数的定义2.3、虚函数的动态绑定机制2.4、重载、重定义、重写的区别 三、纯虚函数3.1、纯虚函数的定义方式3.2、纯虚函数的案例&#xff1a;饮品…

mvnd 安装和 idea配置mvnd

一、mvnd 安装 1、mvnd 下载地址 https://github.com/apache/maven-mvnd 2、安装 解压并配置环境变量到path 打开CMD终端&#xff0c;输入 mvnd -v 看到如下信息提示则安装成功 默认使用的是内置的maven&#xff0c;配置修改&#xff0c;兼容本地的maven 打开 Maven-mvn…

基于Java医院医患管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

如何测试webservice接口

看过网上很多对Web Service的解释&#xff0c;有些文章写得通俗易懂&#xff0c;但是缺少深度解读&#xff1b;有的文章图文并茂&#xff0c;但是没有现成案例&#xff1b;有的文章非常详细&#xff0c;但是没有直观感受。 于是&#xff0c;我想从测试一个web service接口的角…

【复习《剑指Offer》6-12题】【每天40分钟,我们一起用50天刷完 (剑指Offer)】第七天 7/50

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

围“桌”详谈|本周六直播,一起聊聊「发版」那些事儿!

&#x1f30d; 你想要深入了解 ShardingSphere 、与业内技术大咖「面对面」探讨数据库领域的前沿趋势吗&#xff1f;那就一定不能错过由 ShardingSphere 社区出品、SphereEx 公司赞助的全球软件工程师必看的直播节目——《对谈时刻》&#xff01;每期节目&#xff0c;我们都会邀…