Mybatis 参数处理器注入配置解析流程

news2024/12/26 22:41:17

参数处理器的作用

Mybatis作为一个ORM框架, 其最原始的本质就是JDBC,对于JDBC的使用步骤中有2步和参数处理器有关, 就是给预处理器PreparedStatement 设置参数以及通过结果集获取字段值。 这两个步骤在Mybatis中已经成为框架底层逻辑流程, 给用户留下扩展点的就是参数处理器。

参数处理器的顶级接口

/*T 为类型处理泛型*/
public interface TypeHandler<T> {

  /**
   * 
   * @param ps 预表达式
   * @param i  参数位置
   * @param parameter  实参对象
   * @param jdbcType 对于的JDBC类型
   * @throws SQLException
   */
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  /**
   * Gets the result.
   *
   * @param rs
   *          the rs
   * @param columnName
   *          Column name, when configuration <code>useColumnLabel</code> is <code>false</code>
   * @return the result
   * @throws SQLException
   *           the SQL exception
   */
  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

如代码所示, 对JDBC的使用有所了解, 为啥定义这几个方法还是比较容易知道了, 最后一个getResult 是和存储过程相关。

抽象类TypeReference

我们知道泛型类型如何获取, 是需要反射相关的代码来解析的, 所以Mybatis为了得到该参数处理器的泛型参数类型就搞了一个抽象类TypeReference, 用来解析实现类的泛型类型。

public abstract class TypeReference<T> {

  /*泛型类型的原始类型*/
  private final Type rawType;

  protected TypeReference() {
    /*构造方法解析泛型类型  注意这里的getClass 实际调用的是实现类的getClass, 方法具有多态性质*/
    rawType = getSuperclassTypeParameter(getClass());
  }

  Type getSuperclassTypeParameter(Class<?> clazz) {
    /*获取泛型父类*/
    Type genericSuperclass = clazz.getGenericSuperclass();
    /*如果父类是class对象*/
    if (genericSuperclass instanceof Class) {
      // try to climb up the hierarchy until meet something useful
      //如果泛型父类和TypeReference 相等继续往上爬
      if (TypeReference.class != genericSuperclass) {
        return getSuperclassTypeParameter(clazz.getSuperclass());
      }
      /*到这里说明父类不是泛型类型,缺少泛型类型参数,无法解析*/
      throw new TypeException("'" + getClass() + "' extends TypeReference but misses the type parameter. "
        + "Remove the extension or add a type parameter to it.");
    }
    /*获取泛型类型的实际类型*/
    Type rawType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
    // TODO remove this when Reflector is fixed to return Types
    if (rawType instanceof ParameterizedType) {
      rawType = ((ParameterizedType) rawType).getRawType();
    }

    return rawType;
  }

  public final Type getRawType() { /*获取泛型类型*/
    return rawType;
  }

  @Override
  public String toString() {
    return rawType.toString();
  }

}

抽象实现类BaseTypeHandler

BaseTypeHandler 继承了TypeReference 实现了TypeHandler, 不仅是一个参数处理器,还能解析参数处理器的泛型类型。
BaseTypeHandler默认构造方法会掉用父类的无参构造方法,这也就调用了TypeReference的构造方法能够进行泛型类型参数解析。 作为一个抽象类,,其本身也实现父类的方法,并提供更加具体一点的抽象方法共给具体类去实现。

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {

  /**
   * @deprecated Since 3.5.0 - See https://github.com/mybatis/mybatis-3/issues/1203. This field will remove future.
   */
  @Deprecated
  protected Configuration configuration;

  /**
   * Sets the configuration.
   *
   * @param c
   *          the new configuration
   * @deprecated Since 3.5.0 - See https://github.com/mybatis/mybatis-3/issues/1203. This property will remove future.
   */
  @Deprecated
  public void setConfiguration(Configuration c) {
    this.configuration = c;
  }

  @Override
  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
    if (parameter == null) {
      if (jdbcType == null) {
        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
      }
      try {
        ps.setNull(i, jdbcType.TYPE_CODE);
      } catch (SQLException e) {
        throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
              + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
              + "Cause: " + e, e);
      }
    } else {
      try {
        setNonNullParameter(ps, i, parameter, jdbcType);
      } catch (Exception e) {
        throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
              + "Try setting a different JdbcType for this parameter or a different configuration property. "
              + "Cause: " + e, e);
      }
    }
  }

  @Override
  public T getResult(ResultSet rs, String columnName) throws SQLException {
    try {
      return getNullableResult(rs, columnName);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
    }
  }

  @Override
  public T getResult(ResultSet rs, int columnIndex) throws SQLException {
    try {
      return getNullableResult(rs, columnIndex);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column #" + columnIndex + " from result set.  Cause: " + e, e);
    }
  }

  @Override
  public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
    try {
      return getNullableResult(cs, columnIndex);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column #" + columnIndex + " from callable statement.  Cause: " + e, e);
    }
  }

  public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  /**
   * Gets the nullable result.
   *
   * @param rs
   *          the rs
   * @param columnName
   *          Column name, when configuration <code>useColumnLabel</code> is <code>false</code>
   * @return the nullable result
   * @throws SQLException
   *           the SQL exception
   */
  public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;

  public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;

  public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;

}

是不是所有的参数处理器都要实现BaseTypeHandler

嗯, 最好是这样, 这样我们就可以很方便的获取泛型类实际类型,以及一些通用功能的实现, 但是参数处理器的接口本质还是TypeHandler, 其他类只是一些装饰而已。所以也可以直接继承TypeHandler。

参数处理器的配置(自定义参数处理器)

使用参数处理器我们需要指定这个参数处理器处理的Java类型,以及改Java类型对应JDBC类型。也就是配置参数处理器。对于配置我们一般都会使用XML或者注解指定。所以Mybatis也提供这样的功能。

实现自己的参数处理器:

/**
 * 用户参数处理器
 * @author puhaiguo
 * @date 2022-12-13 04:58
 * @version 1.0
 */
@MappedTypes({User.class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class UserTypeHandler implements TypeHandler<User> {
  @Override
  public void setParameter(PreparedStatement ps, int i, User parameter, JdbcType jdbcType) throws SQLException {
    int n = 0;
  }

  @Override
  public User getResult(ResultSet rs, String columnName) throws SQLException {
    return null;
  }

  @Override
  public User getResult(ResultSet rs, int columnIndex) throws SQLException {
    return null;
  }

  @Override
  public User getResult(CallableStatement cs, int columnIndex) throws SQLException {
    return null;
  }
}

然后我们需要把这个类配置到Mybatis配置文件

因为我使用了注解标记了UserTypeHandler 对应的Java类型和JDBC类型, 所以我可以这么配置

<typeHandlers>
    <typeHandler handler="com.learn.mybatis.typehandler.UserTypeHandler"/>
  </typeHandlers>

如果没使用注解指明对应关系
需要这么配置:

<typeHandlers>
    <typeHandler handler="com.learn.mybatis.typehandler.UserTypeHandler" jdbcType="User" javaType="varchar"/>
  </typeHandlers>

参数处理器的解析过程

typeHandlerElement(root.evalNode("typeHandlers"));  //设置类型处理器
private void typeHandlerElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) { /*按照包的配置*/
          String typeHandlerPackage = child.getStringAttribute("name"); //获取包名
          typeHandlerRegistry.register(typeHandlerPackage);/*按照包名进行注册*/
        } else {
          String javaTypeName = child.getStringAttribute("javaType");
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          String handlerTypeName = child.getStringAttribute("handler");
          Class<?> javaTypeClass = resolveClass(javaTypeName);/*别名对应的类型*/
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);/*别名对应的类型*/
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);/*别名对应的类型*/
          if (javaTypeClass != null) {/*如果java类型为空*/
            if (jdbcType == null) {/*如果jdbc类型为空*/
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
  }

如上代码逻辑可以看出 支持直接配置类, 也可以指定包路径进行扫描。

因为我的配置类用的是注解,所以代码逻辑走typeHandlerRegistry.register(typeHandlerClass)

public void register(Class<?> typeHandlerClass) {
    boolean mappedTypeFound = false;
    MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class); //获取注解
    if (mappedTypes != null) { //如果注解不为空
      for (Class<?> javaTypeClass : mappedTypes.value()) { //获取注解的value值, 所有的java类型
        register(javaTypeClass, typeHandlerClass); //进行注册
        mappedTypeFound = true;
      }
    }
    if (!mappedTypeFound) {
      register(getInstance(null, typeHandlerClass));
    }
  }

上面的代码也很好理解就是一些解析注解进行注册的过程。其中因为注解配置的JavaType 和 JDBCType都是数组类型,所以是支持多映射关系。

public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
    register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
  }

代码这里需要进行实例化参数处理器

public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {//java类型 -> 类型处理器
    if (javaTypeClass != null) {/*如果java类型不为空*/
      try {
        Constructor<?> c = typeHandlerClass.getConstructor(Class.class);//进行尝试找到当前java类型的构造函数
        return (TypeHandler<T>) c.newInstance(javaTypeClass); //实例化对象
      } catch (NoSuchMethodException ignored) {
        // ignored   如果找不到那也不跑出异常
      } catch (Exception e) {
        throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
      }
    }
    try {
      Constructor<?> c = typeHandlerClass.getConstructor();//无参实例化对象构造方法
      return (TypeHandler<T>) c.newInstance();
    } catch (Exception e) {
      throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
    }
  }

//Class对象也是继承Type的
  private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
    MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class); //获取类型处理类上的jdbc参数注解
    if (mappedJdbcTypes != null) { //typeHandler 类上存在注解
      for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {//如果jdbc 集合类型不为空
        register(javaType, handledJdbcType, typeHandler);
      }
      /*判断注解是否配置了支持空映射*/
      if (mappedJdbcTypes.includeNullJdbcType()) {
        register(javaType, null, typeHandler);
      }
    } else { //说明没有注解Jdbc
      register(javaType, null, typeHandler);
    }
  }

private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    if (javaType != null) {
      Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType); //是否已经注册过
      if (map == null || map == NULL_TYPE_HANDLER_MAP) { //如果不存在
        map = new HashMap<>();  //创建
      }
      map.put(jdbcType, handler); //jdbctype 和类型处理器
      typeHandlerMap.put(javaType, map); //存入映射
    }
    allTypeHandlersMap.put(handler.getClass(), handler); //所有的映射器
  }

总结

整体流程差不多就是这样了, 并不多难, 主要还是java注解和反射技巧的使用。

走后在说一下, 我们在使用Mybatis的时候是不是很少会去定义类型处理器, 那么Mybatis为什么还是工作, 应为Mybatis一些基本的java类型与jdbc类型的类型处理器已经实现了且自动加载了。

TypeHandlerRegistry

public final class TypeHandlerRegistry {

  private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class); /*类型处理器与Jdbc类型的对应关系*/
  private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();/*java类型与 jdbcTypeHandlerMap的对应关系*/
  private final TypeHandler<Object> unknownTypeHandler;
  private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();/*所有的类型处理器*/

  private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();/*空类型处理器*/

  private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;/*空的类型处理器Class对象*/
  }

可以看见这个类是类型处理器的注册工厂。
其构造方法里面就注入了基本的类型处理器。

public TypeHandlerRegistry(Configuration configuration) {
    this.unknownTypeHandler = new UnknownTypeHandler(configuration);

    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());

    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());

    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());

    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());

    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());

    register(Reader.class, new ClobReaderTypeHandler());
    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.CLOB, new ClobTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
    register(JdbcType.CHAR, new StringTypeHandler());
    register(JdbcType.VARCHAR, new StringTypeHandler());
    register(JdbcType.CLOB, new ClobTypeHandler());
    register(JdbcType.LONGVARCHAR, new StringTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());

    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());

    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());

    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

    register(InputStream.class, new BlobInputStreamTypeHandler());
    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());

    register(Object.class, unknownTypeHandler);
    register(Object.class, JdbcType.OTHER, unknownTypeHandler);
    register(JdbcType.OTHER, unknownTypeHandler);

    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());

    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

    register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());

    register(Instant.class, new InstantTypeHandler());
    register(LocalDateTime.class, new LocalDateTimeTypeHandler());
    register(LocalDate.class, new LocalDateTypeHandler());
    register(LocalTime.class, new LocalTimeTypeHandler());
    register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
    register(OffsetTime.class, new OffsetTimeTypeHandler());
    register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
    register(Month.class, new MonthTypeHandler());
    register(Year.class, new YearTypeHandler());
    register(YearMonth.class, new YearMonthTypeHandler());
    register(JapaneseDate.class, new JapaneseDateTypeHandler());

    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
  }

在配置类加载的时候会创建类型处理器对象。
在这里插入图片描述

到这里 类型处理器的使用和解析已经结束了, 但是注入处理器注册器工厂之后在Mybatis怎么在执行sql的使用调用参数处理器下篇博文在继续了解。

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

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

相关文章

【K8S系列】第十一讲:包管理神器-Helm

目录 序言 1.背景介绍 1.1 k8s 简单介绍 1.2 k8s部署挑战 2.Helm 2.1 Helm介绍 2.1 使用优势&#xff1a; 3.Helm模块 3.1 Helm 3.1.1 安装Helm 3.2 Chart 3.2.1 Chart 基本介绍 3.2.2 Chart目录结构 3.3 Repoistory 3.4 Config 3.5 Release 4.投票 序言 当…

Windows及Linux系统查找某个端口和文件被占用

概述 开发中很常见的问题&#xff0c;每次遇到这个问题&#xff0c;都是去Google搜索&#xff0c;不一定能搜到满意的答案&#xff0c;有点耗时&#xff0c;故记录一下&#xff0c;得到本文。 端口被占用&#xff0c;导致IDEA启动应用失败。又或者某个文件被某个未知的应用使…

c#入门-抽象类

抽象类 有些类存在的意义就是为了让别的类继承。自己本身不应该具有实例。例如 class 单位 {public int 生命; } class 建筑 : 单位 {public int 护甲; } class 英雄 : 单位 {public int 护盾; } class 小兵 : 单位 {public int 经验; }可以使用abstract把他声明为抽象类。 抽…

Linux查找find命令全面剖析

Linux查找find命令全面剖析 1. 文件查找 在文件系统上查找符合条件的文件 1.1 简述 locate 命令 非实时查找(数据库查找) 依赖于事先构建的索引&#xff0c;索引的构建是在系统较为空闲时自动进行(周期性任务) 手动更新数据库(updatedb)&#xff0c;索引构建过程需要遍历整…

【物理应用】基于Matlab模拟13自由度摩托车模型

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

Python使用Selenium WebDriver的入门介绍及安装教程

Selenium WebDriver 入门一、什么是Selenium WebDriver二、安装Selenium WebDriver2.1 安装selenium类库2.2 安装浏览器驱动2.3 配置环境变量三、编写第一个Selenium脚本一、什么是Selenium WebDriver WebDriver 以本地化方式驱动浏览器&#xff0c;就像用户在本地或使用 Sele…

[附源码]Nodejs计算机毕业设计基于Java网上玩具商店Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

R语言基于协方差的SEM结构方程模型中的拟合指数

在实践中&#xff0c; 因子负载较低&#xff08;或测量质量较差&#xff09;的模型的拟合指数要好于因子负载较高的模型。 最近我们被客户要求撰写关于SEM的研究报告&#xff0c;包括一些图形和统计输出。例如&#xff0c;如果两个模型具有相同的错误指定级别&#xff0c;并且…

漫游Linux块IO

前言 在计算机的世界里&#xff0c;我们可以将业务进行抽象简化为两种场景——计算密集型和IO密集型。这两种场景下的表现&#xff0c;决定这一个计算机系统的能力。数据库作为一个典型的基础软件&#xff0c;它的所有业务逻辑同样可以抽象为这两种场景的混合。因此&#xff0…

云原生时代数据库运维体系演进

作者&#xff1a;vivo 互联网服务器团队- Deng Song 本文根据邓松老师在“2022 vivo开发者大会"现场演讲内容整理而成。 数据库运维面临着大规模数据库实例难以有效运维、数据库难以做好资源弹性伸缩以及个人隐私数据安全难以保障这三个方面的挑战。对此&#xff0c;vivo给…

redis之事务分析

写在前面 本文一起看下redis提供的事务功能。 1&#xff1a;事务的ACID A(Atomic)原子性&#xff0c;C&#xff08;Consitency&#xff09;一致性&#xff0c;I&#xff08;Isolation&#xff09;隔离性&#xff0c;D&#xff08;Durability&#xff09;持久性&#xff0c;其…

kubernetes学习之路--BadPods(Part1)

摘要&#xff1a;对Pod配置进行实战学习&#xff0c;以BadPods项目为例学习危险配置。 目录 一.BadPods介绍及使用 二.BadPods配置学习 2.1 less1--Everything allowed 基本操作学习 2.2 less1--Everything allowed 渗透学习 一.BadPods介绍及使用 项目地址&#xff1a;h…

西门子KTP1200触摸屏右上角出现黄色感叹号_报警指示器的组态与应用

西门子KTP1200触摸屏右上角出现黄色感叹号_报警指示器的组态与应用 设备运行时产生报警时通常会在画面右上角有个指示器在闪烁提示报警产生。 本次和大家分享报警指示器的组态和具体使用方法。 报警指示器的组态。 报警指示器使用警告三角来表示报警处于未决状态或要求确认。如…

数字验证学习笔记——SystemVerilog芯片验证15 ——随机约束和分布

一、随机和约束 1.1 随机 定向测试能找到你认为可能存在的缺陷&#xff0c;而随机测试可以找到你没有想到的缺陷。随机测试相对于定向测试可以减少相当多的代码量&#xff0c;而产生的激励较定向测试也更多样。 1.2 约束 我们想要的随机自由是一种合法的随机&#xff0c;需…

JAVA毕业设计——基于Springboot+vue的房屋租赁系统(源代码+数据库)

github代码地址 https://github.com/ynwynw/houserent2-public 毕业设计所有选题地址 https://github.com/ynwynw/allProject 基于Springboot的房屋租赁系统(源代码数据库) 一、系统介绍 本项目分为管理员、经纪人、维修员、普通用户四种角色 管理员角色包含以下功能&#…

C++——AVL树

目录 AVL 树 Insert 控制平衡因子 AVL树的旋转 AVL树验证 AVL树的性能 错误排查技巧 AVL 树 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&…

统计学 | 描述统计

一.导论 统计学是通过收集&#xff0c;整理&#xff0c;分析&#xff0c;描述数据等手段&#xff0c;以达到推断所测对象的本质&#xff0c;甚至预测对象未来的一门综合性科学。其目的是探索数据的内在数量规律性&#xff0c;以达到对客观事物的科学认识 统计的本业是消化数据…

pikachu靶场-4 SQL注入漏洞

SQL注入漏洞 在OWASP发布的TOP 10 中&#xff0c;注入漏洞一直是危害排名第一的漏洞&#xff0c;其中主要指的是SQL Inject漏洞。 一个严重的SQL注入漏洞&#xff0c;可能会直接导致一家公司破产&#xff01; 数据库输入漏洞&#xff0c;主要是开发人员在构建代码时&#xf…

基于人眼视觉模型,实现码率、质量、成本的最优均衡

将编码器的优化目标从经典的保真度最高&#xff0c;调整为「主观体验最好」。 视觉是具有 「掩蔽效应」 的。 通俗地说&#xff0c;人眼作为图像信息的接收端&#xff0c;并不能精准捕捉到图像画面的所有变化。 例如&#xff0c;人眼对于画面中亮度的变化、静止的图像、画面整…

算法竞赛入门【码蹄集进阶塔335题】(MT2126-2150)

算法竞赛入门【码蹄集进阶塔335题】(MT2126-2150&#xff09; 文章目录算法竞赛入门【码蹄集进阶塔335题】(MT2126-2150&#xff09;前言为什么突然想学算法了&#xff1f;为什么选择码蹄集作为刷题软件&#xff1f;目录1. MT2126 奇偶序列2. MT2127 数组扦插3. MT2128 sort4. …