Mybatis TypeHandler 介绍及使用

Mybatis TypeHandler类型转换器是负责Java类和jdbc类型之间的转换

主要涉及到下面这几个类:

  • TypeHandler 类型转换器的顶层接口
  • BaseTypeHandler 抽象类继承自TypeHandlerMybatis中所有的类型转换器实现均继承他。
  • TypeHandlerRegistry 类型转换器注册器,负责存储类型转换器。
  • TypeAliasRegistry 类型别名转换器,用来存储类型与别名的对应关系。

TypeHandler

TypeHandler是类型转换器的顶层接口,其定义了类型转换器应该具有的功能,

TypeHandler主要解决了两个问题:

  1. 可以指定我们在Java实体类所包含的自定义类型存入数据库后的类型是什么
  2. 从数据库中取出该数据后自动转换为我们自定义的Java类型

其源码如下:

public interface TypeHandler<T> {  /** * 用于定义在Mybatis设置参数时该如何把Java类型的参数转换为对应的数据库类型 * @param ps 当前的PreparedStatement对象 * @param i 当前参数的位置 * @param parameter 当前参数的Java对象 * @param jdbcType 当前参数的数据库类型 * @throws SQLException */  void setParameter(PreparedStatement ps, int i, T parameter,  JdbcType jdbcType) throws SQLException;  /** * 用于在Mybatis获取数据结果集时如何把数据库类型转换为对应的Java类型 * @param rs 当前的结果集 * @param columnName 当前的字段名称 * @return 转换后的Java对象 * @throws SQLException */  T getResult(ResultSet rs, String columnName) throws SQLException;  /** * 用于在Mybatis通过字段位置获取字段数据时把数据库类型转换为对应的Java类型 * @param rs 当前的结果集 * @param columnIndex 当前字段的位置 * @return 转换后的Java对象 * @throws SQLException */  T getResult(ResultSet rs, int columnIndex) throws SQLException;  /** * 用于Mybatis在调用存储过程后把数据库类型的数据转换为对应的Java类型 * @param cs 当前的CallableStatement执行后的CallableStatement * @param columnIndex 当前输出参数的位置 * @return * @throws SQLException */  T getResult(CallableStatement cs, int columnIndex) throws SQLException;  }  

BaseTypeHandler

BaseTypeHandler是一个抽象类,改类实现了TypeHandler中的方法并实现了异常捕获。继承改类我们可以很容易的实现一个自定义类型转换器,其源码如下:

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {  protected Configuration configuration;  public void setConfiguration(Configuration c) {  this.configuration = c;  }  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 {  setNonNullParameter(ps, i, parameter, jdbcType);  }  }  public T getResult(ResultSet rs, String columnName) throws SQLException {  T result = getNullableResult(rs, columnName);  if (rs.wasNull()) {  return null;  } else {  return result;  }  }  public T getResult(ResultSet rs, int columnIndex) throws SQLException {  T result = getNullableResult(rs, columnIndex);  if (rs.wasNull()) {  return null;  } else {  return result;  }  }  public T getResult(CallableStatement cs, int columnIndex) throws SQLException {  T result = getNullableResult(cs, columnIndex);  if (cs.wasNull()) {  return null;  } else {  return result;  }  }  public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;  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接口的四个方法做了一个简单的选择,把null值的情况都做了一个过滤,核心的取值和设值的方法还是抽象出来了供子类来实现。使用BaseTypeHandler还有一个好处是它继承了另外一个叫做TypeReference的抽象类,通过TypeReference的getRawType()方法可以获取到当前TypeHandler所使用泛型的原始类型。这对Mybatis在注册TypeHandler的时候是非常有好处的。在没有指定javaType的情况下,Mybatis在注册TypeHandler时可以通过它来获取当前TypeHandler所使用泛型的原始类型作为要注册的TypeHandler的javaType类型,这个在讲到Mybatis注册TypeHandler的方式时将讲到。

3 注册TypeHandler

为什么Java自带的类型在存取的时候不会出错,我们自定义的类型就会出错?那是因为mybatis已经将这些类型的TypeHandler提前写好了,并且注册好了

具体注册了哪些,我们可以看TypeHandlerRegistry这个类:

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 #273register(Character.class, new CharacterTypeHandler());register(char.class, new CharacterTypeHandler());}

4. 实现自定义的TypeHandler

我们可以直接继承BaseTypeHandler来实现我们自己的类型转换器

我们要实现把json字符串转换成为我们的对象,可以这么实现:

public class JsonTypeHandler<T> extends BaseTypeHandler<T> {private final Class<T> type;public JsonTypeHandler(Class<T> type) {if (type == null) {throw new IllegalArgumentException("输入参数不能为空");}this.type = type;}@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)throws SQLException {try {String json = JsonUtil.toJson(parameter);ps.setString(i, json);} catch (Exception e) {throw new RuntimeException("Json对象转换成String失败", e);}}@Overridepublic T getNullableResult(ResultSet rs, String columnName) throws SQLException {String json = rs.getString(columnName);return convertJsonToObject(json);}@Overridepublic T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {String json = rs.getString(columnIndex);return convertJsonToObject(json);}@Overridepublic T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {String json = cs.getString(columnIndex);return convertJsonToObject(json);}private T convertJsonToObject(String json) {try {return JsonUtil.fromJson(json, type);} catch (Exception e) {throw new RuntimeException("json转换成Java对象失败", e);}}
}

因为我们使用的是Spring boot工程,只需要把JsonTypeHandler放到Spring boot可以扫描的目录下即可。

在XML中使用:

<resultMap id="BaseResultMap" type="com.db.model.SettlementBill"><id column="id" jdbcType="INTEGER" property="id" /><result column="receipts_code" jdbcType="VARCHAR" property="receiptsCode" /><result column="finance_info" jdbcType="VARCHAR"  property="financeDetailVO1" javaType="com.db.model.SettlementBill"typeHandler="com.mybatis.handler.JsonArrayTypeHandler" /></resultMap><select id="test" parameterType="java.lang.Integer" resultMap="BaseResultMap">select  id, receipts_code, finance_info from settlement_bill where id = #{id,jdbcType=INTEGER}</select>

Mybatis Plus中的使用方式:

@TableName(value = "settlement_bill", autoResultMap = true)
@Data
public class SettlementBill extends CxmBaseModel implements Serializable {@TableId(value = "id", type = IdType.AUTO)private Integer id;@TableField(value = "finance_info",typeHandler = JsonTypeHandler.class)private BatchFinanceDetailVO1 financeDetailVO1;
}

5. Mybatis自动获取TypeHandle

在介绍了Mybatis是如何注册TypeHandler之后就介绍一下Mybatis是如何获取对应的TypeHandler进行类型转换的。

如果我们在Mapper.xml文件中配置某一个属性或变量的映射关系时指定了该属性对应的javaType和jdbcType,则Mybatis会从注册好的TypeHandler中寻找对应的javaType和jdbcType组合的TypeHandler进行处理,这也是Mybatis最基本的获取TypeHandler进行类型转换的方式。

1.javaType和jdbcType都指定

假设Mybatis配置文件中有这么一段TypeHandler的注册信息:

<typeHandlers>  <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;" jdbcType="VARCHAR"/>  
</typeHandlers>  

看这样一个UserMapper.xml定义:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  <mapper namespace="com.tiantian.mybatis.mapper.UserMapper">  <resultMap id="UserResult" type="User">  <id column="id" property="id"/>  <result column="interests" property="interests" javaType="[Ljava.lang.String;" jdbcType="VARCHAR"/>  </resultMap>  <insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyColumn="id">  insert into t_user(name, age, interests) values(#{name}, #{age}, #{interests, javaType=[Ljava.lang.String;, jdbcType=VARCHAR})  </insert>  <update id="updateUser" parameterType="User">  update t_user set name=#{name}, age=#{age}, interests=#{interests} where id=#{id}  </update>  <select id="findById" parameterType="int" resultMap="UserResult">  select * from t_user where id=#{id}  </select>  <delete id="deleteUser" parameterType="int">  delete from t_user where id=#{id}  </delete>  
</mapper>  

我们可以看到在id为UserResult的resultMap中,我们定义了一个对应字段interests的映射关系,并且定义了其javaType为“[Ljava.lang.String;”,jdbcType为VARCHAR,这个时候Mybatis就会到已经注册了的TypeHandler中寻找到能处理javaType和jdbcType对应的类型转换的TypeHandler来进行处理。在这里就会找到我们注册的StringArrayTypeHandler。在上面id为insertUser的insert语句中,我们也为变量interests指定了它的javaType和jdbcType属性,这时候Mybatis也会寻找javaType和jdbcType对应的TypeHandler。上面这样定义是Mybatis最基本也是最完整地获取到对应的TypeHandler的方法。

2. 只指定变量对应的javaType类型。

       这个时候Mybatis会拿着这个javaType和jdbcType为null的组合到注册的TypeHandler中寻找对应的TypeHandler。

(1)不动StringArrayTypeHandler的注册信息,把我们的UserMapper.xml改为如下形式:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
<mapper namespace="com.tiantian.mybatis.mapper.UserMapper">  <resultMap id="UserResult" type="User">  <id column="id" property="id"/>  <result column="interests" property="interests" javaType="[Ljava.lang.String;"/>  </resultMap>  <select id="findById" parameterType="int" resultMap="UserResult">  select * from t_user where id=#{id}  </select>  
</mapper>  

此时我们发现最终执行的结果interests为null,这说明Mybatis没有使用我们定义的StringArrayTypeHandler来转换interests。

(2)UserMapper.xml还像上面那样定义,但是也只指定javaType属性来注册我们的StringArrayTypeHandler,代码如下:

<typeHandlers>  <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;"/>  </typeHandlers>  

这个时候再运行上面的测试代码,输出结果发现interests转换为User类字符串数组类型的interests属性。

     这是因为我们是以javaType和null注册的StringArrayTypeHandler,然后在需要转换interests时又是以相同的javaType和null来寻找的,所以就会找到我们注册的StringArrayTypeHandler来进行类型转换。

3. 只指定变量对应的jdbcType类型

这个时候Mybatis会利用我们指定的返回类型和对应的属性取该属性在返回类型中对应的javaType,之后再拿着该javaType和我们指定的jdbcType到注册的TypeHandler中获取对应的TypeHandler。

保持之前指定javaType和jdbcType的方式注册StringArrayTypeHandler,然后在定义interests变量的时候不指定javaType,只指定jdbcType,这个时候UserMapper.xml如下所示:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
<mapper namespace="com.tiantian.mybatis.mapper.UserMapper">  <resultMap id="UserResult" type="User">  <id column="id" property="id"/>  <result column="interests" property="interests" jdbcType="VARCHAR"/>  </resultMap>  <select id="findById" parameterType="int" resultMap="UserResult">  select * from t_user where id=#{id}  </select>  
</mapper>  

        这个时候Mybatis是这样获取TypeHandler的:首先它发现我们的interests没有指定javaType,这个时候它就会通过我们指定的类型User和属性interests获取User类的interests属性对应的java类型,即String数组,再拿着获取到的javaType和我们指定的jdbcType即VARCHAR去寻找对应的TypeHandler,这个时候就找到了我们之前以String数组和VARCHAR注册好的StringArrayTypeHandler来处理interests的类型转换。

4. javaType类型和jdbcType类型都不指定

这个时候Mybatis会以方式2中的方式获取到对应的javaType类型,然后再以方式1获取到对应的TypeHandler。

        首先,注册一个javaType为String数组,jdbcType不指定即为null的TypeHandler—StringArrayTypeHandler,代码如下:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE configuration  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-config.dtd">  
<configuration>  <properties resource="config/jdbc.properties"></properties>  <typeAliases>  <package name="com.tiantian.mybatis.model"/>  </typeAliases>  <typeHandlers>  <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;"/>  </typeHandlers>  <environments default="development">  <environment id="development">  <transactionManager type="JDBC" />  <dataSource type="POOLED">  <property name="driver" value="${jdbc.driver}" />  <property name="url" value="${jdbc.url}" />  <property name="username" value="${jdbc.username}" />  <property name="password" value="${jdbc.password}" />  </dataSource>  </environment>  </environments>  <mappers>  <mapper resource="com/tiantian/mybatis/mapper/UserMapper.xml"/>  </mappers>  
</configuration>  

         然后,定义我们的interests字段的映射关系时既不指定javaType,又不指定jdbcType,代码如下:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
<mapper namespace="com.tiantian.mybatis.mapper.UserMapper">  <resultMap id="UserResult" type="User">  <id column="id" property="id"/>  <result column="interests" property="interests"/>  </resultMap>  <select id="findById" parameterType="int" resultMap="UserResult">  select * from t_user where id=#{id}  </select>  
</mapper> 

        此时我们以javaType为String数组和jdbcType为null注册了一个StringArrayTypeHandler,然后在定义interests字段的映射关系时我们没有指明其对应的javaType和jdbcType,这个时候Mybatis会利用我们指定的User类型和interests属性获取到User类的interests属性对应的java类型,即String数组,然后结合jdbcType为null去寻找注册的TypeHandler,这样就找到了StringArrayTypeHandler。经StringArrayTypeHandler的处理就把jdbcType为VARCHAR的数据转换为javaType为String数组的数据。
 

5. 直接通过变量的typeHandler属性指定其对应的TypeHandler

还有一种形式是我们直接通过变量的typeHandler属性指定其对应的TypeHandler,这个时候Mybatis就会使用该用户自己指定的TypeHandler来进行类型转换,而不再以javaType和jdbcType组合的方式获取对应的TypeHandler。

首先在Mybatis的配置文件中以javaType和jdbcType配套的方式注册一个StringArrayTypeHandler,代码如下:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE configuration  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-config.dtd">  
<configuration>  <properties resource="config/jdbc.properties"></properties>  <typeAliases>  <package name="com.tiantian.mybatis.model"/>  </typeAliases>  <typeHandlers>  <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;" jdbcType="VARCHAR"/>  </typeHandlers>  <environments default="development">  <environment id="development">  <transactionManager type="JDBC" />  <dataSource type="POOLED">  <property name="driver" value="${jdbc.driver}" />  <property name="url" value="${jdbc.url}" />  <property name="username" value="${jdbc.username}" />  <property name="password" value="${jdbc.password}" />  </dataSource>  </environment>  </environments>  <mappers>  <mapper resource="com/tiantian/mybatis/mapper/UserMapper.xml"/>  </mappers>  
</configuration>  

按照前面说的Mybatis按照变量的javaType和jdbcType来取对应的TypeHandler的话,这里注册的StringArrayTypeHandler只有在指定变量的javaType为字符串数组而jdbcType为VARCHAR的情况下才能被获取到。

然后我们在UserMapper.xml文件中不指定interests字段对应的javaType和jdbcType,但是通过typeHandler属性指定将以StringArrayTypeHandler来进行类型转换,代码如下:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
<mapper namespace="com.tiantian.mybatis.mapper.UserMapper">  <resultMap id="UserResult" type="User">  <id column="id" property="id"/>  <result column="interests" property="interests" 
typeHandler="com.tiantian.mybatis.handler.StringArrayTypeHandler"/>  </resultMap>  <select id="findById" parameterType="int" resultMap="UserResult">  select * from t_user where id=#{id}  </select>  
</mapper>  

这是因为我们指定了进行interests字段的映射关系时使用StringArrayTypeHandler来进行类型转换。当指定了某一个字段或变量进行映射关系时所使用的TypeHandler时,Mybatis在需要进行类型转换时就使用给定的TypeHandler进行类型转换,而不会再通过javaType和jdbcType的组合去注册好的TypeHandler中寻找对应的TypeHandler。

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

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

相关文章

制药企业固体制剂设备管理及维护要点

在制药企业的生产过程中&#xff0c;固体制剂设备是至关重要的一环。有效管理和维护这些设备对于确保生产质量、提高生产效率以及延长设备寿命至关重要。本文将从以下三个方面介绍制药企业固体制剂设备的主要类型、常见管理问题以及设备维护的关键要点。 制药企业固体制剂设备主…

java智慧工地云平台源码,以物联网、移动互联网技术为基础,结合大数据、云计算等,实现工程管理绿色化、数字化、精细化、智能化的效果

智慧工地将更多人工智能、传感技术、虚拟现实等高科技技术植入到建筑、机械、人员穿戴设施、场地进出关口等各类物体中&#xff0c;围绕人、机、料、法、环等各方面关键因素&#xff0c;彻底改变传统建筑施工现场参建各方现场管理的交互方式、工作方式和管理模式&#xff0c;智…

WPF 设置全局静态参数

可以使用system官方库来设置参数 引入system xmlns:system"clr-namespace:System;assemblymscorlib"设置参数资源 <Window.Resources><system:Double x:Key"ButtonWidth">90</system:Double></Window.Resources>使用参数资源 &l…

分析概览 文章管理 草稿管理 图片管理 站点管理 主站 关于 登出 手写操作系统项目----进程

大家好&#xff0c;我叫徐锦桐&#xff0c;个人博客地址为www.xujintong.com。平时记录一下学习计算机过程中获取的知识&#xff0c;还有日常折腾的经验&#xff0c;欢迎大家来访。 这里记录了&#xff0c;手写操作系统项目中关于进程的部分。 进程四要素 首先进程有四要素。 …

Linux软件包和进程管理

一、RPM软件包管理 1、RPM管理工具 &#xff08;1&#xff09;RPM是红帽包管理(Redhat Package Manager)的缩写。 由Red Hat公司提出的一种软件包管理标准。 是Linux各发行版中应用最广泛的软件包格式之一&#xff08;还有debian的发行版deb安装包&#xff09;。 RPM功能通过…

数据结构:选择题+编程题(每日一练)

目录 选择题&#xff1a; 题一&#xff1a; 题二&#xff1a; 题三&#xff1a; 题四&#xff1a; 题五&#xff1a; 编程题&#xff1a; 题一&#xff1a;单值二叉树 思路一&#xff1a; 题二&#xff1a;二叉树的最大深度 思路一&#xff1a; 本人实力有限可能对…

MySQL的索引——索引的介绍及其数据结构B+树 索引的类型 索引的使用及其失效场景 相关名词解释

前言 索引是存储引擎用于快速查找数据纪录的一种数据结构&#xff0c;索引是数据库中经常提及的一个词&#xff0c;究竟什么是索引&#xff0c;索引的数据结构是什么&#xff0c;索引有什么类型&#xff1f; 本篇博客尝试阐述数据库索引的相关内容&#xff0c;涉及什么是索引…

用 Java 在 PDF 中创建和管理图层,实现交互式文档

PDF 图层&#xff08;也称为可见图层或附加图层等&#xff09;是组织和管理 PDF 文档中内容可见性的一种方法。PDF 图层可用于创建交互式文档、隐藏或显示特定信息、创建多语言版本文档等。通过添加和删除图层&#xff0c;用户可以根据需要定制 PDF 文档指定内容的可见性与显示…

适用于物联网的UI设计工具都有哪些?

随着科学技术的飞速发展&#xff0c;“万物相连的互联网”时代逐渐成为现实。如今&#xff0c;物联网已经不是什么新词了。事实上&#xff0c;早在各种屏幕设备诞生之前&#xff0c;人们就与物理世界交织在一起&#xff0c;产生了无数的互动。如何将人们多年积累的互动经验与物…

Ubuntu下载工具ip addr、ifconfig、ping、make

Ubuntu下载工具ip addr、ifconfig、ping、make ping 在 Ubuntu 上获取网络工具包通常是通过安装相关软件包的方式来完成的。Ubuntu 默认包含一些常见的网络工具&#xff0c;但如果你需要安装其他工具&#xff0c;你可以使用 apt 命令或者 snap 命令进行安装。以下是一些常见的…

蓝桥杯 Java 青蛙过河

import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改/**二分法从大&#xff08;n&#xff09;到小找足够小的步长前缀和记录每个位置的前面有的总石头数&#xff08;一个石头表示可以容纳一个青蛙&#xff0c;一位置有多少个石头hi就是多少&#xff09;&…

025-第三代软件开发-实现需求长时间未操作返回登录界面

第三代软件开发-实现需求长时间未操作返回登录界面 文章目录 第三代软件开发-实现需求长时间未操作返回登录界面项目介绍实现需求长时间未操作返回登录界面实现思路用户操作监控QML 逻辑处理 关键字&#xff1a; Qt、 Qml、 QTimer、 timeout、 eventFilter 项目介绍 欢迎…

秋季期中考复现xj

flow analysis 1 What is the backdoor file name that comes with the server?( Including file suffix) 服务器自带的后门文件是什么&#xff1f;&#xff08;含文件后缀&#xff09; 题目还要求最后把那个文件名MD5一下&#xff0c;再去提交 开始的前三题是流量分析的&…

发挥服务器的无限潜能:创意项目、在线社区和更多

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 ✨特色专栏&#xff1a…

设计模式:观察者模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)

上一篇《命令模式》 下一篇《策略模式》 简介&#xff1a; 观察者模式&#xff0c;它是一种行为型设计模式&#xff0c;它允许一个对象自动通知其依赖者&#xff08;观察者&#xff09;状态的变化。当被…

Python150题day19

4.4 字符串大写转小写 在Python中&#xff0c;可以使用lower()方法将字符串中的大写字母转换为小写字母。lower()方法返回一个新的字符串&#xff0c;原始字符串本身不会被修改。 下面是一个示例&#xff1a; text "HELLO, WORLD!" lower_text text.lower() prin…

linux驱动文件私有数据(字符设备基础二)

编写linux驱动程序时&#xff0c;通常在驱动开发中会为设备定义相关的设备结构体&#xff0c;将硬件属性的描述信息全部放在该结构体中   Linux 中并没有明确规定要使用文件私有数据&#xff0c;但是在 linux 驱动源码中&#xff0c;广泛使用了文件私有数据&#xff0c;这是 …

Spring异步任务笔记

都是使用corn表达式&#xff0c;但是Spring的异步任务框架corn表达式只有6位&#xff0c;不支持第七位。周的表示&#xff1a;Spring中的corn表达式周一到周日表示为1-7&#xff0c;如Quarz则是周日到周六由1-7表示。定时任务不应该阻塞&#xff1a;&#xff08;Spring中默认是…

Non-constant range: argument must be an integer literal

更新 Xcode IDE 后 ForEach 方法抛出了如下异常 Non-constant range: argument must be an integer literal 新增了指向性 id 参数 init(_:content:) 原始方法 ForEach(0 ..< pickerTitleColors.count) {Text(self.pickerTitleColors[$0]).tag($0).foregroundColor(self.…

Windows Server服务器下的Linux子系统

导读Windows、Linux看似死敌一对&#xff0c;但其实微软对于开源实业还是相当支持的&#xff0c;Windows 10系统内就首次内置了一个Linux子系统(Windows Subsystem for Linux/WSL)&#xff0c;允许开发者模拟Linux环境&#xff0c;而且有需要的可以直接从Windows应用商店下载SU…