当前位置: 首页 > news >正文

MyBatis-Plus深入 —— 条件构造器与插件管理

前言

        在前面的文章中,荔枝梳理了一个MyBatis-Plus的基本使用、配置和通用Service接口,我们发现在MyBatis-Plus的辅助增强下我们不再需要通过配置xml文件中的sql语句来实现基本的sql操作了,不愧是最佳搭档!在这篇文章中,荔枝会着重梳理有关MyBatis-Plus的两个知识点:条件构造器、分页插件和乐观锁插件,希望对有需要的小伙伴有帮助~~~


文章目录

前言

一、条件构造器

1.1 组装查询条件

1.2 组装排序条件

1.3 组装删除条件

1.4 使用QueryWrapper实现修改功能 

1.5 条件优先级

1.6 子查询

1.7 使用UpdateWrapper实现修改功能

1.8 使用Condition组装条件

1.9 LambdaQueryWrapper

1.10 LambdaUpdateWrapper 

二、分页插件

2.1 基本使用 

2.2 自定义分页插件 

三、乐观锁插件

3.1 乐观锁和悲观锁

3.2 乐观锁插件

总结


一、条件构造器

        条件构造器,顾名思义就是用来封装当前我们用来查询的条件的,条件构造器的最顶层的接口是Mapper,被AbstractWrapper继承,其下由三个子类分别是:AbstractLambdaWrapper、UpdateWrapper和QueryWrapper。

1.1 组装查询条件

//条件构造器组装查询条件@Testpublic void testWrapper(){QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.like("user_name","crj").between("age",20,30).isNotNull("email");List<User> list = userMapper.selectList(queryWrapper);list.forEach(System.out::println);}

1.2 组装排序条件

//组装排序条件@Testpublic void test1(){//查询用户信息按照年龄的降序排序,若年龄相同则按照id升序排序QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.orderByDesc("age").orderByAsc("uid");List<User> list = userMapper.selectList(queryWrapper);list.forEach(System.out::println);}

1.3 组装删除条件

//组装删除条件@Testpublic void test2(){QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.isNull("email");int result = userMapper.delete(queryWrapper);System.out.println("受影响函数"+result);}

1.4 使用QueryWrapper实现修改功能 

//实现修改功能@Testpublic void  test3(){QueryWrapper<User> queryWrapper = new QueryWrapper<>();//把年龄大于20且姓名为crj或者是邮箱为null的用户信息进行修改queryWrapper.gt("age",20).like("user_name","crj").or().isNull("email");User user = new User();user.setName("CRJ");user.setEmail("123456@123.com");int result = userMapper.update(user,queryWrapper);System.out.println(result);}

1.5 条件优先级

在and()和or()中通过Lambda表达式实现优先级操作,其中Lambda表达式中的条件优先执行。

 //条件优先级@Testpublic  void test4(){//将用户名中含有crj并且(年龄大于20或邮箱为null)的用户信息修改//lambda中的条件优先执行QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.like("user_name","crj").and(i->i.gt("age",20).or().isNull("email"));User user = new User();user.setName("CRJ");user.setEmail("123456@123.com");int result = userMapper.update(user,queryWrapper);System.out.println(result);}

1.6 子查询

//子查询@Testpublic void test5(){//查询id小于等于100的用户信息QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.inSql("uid","select uid from t_user where uid<=100");List<User> list = userMapper.selectList(queryWrapper);}

1.7 使用UpdateWrapper实现修改功能

//使用UpdateWrapper实现修改功能
//将用户名中含有crj并且(年龄大于20或邮箱为null)的用户信息修改@Testpublic  void  test6(){UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();updateWrapper.like("user_name","crj").and(i->i.gt("age",20).or().isNull("email"));updateWrapper.set("user_name","CRJ");userMapper.update(null,updateWrapper);}

1.8 使用Condition组装条件

1.9 LambdaQueryWrapper

    @Testpublic void test8(){String username = "a";Integer ageBegin = null;Integer ageEnd = 30;LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.like(StringUtils.isNotBlank(username),User::getName,username).ge(ageBegin!=null,User::getAge,ageBegin).le(ageEnd!=null,User::getAge,ageEnd);List<User> list = userMapper.selectList(queryWrapper);list.forEach(System.out::println);}

1.10 LambdaUpdateWrapper 

    @Testpublic  void  test9(){LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.like(User::getName,"crj").and(i->i.gt(User::getAge,20).or().isNull(User::getEmail));updateWrapper.set(User::getName,"CRJ");userMapper.update(null,updateWrapper);}

二、分页插件

2.1 基本使用 

 分页插件的配置类

package com.crj.mybatisplus_test.config;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;//配置类,配置MyBatisPlus的插件功能
@Configuration
@MapperScan("com.crj.mybatisplus_test.mapper")
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
}

测试类

package com.crj.mybatisplus_test;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.crj.mybatisplus_test.mapper.UserMapper;
import com.crj.mybatisplus_test.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class MyBatisPlusPluginsTest {@Autowiredprivate UserMapper userMapper;@Testpublic void test1(){Page<User> page = new Page<>(1,3);userMapper.selectPage(page,null);System.out.println(page);}
}

page对象的几个方法:

  • page.getRecords(): 获取当前页数据
  • page.getCurrent():获取当前页的页码
  • page.getSize():获取每页显示的条数
  • page.getPages(): 获取总页数
  • page.getTotal(): 获取总记录数
  • page.hasNext(): 查看有没有下一页
  • page.hasPrevious():查看有没有上一页

配置类型别名:

mybatis-plus:

        type-aliases-package:全路径 

2.2 自定义分页插件 

        之前借助条件构造器来实现分页的操作,通过查看源码知晓,selectPage要求两个参数,返回值和第一个参数都是IPage类型的,而IPage类型的接口是被Page类对象实现的,因此第一个参数一定是page对象。我们需要在userMapper接口中手写一个方法替代原来的selectPage,同时分页插件的配置文件保持不变,配置好MyBatisPlus的插件功能

UserMapper.java 

package com.crj.mybatisplus_test.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.crj.mybatisplus_test.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;@Repository
//继承MyBatis-Plus的BaseMapper接口
public interface UserMapper extends BaseMapper<User> {/*** 根据年龄查询用户信息并分页* @param page mybatis-plus提供的分页对象,必须放在第一个参数中* @param age* @return*/Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);
}

UserMapper.xml 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.crj.mybatisplus_test.mapper.UserMapper"><!--    Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);--><select id="selectPageVo" resultType="User">select uid,name,age from t_user where age > #{age}</select>
</mapper>

测试类

    @Testpublic void testPageVo(){Page<User> page = new Page<>(1,3);userMapper.selectPageVo(page,20);}

三、乐观锁插件

3.1 乐观锁和悲观锁

        说到乐观锁和悲观锁,我们经常通过一个场景来理解:我们需要对一个值为100的数进行+10操作再进行-30操作,这两步使用多线程执行。A和B线程同时取一个值为100的数C,A对C进行+10操作,B对取出来的值进行-30的操作,如果没有加锁控制,那么A处理的值D不能被B拿到且会被B覆盖。对于加锁这里简单归纳两种:乐观锁和悲观锁,悲观锁会格外注重线程安全,只有等A操作完后才能由B取值;而乐观锁则是通过版本控制的方式来检测是否C被修改了。

未加锁的场景模拟

实体类

package com.crj.mybatisplus_test.pojo;import lombok.Data;@Data
public class Product {private Long id;private String name;private Integer price;private Integer version;}

mapper接口

package com.crj.mybatisplus_test.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.crj.mybatisplus_test.pojo.Product;
import org.springframework.stereotype.Service;@Service
public interface ProductMapper extends BaseMapper<Product> {}

测试类

package com.crj.mybatisplus_test;import com.crj.mybatisplus_test.mapper.ProductMapper;
import com.crj.mybatisplus_test.pojo.Product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;//乐观锁插件使用
@SpringBootTest
public class MyBatisLockTest {@Autowiredprivate ProductMapper productMapper;//模拟线程场景@Testpublic void test1(){Product productA = productMapper.selectById(1);System.out.println("A查询的商品价格"+productA.getPrice());Product productB = productMapper.selectById(1);System.out.println("B查询的商品价格"+productB.getPrice());productA.setPrice(productA.getPrice()+10);productMapper.updateById(productA);productB.setPrice(productB.getPrice()-30);productMapper.updateById(productB);//最后结果Product productC = productMapper.selectById(1);System.out.println("A查询的商品价格"+productC.getPrice());}
}

3.2 乐观锁插件

前面知道乐观锁实现需要加上版本号来控制,因此实体类需要进行通过@Version来设置版本号。

实体类

package com.crj.mybatisplus_test.pojo;import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;@Data
public class Product {private Long id;private String name;private Integer price;@Version //标识乐观锁版本号字段private Integer version;}

MyBatis-Plus插件配置类

需要在配置类中配置好乐观锁插件方法OptimisticLockerInnerInterceptor()

package com.crj.mybatisplus_test.config;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;//配置类,配置MyBatisPlus的插件功能
@Configuration
@MapperScan("com.crj.mybatisplus_test.mapper")
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//配置分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//配置乐观锁插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}
}

测试类

需要注意的是B修改数据失败后需要重试即可完成任务需求。 

package com.crj.mybatisplus_test;import com.crj.mybatisplus_test.mapper.ProductMapper;
import com.crj.mybatisplus_test.pojo.Product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;//乐观锁插件使用
@SpringBootTest
public class MyBatisLockTest {@Autowiredprivate ProductMapper productMapper;//模拟线程场景@Testpublic void test1(){Product productA = productMapper.selectById(1);System.out.println("A查询的商品价格"+productA.getPrice());Product productB = productMapper.selectById(1);System.out.println("B查询的商品价格"+productB.getPrice());productA.setPrice(productA.getPrice()+10);productMapper.updateById(productA);productB.setPrice(productB.getPrice()-30);int result = productMapper.updateById(productB);//由于加入了版本号控制,因此需要对修改失败的操作进行重试if(result==0){//失败重试Product productNew = productMapper.selectById(1);productNew.setPrice(productNew.getPrice()-30);productMapper.updateById(productNew);}//最后结果Product productC = productMapper.selectById(1);System.out.println("A查询的商品价格"+productC.getPrice());}
}

总结

        通过条件构造器的几种基本用法使用示例,荔枝对wrapper类的使用有了一个比较直观的理解,同时荔枝觉得更需要注意的是两种插件的使用。接下来的文章中荔枝会对MyBatis-Plus的相关基础知识收尾,同时尝试整合到学习的项目中,跟荔枝一起期待一波吧哈哈哈哈哈~~~

今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~

相关文章:

MyBatis-Plus深入 —— 条件构造器与插件管理

前言 在前面的文章中&#xff0c;荔枝梳理了一个MyBatis-Plus的基本使用、配置和通用Service接口&#xff0c;我们发现在MyBatis-Plus的辅助增强下我们不再需要通过配置xml文件中的sql语句来实现基本的sql操作了&#xff0c;不愧是最佳搭档&#xff01;在这篇文章中&#xff0c…...

C语言结构体的初始化方式

逐个初始化字段&#xff1a;这是最直接的方式&#xff0c;你可以逐个为结构体的每个字段进行初始化。 struct Student { char name[50]; int age; float marks; }; struct Student student1 {"Alice", 20, 89.5}; 2.使用结构体字面值初始化&#xff1a;这种方…...

Vue生成多文件pdf准考证

这是渲染的数据 这是生成的pdf文件&#xff0c;直接可以打印 需要安装和npm依赖和引入封装的pdf.js文件 npm install --save html2canvas // 页面转图片 npm install jspdf --save // 图片转pdfpdf.js文件 import html2canvas from "html2canvas"; import jsPDF …...

Rust的derive思考

这几天在Yew的学习实践中&#xff0c;发现derive中的参数中包含了yew自己的东西&#xff0c;比如yew::Properties。习惯使用#[derive(Clone, Debug, PartialEq)]之后&#xff0c;发现还有新的成员&#xff0c;这让我好奇起来。 首先让我们来回顾一下derive是什么。 #[derive(…...

Python常用模块

文章目录 1. time&#xff1a;时间2. calendar&#xff1a;日历3. datetime&#xff1a;可以运算的时间4. sys&#xff1a;系统5. os&#xff1a;操作系统6. random&#xff1a;随机数7. json&#xff1a;序列化8. pickle&#xff1a;序列化9. logging 模块9.1 什么是logging模…...

Java“牵手”京东商品评论数据接口方法,京东商品评论接口,京东商品评价接口,行业数据监测,京东API实现批量商品评论内容数据抓取示例

京东平台商品评论数据接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取京东商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片、评论内容、评论日期、评论图片、追评内容等详细信息 。 获取商品评论接口API是一种用于获取…...

算法leetcode|75. 颜色分类(rust重拳出击)

文章目录 75. 颜色分类&#xff1a;样例 1&#xff1a;样例 2&#xff1a;提示&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 75. 颜色分类&#xff1a; 给定一个包含红色、白色和蓝色、共 n…...

网络安全(黑客)自学笔记学习路线

谈起黑客&#xff0c;可能各位都会想到&#xff1a;盗号&#xff0c;其实不尽然&#xff1b;黑客是一群喜爱研究技术的群体&#xff0c;在黑客圈中&#xff0c;一般分为三大圈&#xff1a;娱乐圈 技术圈 职业圈。 娱乐圈&#xff1a;主要是初中生和高中生较多&#xff0c;玩网恋…...

NoSQL:非关系型数据库分类

NoSQL&#xff0c;全称 Not Only SQL&#xff0c;意为不仅仅是 SQL&#xff0c;泛指非关系型数据库。NoSQL 是基于键值对的&#xff0c;而且不需要经过 SQL 层的解析&#xff0c;数据之间没有耦合性&#xff0c;性能非常高。 非关系型数据库又可细分如下&#xff1a; 键值存储…...

【Eclipse】Project interpreter not specified 新建项目时,错误提示,已解决

目录 0.环境 1&#xff09;问题截图&#xff1a; 2&#xff09;错误发生原因&#xff1a; 1.解决思路 2.具体步骤 0.环境 windows 11 64位&#xff0c;Eclipse 2021-06 1&#xff09;问题截图&#xff1a; 2&#xff09;错误发生原因&#xff1a; 由于我手欠&#xff0c;将…...

OPENCV实现图像查找

特征匹配+单应性矩阵 # -*- coding:utf-8 -*- """ 作者:794919561 日期:2023/9/4 """ import cv2 import numpy as np# 读图像 img1 = cv2.imread(F:\\learnOpenCV\\openCVLearning\\pictures\\chess...

vue仿企微文档给页面加水印(水印内容可自定义,超简单)

1.在src下得到utils里新建一个文件watermark.js /** 水印添加方法 */let setWatermark (str1, str2) > {let id 1.23452384164.123412415if (document.getElementById(id) ! null) {document.body.removeChild(document.getElementById(id))}let can document.createE…...

“金融级”数字底座:从时代的“源启”,到“源启”的时代

今年初《数字中国建设整体布局规划》正式发布&#xff0c;这代表着数字中国建设迈向了实质的落地阶段&#xff0c;其背后的驱动就是遍及各行各业的数字化转型。 千姿百态、复杂多样的应用场景&#xff0c;可以看作是遍布数字中国的“点”&#xff1b;千行百业、各种类型的行业…...

zabbix自动发现linux系统挂载的nas盘,并实现读写故障的监控告警

一.准备好被监控机器上面执行脚本,以备服务端发现和监控 脚本的内容: ZABBI安装路径可执行文件及配置文件根据实际部署的路径更改 #!/bin/bash >/zabbixconfpath/zbx_nas.conf >/zabbixscriptspath/findnas.sh >/zabbixscriptspath/checknas.sh >/zabbixscripts…...

无涯教程-JavaScript - DAYS函数

描述 DAYS函数返回两个日期之间的天数。 语法 DAYS (end_date, start_date)争论 Argument描述Required/OptionalEnd_dateStart_date and End_date are the two dates between which you want to know the number of days.RequiredStart_dateStart_date and End_date are th…...

48、springboot 的国际化之让用户在程序界面上弄个下拉框,进行动态选择语言

上一篇是直接改浏览器的支持语言。 在浏览器上面直接改国际化语言 这次要实现的功能是直接在程序界面动态选择语言。 Locale 代表语言、国家。 ★ 在界面上动态改变语言 应用之所以能动态呈现不同的语言界面&#xff0c;其实关键在于如何确定客户端的Locale&#xff08;代…...

FPGA可重配置原理及实现(1)——导论

一、概述 可重配置技术是Xilinx提供的用来高效利用FPGA设计资源实现FPGA资源可重复利用的最新的FPGA设计技术&#xff0c;这种技术的发展为FPGA应用提供了更加广阔的前景。 术语“重构”是指FPGA已经配置后的重新编程。FPGA的重构有两种类型&#xff1a;完全的和部分的。完全重…...

Ubuntu系统下使用宝塔面板实现一键搭建Z-Blog个人博客的方法和流程

文章目录 1.前言2.网站搭建2.1. 网页下载和安装2.2.网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测试5.结语 1.前言 Ubuntu系统作…...

数据结构 | 第一章 绪论

问题求解与程序设计 这一节都是介绍性的内容&#xff0c;但是哥尼斯堡的七桥问题值得写写。 #include <stdio.h>int Euler(int mat[4][4], int n) {int count 0;for (int i 0; i < n; i) {int degree 0;for (int j 0; j < n; j) {degree mat[i][j];}if (degr…...

python爬虫入门教程(非常详细):如何快速入门Python爬虫?

示例示例Python爬虫入门教程什么是爬虫爬虫&#xff08;又称网络爬虫&#xff09;是一种按照一定的规则&#xff0c;自动地抓取万维网信息的程序或者脚本。它可以自动地抓取网页内容&#xff0c;并从中提取有用的数据&#xff0c;存储到本地文件或数据库中。 Python爬虫入门教…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用

中达瑞和自2005年成立以来&#xff0c;一直在光谱成像领域深度钻研和发展&#xff0c;始终致力于研发高性能、高可靠性的光谱成像相机&#xff0c;为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…...

网页端 js 读取发票里的二维码信息(图片和PDF格式)

起因 为了实现在报销流程中&#xff0c;发票不能重用的限制&#xff0c;发票上传后&#xff0c;希望能读出发票号&#xff0c;并记录发票号已用&#xff0c;下次不再可用于报销。 基于上面的需求&#xff0c;研究了OCR 的方式和读PDF的方式&#xff0c;实际是可行的&#xff…...