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

2023-12-01 23:15
文章标签 导致 事故 arrays aslist

本文主要是介绍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

📝问题总结:

经验太少…哈哈哈哈

这篇关于Arrays.asList(T... a)导致的事故的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/443063

相关文章

C#多线程编程中导致死锁的常见陷阱和避免方法

《C#多线程编程中导致死锁的常见陷阱和避免方法》在C#多线程编程中,死锁(Deadlock)是一种常见的、令人头疼的错误,死锁通常发生在多个线程试图获取多个资源的锁时,导致相互等待对方释放资源,最终形... 目录引言1. 什么是死锁?死锁的典型条件:2. 导致死锁的常见原因2.1 锁的顺序问题错误示例:不同

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

STM32 ADC+DMA导致写FLASH失败

最近用STM32G070系列的ADC+DMA采样时,遇到了一些小坑记录一下; 一、ADC+DMA采样时进入死循环; 解决方法:ADC-dma死循环问题_stm32 adc dma死机-CSDN博客 将ADC的DMA中断调整为最高,且增大ADCHAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_Buffer_Size); 的ADC_Bu

DAY16:什么是慢查询,导致的原因,优化方法 | undo log、redo log、binlog的用处 | MySQL有哪些锁

目录 什么是慢查询,导致的原因,优化方法 undo log、redo log、binlog的用处  MySQL有哪些锁   什么是慢查询,导致的原因,优化方法 数据库查询的执行时间超过指定的超时时间时,就被称为慢查询。 导致的原因: 查询语句比较复杂:查询涉及多个表,包含复杂的连接和子查询,可能导致执行时间较长。查询数据量大:当查询的数据量庞大时,即使查询本身并不复杂,也可能导致

71-java 导致线程上下文切换的原因

Java中导致线程上下文切换的原因通常包括: 线程时间片用完:当前线程的时间片用完,操作系统将其暂停,并切换到另一个线程。 线程被优先级更高的线程抢占:操作系统根据线程优先级决定运行哪个线程。 线程进入等待状态:如线程执行了sleep(),wait(),join()等操作,使线程进入等待状态或阻塞状态,释放CPU。 线程占用CPU时间过长:如果线程执行了大量的I/O操作,而不是CPU计算

一次生产环境大量CLOSE_WAIT导致服务无法访问的定位过程

1.症状 生产环境的一个服务突然无法访问,服务的交互过程如下所示: 所有的请求都是通过网关进入,之后分发到后端服务。 现在的情况是用户服务无法访问商旅服务,网关有大量java.net.SocketTimeoutException: Read timed out报错日志,商旅服务也不断有日志打印,大多是回调和定时任务日志,所以故障点在网关和商旅服务,大概率是商旅服务无法访问导致网关超时。 后

Qt: 详细理解delete与deleteLater (避免访问悬空指针导致程序异常终止)

前言 珍爱生命,远离悬空指针。 正文 delete 立即删除:调用 delete 后,对象会立即被销毁,其内存会立即被释放。调用顺序:对象的析构函数会被立即调用,销毁该对象及其子对象。无事件处理:如果在对象销毁过程中还涉及到信号和槽、事件处理等,直接 delete 可能会导致问题,尤其是在对象正在处理事件时。适用场景:适用于在确定对象已经不再被使用的情况下,并且不涉及异步处理或事件循环中的

ssh版本升级导致连接失败

公司系统使用的是第三方ssh插件jsch-0.1.39.jar,之前采集正常的,但是厂家服务器ssh升级成2.0版本,然后程序就报错,异常如下: com.jcraft.jsch.JSchException: Algorithm negotiation failat com.jcraft.jsch.Session.receive_kexinit(Session.java:510)at com.

JobScheduler 调用导致的运行时长30分钟的功耗问题

一、SDK 的使用情况与功耗影响 案例是否导致功耗变大onStartJob return true 且子线程没有调用jobFinished()告知系统功耗变大,最长带来30分钟的partial wakelock 长持锁onStartJob return true 且子线程调用jobFinished()告知系统功耗有影响,主要线程执行时长,标准是30秒内onStartJob return fals

Java应用对接pinpoint监控工具的时候,应用名称长度超出限制而导致接入失败

一、背景 java应用需要接入pinpoint,同一个虚拟机上的其他应用接入成功,唯独本应用不行。 首先排除是pinpoint agent的问题,因为其他应用都正常。 然后,我就对比二者的启动脚本。 -javaagent:/opt/pinpoint/pinpoint-bootstrap.jar -Dpinpoint.agentId=DA301004_17 -Dpinpoint.applic