数据人与超自然意识 发表于 2024-8-17 03:02:22

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

一、Hive自定义函数介绍

        
1.内置函数

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

        当Hive提供的内置函数无法满足你的业务处置惩罚需要时,此时就可以思量使用用户自定义函数(UDF:user-defined function)。
3.用户自定义函数类别

        用户自定义函数根据输入参数和输出效果的个数分为以下三类:
① UDF(User-Defined-Function)https://latex.csdn.net/eq?%5Crightarrow 一进一出
② UDAF(User-Defined Aggregation Function)https://latex.csdn.net/eq?%5Crightarrow 聚合函数,多进一出,如:count/max/min
③ UDTF(User-Defined Table-Generating Functions)https://latex.csdn.net/eq?%5Crightarrow 炸裂函数,一进多出,如:explode()
4.官方文档地址

HivePlugins - Apache Hive - Apache Software Foundationhttps://csdnimg.cn/release/blog_editor_html/release2.3.6/ckeditor/plugins/CsdnLink/icons/icon-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(),详细实现效果如下图所示。
https://i-blog.csdnimg.cn/direct/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.getCategory().equals(ObjectInspector.Category.PRIMITIVE)){
            throw new UDFArgumentTypeException(0,"Input Args Type Error!!!");
      }
      //函数本身返回值为int,需要返回int类型的鉴别器对象
      return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
    }         对于输入参数类型的判断,仔细观察上文中的ObjectInspector源码,发现有一个静态枚举类Category,也正是getCategory()方法的返回值类型。所以该枚举类的作用是用来限定ObjectInspector类型辨别器所辨别类型的效果,我们可以使用getCategory()方法来获取函数参数的类型而且直接调用静态枚举类的成员,比较二者是否雷同来判断函数参数类型是否正确(arguments.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.get().toString().length();
} 4.在hive的命令行窗口创建函数

         首先将代码打成jar包上传到服务器的恣意位置,然后在hive命令利用用如下命令将jar包添加到hive的classpath中。
 https://i-blog.csdnimg.cn/direct/acf530af44e64a56a15f1308ea8a827a.png
        创建临时函数与开发好的java class关联 ,其中temporary代表创建临时函数,若创建永久函数需省略;mystrlen是我们的函数名;末了需要跟全类名。
https://i-blog.csdnimg.cn/direct/21f52c3e2e1d4fb2908c630883e67d16.png         运行效果如下:
https://i-blog.csdnimg.cn/direct/203791c29ddd405495bdd4344dd936fa.png
 
三、总结

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Hive自定义函数编写方法(含源代码解读,超详细,易理解)