Arrays.asList(T... a)导致的事故

📚项目场景:

修改数据时,允许将非必填字段清空。

⛔问题描述:

由于使用的是Mybatis-Plus,只能使用LambdaUpdateWrapperUpdateWrapper通过set(column,val)来将字段清空;因为字段太多导致大量set放在一个方法,不符合部分规范,所以封装了一个SqlUtils工具来对字段进行set。

有人会问,为什么不用Mybatis-Plus@TableField(updateStrategy = FieldStrategy.IGNORED)?这不是可以直接加上注解修改的时候就会自己添加置空进去吗?

使用TableField注解确实可以达到你说的效果,但是得区分项目的整体情况;如果整个项目都是使用Mybatis-PlusLambdaUpdateWrapper来对数据的状态或者价格等字段进行更新的话,那么这个注解的可行性就少了一部分。因为有些场景下你只需要对某个字段进行更新而不是更新所有字段,例如:我这是一张申请单据,对应人送审成功,我需要更新未处理状态到处理中,这种情况下我们是不需要更新状态字段之外的其他字段,如果使用这种方式则会把其他数据清空。除非在更新前查一遍数据,然后拿这份数据去修改,这样对程序和数据库来说增加压力!

/*** sql处理工具** @author Smallink ** @version Id: SqlUtils, v 0.1 2023/8/22 11:59 Smallink Exp $*/
@Slf4j
@Component
public class SqlUtils<T> {private static final int FLAG_SERIALIZABLE = 1;private static final List<String> DEFAULT_EXCLUSION_FIELD = Arrays.asList("id", "createBy", "createName", "createTime", "updateBy", "updateName", "updateTime");/*** 将为空字段拼入sql** @param wrapper 封装器* @param object  需要拼入对象* @return LambdaUpdateChainWrapper*/public static UpdateWrapper packSqlUtils(UpdateWrapper<?> wrapper, Object object) {return (UpdateWrapper) packSqlUtils(wrapper, object, false, new ArrayList<>());}/*** 将为空字段拼入sql** @param wrapper   封装器* @param object    需要拼入对象* @param fieldList 不需要拼入的字段* @return LambdaUpdateChainWrapper*/public static UpdateWrapper packSqlUtils(UpdateWrapper<?> wrapper, Object object, List<String> fieldList) {return (UpdateWrapper) packSqlUtils(wrapper, object, false, CollectionUtil.isEmpty(fieldList) ? new ArrayList<>() : fieldList);}/*** 将为空字段拼入sql** @param wrapper         封装器* @param object          需要拼入对象* @param ignoreNullValue 是否忽略值为空的字段* @param fieldList       不需要拼入的字段* @return LambdaUpdateChainWrapper*/public static Update packSqlUtils(Update wrapper, Object object, boolean ignoreNullValue, List<String> fieldList) {if (ObjectUtils.isEmpty(object) || ObjectUtils.isEmpty(wrapper)) {log.info("mandatory parameters cannot be empty");throw new BizException(ErrCodeEnum.WRAPPER_OBJECT_IS_NULL_ERROR.getErrCode(), ErrCodeEnum.WRAPPER_OBJECT_IS_NULL_ERROR.getMsg());}// 添加默认排除字段fieldList.addAll(DEFAULT_EXCLUSION_FIELD);Class<?> aClass = object.getClass();// 将对象转换成MapMap<String, Object> objectMap = BeanUtil.beanToMap(object, new LinkedHashMap<>(), ignoreNullValue, key -> fieldList.contains(key) ? null : StrUtil.toUnderlineCase(key));// 将对象当需要修改值写入objectMap.forEach((key, value) -> {String fieldType = getFieldType(CamelUnderLineUtils.underLineToCamel(key), aClass);if (StringUtils.isNotEmpty(fieldType)) {wrapper.set(key, value);}});return wrapper;}// --------------------------------------------- Lambda/*** 将为空字段拼入sql** @param wrapper 封装器* @param object  需要拼入对象* @return LambdaUpdateChainWrapper*/public LambdaUpdateWrapper packLambdaSqlUtils(LambdaUpdateWrapper<T> wrapper, T object) {return (LambdaUpdateWrapper) packLambdaSqlUtils(wrapper, object, false, new ArrayList<>());}/*** 将为空字段拼入sql** @param wrapper 封装器* @param object  需要拼入对象* @return LambdaUpdateChainWrapper*/public LambdaUpdateChainWrapper packLambdaSqlUtils(LambdaUpdateChainWrapper<T> wrapper, T object) {return (LambdaUpdateChainWrapper) packLambdaSqlUtils(wrapper, object, false, new ArrayList<>());}/*** 将为空字段拼入sql** @param wrapper   封装器* @param object    需要拼入对象* @param fieldList 不需要拼入的字段* @return LambdaUpdateChainWrapper*/public LambdaUpdateWrapper packLambdaSqlUtils(LambdaUpdateWrapper<T> wrapper, T object, List<String> fieldList) {return (LambdaUpdateWrapper) packLambdaSqlUtils(wrapper, object, false, CollectionUtil.isEmpty(fieldList) ? new ArrayList<>() : fieldList);}/*** 将为空字段拼入sql** @param wrapper   封装器* @param object    需要拼入对象* @param fieldList 不需要拼入的字段* @return LambdaUpdateChainWrapper*/public LambdaUpdateChainWrapper packLambdaSqlUtils(LambdaUpdateChainWrapper<T> wrapper, T object, List<String> fieldList) {return (LambdaUpdateChainWrapper) packLambdaSqlUtils(wrapper, object, false, CollectionUtil.isEmpty(fieldList) ? new ArrayList<>() : fieldList);}/*** 将为空字段拼入sql** @param wrapper         封装器* @param object          需要拼入对象* @param ignoreNullValue 是否忽略值为空的字段* @param fieldList       不需要拼入的字段* @return LambdaUpdateChainWrapper*/public Update packLambdaSqlUtils(Update<?, SFunction<T, ?>> wrapper, T object, boolean ignoreNullValue, List<String> fieldList) {if (ObjectUtils.isEmpty(object) || ObjectUtils.isEmpty(wrapper)) {log.info("mandatory parameters cannot be empty");throw new BizException(ErrCodeEnum.WRAPPER_OBJECT_IS_NULL_ERROR.getErrCode(), ErrCodeEnum.WRAPPER_OBJECT_IS_NULL_ERROR.getMsg());}// 添加默认排除字段fieldList.addAll(DEFAULT_EXCLUSION_FIELD);Class<?> aClass = object.getClass();// 将对象转换成MapMap<String, Object> objectMap = BeanUtil.beanToMap(object, new LinkedHashMap<>(), ignoreNullValue, key -> fieldList.contains(key) ? null : key);// 将对象当需要修改值写入objectMap.forEach((key, value) -> {String fieldType = getFieldType(key, aClass);SFunction sFunction = StringToFunction(key, fieldType, aClass);wrapper.set(sFunction, value);});return wrapper;}/*** 将字段名转换为对应的函数式** @param name        字段名* @param fieldType   字段类型* @param entityClass 类* @return*/private static SFunction StringToFunction(String name, String fieldType, Class<?> entityClass) {Class<?> rType = getRType(fieldType);SFunction func;String getMethod = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);final MethodHandles.Lookup lookup = MethodHandles.lookup();//po的返回Integer的一个方法MethodType methodType = MethodType.methodType(rType, entityClass);final CallSite site;try {//方法名叫做:getSecretLevel  转换为 SFunction function interface对象site = LambdaMetafactory.altMetafactory(lookup,"invoke",MethodType.methodType(SFunction.class),methodType,lookup.findVirtual(entityClass, getMethod, MethodType.methodType(rType)),methodType, FLAG_SERIALIZABLE);func = (SFunction) site.getTarget().invokeExact();//数据小于这个级别的都查出来//    mpjLambdaWrapper.le(func, secretLevel);return func;} catch (Throwable e) {log.error("获取getSecretLevel方法错误", e);}return null;}/*** 类型转换** @param fieldType 类型* @return*/private static Class<?> getRType(String fieldType) {Class<?> rtype = null;switch (fieldType) {case "class java.lang.String":rtype = String.class;break;case "class java.lang.Integer":rtype = Integer.class;break;case "class java.lang.Double":rtype = Double.class;break;case "class java.lang.Boolean":rtype = Boolean.class;break;case "class java.util.Date":rtype = Date.class;break;case "class java.time.LocalDate":rtype = LocalDate.class;break;case "class java.time.LocalDateTime":rtype = LocalDateTime.class;break;case "class java.math.BigDecimal":rtype = BigDecimal.class;break;case "class java.lang.Long":rtype = Long.class;break;}return rtype;}/*** 获取字段类型** @param fieldName 字段名* @param clazz     类* @return*/private static String getFieldType(String fieldName, Class<?> clazz) {Field[] fields = ReflectUtils.getAllFields(clazz);for (Field f : fields) {boolean exist = false;TableField tableField = f.getAnnotation(TableField.class);if (tableField != null) {exist = !tableField.exist();}if (fieldName.equals(f.getName()) && !exist) {f.setAccessible(true);return f.getGenericType().toString();}}return null;}}

工具是支持传入参数来排除指定字段修改为空(没有选择使用自定义注解,自定义注解增加了功能的复杂难度与维护,所以使用了最便捷的方式),以下是示例 需要结合SqlUtils工具看

    @Resourceprivate SqlUtils sqlUtils;// 对象中不需要封装的字段名,public static List<String> SALE_QUOTE_EXCLUSION_FIELD = Arrays.asList("customerNo","customerName","quoteStatus","productCategory","productType","deliveryMethod");public BusinessResponse updateQuote(Object obj) {LambdaUpdateWrapper<Object> detailPOLambdaUpdateWrapper = new LambdaUpdateWrapper<>();// 调用SqlUtils工具来封装需要置空的字段sqlUtils.packLambdaSqlUtils(detailPOLambdaUpdateWrapper, obj, SALE_QUOTE_DATEIL_EXCLUSION_FIELD);detailPOLambdaUpdateWrapper.eq(BasePO::getId, id);baseMapper.update(new Object(), detailPOLambdaUpdateWrapper);}

除了指定排除字段之外,还有默认字段所以使用了add()来添加,然后就导致了异常:

乍一看,这不是空指针嘛,so easy啊(内心os:明明就有值,怎么会报这个错误呢?而且编译时也没有报错)

仔细一瞧,这UnsupportedOperationException是个什么玩意?

java.lang.UnsupportedOperationException: nullat java.util.AbstractList.add(AbstractList.java:148) ~[na:1.8.0_151]at java.util.AbstractList.add(AbstractList.java:108) ~[na:1.8.0_151]

🔍原因分析:

秉持出现问题,解决问题的思想 (哈哈哈)。断点调试后,还是有问题;接下来就是看源码了,请展示:

    @SafeVarargs@SuppressWarnings("varargs")public static <T> List<T> asList(T... a) {return new ArrayList<>(a);}private static class ArrayList<E> extends AbstractList<E>implements RandomAccess, java.io.Serializable{private static final long serialVersionUID = -2764017481108945198L;private final E[] a;ArrayList(E[] array) {a = Objects.requireNonNull(array);}@Overridepublic int size() {return a.length;}@Overridepublic Object[] toArray() {return a.clone();}@Override@SuppressWarnings("unchecked")public <T> T[] toArray(T[] a) {int size = size();if (a.length < size)return Arrays.copyOf(this.a, size,(Class<? extends T[]>) a.getClass());System.arraycopy(this.a, 0, a, 0, size);if (a.length > size)a[size] = null;return a;}@Overridepublic E get(int index) {return a[index];}@Overridepublic E set(int index, E element) {E oldValue = a[index];a[index] = element;return oldValue;}@Overridepublic int indexOf(Object o) {E[] a = this.a;if (o == null) {for (int i = 0; i < a.length; i++)if (a[i] == null)return i;} else {for (int i = 0; i < a.length; i++)if (o.equals(a[i]))return i;}return -1;}@Overridepublic boolean contains(Object o) {return indexOf(o) != -1;}@Overridepublic Spliterator<E> spliterator() {return Spliterators.spliterator(a, Spliterator.ORDERED);}@Overridepublic void forEach(Consumer<? super E> action) {Objects.requireNonNull(action);for (E e : a) {action.accept(e);}}@Overridepublic void replaceAll(UnaryOperator<E> operator) {Objects.requireNonNull(operator);E[] a = this.a;for (int i = 0; i < a.length; i++) {a[i] = operator.apply(a[i]);}}@Overridepublic void sort(Comparator<? super E> c) {Arrays.sort(a, c);}}

可以从源码中看出,此ArrayList非彼java.util.ArrayListArrays.asList()使用的是类部类中的ArrayList;它继承的是AbstractList,并没有实现AbstractList里面的add()方法!!!

看到这里相信各位同学应该知道问题了,没错就是没有实现里面的add()所以导致了《空指针》异常🌚

💡问题解决:

既然知道了问题,那么就好解决了!

换一个初始化List集合的工具就行啦!

我换成了hutool的CollectionUtil.toList

📝问题总结:

经验太少…哈哈哈哈

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

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

相关文章

深度学习今年来经典模型优缺点总结,包括卷积、循环卷积、Transformer、LSTM、GANs等

文章目录 1、卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;CNN&#xff09;1.1 优点1.2 缺点1.3 应用场景1.4 网络图 2、循环神经网络&#xff08;Recurrent Neural Networks&#xff0c;RNNs&#xff09;2.1 优点2.2 缺点2.3 应用场景2.4 网络图 3、长短…

L1-010:比较大小

题目描述 本题要求将输入的任意3个整数从小到大输出。 输入格式: 输入在一行中给出3个整数&#xff0c;其间以空格分隔。 输出格式: 在一行中将3个整数从小到大输出&#xff0c;其间以“->”相连。 输入样例: 4 2 8输出样例: 2->4->8 程序代码 #include<stdio.h&…

基于YOLOv8深度学习的安全帽目标检测系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

Git——使用Git进行程序开发

主要介绍个人开发提交记录的主要流程&#xff0c;包括以下内容&#xff1a; 索引- 提交的暂存区。查看工作的状态和内部变更。如何读取用于描述变更的已扩展统一diff格式。支持查询和交互的提交&#xff0c;修改提交。创建、显示和选择&#xff08;切换&#xff09;分支。切换…

面试:如何故意减慢网站速度?

面试问题&#xff1a;假设你为你的客户建立了一个网站。但他没有定期支付费用。现在你想放慢他的网站速度&#xff0c;但又不想让他立即发现。你会怎么做&#xff1f; 我&#xff1a;用较慢的算法和其他与编码相关的选项替换代码中使用的算法。 面试问题&#xff1a;问在这种情…

婴儿专用洗衣机有必要买吗?宝宝洗衣机洗衣服

我们都知道刚出生的宝宝抵抗力较弱&#xff0c;很容易因为细菌感染然后生病&#xff0c;宝宝接触最多的就是衣服&#xff0c;我们在手洗的过程很难把衣服上的细菌清洗掉&#xff0c;而使用我们传统的洗衣机很容易造成细菌的第二次感染&#xff0c;很容易将宝宝的抵抗力弄得越来…

如何通过linux调用企业微信发送告警消息

一、前期准备 1、企业微信具备管理企业权限。 2、服务器有公网IP或者可以将本机端口通过net映射到公网。 二、通过脚本向企业微信发送消息 1、创建sh脚本用来发送消息。 vim 2.sh 注意&#xff1a;脚本中xxxx信息需要在企业微信管理后台获取。 #!/bin/bash # 设置企业…

2023年计网408

第33题 33.在下图所示的分组交换网络中&#xff0c;主机H1和H2通过路由器互连&#xff0c;2段链路的带宽均为100Mbps、 时延带宽积(即单向传播时延带宽)均为1000bits。若 H1向 H2发送1个大小为 1MB的文件&#xff0c;分组长度为1000B&#xff0c;则从H1开始发送时刻起到H2收到…

MySQL 学习笔记(刷题篇)

SQL进阶挑战 聚合分组查询 SQL123 select tag, difficulty, round((sum(score) - max(score) - min(score) ) / (count(score) - 2) ,1) as clip_avg_score from examination_info as ei, exam_record as er where ei.exam_id er.exam_id and ei.tag SQL and ei.diffi…

代码随想录刷题题Day2

刷题的第二天&#xff0c;希望自己能够不断坚持下去&#xff0c;迎来蜕变。&#x1f600;&#x1f600;&#x1f600; 刷题语言&#xff1a;C / Python Day2 任务 977.有序数组的平方 209.长度最小的子数组 59.螺旋矩阵 II 1 有序数组的平方&#xff08;重点&#xff1a;双指针…

将项目放到gitee上

参考 将IDEA中的项目上传到Gitee仓库中_哔哩哔哩_bilibili 如果cmd运行ssh不行的话&#xff0c;要换成git bash 如果初始化后的命令用不了&#xff0c;直接用idea项放右键&#xff0c;用git工具操作

XXL-Job详解(二):安装部署

目录 前言环境下载项目调度中心部署执行器部署 前言 看该文章之前&#xff0c;最好看一下之前的文章&#xff0c;比较方便我们理解 XXL-Job详解&#xff08;一&#xff09;&#xff1a;组件架构 环境 Maven3 Jdk1.8 Mysql5.7 下载项目 源码仓库地址链接: https://github.…

前端对浏览器的理解

浏览器的主要构成 用户界面 &#xff0d; 包括地址栏、后退/前进按钮、书签目录等&#xff0c;也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分。 浏览器引擎 &#xff0d; 用来查询及操作渲染引擎的接口。 渲染引擎 &#xff0d; 用来显示请求的内容&#…

中国丙烯基弹性体PBE市场调研与预测报告(2023版)

内容简介&#xff1a; 丙烯基弹性体&#xff08;PBE&#xff09;是以丙烯为主要原料&#xff0c;加入少量乙烯&#xff08;或α-烯烃&#xff09;单体经溶液聚合而得到的以无定形区域为主的低结晶聚合物。与传统的乙丙橡胶不同&#xff0c;PBE的乙烯含量通常低于20%&#xff0…

某60区块链安全之薅羊毛攻击实战一学习记录

区块链安全 文章目录 区块链安全薅羊毛攻击实战一实验目的实验环境实验工具实验原理实验内容薅羊毛攻击实战一 实验步骤EXP利用 薅羊毛攻击实战一 实验目的 学会使用python3的web3模块 学会分析以太坊智能合约薅羊毛攻击漏洞 找到合约漏洞进行分析并形成利用 实验环境 Ubun…

JVM类加载与运行时数据区

目录 一、类加载器 jvm类的加载过程 第一阶段&#xff1a;加载 第二阶段&#xff1a;链接阶段 第三阶段&#xff1a;初始化阶段&#xff1a; 双亲委派机制 沙箱安全机制 运行时数据区 栈-Xss1m 堆 TLAB 逃逸分析 方法区 常量池中有什么 StringTable为什么要调整位…

c# statusStrip 显示电脑主机名、IP地址、MAC地址

控件&#xff1a; ToolStripStatusLabel 主机名&#xff1a; Dns.GetHostName() IP地址&#xff1a; Dns.GetHostAddresses(Dns.GetHostName())[0].ToString() 当前程序的版本&#xff1a; Assembly.GetExecutingAssembly().GetName().Version.ToString() 获取系统版本 …

VS Code C++可视化调试配置Natvis,查看Qt、STL变量内容

VS Code C可视化调试配置Natvis 使用GlobalVisualizersDirectory Windows下 C:\Users\YourName\.vscode\extensions\ms-vscode.cpptools-1.18.5-win32-x64\debugAdapters\vsdbg\bin\Visualizers\Linux下 ~\.vscode\extensions\ms-vscode.cpptools-1.18.5-win32-x64\debugAd…

Spring Cloud 原理(第一节)

一、百度百科 Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发&#xff0c;如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等&#xff0c;都可以用Spring Boot的开发风格做到一键启动和部署。Spri…

播放器开发(六):音频帧处理并用SDL播放

目录 学习课题&#xff1a;逐步构建开发播放器【QT5 FFmpeg6 SDL2】 步骤 AudioOutPut模块 1、初始化【分配缓存、读取信息】 2、开始线程工作【从队列读帧->重采样->SDL回调->写入音频播放数据->SDL进行播放】 主要代码 分配缓存 // 对于样本队列 av_audio_…