MyBatisPlus更新字段为null的正确姿势以及lambda方式的条件字段解析之源码解析

news2024/11/18 9:34:17

文章目录

    • @[toc]
  • 1.问题
  • 2.原因
  • 3.解决方法
    • 3.1错误方法
      • 方式一:配置全局字段策略
      • 方式二:在实体上添加字段策略注解
    • 3.2正确姿势
      • 方式一:使用LambdaUpdateWrapper (推荐)
      • 方式二:使用UpdateWrapper
      • 方式三
  • 总结

1.问题

  由于在项目中使用MyBatisPlus的updateById(Entity)接口api根据用户点击不同的操作切换,需要根据表里面的主键id更新表的字段为null的操作,在使用这个接口api根据主键设置实体字段为null更新居然不生效,也是奇奇怪怪的问题。

2.原因

  原因是MyBatisPlus的字段更新策略惹的祸,MyBatisPlus有以下几种策略:

  常用和主要的就前面三个

public enum FieldStrategy {
    IGNORED, //忽略
    NOT_NULL, //非NULL,默认策略,不忽略""
    NOT_EMPTY, //非空,会忽略"",会忽略NULL
    DEFAULT, // 默认
    NEVER;  //无

    private FieldStrategy() {
    }
}

  由于默认策略是NOT_NULL,就会导致以下两个api更新实体字段为null到表中失效,该策略是只会更新实体中非null字段的值,为null的字段会被剔除忽略。

this.updateById(entity);
this.update(entity, updateWrapper);

3.解决方法

3.1错误方法

方式一:配置全局字段策略

mybatis-plus:
  global-config:
  	#字段策略 0:"忽略判断",1:"非 NULL 判断",2:"非空判断"
    field-strategy: 0

  全局的这种没有配置调试过,只试了下面方式二的情况是有问题的,这种方法是全局的影响会有点广,所以要谨慎使用

方式二:在实体上添加字段策略注解

@TableField(updateStrategy = FieldStrategy.IGNORED)

  方式二在实体字段上加了改注解,调试发现会生效,运行一次后数据库表中的字段被设置位null了,然后在运行几次,修改的实体字段设置有值的情况去更新,发现不会设置值了不更新,表中字段还是null,这个也是一个大坑,所以姿势不对就会很坑。

3.2正确姿势

方式一:使用LambdaUpdateWrapper (推荐)

LambdaUpdateWrapper<WhiteListManagementEntity> 
    lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
//条件
lambdaUpdateWrapper.eq(WhiteListManagementEntity::getId,
                       updateWhiteListManagementEntity.getId());
//设置字段值
lambdaUpdateWrapper.set(WhiteListManagementEntity::getIsNight, 0);
lambdaUpdateWrapper.set(WhiteListManagementEntity::getNightStart, null);
lambdaUpdateWrapper.set(WhiteListManagementEntity::getNightEnd, null);
//更新
this.update(lambdaUpdateWrapper);

  这种方式简洁也巧妙,巧妙的点是用到了JDK8的Lambda语法特性,使用了SerializedLambda来解析实体字段然后拼接为查询条件

  这个抽象AbstractWrapper的AbstractLambdaWrapper子类泛型的参数SFunction<T, R>所以就可以传Lambda接口表达式的那种写法

  AbstractWrapper类的关系图如下:

图片

  AbstractLambdaWrapper类的关系图如下:

图片

  SFunction<T, R>接口源码如下:

package com.baomidou.mybatisplus.core.toolkit.support;

import java.io.Serializable;
import java.util.function.Function;

/**
 * 支持序列化的 Function
 *
 * @author miemie
 * @since 2018-05-12
 */
@FunctionalInterface
public interface SFunction<T, R> extends Function<T, R>, Serializable {
}

  LambdaUpdateWrapper的set方法源码如下:

 @Override
  public LambdaUpdateWrapper<T> set(boolean condition, SFunction<T, ?> column, Object val) {
      if (condition) {
         sqlSet.add(String.format("%s=%s", columnToString(column), formatSql("{0}", val)));
      }
      return typedThis;
  }
 @Override
    protected String columnToString(SFunction<T, ?> column) {
        return columnToString(column, true);
    }

    protected String columnToString(SFunction<T, ?> column, boolean onlyColumn) {
        return getColumn(LambdaUtils.resolve(column), onlyColumn);
    }

    /**
     * 获取 SerializedLambda 对应的列信息,从 lambda 表达式中推测实体类
     * <p>
     * 如果获取不到列信息,那么本次条件组装将会失败
     *
     * @param lambda     lambda 表达式
     * @param onlyColumn 如果是,结果: "name", 如果否: "name" as "name"
     * @return 列
     * @throws com.baomidou.mybatisplus.core.exceptions.MybatisPlusException 获取不到列信息时抛出异常
     * @see SerializedLambda#getImplClass()
     * @see SerializedLambda#getImplMethodName()
     */
    private String getColumn(SerializedLambda lambda, boolean onlyColumn) throws MybatisPlusException {
        //使用反射解析SerializedLambda对象的is/get开头的方法拿到属性字段的名称
        String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
        Class<?> aClass = lambda.getInstantiatedType();
        if (!initColumnMap) {
            columnMap = LambdaUtils.getColumnMap(aClass);
            initColumnMap = true;
        }
        Assert.notNull(columnMap, "can not find lambda cache for this entity [%s]", aClass.getName());
        ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName));
        Assert.notNull(columnCache, "can not find lambda cache for this property [%s] of entity [%s]",
            fieldName, aClass.getName());
        return onlyColumn ? columnCache.getColumn() : columnCache.getColumnSelect();
    }

  PropertyNamer的methodToProperty方法源码如下:

    public static String methodToProperty(String name) {
        if (name.startsWith("is")) {
            name = name.substring(2);
        } else {
            if (!name.startsWith("get") && !name.startsWith("set")) {
                throw new ReflectionException("Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");
            }

            name = name.substring(3);
        }

        if (name.length() == 1 || name.length() > 1 && !Character.isUpperCase(name.charAt(1))) {
            name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
        }

        return name;
    }

  Lambda表达式的解析是通过这个LambdaUtils工具类的LambdaUtils.resolve(column)方法实现的,源码如下:

package com.baomidou.mybatisplus.core.toolkit;

import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import static java.util.Locale.ENGLISH;

/**
 * Lambda 解析工具类
 *
 * @author HCL, MieMie
 * @since 2018-05-10
 */
public final class LambdaUtils {

    /**
     * 字段映射
     */
    private static final Map<String, Map<String, ColumnCache>> COLUMN_CACHE_MAP = new ConcurrentHashMap<>();

    /**
     * SerializedLambda 反序列化缓存
     */
    private static final Map<String, WeakReference<SerializedLambda>> FUNC_CACHE = new ConcurrentHashMap<>();

    /**
     * 解析 lambda 表达式, 该方法只是调用了 {@link SerializedLambda#resolve(SFunction)} 中的方法,在此基础上加了缓存。
     * 该缓存可能会在任意不定的时间被清除
     *
     * @param func 需要解析的 lambda 对象
     * @param <T>  类型,被调用的 Function 对象的目标类型
     * @return 返回解析后的结果
     * @see SerializedLambda#resolve(SFunction)
     */
    public static <T> SerializedLambda resolve(SFunction<T, ?> func) {
        Class<?> clazz = func.getClass();
        String canonicalName = clazz.getCanonicalName();
        return Optional.ofNullable(FUNC_CACHE.get(canonicalName))
            .map(WeakReference::get)
            .orElseGet(() -> {
                SerializedLambda lambda = SerializedLambda.resolve(func);
                FUNC_CACHE.put(canonicalName, new WeakReference<>(lambda));
                return lambda;
            });
    }

    /**
     * 格式化 key 将传入的 key 变更为大写格式
     *
     * <pre>
     *     Assert.assertEquals("USERID", formatKey("userId"))
     * </pre>
     *
     * @param key key
     * @return 大写的 key
     */
    public static String formatKey(String key) {
        return key.toUpperCase(ENGLISH);
    }

    /**
     * 将传入的表信息加入缓存
     *
     * @param tableInfo 表信息
     */
    public static void installCache(TableInfo tableInfo) {
        COLUMN_CACHE_MAP.put(tableInfo.getEntityType().getName(), createColumnCacheMap(tableInfo));
    }

    /**
     * 缓存实体字段 MAP 信息
     *
     * @param info 表信息
     * @return 缓存 map
     */
    private static Map<String, ColumnCache> createColumnCacheMap(TableInfo info) {
        Map<String, ColumnCache> map = new HashMap<>();

        String kp = info.getKeyProperty();
        if (StringUtils.isNotBlank(kp)) {
            map.put(formatKey(kp), new ColumnCache(info.getKeyColumn(), info.getKeySqlSelect()));
        }

        info.getFieldList().forEach(i ->
            map.put(formatKey(i.getProperty()), new ColumnCache(i.getColumn(), i.getSqlSelect()))
        );
        return map;
    }

    /**
     * 获取实体对应字段 MAP
     *
     * @param clazz 实体类
     * @return 缓存 map
     */
    public static Map<String, ColumnCache> getColumnMap(Class<?> clazz) {
        return COLUMN_CACHE_MAP.computeIfAbsent(clazz.getName(), key -> {
            TableInfo info = TableInfoHelper.getTableInfo(clazz);
            return info == null ? null : createColumnCacheMap(info);
        });
    }
    
}

  通过LambdaUtils.resolve(column)方法的解析可以获取到SerializedLambda的解析对象,只不多SerializedLambda类是MyBatisPlus的作者从JDK8源码中拷贝到项目中的,SerializedLambda位置如下:

图片

  解析Lambda表达式就是调用该类的resolve方法:

SerializedLambda lambda = SerializedLambda.*resolve*(func);

  LambdaUtils中的installCache初始化TableInfo加入缓存逻辑入口是加了@TableName实体解析和条件匹配通过SerializedLambda连接起来的桥梁,至于TableInfo的解析初始化流程使用idea反向顺藤摸瓜去找这个方法的上层,一层一层的找就知道真正的入口在哪里了,正在的入口是在mybatis构建Session的时候 解析MyBatisPlus的映射,有XML类型的和Mapper接口注解类型的映射解析,MybatisSqlSessionFactoryBuilder.build()方法:

package com.baomidou.mybatisplus.core;

import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.injector.SqlRunnerInjector;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;

/**
 * 重写SqlSessionFactoryBuilder
 *
 * @author nieqiurong 2019/2/23.
 */
public class MybatisSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {

    @SuppressWarnings("Duplicates")
    @Override
    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
            //TODO 这里换成 MybatisXMLConfigBuilder 而不是 XMLConfigBuilder
            MybatisXMLConfigBuilder parser = new MybatisXMLConfigBuilder(reader, environment, properties);
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                reader.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }

    @SuppressWarnings("Duplicates")
    @Override
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            //TODO 这里换成 MybatisXMLConfigBuilder 而不是 XMLConfigBuilder
            MybatisXMLConfigBuilder parser = new MybatisXMLConfigBuilder(inputStream, environment, properties);
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }

    // TODO 使用自己的逻辑,注入必须组件
    @Override
    public SqlSessionFactory build(Configuration config) {
        MybatisConfiguration configuration = (MybatisConfiguration) config;
        GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);
        final IdentifierGenerator identifierGenerator;
        if (globalConfig.getIdentifierGenerator() == null) {
            if (null != globalConfig.getWorkerId() && null != globalConfig.getDatacenterId()) {
                identifierGenerator = new DefaultIdentifierGenerator(globalConfig.getWorkerId(), globalConfig.getDatacenterId());
            } else {
                identifierGenerator = new DefaultIdentifierGenerator();
            }
            globalConfig.setIdentifierGenerator(identifierGenerator);
        } else {
            identifierGenerator = globalConfig.getIdentifierGenerator();
        }
        //TODO 这里只是为了兼容下,并没多大重要,方法标记过时了.
        IdWorker.setIdentifierGenerator(identifierGenerator);

        if (globalConfig.isEnableSqlRunner()) {
            new SqlRunnerInjector().inject(configuration);
        }

        SqlSessionFactory sqlSessionFactory = super.build(configuration);

        // 缓存 sqlSessionFactory
        globalConfig.setSqlSessionFactory(sqlSessionFactory);

        return sqlSessionFactory;
    }
}

  具体走那个根据解析参数类型来,根据mybatis的调用,总之会有一个方法被调用到的,只不过mybaisPlus对这个SqlSession的构建逻辑进行了重写和扩展,总体上是在mybatis的流程和实现上做了增强和简化,简化操作和提高了效率这是被开发者喜欢和信赖最有价值的地方

  最后会在MybatisMapperAnnotationBuilder类中的Mapper接口注册解析parse()接口里面有一段代码逻辑:

@Override
    public void parse() {
        String resource = type.toString();
        if (!configuration.isResourceLoaded(resource)) {
             ,,,,,,,,,,,
            // TODO 注入 CURD 动态 SQL , 放在在最后, because 可能会有人会用注解重写sql
            if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {
                GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);
            }
        }
        parsePendingMethods();
    }

  然后走到抽象sql注入器AbstractSqlInjector的inspectInject()方法中:

package com.baomidou.mybatisplus.core.injector;

import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.List;
import java.util.Set;

/**
 * SQL 自动注入器
 *
 * @author hubin
 * @since 2018-04-07
 */
public abstract class AbstractSqlInjector implements ISqlInjector {

    private static final Log logger = LogFactory.getLog(AbstractSqlInjector.class);

    @Override
    public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
        Class<?> modelClass = extractModelClass(mapperClass);
        if (modelClass != null) {
            String className = mapperClass.toString();
            Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
            if (!mapperRegistryCache.contains(className)) {
                List<AbstractMethod> methodList = this.getMethodList(mapperClass);
                if (CollectionUtils.isNotEmpty(methodList)) {
                    //这里就是初始tableInfo信息加入到缓存Map中的逻辑
                    TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
                    // 循环注入自定义方法
                    methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
                } else {
                    logger.debug(mapperClass.toString() + ", No effective injection method was found.");
                }
                mapperRegistryCache.add(className);
            }
        }
    }

    /**
     * <p>
     * 获取 注入的方法
     * </p>
     *
     * @param mapperClass 当前mapper
     * @return 注入的方法集合
     * @since 3.1.2 add  mapperClass
     */
    public abstract List<AbstractMethod> getMethodList(Class<?> mapperClass);

    /**
     * 提取泛型模型,多泛型的时候请将泛型T放在第一位
     *
     * @param mapperClass mapper 接口
     * @return mapper 泛型
     */
    protected Class<?> extractModelClass(Class<?> mapperClass) {
        Type[] types = mapperClass.getGenericInterfaces();
        ParameterizedType target = null;
        for (Type type : types) {
            if (type instanceof ParameterizedType) {
                Type[] typeArray = ((ParameterizedType) type).getActualTypeArguments();
                if (ArrayUtils.isNotEmpty(typeArray)) {
                    for (Type t : typeArray) {
                        if (t instanceof TypeVariable || t instanceof WildcardType) {
                            break;
                        } else {
                            target = (ParameterizedType) type;
                            break;
                        }
                    }
                }
                break;
            }
        }
        return target == null ? null : (Class<?>) target.getActualTypeArguments()[0];
    }
}

方式二:使用UpdateWrapper

UpdateWrapper和LambdaUpdateWrapper 的区别是写法不一样,第一个参数需要写表的字段名(xxx_xxx)

UpdateWrapper<WhiteListManagementEntity> 
      updateWrapper = new UpdateWrapper<WhiteListManagementEntity>();
//条件
updateWrapper.eq("id", updateWhiteListManagementEntity.getId());	
//设置字段值
updateWrapper.set("is_night", 0);
updateWrapper.set("night_start", null);
updateWrapper.set("night_end", null);
//更新
this.update(updateWrapper);

方式三

  在实体上加上如下注解

@TableField(fill = FieldFill.UPDATE)
//然后在调用如下方法
this.updateById(entity);
this.update(entity, updateWrapper);

  这种方式没有亲测过,只有方式一和方式二亲测有效的,方式一和方式二我在实体上都加了这个注解了也是有效的。

总结

  很多持久层ORM框架都是使用了JDK8的Lambda表达式的特性和这个类SerializedLambda解析Lambda表达式接口对象信息,然后使用Java的反射或者是字节码技术对Clazz文件做进一步处理和解析,比如:EasyEs开源框架,使用ORM的思想让操作ES数据库变得简单和高效 ,这款框架的ORM层的解析思想也是借鉴了MyBatisPlus的思想来实现的,有兴趣的小伙伴可以去看下源码,我的分享就到这里,希望能给你带来帮助,喜欢的话,请一键三连加关注,么么哒!

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

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

相关文章

沉降仪工作原理

输电线路杆塔倾斜北斗在线监测装置 一、产品概述 杆塔、铁塔在时间、自然因素的影响下&#xff0c;发生的倾斜、偏离等现象&#xff0c;而在人工巡检电力设施时是不容易通过人眼判别的&#xff0c;在日积月累的变化中&#xff0c;铁塔、杆塔会因倾斜幅度过大进一步引发严重的坍…

基于 Bert 论文构建 Question-Answering 模型

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 摘要 本文拜读了提出 Bert 模型的论文&#xff0c;考虑了在 Bert 中算法模型的实现.比较了 Bert 与其他如 Transformer、GPT 等热门 NLP 模型.BERT 在概念上很简单&#xff0c;在经验上也很强大。它推动了 11 项自然语言处理任…

“她经济”崛起,茉莉智慧如何以科技赋能月子中心迭代升级?

近年来&#xff0c;利好生育政策频出&#xff0c;女性消费能力不断提升&#xff0c;以月子中心为核心的产后护理赛道发展势头良好。据iiMedia Research数据&#xff0c;2022年中国月子中心市场规模突破223.0亿元。iiMedia Research市场调查显示&#xff0c;93.5%的受访者认为产…

ubuntu命令记录

centos 下载地址&#xff1a; 网易镜像&#xff1a;http://mirrors.163.com/centos/6/isos/ 搜狐镜像&#xff1a;http://mirrors.sohu.com/centos/6/isos/ VM与LINUX的安装&#xff08;虚拟机的安装&#xff09; 注意&#xff1a;a.必须开启虚拟化&#xff08;一般电脑都默认…

BFT 最前线 | 王小川:2033机器智慧将超人类;扎克伯格财富暴涨;哈工大:能跳跃的昆虫机器人;北京支持“1+4”机器人领域

原创 | 文 BFT机器人 名人动态 CELEBRITY NEWS 01 王小川&#xff1a;10年后机器智慧将超过人类 年底将推出对标GPT-3.5的模型 科技预言大师雷库兹韦尔说人工智能的奇点&#xff0c;机器智慧超过人类会发生在2045年&#xff0c;王小川的判断比这更激进&#xff0c;他认为这一…

复杂的C++继承

文章目录 什么是继承继承方式赋值规则继承中的作用域&#xff08;隐藏&#xff09;子类中的默认成员函数需要自己写默认成员函数的情况 继承与友元及静态成员多继承菱形继承菱形继承的问题菱形虚拟继承 继承和组合 面向对象三大特性&#xff1a;封装继承和多态。封装在类和对象…

2172. 最大公约数

Powered by:NEFU AB-IN Link 文章目录 2172. 最大公约数题意思路代码 2022年第十三届决赛真题 2172. 最大公约数 题意 给定一个数组, 每次操作可以选择数组中任意两个相邻的元素 x , y x, yx,y 并将其 中的一个元素替换为 gcd ⁡ ( x , y ) \operatorname{gcd}(x, y)gcd(x,y),…

从月薪5000到月薪20000,自动化测试应该这样学...

绝大多数测试工程师都是从功能测试做起的&#xff0c;工作忙忙碌碌&#xff0c;每天在各种业务需求学习和点点中度过&#xff0c;过了好多年发现自己还只是一个功能测试工程师。 随着移动互联网的发展&#xff0c;从业人员能力的整体进步&#xff0c;软件测试需要具备的能力要…

征稿丨IJCAI‘23大模型论坛,优秀投稿推荐AI Open和JCST发表

第一届LLMIJCAI’23 Symposium征稿中&#xff0c;优秀投稿论文推荐《AI Open》和 《JCST》发表。 大规模语言模型&#xff08;LLMs&#xff09;&#xff0c;如ChatGPT和GPT-4&#xff0c;以其在自然语言理解和生成方面的卓越能力&#xff0c;彻底改变了人工智能领域。 LLMs广泛…

Go语言文件I/O操作

go语言中的io操作主要学习目标 掌握文件的常规操作掌握ioutil包的使用掌握bufio包的使用 在go中使用 FileInfo接口 定义了IO的一些函数 FileInfo接口 源码追溯 //type.go // A FileInfo describes a file and is returned by Stat and Lstat. type FileInfo fs.FileInfo/…

ChatGPT:你真的了解网络安全吗?浅谈攻击防御进行时之传统的网络安全

ChatGPT&#xff1a;你真的了解网络安全吗&#xff1f;浅谈网络安全攻击防御进行时 传统的网络安全总结 ChatGPT&#xff08;全名&#xff1a;Chat Generative Pre-trained Transformer&#xff09;&#xff0c;美国OpenAI 研发的聊天机器人程序&#xff0c;是人工智能技术驱动…

什么是网络安全?如何让小白简单的学习网络安全

一、什么是网络安全 网络安全是一个庞大的学科&#xff0c;如果只是普及网络安全技能是非常枯燥的&#xff0c;所以建议从大众容易接受的网络安全诈骗入手&#xff0c;可以先介绍一下近年来频发的网络安全诈骗案例&#xff0c;钓鱼邮件、中奖短信、冒充公检法等多种诈骗手段&am…

Koala:加州大学BAIR团队使用ChatGPT蒸馏数据和公开数据集微调LLaMA模型得到

自从Meta发布LLaMA以来&#xff0c;围绕它开发的模型与日俱增&#xff0c;比如Alpaca、llama.cpp、ChatLLaMA以及Vicuna等等&#xff0c;相关的博客可以参考如下&#xff1a; 【Alpaca】斯坦福发布了一个由LLaMA 7B微调的模型Alpaca&#xff08;羊驼&#xff09;&#xff0c;训…

SpringBoot+Vue前后端分离项目——订单模块——订单管理页面设计

接口返回数据格式&#xff1a; {"msg": "查询成功","total": 1,"code": 200,"data": [{"orderId": "qwer1234","userId": "1","userName": "admin","ad…

全网最全JAVA面试八股文,终于整理完了,堪称2023最强

当今互联网行业中&#xff0c;Java作为一种广泛应用的编程语言&#xff0c;对于求职者来说仍是一项受欢迎的技能。然而&#xff0c;随着市场上的开发人员数量越来越多&#xff0c;Java面试的竞争也愈加激烈。 目前Java面试有着以下现状&#xff1a; 面试难度加大 与过去相比…

9:00进去,9:05就出来了,这问的也太变态了···

从外包出来&#xff0c;没想到死在另一家厂子了。 自从加入这家公司&#xff0c;每天都在加班&#xff0c;钱倒是给的不少&#xff0c;所以也就忍了。没想到5月一纸通知&#xff0c;所有人不许加班&#xff0c;薪资直降30%&#xff0c;顿时有吃不起饭的赶脚。 好在有个兄弟内推…

第四章 内存管理

4.1 内存的基本知识 4.1.1 知识总览 1、内存的作用&#xff1a; 内存可存放数据。程序执行前需要先放到内存中才能被CPU处理--->缓和CPU与硬盘之前的速度矛盾 2、内存的存放&#xff1a; 内存的存放类似于酒店&#xff0c;给内存的每个存储单元都编号。&#xff08;在多道…

使用柔性数组重写MyString

hello&#xff0c;各位宝子&#xff0c;今天阿崽将使用c和柔性数组的方式重新去写String类 在开始本次知识前&#xff0c;首先给大家介绍下柔性数组这个buff特点&#xff1a; 结构中的柔性数组成员前面至少要包含一个其他成员 sizeof返回的这种结构大小不包括柔性数组的内存 …

不想打工做什么好呢?厌倦打工的不妨试试以下几种赚钱方式

除开一些比较幸运的人&#xff0c;谁不是一边打工养家&#xff0c;一边在找寻好机会&#xff1f;人与人之间本来就存在诸多的不公平。有的人这辈子只能靠打工养家&#xff0c;只有少数人能不靠打工来挣钱养家&#xff0c;但是这极少部分人凭什么是你&#xff1f; 现在社会上&am…

( 数组) 209. 长度最小的子数组——【Leetcode每日一题】

❓209. 长度最小的子数组 难度&#xff1a;中等 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;…