hive 自定义函数实现

UDF | GDF | UDTF 区别

  • UDF:一进一出(hive3 已经废除)
  • GDF:一进一出(hive3 支持的GDF)
  • UDTF:一进多出

业务前景

测试数据为:字符串(JSON数组格式 [{},{},{}] )

[{\"title\": \"转让背书\", \"endorseName\": \"山东泰山钢铁集团有限公司\", \"endorseeName\": \"山东汶汇港物流有限公司\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-10\"}, {\"title\": \"转让背书\", \"endorseName\": \"山东汶汇港物流有限公司\", \"endorseeName\": \"山东泰通达物流有限公司\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-11\"}]

通过hive自定义函数将json数组中的 endorseNameendorseeName取出后并去重输出,或者将二者取出后在hive中进行去重操作

输出格式:

-- UDF GDF输出格式:
山东泰山钢铁集团有限公司,山东汶汇港物流有限公司,山东汶汇港物流有限公司,山东泰通达物流有限公司
-- UDTF 输出格式
山东泰山钢铁集团有限公司
山东汶汇港物流有限公司
山东汶汇港物流有限公司
山东泰通达物流有限公司

实现思路:

  1. 将字符串转换为JSON数组进行处理
  2. 将字符串转换为对象集合的方式获取数据格式

前期准备

  1. 使用IDEA创建Maven项目
  2. 导入相应依赖包
  3. 创建相应的package并创建相应JAVA类
  4. 所有代码编写完成后将项目进行打包处理

创建项目

我的创建项目已经目录截图如下,以下格式仅供参考,需根据自己的需求进行整理:

导入maven

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>domain_endorse</artifactId><version>1.0-SNAPSHOT</version><!-- 定义hive项目版本信息 --><properties><project.build.sourceEncoding>UTF8</project.build.sourceEncoding><hive.version>3.1.2</hive.version></properties><dependencies><!--添加hive依赖--><dependency><groupId>org.apache.hive</groupId><artifactId>hive-exec</artifactId><version>${hive.version}</version></dependency><!-- 字符串转对象集合的时候才导此包--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version></dependency><!-- 注解 @Data 使用的依赖--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.10</version></dependency></dependencies><!-- 项目打jar包时使用 --><build><plugins><plugin><artifactId>maven-compiler-plugin</artifactId><version>2.3.2</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin><plugin><artifactId>maven-assembly-plugin</artifactId><configuration><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs></configuration><executions><execution><id>make-assembly</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions></plugin></plugins></build>
</project>

UDF函数实现

将字符串转换为集合对象来实现

package com.sddw.udf;import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.json.JSONException;import java.util.*;public class EndorseUDF extends UDF {// 对象集合@Datapublic static class EndorseObj{private String endorseName;private String endorseeName;}public String evaluate(String line) {// 定义set集合,接收背书企业信息,(去重)Set set = new HashSet<String>();// 处理脏数据if (line == null || line == "") {return null;}// 判断数据的完整性,不完整数据剔除if (line.trim().startsWith("[{") && line.trim().endsWith("}]")) {try {// 将数据转换为对象集合List<EndorseObj> list = JSONObject.parseArray(line, EndorseObj.class);for (int i = 0; i < list.size(); i++) {set.add(list.get(i).endorseeName);set.add(list.get(i).endorseName);}/*String[] lines = line.replace("\"","").split(",");for (int i = 0 ;i < lines.length;i++){if (lines[i].contains("endorseeName") || lines[i].contains("endorseName")) {String[] name = lines[i].split(":");set.add(name[1]);}}*/} catch (JSONException e) {e.printStackTrace();}} else {// 剔除不完整数据return null;}// 将set集合准换为字符串String endorsename = String.join(",", set);return endorsename;}public static void main(String[] args) {String line = "[{\"title\": \"转让背书\", \"endorseName\": \"山东泰山钢铁集团有限公司\", \"endorseeName\": \"山东汶汇港物流有限公司\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-10\"}, {\"title\": \"转让背书\", \"endorseName\": \"山东汶汇港物流有限公司\", \"endorseeName\": \"山东泰通达物流有限公司\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-11\"}, {\"title\": \"转让背书\", \"endorseName\": \"山东泰通达物流有限公司\", \"endorseeName\": \"山东维利达经贸有限公司\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-11\"}, {\"title\": \"转让背书\", \"endorseName\": \"山东维利达经贸有限公司\", \"endorseeName\": \"莱芜市合盛铸造材料经营部\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-11\"}, {\"title\": \"转让背书\", \"endorseName\": \"莱芜市合盛铸造材料经营部\", \"endorseeName\": \"山东六六六贸易有限公司\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-11\"}, {\"title\": \"转让背书\", \"endorseName\": \"山东六六六贸易有限公司\", \"endorseeName\": \"山东禾壮信息技术有限公司\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-21\"}, {\"title\": \"转让背书\", \"endorseName\": \"山东禾壮信息技术有限公司\", \"endorseeName\": \"山东纳凯建材有限公司\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-21\"}, {\"title\": \"转让背书\", \"endorseName\": \"山东纳凯建材有限公司\", \"endorseeName\": \"聊城信源集团有限公司\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-21\"}, {\"title\": \"转让背书\", \"endorseName\": \"聊城信源集团有限公司\", \"endorseeName\": \"苏州弗兰特环保科技有限公司\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-22\"}, {\"title\": \"转让背书\", \"endorseName\": \"苏州弗兰特环保科技有限公司\", \"endorseeName\": \"潍坊祥盛控制设备科技有限公司\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-30\"}, {\"title\": \"转让背书\", \"endorseName\": \"潍坊祥盛控制设备科技有限公司\", \"endorseeName\": \"德州陵城区陆达商贸有限公司\", \"isTransfer\": \"可以转让\", \"endorseDate\": \"2021-06-30\"}]";String x = new EndorseUDF().evaluate(line);System.out.println(x);}
}

GDF实现

将字符串转换为集合对象来实现

package com.sddw.gdf;import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.json.JSONException;import java.util.*;/*** 设置GDF实现将Json中的背书企业,背背书企业名称进行字符串输出*/
public class EndorseGDF extends GenericUDF {@Datapublic static class EndorseObj{private String endorseName;private String endorseeName;}/**** @param objectInspectors 输入参数类型的鉴别器对象* @return 返回值* @throws UDFArgumentException*/@Overridepublic ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {// 判断输入参数的个数if (objectInspectors.length != 1) {throw new UDFArgumentLengthException("Input Args Length Error!!!");}// 判断输入参数的类型if (!objectInspectors[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)) {throw new UDFArgumentTypeException(0, "Input Args Type Error!!!");}//函数本身返回值为string,需要返回string类型的鉴别器对象return PrimitiveObjectInspectorFactory.javaStringObjectInspector;}/**** @param deferredObjects 输入参数* @return 返回值* @throws HiveException*/@Overridepublic String evaluate(DeferredObject[] deferredObjects) throws HiveException {String line = deferredObjects[0].get().toString();// 定义set集合,接收背书企业信息,(去重)Set set = new HashSet<String>();// 处理脏数据if (line == null || line.length() == 0) {return null;}// 判断数据的完整性,不完整数据剔除if (line.trim().startsWith("[{") && line.trim().endsWith("}]")) {try {// 将数据转换为对象集合List<EndorseObj> list = JSONObject.parseArray(line, EndorseObj.class);for (int i = 0; i < list.size(); i++) {set.add(list.get(i).endorseeName);set.add(list.get(i).endorseName);}} catch (JSONException e) {e.printStackTrace();}} else {// 剔除不完整数据return null;}// 将set集合准换为字符串String endorsename = String.join(",", set);return endorsename;}@Overridepublic String getDisplayString(String[] strings) {return null;}
}

UDTF实现

通过将字符串转换为JSON数组的方式进行实现

package com.sddw.udtf;import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructField;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.json.JSONArray;
import org.json.JSONObject;import java.util.ArrayList;
import java.util.List;public class EndorseUDTF extends GenericUDTF {/*** 初始化* @param argOIs* @return* @throws UDFArgumentException*/@Overridepublic StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {//1.获取传入的参数List<? extends StructField> inputFields = argOIs.getAllStructFieldRefs();//2.判断参数个数是否为一个?if (inputFields.size() != 1) {throw new UDFArgumentException("只需要一个参数");}//3.定义返回值名称和类型//返回的字段名List<String> fieldNames = new ArrayList<>();fieldNames.add("endorsecorp");//返回的字段类型List<ObjectInspector> fieldOIs = new ArrayList<>();fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);}/*** 具体实现方法* @param objects* @throws HiveException*/@Overridepublic void process(Object[] objects) throws HiveException {//1.获取传入的数据String jsonArray = objects[0].toString();// 判断JSON数组的完整性,非完整的剔除if (jsonArray.startsWith("[{") && jsonArray.endsWith("}]")) {//2.将string转化为json数组JSONArray actions = new JSONArray(jsonArray);//3.循环取出json数组的元素,依次写出for (int i = 0; i < actions.length(); i++) {String[] result = new String[1];result[0] = actions.getString(i);JSONObject jsonObject = new JSONObject(result[0]);if (jsonObject.toString().contains("endorseName") && jsonObject.toString().contains("endorseeName")) {// 获取JSON中的数据String endorseName = jsonObject.getString("endorseName").replace("(", "(").replace(")", ")");String endorseeName = jsonObject.getString("endorseeName").replace("(", "(").replace(")", ")");// 将数据写入forward(endorseName);forward(endorseeName);}}}}@Overridepublic void close() throws HiveException {}
}

项目打包

项目完成并测试通过后,将项目进行打包上传到hive所在的集群中
项目打包完成后会产生两个文件,一个是含有maven,一个是不含maven依赖,如果我们只使用hive的依赖不涉及到其他的依赖建议直接上传不含maven依赖的jar包,如果涉及到其它maven依赖的,必须上传含有maven依赖的jar包

创建临时函数

add jar /opt/data/domain_endorse.jar;
create temporary function endorseudf as 'com.sddw.udf.EndorseUDF';
create temporary function endorsegdf as 'com.sddw.gdf.EndorseGDF';
create temporary function endorseudtf as 'com.sddw.udtf.EndorseUDTF';
-- 临时喊出退出shell终端后函数立马失效drop function if exists endorsegdf;
drop function if exists endorseudf;
drop function if exists endorseudf;-- 测试:
select endorseudtf('[{"title": "转让背书", "endorseName": "江苏普莱姆新材料有限公司", "endorseeName": "邯郸市邯山区润川贸易有限公司", "isTransfer": "可转让", "endorseDate": "2020-12-24"}, {"title": "转让背书", "endorseName": "邯郸市邯山区润川贸易有限公司", "endorseeName": "宁波久营贸易有限公司", "isTransfer": "可转让", "endorseDate": "2020-12-25"}, {"title": "转让背书", "endorseName": "宁波久营贸易有限公司", "endorseeName": "嵊州市恒鑫金属制管有限公司", "isTransfer": "可转让", "endorseDate": "2020-12-25"}, {"title": "转让背书", "endorseName": "嵊州市恒鑫金属制管有限公司", "endorseeName": "建龙北满特殊钢有限责任公司", "isTransfer": "可转让", "endorseDate": "2020-12-25"}, {"title": "转让背书", "endorseName": "建龙北满特殊钢有限责任公司", "endorseeName": "无锡容大环境科技有限公司", "isTransfer": "可转让", "endorseDate": "2020-12-31"}, {"title": "转让背书", "endorseName": "无锡容大环境科技有限公司", "endorseeName": "宜兴市清泰净化剂有限公司", "isTransfer": "可转让", "endorseDate": "2021-01-19"}, {"title": "转让背书", "endorseName": "宜兴市清泰净化剂有限公司", "endorseeName": "上海碧源化学品有限公司", "isTransfer": "可转让", "endorseDate": "2021-01-21"}, {"title": "转让背书", "endorseName": "上海碧源化学品有限公司", "endorseeName": "安徽巨成精细化工有限公司", "isTransfer": "可转让", "endorseDate": "2021-01-22"}, {"title": "转让背书", "endorseName": "安徽巨成精细化工有限公司", "endorseeName": "爱森(如东)化工有限公司", "isTransfer": "可转让", "endorseDate": "2021-01-26"}, {"title": "转让背书", "endorseName": "爱森(如东)化工有限公司", "endorseeName": "中国三冶集团有限公司", "isTransfer": "可转让", "endorseDate": "2021-03-19"}, {"title": "转让背书", "endorseName": "中国三冶集团有限公司", "endorseeName": "铁西区玫美工程机械租赁站", "isTransfer": "可转让", "endorseDate": "2021-03-25"}]');

创建永久函数

-- 注意:创建永久函数的时候需要进入到项目的数据库下,我的数据库为“sddw”,如果我们想要更换代码实现逻辑,只需要我们将HDFS文件系统(或者lib目录下的涉及到jar包文件)中的jar文件进行替换,退出hive终端并重新进入即可。-- 第一种方式:将jar包放在hive的lib目录下(这样可能存在jar包冲突,如果maven不涉及到其他依赖的时候可以直接放入到lib目录下)
add jar /opt/module/hive-3.1.2/lib/domain_endorse.jar;
create function endorseudf as 'com.sddw.udf.EndorseUDF';
create function endorsegdf as 'com.sddw.gdf.EndorseGDF';
-- 前提:hive-env.sh
-- export HIVE_AUX_JARS_PATH=/opt/module/hive-3.1.2/lib-- 第二种方式:将jar包上传到HDFS 文件系统(jar包一定包含Maven依赖)
create function endorseudf as 'com.sddw.udf.EndorseUDF' using jar 'hdfs:///sddw/function/domain_endorse.jar'
create function endorsegdf as 'com.sddw.gdf.EndorseGDF' using jar 'hdfs:///sddw/function/domain_endorse.jar'
create function endorseudtf as 'com.sddw.udtf.EndorseUDTF' using jar 'hdfs:///sddw/function/domain_endorse.jar'-- 查看创建的函数
show functions like '*endorse*' ;-- 删除函数
drop function if exists sddw.endorseudf;
drop function if exists sddw.endorsegdf;
drop function if exists sddw.endorseudtf;
-- 测试:
select sddw.endorseudtf('[{"title": "转让背书", "endorseName": "江苏普莱姆新材料有限公司", "endorseeName": "邯郸市邯山区润川贸易有限公司", "isTransfer": "可转让", "endorseDate": "2020-12-24"}, {"title": "转让背书", "endorseName": "邯郸市邯山区润川贸易有限公司", "endorseeName": "宁波久营贸易有限公司", "isTransfer": "可转让", "endorseDate": "2020-12-25"}, {"title": "转让背书", "endorseName": "宁波久营贸易有限公司", "endorseeName": "嵊州市恒鑫金属制管有限公司", "isTransfer": "可转让", "endorseDate": "2020-12-25"}, {"title": "转让背书", "endorseName": "嵊州市恒鑫金属制管有限公司", "endorseeName": "建龙北满特殊钢有限责任公司", "isTransfer": "可转让", "endorseDate": "2020-12-25"}, {"title": "转让背书", "endorseName": "建龙北满特殊钢有限责任公司", "endorseeName": "无锡容大环境科技有限公司", "isTransfer": "可转让", "endorseDate": "2020-12-31"}, {"title": "转让背书", "endorseName": "无锡容大环境科技有限公司", "endorseeName": "宜兴市清泰净化剂有限公司", "isTransfer": "可转让", "endorseDate": "2021-01-19"}, {"title": "转让背书", "endorseName": "宜兴市清泰净化剂有限公司", "endorseeName": "上海碧源化学品有限公司", "isTransfer": "可转让", "endorseDate": "2021-01-21"}, {"title": "转让背书", "endorseName": "上海碧源化学品有限公司", "endorseeName": "安徽巨成精细化工有限公司", "isTransfer": "可转让", "endorseDate": "2021-01-22"}, {"title": "转让背书", "endorseName": "安徽巨成精细化工有限公司", "endorseeName": "爱森(如东)化工有限公司", "isTransfer": "可转让", "endorseDate": "2021-01-26"}, {"title": "转让背书", "endorseName": "爱森(如东)化工有限公司", "endorseeName": "中国三冶集团有限公司", "isTransfer": "可转让", "endorseDate": "2021-03-19"}, {"title": "转让背书", "endorseName": "中国三冶集团有限公司", "endorseeName": "铁西区玫美工程机械租赁站", "isTransfer": "可转让", "endorseDate": "2021-03-25"}]');-- 输出:
上海碧源化学品有限公司,中国三冶集团有限公司,无锡容大环境科技有限公司,江苏普莱姆新材料有限公司,建龙北满特殊钢有限责任公司,铁西区玫美工程机械租赁站,邯郸市邯山区润川贸易有限公司,宁波久营贸易有限公司,爱森(如东)化工有限公司,宜兴市清泰净化剂有限公司,安徽巨成精细化工有限公司,嵊州市恒鑫金属制管有限公司

UDF|GDF数据行转列

-- 要想UDF、GDF也实现UDTF这种输出,需要对该数据进行行转列处理
select
corp_name
from
(
select '上海碧源化学品有限公司,中国三冶集团有限公司,无锡容大环境科技有限公司,江苏普莱姆新材料有限公司,建龙北满特殊钢有限责任公司,铁西区玫美工程机械租赁站,邯郸市邯山区润川贸易有限公司,宁波久营贸易有限公司,爱森(如东)化工有限公司,宜兴市清泰净化剂有限公司,安徽巨成精细化工有限公司,嵊州市恒鑫金属制管有限公司' as endorse_coms
)t1 lateral view explode(split(endorse_coms,',')) num as corp_name;

END

到这里,hive的UDF、GDF、UDTF均实现该方法

Hive UDF GDF UDTF编写与实现相关推荐

  1. Hive自定义UDF UDAF UDTF

    Hive是一种构建在Hadoop上的数据仓库,Hive把SQL查询转换为一系列在Hadoop集群中运行的MapReduce作业,是MapReduce更高层次的抽象,不用编写具体的MapReduce方法 ...

  2. Hive 之 用户自定义函数 UDF UDAF UDTF

    一 什么是UDF UDF是UserDefined Function 用户自定义函数的缩写.Hive中除了原生提供的一些函数之外,如果还不能满足我们当前需求,我们可以自定义函数. 除了UDF 之外,我们 ...

  3. hive java udf_hive java编写udf函数

    (一)创建JAVA 代码--例子 package hiveOpt; import org.apache.hadoop.hive.ql.exec.UDF; import org.apache.hadoo ...

  4. Hive常用函数、列转行与行转列、开窗函数、UDF、UDTF

    Hive函数.开窗函数.UDF.UDTF 1.系统内置函数 2.常用函数 2.1.关系运算 2.2.数值运算 2.3.条件函数 2.4.日期函数 2.5.字符串函数 2.6.Hive求WordCoun ...

  5. hive的udf,udaf,udtf各自依賴兩種class(转载+分析整理)

    Hive自定义函数包括三种UDF.UDAF.UDTF 名稱縮寫 特點 依賴 UDF(User-Defined-Function) 一进一出 org.apache.hadoop.hive.ql.exec ...

  6. Hive UDF,就这

    摘要:Hive UDF是什么?有什么用?怎么用?什么原理?本文从UDF使用入手,简要介绍相关源码,UDF从零开始. 本文分享自华为云社区<Hive UDF,就这>,作者:汤忒撒. Hive ...

  7. udf,udaf,udtf之间的区别

    1.UDF:用户定义(普通)函数,只对单行数值产生作用: 继承UDF类,添加方法 evaluate() /*** @function 自定义UDF统计最小值* @author John**/publi ...

  8. UDF UDAF UDTF 区别

    UDF UDAF UDTF 区别 UDF 概念: User-Defined-Function 自定义函数 .一进一出:只对单行数据产生作用: 实际使用时,UDF函数以匿名函数的形式进行操作使用 背景: ...

  9. CDH 创建Hive UDF函数

    导入依赖包: hive-exec.jar hadoop-common.jar 注意:函数名必须为 evaluate ,否则hive无法识别! package com.example.hive.udf; ...

最新文章

  1. Redis入门教程(二)
  2. Spring boot切换Servlet容器
  3. C/Cpp / string 截取、替换、查找
  4. python中字典数据的特点_Python数据类型(字典)
  5. OpenCV文档阅读笔记-inRange官方解析及实例
  6. QBC的distinct查询
  7. 技术人 | 我在支付宝体验技术部这四年学到了什么?
  8. matlab ofdm qpsk,Matlab关于ofdm系统qpsk调制、awgn信道下的仿真
  9. bat批处理的注释语句
  10. 理论篇3:深度学习之----Momentum优化器(2)
  11. 通过Python绘制分段函数
  12. 2.PyTorch的Dataset和DataLoader
  13. ResponseBodyAdvice的使用
  14. 【Linux系统编程】进程退出和回收进程资源
  15. 简历c语言项目,C/C++:如何介绍简历中的项目?
  16. POS--权益证明机制
  17. 山外山在科创板上市:市值约47亿元,高光勇为实际控制人
  18. 华为P30、P30pro亲手体验,最新壁纸大曝光!
  19. java工具多,[转帖]一个 Java 工具到底有多大?
  20. Jmeter 之 Beanshell

热门文章

  1. Java操作excel之poi
  2. 数据库——最小支持度最小置信度
  3. 自由浏览播放不了html5,傲游浏览器畅享HTML5特性乐趣
  4. Oracle P6软件编制项目计划及进度控制程序
  5. 原生APP和Web APP的区别
  6. 关于vs code 配置 Golang时下载go tools 时失败的解决方法
  7. 采用XLL封装工作表函数的演示,确保工作表上的公式不暴露给用户
  8. Kali Linux 更新系统 2020.3
  9. 关于IP SLA及与EEM联动的探讨(转)
  10. 联系电脑清除BIOS密码方法