Mybatis-TypeHandler类型转换器

文章目录

  • TypeHandler 接口
  • TypeHandler 注册
  • TypeHandler 查询
  • 别名管理
  • 总结

TypeHandler 接口

TypeHandler 这个接口 就是Mybatis的类型转换器

/*** @author Clinton Begin*/
public interface TypeHandler<T> {// 在通过PreparedStatement为SQL语句绑定参数时,会将传入的实参数据由JdbcType类型转换成Java类型void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;// 从ResultSet中获取数据时会使用getResult()方法,其中会将读取到的数据由Java类型转换成JdbcType类型T getResult(ResultSet rs, String columnName) throws SQLException;T getResult(ResultSet rs, int columnIndex) throws SQLException;T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}

MyBatis 中定义了 BaseTypeHandler 抽象类来实现一些 TypeHandler 的公共逻辑,BaseTypeHandler 在实现 TypeHandler 的同时,还实现了 TypeReference 抽象类。其继承关系如下图所示:

在这里插入图片描述
在 BaseTypeHandler 中,简单实现了 TypeHandler 接口的 setParameter() 方法和 getResult() 方法。

  • 在 setParameter() 实现中,会判断传入的 parameter 实参是否为空,如果为空,则调用 PreparedStatement.setNull() 方法进行设置;如果不为空,则委托 setNonNullParameter() 这个抽象方法进行处理,setNonNullParameter() 方法由 BaseTypeHandler 的子类提供具体实现。
  • 在 getResult() 的三个重载实现中,会直接调用相应的 getNullableResult() 抽象方法,这里有三个重载的 getNullableResult() 抽象方法,它们都由 BaseTypeHandler 的子类提供具体实现。

下图展示了 BaseTypeHandler 的全部实现类,虽然实现类比较多,但是它们的实现方式大同小异。
在这里插入图片描述
这里我们以 LongTypeHandler 为例进行分析,具体实现如下:

/*** @author Clinton Begin*/
public class LongTypeHandler extends BaseTypeHandler<Long> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType)throws SQLException {// 调用PreparedStatement.setLong()实现参数绑定ps.setLong(i, parameter);}@Overridepublic Long getNullableResult(ResultSet rs, String columnName)throws SQLException {// 调用ResultSet.getLong()获取指定列值long result = rs.getLong(columnName);return result == 0 && rs.wasNull() ? null : result;}@Overridepublic Long getNullableResult(ResultSet rs, int columnIndex)throws SQLException {// 调用ResultSet.getLong()获取指定列值long result = rs.getLong(columnIndex);return result == 0 && rs.wasNull() ? null : result;}@Overridepublic Long getNullableResult(CallableStatement cs, int columnIndex)throws SQLException {// 调用ResultSet.getLong()获取指定列值long result = cs.getLong(columnIndex);return result == 0 && cs.wasNull() ? null : result;}
}

可以看到:LongTypeHandler 的核心还是通过 PreparedStatement.setLong() 方法以及 ResultSet.getLong() 方法实现的。至于其他 BaseTypeHandler 的核心实现,同样也是依赖了 JDBC 的 API,这里就不再展开介绍了。

TypeHandler 注册

了解了 TypeHandler 接口实现类的核心原理之后,我们就来解决下面两个问题

  • MyBatis 如何管理这么多的 TypeHandler 接口实现呢?
  • 如何在合适的场景中使用合适的 TypeHandler 实现进行类型转换呢?

你若使用过 MyBatis 的话,应该知道我们可以在 mybatis-config.xml 中通过 标签配置自定义的 TypeHandler 实现,也可以在 Mapper.xml 配置文件定义 的时候指定 typeHandler 属性。无论是哪种配置方式,MyBatis 都会在初始化过程中,获取所有已知的 TypeHandler(包括内置实现和自定义实现),然后创建所有 TypeHandler 实例并注册到 TypeHandlerRegistry 中,由 TypeHandlerRegistry 统一管理所有 TypeHandler 实例。 TypeHandlerRegistry 管理 TypeHandler 的时候,用到了以下四个最核心的集合。

  • jdbcTypeHandlerMap(Map>类型):该集合记录了 JdbcType 与 TypeHandler 之间的关联关系。JdbcType 是一个枚举类型,每个 JdbcType 枚举值对应一种 JDBC 类型,例如,JdbcType.VARCHAR 对应的就是 JDBC 中的 varchar 类型。在从 ResultSet 中读取数据的时候,就会从 JDBC_TYPE_HANDLER_MAP 集合中根据 JDBC 类型查找对应的 TypeHandler,将数据转换成 Java 类型。
  • typeHandlerMap(Map>>类型):该集合第一层 Key 是需要转换的 Java 类型,第二层 Key 是转换的目标 JdbcType,最终的 Value 是完成此次转换时所需要使用的 TypeHandler 对象。那为什么要有两层 Map 的设计呢?这里我们举个例子:Java 类型中的 String 可能转换成数据库中的 varchar、char、text 等多种类型,存在一对多关系,所以就可能有不同的 TypeHandler 实现。
  • allTypeHandlersMap(Map类型):该集合记录了全部 TypeHandler 的类型以及对应的 TypeHandler 实例对象。
  • NULL_TYPE_HANDLER_MAP(Map>类型):空 TypeHandler 集合的标识,默认值为 Collections.emptyMap()。

在 MyBatis 初始化的时候,实例化全部 TypeHandler 对象之后,会立即调用 TypeHandlerRegistry 的 register() 方法完成这些 TypeHandler 对象的注册,这个注册过程的核心逻辑就是向上述四个核心集合中添加 TypeHandler 实例以及与 Java 类型、JDBC 类型之间的映射。

TypeHandlerRegistry.register() 方法有多个重载实现,这些重载中最基础的实现是三个参数的重载实现,具体实现如下:

private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {if (javaType != null) { // 检测是否明确指定了TypeHandler能够处理的Java类型// 根据指定的Java类型,从typeHandlerMap集合中获取相应的TypeHandler集合Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);if (map == null || map == NULL_TYPE_HANDLER_MAP) {map = new HashMap<>();}// 将TypeHandler实例记录到typeHandlerMap集合map.put(jdbcType, handler);typeHandlerMap.put(javaType, map);}// 向allTypeHandlersMap集合注册TypeHandler类型和对应的TypeHandler对象allTypeHandlersMap.put(handler.getClass(), handler);}

除了上面的 register() 重载,在有的 register() 重载中会尝试从 TypeHandler 类中的@MappedTypes 注解和 @MappedJdbcTypes 注解中读取信息。其中,@MappedTypes 注解中可以配置 TypeHandler 实现类能够处理的 Java 类型的集合,@MappedJdbcTypes 注解中可以配置该 TypeHandler 实现类能够处理的 JDBC 类型集合。

如下就是读取 @MappedJdbcTypes 注解的 register() 重载方法:

 private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {// 尝试从TypeHandler类中获取@MappedJdbcTypes注解MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);if (mappedJdbcTypes != null) {// 根据@MappedJdbcTypes注解指定的JDBC类型进行注册for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {// 交给前面的三参数重载处理register(javaType, handledJdbcType, typeHandler);}// 如果支持jdbcType为null,也是交给前面的三参数重载处理if (mappedJdbcTypes.includeNullJdbcType()) {register(javaType, null, typeHandler);}} else {// 如果没有配置MappedJdbcTypes注解,也是交给前面的三参数重载处理register(javaType, null, typeHandler);}}

下面是读取 @MappedTypes 注解的 register() 方法重载:

@SuppressWarnings("unchecked")public <T> void register(TypeHandler<T> typeHandler) {boolean mappedTypeFound = false;// 读取TypeHandler类中定义的@MappedTypes注解MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);if (mappedTypes != null) {// 根据@MappedTypes注解中指定的Java类型进行注册for (Class<?> handledType : mappedTypes.value()) {// 交给前面介绍的register()重载读取@MappedJdbcTypes注解并完成注册register(handledType, typeHandler);mappedTypeFound = true;}}// 从3.1.0版本开始,如果TypeHandler实现类同时继承了TypeReference这个抽象类,// 这里会尝试自动查找对应的Java类型if (!mappedTypeFound && typeHandler instanceof TypeReference) {try {TypeReference<T> typeReference = (TypeReference<T>) typeHandler;// 交给前面介绍的register()重载读取@MappedJdbcTypes注解并完成注册register(typeReference.getRawType(), typeHandler);mappedTypeFound = true;} catch (Throwable t) {// maybe users define the TypeReference with a different type and are not assignable, so just ignore it}}if (!mappedTypeFound) {register((Class<T>) null, typeHandler);}}

我们接下来看最后一个 register() 重载。TypeHandlerRegistry 提供了扫描一个包下的全部 TypeHandler 接口实现类的 register() 重载。在该重载中,会首先读取指定包下面的全部的 TypeHandler 实现类,然后再交给 register() 重载读取 @MappedTypes 注解和 @MappedJdbcTypes 注解,并最终完成注册。这个 register() 重载的具体实现比较简单,这里就不再展示,你若感兴趣的话可以参考源码进行学习。

最后,我们再来看看 TypeHandlerRegistry 的构造方法,其中会通过 register() 方法注册多个 TypeHandler 对象,下面就展示了为 String 类型注册 TypeHandler 的核心实现:

public TypeHandlerRegistry() {// StringTypeHandler可以实现String类型与char、varchar、longvarchar类型之间的转换register(String.class, JdbcType.CHAR, new StringTypeHandler());register(String.class, JdbcType.VARCHAR, new StringTypeHandler());register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());// ClobTypeHandler可以完成String类型与clob类型之间的转换register(String.class, JdbcType.CLOB, new ClobTypeHandler());// NStringTypeHandler可以完成String类型与NVARCHAR、NCHAR类型之间的转换register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());register(String.class, JdbcType.NCHAR, new NStringTypeHandler());// NClobTypeHandler可以完成String类型与NCLOB类型之间的转换register(String.class, JdbcType.NCLOB, new NClobTypeHandler());// 省略其他TypeHandler实现的注册逻辑}

TypeHandler 查询

分析完注册 TypeHandler 实例的具体实现之后,我们接下来就来看看 MyBatis 是如何从 TypeHandlerRegistry 底层的这几个集合中查找正确的 TypeHandler 实例,该功能的具体实现是在 TypeHandlerRegistry 的 getTypeHandler() 方法中

这里的 getTypeHandler() 方法也有多个重载,最核心的重载是 getTypeHandler(Type,JdbcType) 这个重载方法,其中会根据传入的 Java 类型和 JDBC 类型,从底层的几个集合中查询相应的 TypeHandler 实例,具体实现如下:

private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {if (ParamMap.class.equals(type)) {return null; // 过滤掉ParamMap类型}// 根据Java类型查找对应的TypeHandler集合Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);TypeHandler<?> handler = null;if (jdbcHandlerMap != null) {// 根据JdbcType类型查找对应的TypeHandler实例handler = jdbcHandlerMap.get(jdbcType);if (handler == null) {// 没有对应的TypeHandler实例,则使用null对应的TypeHandlerhandler = jdbcHandlerMap.get(null);}if (handler == null) {// 如果jdbcHandlerMap只注册了一个TypeHandler,则使用此TypeHandler对象handler = pickSoleHandler(jdbcHandlerMap);}}return (TypeHandler<T>) handler;}

在 getTypeHandler() 方法中会调用 getJdbcHandlerMap() 方法检测 typeHandlerMap 集合中相应的 TypeHandler 集合是否已经初始化

  • 如果已初始化,则直接使用该集合进行查询
  • 如果未初始化,则尝试以传入的 Java 类型的、已初始化的父类对应的 TypeHandler 集合作为初始集合;
  • 如果该 Java 类型的父类没有关联任何已初始化的 TypeHandler 集合,则将该 Java 类型对应的 TypeHandler 集合初始化为 NULL_TYPE_HANDLER_MAP 标识。

getJdbcHandlerMap() 方法具体实现如下:

private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {// 首先查找指定Java类型对应的TypeHandler集合Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(type);if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) { // 检测是否为空集合标识return null;}// 初始化指定Java类型的TypeHandler集合if (jdbcHandlerMap == null && type instanceof Class) {Class<?> clazz = (Class<?>) type;if (Enum.class.isAssignableFrom(clazz)) { // 针对枚举类型的处理Class<?> enumClass = clazz.isAnonymousClass() ? clazz.getSuperclass() : clazz;jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(enumClass, enumClass);if (jdbcHandlerMap == null) {register(enumClass, getInstance(enumClass, defaultEnumTypeHandler));return typeHandlerMap.get(enumClass);}} else {// 查找父类关联的TypeHandler集合,并将其作为clazz对应的TypeHandler集合jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);}}// 如果上述查找皆失败,则以NULL_TYPE_HANDLER_MAP作为clazz对应的TypeHandler集合typeHandlerMap.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);return jdbcHandlerMap;}

这里调用的 getJdbcHandlerMapForSuperclass() 方法会判断传入的 clazz 的父类是否为空或 Object。如果是,则方法直接返回 null;如果不是,则尝试从 typeHandlerMap 集合中获取父类对应的 TypeHandler 集合,但如果父类没有关联 TypeHandler 集合,则递归调用 getJdbcHandlerMapForSuperclass() 方法顺着继承树继续向上查找父类,直到查找到父类的 TypeHandler 集合,然后直接返回。

下面是 getJdbcHandlerMapForSuperclass() 方法的具体实现:

private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {Class<?> superclass = clazz.getSuperclass();if (superclass == null || Object.class.equals(superclass)) {return null; // 父类为Object或null则查找结束}Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(superclass);if (jdbcHandlerMap != null) {return jdbcHandlerMap;} else {// 顺着继承树,递归查找父类对应的TypeHandler集合return getJdbcHandlerMapForSuperclass(superclass);}}

别名管理

TypeAliasRegistry 是维护别名配置的核心实现所在,其中提供了别名注册、别名查询的基本功能。在 TypeAliasRegistry 的 typeAliases 字段(Map>类型)中记录了别名与 Java 类型之间的对应关系,我们可以通过 registerAlias() 方法完成别名的注册,具体实现如下

public void registerAlias(String alias, Class<?> value) {if (alias == null) { // 传入的别名为null,直接抛出异常throw new TypeException("The parameter alias cannot be null");}// 将别名全部转换为小写String key = alias.toLowerCase(Locale.ENGLISH);// 检测别名是否存在冲突,如果存在冲突,则直接抛出异常if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {throw new TypeException("...");}// 在typeAliases集合中记录别名与类之间的映射关系typeAliases.put(key, value);}

在 TypeAliasRegistry 的构造方法中,会通过上述 registerAlias() 方法将 Java 的基本类型、基本类型的数组类型、基本类型的封装类、封装类型的数组类型、Date、BigDecimal、BigInteger、Map、HashMap、List、ArrayList、Collection、Iterator、ResultSet 等常用类型添加了别名,具体实现比较简单,这里就不再展示,你若感兴趣的话可以参考源码进行学习。

除了明确传入别名与相应的 Java 类型之外,TypeAliasRegistry 还提供了扫描指定包名下所有的类中的 @Alias 注解获取别名配置,并完成注册的功能,这个功能涉及两个 registerAliases() 方法的重载,相关实现如下:

public void registerAliases(String packageName, Class<?> superType) {ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();// 查找指定包下所有的superType类型resolverUtil.find(new ResolverUtil.IsA(superType), packageName);Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();for (Class<?> type : typeSet) {// 过滤掉内部类、接口以及抽象类if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {// 扫描类中的@Alias注解registerAlias(type);}}}public void registerAlias(Class<?> type) {// 获取类的简单名称,其中不会包含包名String alias = type.getSimpleName();// 获取类中的@Alias注解Alias aliasAnnotation = type.getAnnotation(Alias.class);if (aliasAnnotation != null) { // 获取特定别名alias = aliasAnnotation.value();}// 这里的@Alias注解指定的别名与type类型绑定registerAlias(alias, type);}

总结

在这一讲我们重点介绍了 MyBatis 中 JdbcType 与 Java 类型之间转换的相关实现。

  • 首先,介绍了 JdbcType 与 Java 类型之间的常见映射关系,以及两种类型之间转换的基础知识,深入分析了 TypeHandler 接口及其核心实现,了解了两种类型转换的原理;
  • 接下来,又讲解了 TypeHandler 的注册和查询机制,明确了 MyBatis 是如何管理和使用众多的 TypeHandler 实现;
  • 最后,分析了 MyBatis 中的别名实现。

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

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

相关文章

stm32项目(14)——基于stm32f103zet6的循迹避障小车

1.功能设计 stm32循迹避障小车&#xff0c;使用超声波测距&#xff0c;使用红外循迹模块追踪黑线&#xff0c;实现循迹功能。此外&#xff0c;还可以检测烟雾、火焰、人体、温湿度。温湿度显示在LCD屏幕上。检测到有人、有火焰、有烟雾时&#xff0c;蜂鸣器报警&#xff01; 功…

强化学习(五)-Deterministic Policy Gradient (DPG) 算法及公式推导

针对连续动作空间&#xff0c;策略函数没法预测出每个动作选择的概率。因此使用确定性策略梯度方法。 0 概览 1 actor输出确定动作2 模型目标&#xff1a; actor目标&#xff1a;使critic值最大 critic目标&#xff1a; 使TD error最大3 改进&#xff1a; 使用两个target 网络…

【接口测试】JMeter调用JS文件实现RSA加密

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

SQL分类

SQL分类 DDL 查询库 查询表 创建表 修改表 DML 添加数据 修改数据 删除数据 DQL 基本查询 条件查询 聚合函数 分组查询 排序查询 分页查询 执行顺序 DCL 管理用户 管理权限 数据类型 数值类型 字符串类型 日期类型

使用@jiaminghi/data-view实现一个数据大屏

<template><div class"content bg"><!-- 全局容器 --><!-- <dv-full-screen-container> --><!-- 第二行 --><div class"module-box" style"align-items: start; margin-top: 10px"><!-- 左 -->…

1162字符串逆序

一&#xff1a;题目 二.思路分析 1.如果不用递归&#xff0c;可以输入字符串后&#xff0c;再逆序输出&#xff0c;但是题目要求使用递归 2.使用递归&#xff1a; 2.1输入字符&#xff0c;直到输入的字符是‘&#xff01;’&#xff0c;停止输入&#xff0c;否则继续输入&…

自学华为鸿蒙开发?一般人我还是劝你算了吧!!!

本人纯屌丝一枚&#xff0c;在学编程之前对电脑的认知也就只限于上个网&#xff0c;玩个办公软件。这里不能跑题&#xff0c;我为啥说自学鸿蒙开发&#xff0c;一般人我还是劝你算了吧。因为我就是那个一般人。 基础真的很简单&#xff0c;是个人稍微认点真都能懂&#xff0c;…

win10下使用fastdds

参照网址&#xff1a;https://www.cnblogs.com/edkong/p/16522836.html 需要安装的软件和用处&#xff1a; Visual Studio&#xff0c;用于打开和编译fastdds示例工程。下载地址&#xff1a;下载 Visual Studio Tools - 免费安装 Windows、Mac、LinuxCmake&#xff0c;用于将…

【String str = new String(“hollis“) 创建了几个对象?】

✅典型解析 创建的对象数应该是1个或者2个。 首先要清楚什么是对象? Java是一种面向对象的语言&#xff0c;而Java对象在JVM中的存储也是有一定的结构的&#xff0c;在HotSpot虚机中&#xff0c;存储的形式就是oop-klass model&#xff0c;即ava对象模型。我们在Java代码中&am…

FastGPT+ChatGLM3-6b搭建知识库

前言&#xff1a;我用fastgpt直接连接chatglm3&#xff0c;没有使用oneai&#xff0c;不是很复杂&#xff0c;只需要对chatglm3项目代码做少量修改就能支持使用embeddings&#xff0c;向量模型用的m3e&#xff0c;效果还可以 我的配置&#xff1a; 处理器&#xff1a;i5-13500 …

VideoPoet: Google的一种用于零样本视频生成的大型语言模型

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【C++进阶02】多态

一、多态的概念及定义 1.1 多态的概念 多态简单来说就是多种形态 同一个行为&#xff0c;不同对象去完成时 会产生出不同的状态 多态分为静态多态和动态多态 静态多态指的是编译时 在程序编译期间确定了程序的行为 比如&#xff1a;函数重载 动态多态指的是运行时 在程序运行…

【Java异常】聊聊异常可能带来的坑

一个活生生的案例 本周帮同事排查了一个问题&#xff0c;比较诡异的是他通过测试&#xff0c;并没有找到根本原因&#xff0c;只是发现有对应的错误日志。 但是其实并没有将堆栈信息打印出来。很难看出问题。添加了 e.printStackTrace(); get exception in exter: / by zero显…

HP笔记本电脑进入BIOS的方法主要有两种,它们使用场合不同

BIOS&#xff08;基本输入输出系统&#xff09;是一种实用程序&#xff0c;它在你按下电源按钮后启动并加载操作系统。无论是要更新HP笔记本电脑的BIOS系统&#xff0c;还是清除前一个系统中的错误&#xff0c;第一步都是进入BIOS实用程序。 在按键输入BIOS设置并对其进行修改…

循环神经⽹络中的梯度算法GRU

1. 什么是GRU 在循环神经⽹络中的梯度计算⽅法中&#xff0c;我们发现&#xff0c;当时间步数较⼤或者时间步较小时&#xff0c;**循环神经⽹络的梯度较容易出现衰减或爆炸。虽然裁剪梯度可以应对梯度爆炸&#xff0c;但⽆法解决梯度衰减的问题。**通常由于这个原因&#xff0…

Java开发框架和中间件面试题(3)

14.Spring事务中的隔离级别有哪几种&#xff1f; 在TransactionDefinition接口中定义了五个表示隔离级别的常量&#xff1a; 1⃣️ISOLATION DEFAULT&#xff1a;使用后端数据库默认的隔离级别&#xff0c;Mysql默认采用的可重复读隔离级别&#xff1b;Oracle默认采用的读已提…

在Linux下探索MinIO存储服务如何远程上传文件

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 创建Buckets和Access Keys二. Linux 安装Cpolar三. 创建连接MinIO服务公网地…

JavaWeb—html, css, javascript, dom,xml, tomcatservlet

文章目录 快捷键HTML**常用特殊字符替代:****标题****超链接标签****无序列表、有序列表****无序列表**:ul/li 基本语法**有序列表ol/li:****图像标签(img)**** 表格(table)标签****表格标签-跨行跨列表格****form(表单)标签介绍****表单form提交注意事项**div 标签p 标签sp…

Linux命令-查看内存、GC情况及jmap 用法

查看进程占用内存、CPU使用情况 1、查看进程 #jps 查看所有java进程 #top 查看cpu占用高进程 输入m &#xff1a;根据内存排序 topMem: 16333644k total, 9472968k used, 6860676k free, 165616k buffers Swap: 0k total, 0k used, 0k free, 6…

Kubernetes 的用法和解析(K8S 日志方案) -- 8

一、统一日志管理的整体方案 通过应用和系统日志可以了解Kubernetes集群内所发生的事情&#xff0c;对于调试问题和监视集群活动来说日志非常有用。对于大部分的应用来说&#xff0c;都会具有某种日志机制。因此&#xff0c;大多数容器引擎同样被设计成支持某种日志机制。 对…