首页 > 其他分享 >Hive自定义函数编写方法(含源代码解读,超详细,易理解)

Hive自定义函数编写方法(含源代码解读,超详细,易理解)

时间:2024-07-17 10:59:44浏览次数:10  
标签:函数 自定义 ObjectInspector hive Hive 类型 源代码

一、Hive自定义函数介绍

        

1.内置函数

        Hive 自带了一些函数。比如:max/min等,但是数量有限,自己可以通过自定义UDF来方便的扩展。

2.自定义函数

        当Hive提供的内置函数无法满足你的业务处理需要时,此时就可以考虑使用用户自定义函数(UDF:user-defined function)。

3用户自定义函数类别

        用户自定义函数根据输入参数和输出结果的个数分为以下三类:

UDF(User-Defined-Function)eq?%5Crightarrow 一进一出

UDAF(User-Defined Aggregation Function)eq?%5Crightarrow 聚合函数多进一出,如:count/max/min

UDTF(User-Defined Table-Generating Functions)eq?%5Crightarrow 炸裂函数一进多出,如:explode()

4官方文档地址

HivePlugins - Apache Hive - Apache Software Foundationicon-default.png?t=N7T8https://cwiki.apache.org/confluence/display/Hive/HivePlugins

5编程实现步骤

① 继承Hive提供的类:

org.apache.hadoop.hive.ql.udf.generic.GenericUDF  

·org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;

② 实现类中的抽象方法

③ 在hive的命令行窗口创建函数

二、具体实现 

        为了能够更好地演示过程,本文以编写一个判断字符串长度的函数为例,函数命名为mystrlen(),具体实现效果如下图所示。

203791c29ddd405495bdd4344dd936fa.png

1.准备工作

① 创建一个Maven工程Hive

② 在工程项目的pom.xml文件中导入依赖,具体代码如下。本文选用的是Hive3.1.2版本,大家可以根据自己的版本自行更改。

<dependencies>
		<dependency>
			<groupId>org.apache.hive</groupId>
			<artifactId>hive-exec</artifactId>
			<version>3.1.2</version>
		</dependency>
</dependencies>

2.继承Hive提供的类

         经过分析不难发现,该函数应该以一个字符串为输入参数,最终输出一个int型的整数以表明该字符串的长度。其对应的自定义函数类别是UDF(一进一出)类型。所以我们应该继承前文所提到的GenericUDF类。

        创建my_strlen类以继承GenericUDF类。由于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;

/**
 * 自定义UDF函数,需要继承GenericUDF类
 * 需求: 计算指定字符串的长度
 */

public class my_strlen extends GenericUDF {
    /**
     *
     * @param arguments 输入参数类型的鉴别器对象
     * @return 返回值类型的鉴别器对象
     * @throws UDFArgumentException
     */
    @Override
    public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
        return null;
    }
    
    /**
     * 函数的逻辑处理
     * @param arguments 输入的参数
     * @return 返回值
     * @throws HiveException
     */
    @Override
    public Object evaluate(DeferredObject[] arguments) throws HiveException {
        return null;
    }

    @Override
    public String getDisplayString(String[] children) {
        return null;
    }
}

        看到上述idea自动生成的代码,可能大家有点慌了。ObjectInspector是什么?DeferredObject又是什么?这些函数具体是做啥的?让我们一步一步的来。

        在这里,第一个方法initialize()的作用为对自定义函数进行初始化。具体需要我们实现的有:检验函数的参数个数和参数类型是否正确,并且返回函数返回值类型的ObjectInspector。对于第二个方法evaluate,我们需要实现函数的具体功能,并且返回函数最终的输出结果。两个方法的形参都是argument,顾名思义,此形参传递的便是函数的参数信息。

        对于不认识的类,我们首先应该去翻译其类名,然后分析其源码。ObjectInspector,顾名思义,该类应该是一个鉴别器,具体鉴别什么就需要我们对源码进行分析。让我们看下列源码,不难发现一些关键词 :gettype、getCategory,那么这便是一个对象数据类型的鉴别器。对于第一个方法initialize()的形参,便是由若干个函数输入参数的类型鉴别器所组成的数组。

public interface ObjectInspector extends Cloneable {
    String getTypeName();

    Category getCategory();

    public static enum Category {
        PRIMITIVE,
        LIST,
        MAP,
        STRUCT,
        UNION;

        private Category() {
        }
    }
}

        再来观察DeferredObject的源码。上文提到我们需要在evaluate()方法中实现函数的具体功能,那么首先我们需得到函数的参数值,再根据参数来编写功能逻辑。在DeferredObject的源码中我们发现了get()方法,根据经验我们可以通过此方法来获得函数的参数值。

 public static interface DeferredObject {
    void prepare(int version) throws HiveException;
    Object get() throws HiveException;
 };

3.实现类中的抽象方法  

        分析完各个模块,就可以开始重写方法啦。initialize()方法重写如下,其难点在于对函数输入参数类型的判断和如何返回int类型的鉴别器对象。

public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
        // 判断输入参数的个数
        if(arguments.length !=1){
            throw new UDFArgumentLengthException("Input Args Length Error!!!");
        }
        // 判断输入参数的类型
        if(!arguments[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)){
            throw new UDFArgumentTypeException(0,"Input Args Type Error!!!");
        }
        //函数本身返回值为int,需要返回int类型的鉴别器对象
        return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
    }

        对于输入参数类型的判断,仔细观察上文中的ObjectInspector源码,发现有一个静态枚举类Category,也正是getCategory()方法的返回值类型。所以该枚举类的作用是用来限制ObjectInspector类型鉴别器所鉴别类型的结果,我们可以使用getCategory()方法来获取函数参数的类型并且直接调用静态枚举类的成员,比较二者是否相同来判断函数参数类型是否正确(arguments[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE))。在这里我使用ObjectInspector.Category.PRIMITIVE来调用枚举类Category中的PRIMITIVE,因为我们需要限制输入参数的类型为String,其对应的便是PRIMITIVE(原始类型)。

        如果在前两步的判断中发现参数不符合要求,我们便可以抛出异常,代码中的两个异常是initialize方法所抛出异常UDFArgumentException的子类,适用于不同的场景,可以更好地起到异常提示效果。 

        对于返回int类型的鉴别器对象,因ObjectInspector是一个接口,所以我们需要调用工厂类中的实例,通过PrimitiveObjectInspectorFactory.javaIntObjectInspector获得。关于何为静态工厂类中的实例大家可以参考以下两篇文章:java静态工厂方法详细解析Hive之ObjectInspector接口解析笔记

        对于evaluate()方法的实现,因函数本身逻辑简单,所以其实现难度不大。需要注意的是在使用get()方法获取到参数值后需要toString(),因为从DeferredObject源码中发现get()方法的返回值类型是object,所以需要我们将其转化为String类型。

public Object evaluate(DeferredObject[] arguments) throws HiveException {
    return arguments[0].get().toString().length();
}

4.在hive的命令行窗口创建函数

         首先将代码打成jar包上传到服务器的任意位置,然后在hive命令行使用如下命令将jar包添加到hive的classpath中。

 acf530af44e64a56a15f1308ea8a827a.png

        创建临时函数与开发好的java class关联 ,其中temporary代表创建临时函数,若创建永久函数需省略;mystrlen是我们的函数名;最后需要跟全类名。

21f52c3e2e1d4fb2908c630883e67d16.png         运行效果如下:

203791c29ddd405495bdd4344dd936fa.png

三、总结

        Hive创建自定义函数的逻辑并不难,只需继承相关类,实现相关方法,打成jar包上传集群即可。但在代码编写阶段有一定难度,需要一定的java基础。

标签:函数,自定义,ObjectInspector,hive,Hive,类型,源代码
From: https://blog.csdn.net/ffads/article/details/140451546

相关文章

  • kettle从入门到精通 第七十六课 ETL之kettle kettle连接hive教程
     1、群里有小伙伴询问kettle连接hive的demo,今天抽点时间整理下。其实kettle连接hive和连接mysql数据库也是一样的。1)kettle中的lib目录下放hive驱动jar,这里我使用的是kyuubi-hive-jdbc-shaded-1.9.0.jar。2)设置hive连接参数。3)通过表输入进行读取数据。 2、下载kyuubi-hive......
  • 数据仓库建模工具之一——Hive学习第二天
    Hive的概述1、Hive基本概念1.1 Hive简介Hive本质是将SQL转换为MapReduce的任务进行运算,底层由HDFS来提供数据存储,说白了hive可以理解为一个将SQL转换为MapReduce的任务的工具,甚至更近一步说hive就是一个MapReduce客户端。为什么使用Hive?使用hadoop,成本太高,项目要求周期太......
  • 三分钟了解自定义表单自定义工作流的多个优势
    降本、提高效率、解决信息孤岛是很多企业亟需要解决的问题。什么样的软件平台可以实现这一目标?可以随时来了解低代码技术平台。它当中的自定义表单自定义工作流拥有多个优势特点,可以为企业降低技术门槛、提高工作效率,可视化操作界面的便利性更让职场朋友们深知是实现流程化办公的......
  • Nuxt.js头部魔法:轻松自定义页面元信息,提升用户体验
    扫描二维码关注或者微信搜一搜:编程智域前端至全栈交流与成长useHead 函数概述useHead是一个用于在Nuxt应用中自定义页面头部属性的函数。它由Unhead库提供支持,允许开发者以编程和响应式的方式设置每个页面的头部信息。useHead 函数类型useHead(meta:MaybeComputedRef<......
  • gdb调试c、cpp源代码方法步骤
    1.代码举例//example.c#include<stdio.h>intaddNumbers(intnum1,intnum2){returnnum1+num2;}intmain(){inta=10;intb=20;intsum=addNumbers(a,b);printf("Thesumis:%d\n",sum);return0;}......
  • C++自定义双向迭代器
    #include<cassert>#include<memory>#include<vector>#include<iostream>classRange{public:usingIndex=uint64_t;usingSignedIndex=int64_t;usingOffset=int64_t;usingSize=uint64_t;Range()=d......
  • 外贸国际短信群发工具的开发源代码!
    在外贸行业中,快速、准确地与客户进行沟通是业务成功的关键之一,随着科技的不断进步,国际短信群发工具成为了外贸从业者不可或缺的工具。本文将通过科普五段源代码,带您深入了解外贸国际短信群发工具的开发原理和实现过程。一、概述外贸国际短信群发工具是一种利用计算机技术和......
  • 自定义localStorage监听事件
    一、问题在项目开发过程中,发现有很多时候进行localStorage.setItem()操作设置本地存储后,页面必须刷新才能够获取到存储数据,而有些时候本地缓存更新后,页面无法通过再次刷新以获取本地缓存,这就导致依赖本地缓存的数据无法进行更新。为了解决这个问题,就必须要用到自定义localStorage......
  • 2024最新的源代码防泄漏方案分享
    源代码是软件开发的核心资产,一旦泄露,不仅会导致知识产权损失,还可能被竞争对手利用,给企业带来巨大的经济损失和法律风险。那么有没有针对源代码的防泄漏方案呢?接下来我为大家介绍2024最新的源代码防泄漏解决方案。1.访问控制:实施严格的访问控制策略,确保只有授权的开发者和......
  • elementui的el-cascader-panel在jsx里如何自定义label和props属性
    render(){return(<el-cascader-panelonChange={(val)=>{this.handleFormatChange(val,'format','dataColumns',indexInMap)}}props={{renderLabel:(params)=>{......