Hive 之 UDF 运用(包会的)

文章目录

    • UDF 是什么?
    • reflect
      • 静态方法调用
      • 实例方法调用
    • 自定义 UDF(GenericUDF)
      • 1.创建项目
      • 2.创建类继承 UDF
      • 3.数据类型判断
      • 4.编写业务逻辑
      • 5.定义函数描述信息
      • 6.打包与上传
      • 7.注册 UDF 函数并测试
      • 返回复杂的数据类型

UDF 是什么?

Hive 中的 UDF 其实就是用户自定义函数,允许用户注册使用自定义的逻辑对数据进行处理,丰富了Hive 对数据处理的能力。

UDF 负责完成对数据一进一出处理的操作,和 Hive 中存在的函数 yearmonthday 等相同。

reflect

在 Hive 中,可以使用 reflect() 方法通过 Java 反射机制调用 Java 类的方法。

通俗来说,它可以调用 Hive 中不存在,但是 JDK 中拥有的方法。

语法

  • reflect() 函数的语法为:reflect(class,method[,arg1[,arg2..]])

静态方法调用

假设当前在 Java 中存在类如下:

package com.example;public class MathUtils {public static int addNumbers(int a, int b) {return a + b;}
}

那么使用 reflect() 方法调用时,如下所示:

SELECT reflect("com.example.MathUtils", "addNumbers", 3, 5) AS result;

注意! 这里的类 "com.example.MathUtils" 并不是在 JDK 中真实存在的,只是我作为说明的一个案例, reflect() 方法只能调用 JDK 中(原生内置)存在的方法。

所以当你需要使用 reflect() 方法时,需要先去查找调用的目标方法全类名、方法名以及是否需要传递参数。

实例方法调用

当我们需要调用 Java 中的实例方法时,先创建 Java 对象,然后再调用其方法。

例如:将乱码的字符串进行解析。

SELECT reflect('java.net.URLDecoder', 'decode', "Mozilla/5.0%20(compatible;%20MJ12bot/v1.4.7;%20http://www.majestic12.co.uk/bot.php?+)
" ,'utf-8') as result;

结果输出如下:

在这里插入图片描述

自定义 UDF(GenericUDF)

Hive 支持两种 UDF 函数自定义操作,分别是:

  • GenericUDF(通用UDF):用于实现那些可以处理任意数据类型的函数。它们的输入和输出类型可以是任意的,但需要在函数内部处理类型转换和逻辑,可以实现更复杂的逻辑处理。

  • UDF:用于实现那些只能处理特定数据类型的函数。每个 UDF 都明确指定了输入参数的类型和返回值类型,使用更为简单。

本文采用的是通用 UDF —— GenericUDF 实现方法

这里通过一个在 Hive 中实现两数相加的自定义 UDF 案例来进行说明,看完你就会啦,轻松拿捏。

1.创建项目

在 IDEA 中创建一个 Maven 项目,引入 Hive 依赖,如下所示:

<?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.jsu</groupId><artifactId>MyUDF</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><!-- hive-exec依赖无需打到jar包,故scope使用provided--><dependency><groupId>org.apache.hive</groupId><artifactId>hive-exec</artifactId><version>3.1.3</version><scope>provided</scope></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><version>3.0.0</version><configuration><!--将依赖编译到jar包中--><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs></configuration><executions><!--配置执行器--><execution><id>make-assembly</id><!--绑定到package执行周期上--><phase>package</phase><goals><!--只运行一次--><goal>single</goal></goals></execution></executions></plugin></plugins></build></project>

注意,引入的 Hive 依赖版本请保持和你集群中使用的版本一致。

2.创建类继承 UDF

创建一个类,我这里取名为 AddTest,继承 Hive UDF 父类 GenericUDF,需要重写三个方法,如下所示:

import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
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;public class AddTest extends GenericUDF {@Overridepublic ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {return null;}@Overridepublic Object evaluate(DeferredObject[] deferredObjects) throws HiveException {return null;}@Overridepublic String getDisplayString(String[] strings) {return null;}
}
  • initialize(ObjectInspector[] objectInspectors) 方法
    这个方法是在 UDF 初始化时调用的。它用于执行一些初始化操作,并且可以用来验证 UDF 的输入参数类型是否正确。参数 objectInspectors 是一个包含输入参数的 ObjectInspector 数组,它描述了每个输入参数的类型和结构。
    一般在这个方法中检查输入参数的数量和类型是否满足你的函数的要求。如果输入参数不符合预期,你可以抛出 UDFArgumentException 异常。如果一切正常,你需要返回一个合适的 ObjectInspector 对象,它描述了你的函数返回值的类型。

  • evaluate(DeferredObject[] deferredObjects) 方法
    在这个方法中定义真正执行 UDF 逻辑的地方,获取输入的参数,并且根据输入参数执行相应的计算或操作。参数 deferredObjects 是一个包含输入参数的 DeferredObject 数组,你可以通过它来获取实际的输入值。

  • getDisplayString(String[] strings) 方法
    这个方法用于描述 UDF 的信息,用于生成可读的查询执行计划(Explain),以便用户了解查询的结构和执行过程。

3.数据类型判断

实现 UDF 的第一步操作就是在 initialize 方法中,判断用户输入的参数是否合法,出现错误时,进行反馈。

在这里主要分为三个步骤:

  1. 检验参数个数

  2. 检查参数类型

  3. 定义函数返回值类型

一般情况下,可以使用下面的模板:

    @Overridepublic ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {// 1.校验参数个数if (objectInspectors.length != 2) {throw new UDFArgumentException("参数个数有误!");}// 2.检查第1个参数是否是int类型// 判断第1个参数的基本类型ObjectInspector num1 = objectInspectors[0];if (num1.getCategory() != ObjectInspector.Category.PRIMITIVE) {throw new UDFArgumentException("第1个参数不是基本数据类型");}// 第1个参数类型判断PrimitiveObjectInspector temp = (PrimitiveObjectInspector) num1;if (PrimitiveObjectInspector.PrimitiveCategory.INT != temp.getPrimitiveCategory()) {throw new UDFArgumentException("第1个参数应为INT类型");}// 2.检查第2个参数是否是int类型// 判断第2个参数的基本类型ObjectInspector num2 = objectInspectors[1];if (num2.getCategory() != ObjectInspector.Category.PRIMITIVE) {throw new UDFArgumentException("第2个参数不是基本数据类型");}// 第2个参数类型判断PrimitiveObjectInspector temp2 = (PrimitiveObjectInspector) num2;if (PrimitiveObjectInspector.PrimitiveCategory.INT != temp2.getPrimitiveCategory()) {throw new UDFArgumentException("第2个参数应为INT类型");}// 3.设置函数返回值类型(返回一个整型数据)return PrimitiveObjectInspectorFactory.javaIntObjectInspector;}

4.编写业务逻辑

evaluate 方法中定义业务逻辑,这里比较简单,就是实现两数相加。

    @Overridepublic Object evaluate(DeferredObject[] deferredObjects) throws HiveException {// 完成两数相加的逻辑计算int num1 = Integer.parseInt(deferredObjects[0].get().toString());int num2 = Integer.parseInt(deferredObjects[1].get().toString());return num1 + num2;}

5.定义函数描述信息

getDisplayString 方法中定义函数在 Explain 中的描述信息,一般都是固定写法,如下所示:

    @Overridepublic String getDisplayString(String[] strings) {return getStandardDisplayString("AddTest", strings);}

把对应的函数名称进行替换即可。

6.打包与上传

对编写的项目进行打包,并上传到 HDFS 上

在这里插入图片描述

本案例的完整代码如下所示:

import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
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.PrimitiveObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;public class AddTest extends GenericUDF {@Overridepublic ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {// 1.校验参数个数if (objectInspectors.length != 2) {throw new UDFArgumentException("参数个数有误!");}// 2.检查第1个参数是否是int类型// 判断第1个参数的基本类型ObjectInspector num1 = objectInspectors[0];if (num1.getCategory() != ObjectInspector.Category.PRIMITIVE) {throw new UDFArgumentException("第1个参数不是基本数据类型");}// 第1个参数类型判断PrimitiveObjectInspector temp = (PrimitiveObjectInspector) num1;if (PrimitiveObjectInspector.PrimitiveCategory.INT != temp.getPrimitiveCategory()) {throw new UDFArgumentException("第1个参数应为INT类型");}// 2.检查第2个参数是否是int类型// 判断第2个参数的基本类型ObjectInspector num2 = objectInspectors[1];if (num2.getCategory() != ObjectInspector.Category.PRIMITIVE) {throw new UDFArgumentException("第2个参数不是基本数据类型");}// 第2个参数类型判断PrimitiveObjectInspector temp2 = (PrimitiveObjectInspector) num2;if (PrimitiveObjectInspector.PrimitiveCategory.INT != temp2.getPrimitiveCategory()) {throw new UDFArgumentException("第2个参数应为INT类型");}// 3.设置函数返回值类型(返回一个整型数据)return PrimitiveObjectInspectorFactory.javaIntObjectInspector;}@Overridepublic Object evaluate(DeferredObject[] deferredObjects) throws HiveException {// 完成两数相加的逻辑计算int num1 = Integer.parseInt(deferredObjects[0].get().toString());int num2 = Integer.parseInt(deferredObjects[1].get().toString());return num1 + num2;}@Overridepublic String getDisplayString(String[] strings) {return getStandardDisplayString("AddTest", strings);}}

7.注册 UDF 函数并测试

进入 Hive 中对创建的 UDF 函数进行注册。

如果你期间修改了 JAR 包并重新上传,则需要重启与 Hive 的连接,建立新的会话才会生效。

-- 永久注册
create function testAdd as 'AddTest' using jar 'hdfs://hadoop201:8020/test/MyUDF-1.0-SNAPSHOT-jar-with-dependencies.jar';-- 删除注册的函数
drop function if exists testAdd;
  • testAdd:注册的 UDF 函数名称。

  • as 'AddTest':编写的 UDF 函数全类名。

  • using jar:指定 JAR 包的全路径。

注册成功后,如下所示:

在这里插入图片描述

测试

select testAdd(1,2);

在这里插入图片描述

如果输入错误的数据类型,会进行报错提示:

在这里插入图片描述

返回复杂的数据类型

在更多的场景下,我们可能有多个返回值,那么该如何定义与配置呢?

这里还是通过上面的两数相加的案例来进行说明,套下面的模板使用:

import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
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.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;import java.util.ArrayList;public class AddTestReturnList extends GenericUDF {@Overridepublic ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {// 1.校验参数个数if (objectInspectors.length != 2) {throw new UDFArgumentException("参数个数有误!");}// 2.检查第1个参数是否是int类型// 判断第1个参数的基本类型ObjectInspector num1 = objectInspectors[0];if (num1.getCategory() != ObjectInspector.Category.PRIMITIVE) {throw new UDFArgumentException("第1个参数不是基本数据类型");}// 第1个参数类型判断PrimitiveObjectInspector temp = (PrimitiveObjectInspector) num1;if (PrimitiveObjectInspector.PrimitiveCategory.INT != temp.getPrimitiveCategory()) {throw new UDFArgumentException("第1个参数应为INT类型");}// 2.检查第2个参数是否是int类型// 判断第2个参数的基本类型ObjectInspector num2 = objectInspectors[1];if (num2.getCategory() != ObjectInspector.Category.PRIMITIVE) {throw new UDFArgumentException("第2个参数不是基本数据类型");}// 第2个参数类型判断PrimitiveObjectInspector temp2 = (PrimitiveObjectInspector) num2;if (PrimitiveObjectInspector.PrimitiveCategory.INT != temp2.getPrimitiveCategory()) {throw new UDFArgumentException("第2个参数应为INT类型");}// 3.设置函数返回值类型(返回一个键值对数据)ArrayList<String> structFieldNames = new ArrayList<>();ArrayList<ObjectInspector> structFieldObjectInspectors = new ArrayList<>();structFieldNames.add("result");structFieldObjectInspectors.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);return ObjectInspectorFactory.getStandardStructObjectInspector(structFieldNames, structFieldObjectInspectors);}@Overridepublic Object evaluate(DeferredObject[] deferredObjects) throws HiveException {// 完成两数相加的逻辑计算ArrayList<Integer> arrayList = new ArrayList<>();int num1 = Integer.parseInt(deferredObjects[0].get().toString());int num2 = Integer.parseInt(deferredObjects[1].get().toString());arrayList.add(num1 + num2);return arrayList;}@Overridepublic String getDisplayString(String[] strings) {return getStandardDisplayString("AddTestReturnList", strings);}}

(退出当前与 Hive 的连接,建立新的连接,刷新缓存)

同样的,打包上传到 HDFS 上进行注册:

create function AddTestReturnList as 'AddTestReturnList' using jar 'hdfs://hadoop201:8020/test/MyUDF-1.0-SNAPSHOT-jar-with-dependencies.jar';

此时,可能会发生报错,这是由于我们之前已经加载过该 JAR 包了,再次加载时 Hive 会抛出异常,我们可以通过下面的语句进行调整:

-- 关闭向量化查询
set hive.vectorized.execution.enabled=false;

重新注册即可。

进行测试:

select AddTestReturnList(1,2);

计算结果如下:

在这里插入图片描述

是不是轻松拿捏了~

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

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

相关文章

记一次靶场渗透测试(1)

本环境为黑盒测试&#xff0c;在不提供虚拟机帐号密码的情况下进行黑盒测试拿到域控里面的flag。 环境搭建 内网网段&#xff1a;192.168.93.0/24 外网网段&#xff1a;192.168.1.0/24 攻击机&#xff1a; kali&#xff1a;192.168.1.10 靶场&#xff1a; CentOS(内)&am…

Linux笔记之制作基于ubuntu20.4的最小OpenGL C++开发docker镜像

Linux笔记之制作基于ubuntu20.4的最小OpenGL C开发docker镜像 —— 2024-04-03 夜 code review! 文章目录 Linux笔记之制作基于ubuntu20.4的最小OpenGL C开发docker镜像1.这里把这本书的例程代码放在了Dockerfile所在的文件夹内以使镜像预装例程代码2.创建Dockerfile3.构建Do…

PDF编辑和格式转换工具 Cisdem PDFMaster for Mac

Cisdem PDFMaster for Mac是一款功能强大的PDF编辑和格式转换工具。它为用户提供了直观且易于使用的界面&#xff0c;使常用功能触手可及&#xff0c;从而帮助用户轻松管理、编辑和转换PDF文件。 软件下载&#xff1a;Cisdem PDFMaster for Mac v6.0.0激活版下载 作为一款完整的…

15 个最佳 Word 文档恢复工具 [免费下​​载]

MS Word 文档恢复的重要性 对于严重依赖 Microsoft Word 创建和编辑文档的个人和企业来说&#xff0c;MS Word 文档恢复是一个至关重要的方面。 文件损坏、系统崩溃和其他意外事件可能会导致 Word 文档中存储的重要数据丢失。 及时恢复这些文档有助于节省时间、精力和资源。 本…

GD32F470_寻迹避障模块 TCRT5000红外反射传感器模块移植

2.6 红外循迹传感器 红外循迹传感器采用TCRT5000红外反射传感器&#xff0c;一种集发射与接收于一体的光电传感器&#xff0c;它由一个红外发光二极管和一个NPN红外光电三极管组成。检测反射距离1mm-25mm适用&#xff0c;传感器特设M3固定安装孔&#xff0c;调节方向与固定方便…

【绩效管理】帮助零售企业建立分层分类绩效考核体系项目纪实

购物中心张经理评价&#xff1a;“员工的绩效管理一直是困扰我公司的难题&#xff0c;我们只懂得怎么经营&#xff0c;至于怎么做人力资源管理&#xff0c;真是一点都不懂。这次华恒智信为我们提供的服务对我们的帮助很大。基于企业实际调研情况&#xff0c;华恒智信专家明确指…

前端学习之DOM编程-案例div移动

这个案例是当你的鼠标按压下去后&#xff0c;div跟着你的鼠标移动而移动&#xff0c;当你的鼠标抬起后&#xff0c;div不随着鼠标移动而移动。类似于电脑移动应用图标的感觉。 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset&quo…

day02-SpringCloud02(Nacos、Feign、Gateway)

1.Nacos 配置管理 Nacos 除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多&#xff0c;达到数十、数百时&#xff0c;逐个修改微服务配置就会让人抓狂&#xff0c;而且很容易出错。我们需要一种统一配置管理方案&#x…

比例多路阀控制器US-DAT2-A

液压比例阀放大器是一种用于精确控制液压系统的技术&#xff0c;它通过电信号实现对液压阀的连续量控制。接收来自控制器的低功率电信号&#xff0c;然后将其放大并转换为高功率信号&#xff0c;这个高功率信号足以驱动比例阀的开启和关闭。这种技术允许进行非常精细的调节&…

面经分享(Flask,轻量级Web框架)

1. Flask的核心特点 a. 轻量级&#xff1a;核心简洁&#xff0c;只提供了基本的功能&#xff0c;其他高级功能可以通过插件或扩展来添加。 b. 灵活性&#xff1a;允许开发者选择适合自己项目的组件和工具&#xff0c;没有强制的项目结构和设计模式。 c. 易于扩展&#xff1a;提…

深度剖析:网络安全中的红蓝对抗策略

红蓝对抗 红蓝对抗服务方案 在蓝队服务中&#xff0c;作为攻击方将开展对目标资产的模拟入侵&#xff0c;寻找攻击路径&#xff0c;发现安全漏洞和隐患。除获取目标系统的关键信息&#xff08;包括但不限于资产信息、重要业务数据、代码或管理员账号等&#xff09;外&#x…

ubuntu20.04.6将虚拟机用户目录映射为磁盘Z

文章目录 linux虚拟机设置为NAT模式安装sshd服务映射目录到windows磁盘安装samba套件修改配置文件smb.conf重启smbd并设置用户名和密码 windows映射遇到的问题1、设置好之后映射不成功2、smbd下载失败3、smbd密码配置问题4、当有改动时候&#xff0c;最好重启一下smbd服务 linu…

碧桂园服务净利降两成,关联交易收入仅占2.9%,发力增值服务充电桩日进超10万

自2018年分拆上市以来&#xff0c;碧桂园服务经历过非常高速的发展&#xff0c;曾是物管市场的“并购王”&#xff0c;但从2023年开始&#xff0c;希望从外延式的增长向内生式增长转型&#xff0c;将往期的经验与教训&#xff0c;通过投后管理沉淀下来&#xff0c;向高质量发展…

【Ambari】Ansible自动化部署大数据集群

目录 一&#xff0e;版本说明和介绍信息 1.1 大数据组件版本 1.2 Apache Components 1.3 Databases支持版本 二&#xff0e;安装包上传和说明 三&#xff0e;服务器基础环境配置 3.1global配置修改 3.2主机名映射配置 3.3免密用户名密码配置 3.4 ansible安装 四. 安…

2024年MathorCup妈妈杯数学建模思路C题思路解析+参考成品

1 赛题思路 (赛题出来以后第一时间在群内分享&#xff0c;点击下方群名片即可加群) 2 比赛日期和时间 报名截止时间&#xff1a;2024年4月11日&#xff08;周四&#xff09;12:00 比赛开始时间&#xff1a;2024年4月12日&#xff08;周五&#xff09;8:00 比赛结束时间&…

认知觉醒:开启自我改变的原动力-- 读书笔记

文章目录 关于本书以及作者内容第一章 大脑——一切问题的起源摘抄思考 第二章 生命留给我们的彩蛋摘抄思考 第三章 人类的终极能能力摘抄思考 第四章 专注力——情绪和智慧的交叉地带摘抄思考 第五章 学习力——学习不是一味地努力摘抄思考 第六章 行动力——没有行动世界只是…

DFS:floodfill算法解决矩阵联通块问题

floodfill&#xff0c;翻译为洪水灌溉&#xff0c;而floodfill算法本质上是为了解决在矩阵中性质相同的联通块问题。 一、图像渲染 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int dx[4]{0,0,1,-1};int dy[4]{1,-1,0,0};int prev;//记住初始值int m,…

《QT实用小工具·十六》IP地址输入框控件

1、概述 源码放在文章末尾 该项目为IP地址输入框控件&#xff0c;主要包含如下功能&#xff1a; 可设置IP地址&#xff0c;自动填入框。 可清空IP地址。 支持按下小圆点自动切换。 支持退格键自动切换。 支持IP地址过滤。 可设置背景色、边框颜色、边框圆角角度。 下面…

2023年度总结:允许迷茫,破除迷茫;专注自身,把握当下

0、前言 &#x1f4dc;为什么24年已经过了几个月&#xff0c;才提笔写这年度总结呢&#xff1f;毫不羞愧直问我的内心&#xff0c;其实就是懒罢了。直到前几天朋友看到了我去年写的总结&#xff0c;我自己点进那篇总结&#xff0c;完完整整的看了一遍&#xff0c;又翻看我23年…

力扣Lc28---- 557. 反转字符串中的单词 III(java版)-2024年4月06日

1.题目描述 2.知识点 1)用StringBuilder的方法 实现可变字符串结果 最后返回的时候用.toString的方法 2)在Java中使用StringBuilder的toString()方法时&#xff0c;它会返回StringBuilder对象当前包含的所有字符序列的字符串表示。 在我们的例子中&#xff0c;sb是一个Stri…