0


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

一、Hive自定义函数介绍

1****.内置函数

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

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 Foundationhttps://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基础。

本文转载自: https://blog.csdn.net/ffads/article/details/140451546
版权归原作者 研究生狗都不读 所有, 如有侵权,请联系我们删除。

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

还没有评论