1 概述
当Hive提供的内置函数无法满足业务处理需求时,可以通过自定义UDF函数来扩展。
用户自定义函数类别:
(1)UDF(User-Defined-Function):一进一出。
(2)UDAF(User-Defined Aggregation Function):用户自定义聚合函数,多进一出。类似于:count/max/min
(3)UDTF(User-Defined Table-Generating Functions):用户自定义表生成函数,一进多出。如lateral view explode()。
自定义函数步骤:
(1)继承Hive提供的类:
org.apache.hadoop.hive.ql.udf.generic.GenericUDF
org.apache.hadoop.hive.ql.udf.generic.GenericUDTF
(2)实现类中的抽象方法:
(3)在hive的命令行窗口创建函数:
--添加jar,添加到hive的节点
add jar linux_jar_path;
--创建function
create [temporary] function[dbname.]function_name AS class_name;
(4)在hive的命令行窗口删除函数:
drop [temporary] function [if exists] [dbname.]function_name;
2 自定义UDF函数(例子)
需求:自定义UDF实现计算给定基本数据类型的长度,例如:
select my_len("asdf"); --4
步骤:
(1)创建一个Maven工程HiveDemo,在pom.xml中导入依赖
<dependencies>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>3.1.3</version>
</dependency>
</dependencies>
(2)创建一个类
package org.example.hive.stady.udf;
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.PrimitiveObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
public class MyLength extends GenericUDF {
/**
* 初始化,在运行sql语句时调用自定义函数之前调用一次
* 判断传进来的参数的类型和长度
* 约定返回的数据类型
*/
@Override
public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
// ObjectInspector为对象检查器
//利用数组长度来校验函数接收参数的个数
if (arguments.length != 1) {
throw new UDFArgumentLengthException("只接收一个参数");
}
//校验参数的类型是不是基本数据类型
ObjectInspector argument = arguments[0];
if (!argument.getCategory().equals(ObjectInspector.Category.PRIMITIVE)) {
throw new UDFArgumentTypeException(1, "只接收基本数据类型参数");
}
//将基本数据类型强转为PrimitiveObjectInspector
PrimitiveObjectInspector primitiveObjectInspector = (PrimitiveObjectInspector) argument;
if (primitiveObjectInspector.getPrimitiveCategory() != PrimitiveObjectInspector.PrimitiveCategory.STRING) {
throw new UDFArgumentTypeException(1, "只接收string类型参数");
}
return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
}
/**
* 核心处理逻辑,每处理一行数据就调用一次evaluate方法
* 解决具体逻辑的
*/
@Override
public Object evaluate(DeferredObject[] arguments) throws HiveException {
//DeferredObject是hive中的懒加载的类,拿到数据需要get一下。
DeferredObject argument = arguments[0]; //直接用,因为在初始化时已经校验过参数个数,不会发生数组越界问题
Object o = argument.get();
if (o == null)
return 0;
else return
o.toString().length();
}
// 用于获取(执行计划中的及sql中 explain)解释的字符串
@Override
public String getDisplayString(String[] strings) {
return null;
}
}
(3)创建临时函数(只在当前会话有效)
打成jar包上传到(hive的节点上)服务器/opt/module/hive/datas/下。
--将jar包添加到hive的classpath,临时生效:
add jar /opt/module/hive/datas/MyLength.jar;
--创建临时函数与开发好的java class关联:
create temporary function my_len as "org.example.hive.stady.udf. MyLength";
--查看函数,如果存在可以当作内置函数直接使用:
show functions like “my_len”;
--传值测试:
select my_len(“asdf”);
--删除临时函数:
drop temporary function my_len;
临时函数只跟会话有关系,跟库没有关系。只要创建临时函数的会话不断,在当前会话下,任意一个库都可以使用,其他会话全都不能使用。
(4)创建永久函数
因为add jar本身也是临时生效且使用元数据,所以在创建永久函数的时候,需要指定HDFS路径。
先把jar包上传到HDFS指定路径下
--创建永久函数
create function my_len as " org.example.hive.stady.udf. MyLength " using jar "hdfs://hadoop102:8020/udf/ MyLength.jar ";
--查看函数,如果存在可以当作内置函数直接使用,结果显示:数据库名.my_len
show functions like “*my_len*”;
--传值测试:
select 数据库名.my_len(“asdf”);
--删除永久函数:
drop function my_len;
永久函数跟会话没有关系,创建函数的会话断了以后,其他会话也可以使用。
永久函数创建的时候,在函数名之前需要自己加上库名,如果不指定库名的话,会默认把当前库的库名给加上。
永久函数使用的时候,需要在指定的库里面操作,或者在其他库里面使用的话加上,库名.函数名。