mybatis-plus中更新null值的问题

news2024/11/20 8:33:21

文章目录

  • 前言
  • 一、情景介绍
  • 二、方法分析
  • 三、原因分析
  • 四、解决方式
  • 五、方式扩展
  • 总结


前言

本文主要介绍 mybatis-plus 中常使用的 update 相关方法的区别,以及更新 null 的方法有哪些等。

至于为什么要写这篇文章,首先是在开发中确实有被坑过几次,导致某些字段设置为 null 值设置不上,其次是官方文档对于这块内容并没有提供一个很完善的解决方案,所以我就总结一下。


一、情景介绍

关于 Mybatis-plus 这里我就不多做介绍了,如果之前没有使用过该项技术的可参考以下链接进行了解。

mybatis-plus 官方文档:https://baomidou.com/

在这里插入图片描述

我们在使用 mybatis-plus 进行开发时,默认情况下, mybatis-plus 在更新数据时时会判断字段是否为 null,如果是 null 则不设置值,也就是更新后的该字段数据依然是原数据,虽然说这种方式在一定程度上可以避免数据缺失等问题,但是在某些业务场景下我们就需要设置某些字段的数据为 null。


二、方法分析

这里我准备了一个 student 表进行测试分析,该表中仅有两条数据:

mysql> SELECT * FROM student;
+-----+---------+----------+
|  id |   name  |   age   |
+-----+---------+----------+
|  1  |  米大傻  |   18    |
+-----+---------+----------+
|  2  |  米大哈  |   20    |
+-----+---------+----------+

在 mybatis-plus 中,我们的 mapper 类都会继承 BaseMapper 这样一个类

public interface StudentMapper extends BaseMapper<Student> {

}

进入到 BaseMapper 这个接口可以查看到该类仅有两个方法和更新有关(这里我就不去分析 IService 类中的那些更新方法了,因为那些方法低层最后也是调用了 BaseMapper 中的这两个 update 方法)

在这里插入图片描述

所以就从这两个方法入手分析:

  • updateById() 方法
    @Test
    public void testUpdateById() {
        Student student = studentMapper.selectById(1);
        student.setName("李大霄");
        student.setAge(null);
        studentMapper.updateById(student);
    }

在这里插入图片描述

可以看到使用 updateById() 的方法更新数据,尽管在代码中将 age 赋值为 null,但是最后执行的 sql 确是:

UPDATE student SET name = '李大霄' WHERE id = 1

也就是说在数据库中,该条数据的 name 值发生了变化,但是 age 保持不变

mysql> SELECT * FROM student WHERE id = 1;
+-----+---------+----------+
|  id |   name  |   age   |
+-----+---------+----------+
|  1  |  李大霄  |   18    |
+-----+---------+----------+
  • update() 方法 — UpdateWrapper 不设置属性

恢复 student 表中的数据为初始数据。

    @Test
    public void testUpdate() {
        Student student = studentMapper.selectById(1);
        student.setName("李大霄");
        student.setAge(null);
        studentMapper.update(student, new UpdateWrapper<Student>()
                .lambda()
                .eq(Student::getId, student.getId())
        );
    }

在这里插入图片描述

可以看到如果 update() 方法这样子使用,效果是和 updateById() 方法是一样的,为 null 的字段会直接跳过设置,执行 sql 与上面一样:

UPDATE student SET name = '李大霄' WHERE id = 1
  • update() 方法 — UpdateWrapper 设置属性

恢复 student 表中的数据为初始数据。

因为 UpdateWrapper 是可以去字段属性的,所以再测试下 UpdateWrapper 中设置为 null 值是否能起作用

    @Test
    public void testUpdateSet() {
        Student student = studentMapper.selectById(1);
        student.setName("李大霄");
        student.setAge(null);
        studentMapper.update(student, new UpdateWrapper<Student>()
                .lambda()
                .eq(Student::getId, student.getId())
                .set(Student::getAge, student.getAge())
        );
    }

在这里插入图片描述

从打印的日志信息来看,是可以设置 null 值的,sql 为:

UPDATE student SET name='李大霄', age=null WHERE id = 1

查看数据库:

mysql> SELECT * FROM student WHERE id = 1;
+-----+---------+----------+
|  id |   name  |   age   |
+-----+---------+----------+
|  1  |  李大霄  |   NULL  |
+-----+---------+----------+

三、原因分析

从方法分析中我们可以得出,如果不使用 UpdateWrapper 进行设置值,通过 BaseMapper 的更新方法是没法设置为 null 的,可以猜出 mybatis-plus 在默认的情况下就会跳过属性为 null 值的字段,不进行设值。

通过查看官方文档可以看到, mybatis-plus 有几种字段策略:

在这里插入图片描述

也就是说在默认情况下,字段策略应该是 FieldStrategy.NOT_NULL 跳过 null 值的

可以先设置实体类的字段更新策略为 FieldStrategy.IGNORED 来验证是否会忽略判断 null

@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value="Student对象", description="学生表")
public class Student extends BaseEntity {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "主键ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @ApiModelProperty(value = "姓名")
    @TableField(updateStrategy = FieldStrategy.IGNORED) // 设置字段策略为:忽略判断
    private String name;

    @ApiModelProperty(value = "年龄")
    @TableField(updateStrategy = FieldStrategy.IGNORED) // 设置字段策略为:忽略判断
    private Integer age;
    
}

再运行以上 testUpdateById()testUpdate() 代码

在这里插入图片描述

从控制台打印的日志可以看出,均执行 sql:

UPDATE student SET name='李大霄', age=null WHERE id = 1

所以可知将字段更新策略设置为: FieldStrategy.IGNORED 就能更新数据库的数据为 null

翻阅 @TableField 注解的源码:

在这里插入图片描述

可以看到在源码中,如果没有进行策略设置的话,它默认的策略就是 FieldStrategy.DEFAULT 的,那为什么最后处理的结果是使用了 NOT_NULL 的策略呢?

再追进源码中,可以得知每个实体类都对应一个 TableInfo 对象,而实体类中每一个属性都对应一个 TableFieldInfo 对象

在这里插入图片描述

进入到 TableFieldInfo 类中查看该类的属性是有 updateStrategy(修改属性策略的)

在这里插入图片描述

查看构造方法 TableFieldInfo()

在这里插入图片描述

可以看到如果字段策略为 FieldStrategy.DEFAULT,取的是 dbConfig.getUpdateStrategy(),如果字段策略不等于 FieldStrategy.DEFAULT,则取注解类 TableField 指定的策略类型。

点击进入对象 dbConfig 所对应的类 DbConfig

在这里插入图片描述

可以看到在这里 DbConfig 默认的 updateStrategy 就是 FieldStrategy.NOT_NULL,所以说 mybatis-plus 默认情况下就是跳过 null 值不设置的。

那为什么通过 UpdateWrapperset 方法就可以设置值呢?

同样取查看 set() 方法的源码:

在这里插入图片描述

看到这行代码已经明了,因为可以看到它是通过 String.format("%s=%s",字段,值) 拼接 sql 的方式,也是是说不管设置了什么值都会是 字段=值 的形式,所以就会被设置上去。


四、解决方式

从上文分析就可以知道已经有两种方式实现更新 null ,不过除此之外就是直接修改全局配置,所以这三种方法分别是:

  • 方式一:修改单个字段策略模式
  • 方式二:修改全局策略模式
  • 方式三:使用 UpdateWrapper 进行设置


方式一:修改单个字段策略模式

这种方式在上文已经叙述过了,直接在实体类上指定其修改策略模式即可

@TableField(updateStrategy = FieldStrategy.IGNORED)

在这里插入图片描述

如果某些字段需要可以在任何时候都能更新为 null,这种方式可以说是最方便的了。


方式二:修改全局策略模式

通过刚刚分析源码可知,如果没有指定字段的策略,取的是 DbConfig 中的配置,而 DbConfigGlobalConfig 的静态内部类

在这里插入图片描述

所以我们可以通过修改全局配置的方式,改变 updateStrategy 的策略不就行了吗?

yml 方式配置如下

mybatis-plus:
  global-config:
    db-config:
      update-strategy: IGNORED

注释 @TableField(updateStrategy = FieldStrategy.IGNORED)

在这里插入图片描述

恢复 student 表中的数据为初始数据,进行测试。

在这里插入图片描述
可以看到是可行的,执行的 sql 为:

UPDATE student SET name='李大霄', age=null WHERE id = 1

但是值得注意的是,这种全局配置的方法会对所有的字段都忽略判断,如果一些字段不想要修改,也会因为传的是 null 而修改,导致业务数据的缺失,所以并不推荐使用。


方式三:使用 UpdateWrapper 进行设置

这种方式前面也提到过了,就是使用 UpdateWrapper 或其子类进行 set 设置,例如:

        studentMapper.update(student, new UpdateWrapper<Student>()
                .lambda()
                .eq(Student::getId, student.getId())
                .set(Student::getAge, null)
                .set(Student::getName, null)
        );

这种方式对于在某些场合,需要将少量字段更新为 null 值还是比较方便,灵活的。

PS:除此之外还可以通过直接在 mapper.xml 文件中写 sql,但是我觉得这种方式就有点脱离 mybatis-plus 了,就是 mybatis 的操作,所以就不列其上。


五、方式扩展

虽然上面提供了一些方法来更新 null 值,但是不得不说,各有弊端,虽然说是比较推荐使用 UpdateWrapper 来更新 null 值,但是如果在某个表中,某个业务场景下需要全量更新 null 值,而且这个表的字段又很多,一个个 set 真的很折磨人,像 tk.mapper 都有方法进行全量更新 null 值,那有没有什么方法可以全量更新?

虽然 mybaatis-plus 没有,但是可以自己去实现,我是看了起风哥:让mybatis-plus支持null字段全量更新 这篇博客,觉得蛮好的,所以整理下作此分享。

  • 实现方式一:使用 UpdateWrapper 循环拼接 set

提供一个已 set 好全部字段 UpdateWrapper 对象的方法:

public class WrappersFactory {

	// 需要忽略的字段
    private final static List<String> ignoreList = new ArrayList<>();

    static {
        ignoreList.add(CommonField.available);
        ignoreList.add(CommonField.create_time);
        ignoreList.add(CommonField.create_username);
        ignoreList.add(CommonField.update_time);
        ignoreList.add(CommonField.update_username);
        ignoreList.add(CommonField.create_user_code);
        ignoreList.add(CommonField.update_user_code);
        ignoreList.add(CommonField.deleted);
    }

    public static <T> LambdaUpdateWrapper<T> updateWithNullField(T entity) {
        UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
        List<Field> allFields = TableInfoHelper.getAllFields(entity.getClass());
        MetaObject metaObject = SystemMetaObject.forObject(entity);
        for (Field field : allFields) {
            if (!ignoreList.contains(field.getName())) {
                Object value = metaObject.getValue(field.getName());
                updateWrapper.set(StringUtils.camelToUnderline(field.getName()), value);
            }
        }
        return updateWrapper.lambda();
    }
}

使用:

studentMapper.update(
	WrappersFactory.updateWithNullField(student)
		.eq(Student::getId,id)
);

或者可以定义一个 GaeaBaseMapper(全局 Mapper) 继承 BaseMapper,所有的类都继承自 GaeaBaseMapper,例如:

public interface StudentMapper extends GaeaBaseMapper<Student> {

}

编写 updateWithNullField() 方法:

public interface GaeaBaseMapper<T extends BaseEntity> extends BaseMapper<T> {

    /**
     * 返回全量修改 null 的 updateWrapper
     */
    default LambdaUpdateWrapper<T> updateWithNullField(T entity) {
        UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
        List<Field> allFields = TableInfoHelper.getAllFields(entity.getClass());
        MetaObject metaObject = SystemMetaObject.forObject(entity);
        allFields.forEach(field -> {
            Object value = metaObject.getValue(field.getName());
            updateWrapper.set(StringUtils.cameToUnderline(field.getName()), value);
        });
        return updateWrapper.lambda();
    }
}

StringUtils.cameToUnderline() 方法


    /**
     * 驼峰命名转下划线
     * @param str 例如:createUsername
     * @return 例如:create_username
     */
    public static String cameToUnderline(String str) {
        Matcher matcher = Pattern.compile("[A-Z]").matcher(str);
        StringBuilder builder = new StringBuilder(str);
        int index = 0;
        while (matcher.find()) {
            builder.replace(matcher.start() + index, matcher.end() + index, "_" + matcher.group().toLowerCase());
            index++;
        }
        if (builder.charAt(0) == '_') {
            builder.deleteCharAt(0);
        }
        return builder.toString();
    }

使用:

    @Test
    public void testUpdateWithNullField() {
        Student student = studentMapper.selectById(1);
        student.setName("李大霄");
        student.setAge(null);
        studentMapper
                .updateWithNullField(student)
                .eq(Student::getId, student.getId());
    }
  • 实现方式二:mybatis-plus常规扩展—实现 IsqlInjector

像 mybatis-plus 中提供的批量添加数据的 InsertBatchSomeColumn 方法类一样

在这里插入图片描述

首先需要定义一个 GaeaBaseMapper(全局 Mapper) 继承 BaseMapper,所有的类都继承自 GaeaBaseMapper,例如:

public interface StudentMapper extends GaeaBaseMapper<Student> {

}

然后在这个 GaeaBaseMapper 中添中全量更新 null 的方法

public interface StudentMapper extends GaeaBaseMapper<Student> {

	/**
     * 全量更新null
     */
    int updateWithNull(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
}

构造一个方法 UpdateWithNull 的方法类

public class UpdateWithNull extends AbstractMethod {

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        // 处理逻辑
        return null;
    }

}

之前说过可以设置字段的更新策略属性为:FieldStrategy.IGNORED 使其可以更新 null 值,现在方法参数中有 TableInfo 对象,通过 TableInfo 我们可以拿到所有的 TableFieldInfo,通过反射设置所有的 TableFieldInfo.updateStrategyFieldStrategy.IGNORED,然后参照 mybatis-plus 自带的 Update.java 类的逻辑不就行了。

Update.java 源码:

package com.baomidou.mybatisplus.core.injector.methods;

import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

public class Update extends AbstractMethod {
    public Update() {
    }

    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        SqlMethod sqlMethod = SqlMethod.UPDATE;
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), this.sqlSet(true, true, tableInfo, true, "et", "et."), this.sqlWhereEntityWrapper(true, tableInfo), this.sqlComment());
        SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, modelClass);
        return this.addUpdateMappedStatement(mapperClass, modelClass, this.getMethod(sqlMethod), sqlSource);
    }
}

所以 UpdateWithNull 类中的代码可以这样写:

import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

import java.lang.reflect.Field;
import java.util.List;

/**
 * 全量更新 null
 */
public class UpdateWithNull extends AbstractMethod {

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {

        // 通过 TableInfo 获取所有的 TableFieldInfo
        final List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        // 遍历 fieldList
        for (final TableFieldInfo tableFieldInfo : fieldList) {
            // 反射获取 TableFieldInfo 的 class 对象
            final Class<? extends TableFieldInfo> aClass = tableFieldInfo.getClass();
            try {
                // 获取 TableFieldInfo 类的 updateStrategy 属性
                final Field fieldFill = aClass.getDeclaredField("updateStrategy");
                fieldFill.setAccessible(true);
                // 将 updateStrategy 设置为 FieldStrategy.IGNORED
                fieldFill.set(tableFieldInfo, FieldStrategy.IGNORED);
            } catch (final NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        SqlMethod sqlMethod = SqlMethod.UPDATE;
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(),
                this.sqlSet(true, true, tableInfo, true, "et", "et."),
                this.sqlWhereEntityWrapper(true, tableInfo), this.sqlComment());
        SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, modelClass);
        return this.addUpdateMappedStatement(mapperClass, modelClass, this.getMethod(sqlMethod), sqlSource);
    }

    public String getMethod(SqlMethod sqlMethod) {
        return "updateWithNull";
    }

}

再声明一个 IsqlInjector 继承 DefaultSqlInjector

public class BaseSqlInjector extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        // 此 SQL 注入器继承了 DefaultSqlInjector (默认注入器),调用了 DefaultSqlInjector 的 getMethodList 方法,保留了 mybatis-plus 自带的方法
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        // 批量插入
        methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
        // 全量更新 null
        methodList.add(new UpdateWithNull());
        return methodList;
    }

}

然后在 mybatis-plus 的配置类中将其配置为 springbean 即可:

@Slf4j
@Configuration
@EnableTransactionManagement
public class MybatisPlusConfig {

	...

	@Bean
    public BaseSqlInjector baseSqlInjector() {
        return new BaseSqlInjector();
    }

	...
}

我写的目录结构大概长这样(仅供参考):

在这里插入图片描述

恢复 student 表中的数据为初始数据,进行测试。

测试代码:

    @Test
    public void testUpdateWithNull() {
        Student student = studentMapper.selectById(1);
        student.setName("李大霄");
        student.setAge(null);
        studentMapper.updateWithNull(student,
                new UpdateWrapper<Student>()
                        .lambda()
                        .eq(Student::getId, student.getId())
        );

        student.setName(null);
        student.setAge(18);
        studentMapper.updateById(student);
    }

sql 打印如下:

在这里插入图片描述

可以看到使用 updateWithNull() 方法更新了 null。


总结

以上就是我对 mybatis-plus 更新 null 值问题做的探讨,结合测试实例与源码分析,算是解释得比较明白了,尤其是最后扩展的两种方法自认为是比较符合我的需求的,最后扩展的那两种方法都在实体类 Mapper 和 mybatis-plus 的 BaseMapper 中间多抽了一层 GaeaBaseMapper ,这种方式我是觉得比较推荐的,增加了系统的扩展性和灵活性。


扩展 MybatisPlus update 更新时指定要更新为 null 的方法:https://blog.csdn.net/qq_36279799/article/details/132585263
让mybatis-plus支持null字段全量更新:https://blog.csdn.net/a807719447/article/details/129008176
Mybatis-Plus中update()和updateById()将字段更新为null:https://www.jb51.net/article/258648.htm
Mybatis-Plus中update更新操作用法:https://blog.csdn.net/weixin_43888891/article/details/131142279
MyBatis-plus源码解析:https://www.cnblogs.com/jelly12345/p/15628277.html

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

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

相关文章

Json文件序列化读取

Json文件 [{"name":"清华大学","location":"北京","grade":"1"},{"name":"北京大学","location":"北京","grade":"2"} ] 安装包 代码 Program.c…

睿趣科技:抖音开网店真的可以相信吗

随着社交媒体的快速发展&#xff0c;抖音已经成为了一个备受欢迎的平台&#xff0c;尤其是对于那些希望在电商领域有所作为的人们。许多人开始在抖音上开设网店&#xff0c;以获取额外的收入或建立自己的事业。然而&#xff0c;对于抖音开网店是否真的可以相信&#xff0c;存在…

java基础学习之变量与运算符

一&#xff0c;关键字 1&#xff0c;定义&#xff1a;被java语言赋予了特殊含义&#xff0c;用作专门用途的字符串或单词。 2&#xff0c;特点&#xff1a;关键字全都是小写字母。 3&#xff0c;关键字一共50个&#xff0c;其中const和goto是保留字。 4&#xff0c;true&#x…

硕士应聘大专老师

招聘信息 当地人社局、学校&#xff08;官方&#xff09; 公众号&#xff08;推荐&#xff09;&#xff1a; 辅导员招聘 厦门人才就业信息平台 高校人才网V 公告出完没多久就要考试面试&#xff0c;提前联系当地院校&#xff0c;问是否招人。 校招南方某些学校会直接去招老师。…

Web自动化测试测试常见BUG

【软件测试面试突击班】如何逼自己一周刷完软件测试八股文教程&#xff0c;刷完面试就稳了&#xff0c;你也可以当高薪软件测试工程师&#xff08;自动化测试&#xff09; 1.翻页 翻页时&#xff0c;没有加载数据为空&#xff0c;第二页数据没有请求 翻页时&#xff0c;重复请…

“淘宝” 开放平台接口设计思路(内附API接口免费接入地址)

最近对接的开放平台有点多&#xff0c;像淘宝、天猫、京东、拼多多、快手、抖音等电商平台的开放平台基本对接了个遍&#xff0c;什么是CRUD BODY也许就是这样的吧&#xff01;&#xff01;&#xff01; 经过这几天的整理&#xff0c;脑子里大概有了个开放平台接口的设计套路&…

mysql优化之索引

索引官方定义&#xff1a;索引是帮助mysql高效获取数据的数据结构。 索引的目的在于提高查询效率&#xff0c;可以类比字典。 可以简单理解为&#xff1a;排好序的快速查找数据结构 在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这种数据…

面试题:ElasticSearch是什么?应用场景是什么?

文章目录 1、什么是搜索2、如果用数据库做搜索会怎么样3、什么是全文检索、倒排索引和Lucene4、ElasticSearch是什么ElasticSearch的功能ElasticSearch的应用场景ElasticSearch的特点 ElasticSearch是一个分布式&#xff0c;高性能、高可用、可伸缩的搜索和分析系统 看了上面这…

泛型编程<T extends Comparable<? super T>>是什么意思

今天看到了两个这样的写法,非常好奇。 <T extends Comparable<? super T>>public class BplusTree<K extends Comparable </K/>,V>下面是不同人对这种写法的解释 大概理解的意思是实现不同类之间属性的对比 转载链接 这段代码是什么意思呢 public…

小米系列机型--MIUI14关闭广告 推送广告 开屏广告 通用其他miui版本

在现代社会中&#xff0c;手机广告已经成为我们日常生活中的一种困扰。每当我们使用手机时&#xff0c;不断弹出的广告不仅打扰了我们的正常操作&#xff0c;还消耗了我们的流量和电池电量&#xff0c;以小米机型为例.根据下方的设置完以后基本手机就相对来说很干净。 以前博文…

2000-2019年高新技术企业统计情况

2000-2019年高新技术企业统计情况 1、时间&#xff1a;2000-2019年 2、指标&#xff1a;统计年度、所属省份、所属省份代码、所属城市、所属城市代码、认定对象身份、认定总次数、涉及公司总数量 3、来源&#xff1a;csmar 4、指标说明&#xff1a; SgnYear [统计年度] - …

高效查询大量快递信息,轻松掌握技巧

在如今快节奏的生活中&#xff0c;快递已经成为我们日常不可或缺的一部分。然而&#xff0c;对于一些忙碌的人来说&#xff0c;单个查询每一个快递单号可能会浪费太多时间。因此&#xff0c;我们需要一款可以帮助我们批量查询快递的软件。 在市场上&#xff0c;有很多款专门用于…

vue+express、gitee pm2部署轻量服务器(20230923)

一、代码配置 前后端接口都保持 127.0.0.1:3000 vue 项目 创建文件 pm2.config.cjs module.exports {apps: [{name: xin-web, // 应用程序的名称script: npm, // 启动脚本args: run dev, // 启动脚本的参数cwd: /home/vue/xin_web, // Vite 项目的根目录interpreter: none,…

Matlab图像处理-模式识别

模式识别 模式识别就是用计算的方法根据样本的特征将样本划分到一定的类别中去。模式识别就是通过计算机用数学技术方法来研究模式的自动处理和判读&#xff0c;把环境与客体统称为“模式”。模式识别以图像处理与计算机视觉、语音语言信息处理、脑网络组、类脑智能等为主要研…

奇舞周刊第507期:通过 View Transition API 在状态之间添加丰富的过渡动画

记得点击文章末尾的“ 阅读原文 ”查看哟~ 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ 通过 View Transition API 在状态之间添加丰富的过渡动画 W3C 2023 年度全球技术大会 (TPAC2023) 于今年9月 11 - 15 日召开。W3C CSS 工作组成员 Bramus Van Damme(Google) 为本届…

学习ssh配置

三台设备ar1、ar2、ar3 ar1 通过ssh密码方式登陆ar3 ar2 通过免密码方式登陆ar3 ar3的配置如下&#xff1a;进入路由器 rsa local-key-pair creat 选择y&#xff08;yes&#xff09; Input the bits in the modulus[default 512]:2048 //需要创建密钥的长度user-interface v…

RocketMQ生产环境常见问题分析与总结

一、 RocketMQ 如何保证消息不丢失 这个是在面试时&#xff0c;关于 MQ &#xff0c;面试官最喜欢问的问题。这个问题是所有 MQ 都需要面对的一个共性问题。大致的解决思路都是一致的&#xff0c;但是针对不同的MQ 产品又有不同的解决方案。分析这个问题要从以下几个角度入手&…

【C++】bitset位图的简单模拟实现及常见面试题

文章目录 前言一、 bitset模拟实现二、 常见面试题1.给你一百亿个整数&#xff0c;找到只出现一次的数字2. 给两个文件&#xff0c;分别有100亿个整数&#xff0c;我们只有1G内存&#xff0c;如何找到两个文件交集&#xff1f; 前言 快速查找某个数据是否在一个集合中排序 去重…

Java Fluent编程

背景 Fluent Api最早是由Martin Fowler1提出的一种简洁的编程风格, 其每一步的执行都返回一个对象并可用于进一步的方法调用. 这样的编程风格可以很好的简化某些领域的开发, 并显著地提高代码的可读性和可维护性. 无论是在Java的流式api中, 还是众多DLS中都有它的身影. 原因主…

应急响应学习

网站 首先确定网站的基本信息&#xff0c;如语言&#xff0c;数据库&#xff0c;中间件&#xff0c;CMS&#xff0c;系统等。对网站可能存在的漏洞有初步判断 基于时间分析 询问管理员&#xff0c;确定网站发生异常的时间 查看日志&#xff0c;查看状态码&#xff0c;200为成…