自定义函数
1 自定义函数的简介
1.1自定义函数来历
- hive的内置函数满足不了所有的业务需求‘
- hive提供很多的模板可以自定义功能,比如:自定义函数、serde、输入输出格式等。
1.2 自定义函数分类
- UDF:用户自定义函数,user defined function。一对一的输入输出。(最常用的)。
- UDTF:用户自定义表生成函数。user defined table-generate function,一对多的输入输出。lateral view explode
- UDAF:用户自定义聚合函数。user defined aggregate function 。多对一的输入输出count sum max。
2 自定义函数实现
2.1 UDF格式
创建maven项目,在pom.xml加入一下maven的依赖包
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>2.1.1</version>
</dependency>
定义UDF函数要注意下面几点:
- 继承 org.apache.hadoop.hive.ql.exec.UDF
- 重写evaluate(),这个方法不是由接口定义的,因为它可接受的参数的个数,数据类型都是不确定。Hive会检查UDF,看能否找到和函数调用相匹配的evaluate()方法。
2.1.1 自定义函数第一个案例--------实现小写转大写
public class FirstUDF extends UDF {
public String evaluate(String str){
//关于默认输出值是null,还是“”,这个要看需求具体定义,在这里先默认定义为 null,
String result = null;
//1、检查输入参数
if (!StringUtils.isEmpty(str)){
result = str.toUpperCase();
}
return result;
}
//调试自定义函数
public static void main(String[] args){
System.out.println(new FirstUDF().evaluate("myedu"));
}
}
2.2 函数加载方式
2.2.1第一种方法:命令加载(临时有效)
这种加载只对本session有效
# 将编写的udf的jar包上传到服务器上.
# 并且将jar包添加到hive的class path中
#实现方法
# 进入到hive客户端,执行下面命令
hive> add jar /opt/jar/udf.jar
-- 创建一个临时函数名,要跟上面hive在同一个session里面:
hive> create temporary function 函数名 as '类所在的路径即包名加类名’;
实例:
hive> create temporary function toUP as 'com.xxx.hive.FirstUDF';
-- 检查函数是否创建成功
hive> show functions;
-- 测试功能
hive> select toUp('abcdef');
-- 删除函数
hive> drop temporary function if exists toUP;
2.2.2 启动参数加载(也是在本session有效,临时函数)(临时生效)
# 1、将编写的udf的jar包上传到服务器上
# 2、在hive的文件目录下创建配置文件
[root@master hive]# vi ./hive-init
# 加入下面脚本
add jar /opt/jar/udf.jar;
create temporary function toup as 'com.qf.hive.FirstUDF';
# 3、启动hive的时候带上初始化文件:
[root@master hive]# hive -i ./hive-init
hive> select toup('abcdef')
2.2.3 配置文件加载(永久生效)
通过配置文件方式这种只要用hive命令行启动都会加载函数
# 1、将编写的udf的jar包上传到服务器上
# 2、在hive的安装目录的bin目录下创建一个配置文件,文件名:.
hiverc [root@master hive]# vi ./bin/.hiverc
add jar /hivedata/udf.jar;
create temporary function toup as 'com.qf.hive.FirstUDF';
3、启动hive
[root@master hive]# hive
2.3 UDTF格式
UDTF是一对多的输入输出,实现UDTF需要完成下面步骤
- 继承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF
- 重写 initlizer() 、 process() 、 close() 。
执行流程如下:
- UDTF首先会调用initialize方法,此方法返回UDTF的返回行的信息(返回个数,类型)。
- 初始化完成后,会调用process方法,真正的处理过程在process函数中,在process中每一次forward()调用产生一行;如果产生多列可以将多个列的值放在一个数组中,然后将数组传入到forward()函数。
- 最后close()方法调用,对需要清理的方法进行清理。
2.3.1 需求
把"k1:v1;k2:v2;k3:v3"类似的的字符串解析成每一行多行,每一行按照key:value格式输出
2.3.2 代码
自定义函数如下:
/**
* 定义一个UDTF的Hive自定义函数(一对多),默认要继承与GenericUDTF
*/
public class ParseMapUDTF extends GenericUDTF {
//在initializez中初始化要输出字段的名称和类型
@Override
public StructObjectInspector initialize(ObjectInspector[] argOIs) throws UDFArgumentException {
//定义要输出列的名字的List,并且添加要输出的列名
List<String> structFieldNames = new ArrayList<>();
structFieldNames.add("key");
structFieldNames.add("value");
// 定义要输出列的类型的List,并且添加要输出列的类型
List<ObjectInspector> objectInspectorList = new ArrayList<>();
objectInspectorList.add(PrimitiveObjectInspectorFactory.javaStringObjectI nspector);
objectInspectorList.add(PrimitiveObjectInspectorFactory.javaStringObjectI nspector);
return ObjectInspectorFactory.getStandardStructObjectInspector(structFieldNames, objectInspectorList);
}
// process方法用来处理输入的每行数据,每行数据处理调用一次process,类似于 Mapper中的map方法
@Override
public void process(Object[] objects) throws HiveException {
// 得到第一个参数,转化为字符串,类似于->
//name:zhang;age:30;address:shenzhen
String insputString = objects[0].toString();
// 把上述例子字符串按照分号;切分为数组
String[] split = insputString.split(";");
// s=name:zhang
for (String s : split) {
// 把每个切分后的key value分开
String[] kvArray = s.split(":");
// 如果产生多列可以将多个列的值放在一个数组中,然后将该数组传入到 forward()函数。
forward(kvArray);
}
}
@Override
public void close() throws HiveException {
}
}
2.3.3 打包加载
对上述命令源文件打包为udtf.jar,拷贝到服务器的/opt/jar/目录
在Hive客户端把udf.jar加入到hive中,如下:
hive> add jar /opt/jar/udtf.jar;
2.3.4 创建临时函数
在Hive客户端创建函数:
# 创建一个临时函数parseMap
hive> create temporary function parseMap as 'com.qf.hive.ParseMapUDTF';
# 查看函数是否加入
hive> show functions ;
2.3.5 测试临时函数
hive> select parseMap("name:zhang;age:30;address:shenzhen");
结果如下:
#map key
name zhang
age 30
address shenzhen
2.4 UDAF格式
用户自定义聚合函数。user defined aggregate function。多对一的输入输出 count summax。定义一个UDAF需要如下步骤:
- UDAF自定义函数必须是org.apache.hadoop.hive.ql.exec.UDAF的子类,并且包含一个或者多个嵌套的函数实现了org.apache.hadoop.hive.ql.exec.UDAFEvaluator的静态类。
- 函数类需要继承UDAF类,内部类Evaluator实现UDAFEvaluator接口。
- Evaluator需要实现 init、iterate、terminatePartial、merge、terminate这几个函数
这几个函数作用如下:
2.4.1 需求
计算一组整数的最大值
2.4.2 代码
/**
* 定义一个UDAF自定义函数类,默认要继承于UDAF类
*/
//给当前函数添加描述信息,方便在desc function方法时查看
@Description(name="maxInt",value = "Find Max Value" ,extended = "Extended:Find Max Value for all Col")
public class MaxValueUDAF extends UDAF {
//UDAF要求 并且包含一个或者多个嵌套的的实现了
// org.apache.hadoop.hive.ql.exec.UDAFEvaluator的静态类。
public static class MaxnumIntUDAFEvaluator implements UDAFEvaluator {
//在静态类内部定义一个返回值,作为当前UDAF最后的唯一返回值,因为返回值要在 hive调用,所以必须要使用序列化类型
private IntWritable result;
/**
* 在初始化是把返回值设为null,避免和上次调用时混淆
*/
@Override
public void init() {
result=null;
}
//定义一个函数iterate用来处理遍历多行时,每行值传进来是调用的函数
public boolean iterate(IntWritable value) {
// 把遍历每行的值value传入,和result比较,如果比result大,那么result就 设置为value,否则result不变
if (value == null) {
return true;
}
//如果是第一行数据,那么直接给result赋值为第一行数据
if (result == null) {
result = new IntWritable(value.get());
} else {
// 给result赋值result和value之间的最大值
result.set(Math.max(result.get(),value.get()));
}
return true;
}
/**
* 在map端进行并行执行后的结果
* @return
*/
public IntWritable terminatePartial() {
return result;
}
/**
* 接收terminatePartial的返回结果,进行数据merge操作,其返回类型为 boolean。
* @param other
* @return
*/
public boolean merge(IntWritable other) {
return iterate( other );
}
/**
* 将最终的结果返回为Hive
* @return
*/
public IntWritable terminate() {
return result;
}
}
}
注意:如果你要给自己写的函数加上desc function后的说明,可以在自定义函数类上面加上下面的注解:
@Description(name = "maxValue",value = "Find Max Value",extended ="Extended:Find Max Value for all Col")
hive> desc function maxInt;
hive> desc function extended maxInt;
2.4.3打包加载
对上述命令源文件打包为udaf.jar,拷贝到服务器的/opt/jar目录
在Hive客户端把udf.jar加入搭配hive中,如下:
hive> add jar /opt/jar/udaf.jar;
2.4.4创建临时函数
在Hive客户端创建函数
hive> create temporary function maxInt as 'com.qf.hive.MaxValueUDAF';
# 查看函数是否加入
hive> show functions ;
2.4.5测试临时函数
-- 使用前面的任意一个有int类型字段的表进行测试
hive> select maxInt(id) from dy_part1;