UDF(User-Defined-Function)一进一出
创建UDF的一般步骤
(1)继承 org.apache.hadoop.hive.ql.UDF
(2)需要实现 evaluate 函数;evaluate 函数支持重载;
(3)添加 jar
add jar linux_jar_path
(4)创建 function
create [temporary] function [dbname.]function_name AS class_name;
(5)在 hive 的命令行窗口删除函数
Drop [temporary] function [if exists] [dbname.]function_name;
实例
1、打开idea创建一个快速的Maven工程,在pom文件导入hive依赖
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>1.1.0</version>
</dependency>
2、创建一个类MyUDF继承UDF,创建一个方法名为evaluate,如下图
3、用maven直接打jar包,把target文件下生成的jar包重命名为hive-jar,放入linux的root目录
4、打开hive,加载jar包
add jar /root/hive-jar.jar
创建函数
注意点
(1)MyUDf是自己创建的类的相对路径
(2)我是在default库下创建的UDF就只能在default库下使用,其他库不能用
(3)我并没有在function前加temporary,说明我创建的是永久表,退出hive重新进入也是可以使用的,而temporary就只能在本次hive程序下运行,退出后自行删除
create function addFive as 'MyUDF';
5、调用函数
6、删除函数
UDTF(User-Defined Table-Generating Functions)一进多出
需求:将一行内容按某一个分隔符分割,炸裂成多行
比如hello,world,hadoop,hive这个字符串用,分割,炸裂成多行
select udf("hello,world,hadoop,hive",",");
结果
hello
hadoop
hive
要重写StructObjectInspector、process、close这三个方法,分别用于初始化、逻辑过程、关闭资源。很像MapReduce的Mapper中的步骤
package udtf;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import java.util.ArrayList;
import java.util.List;
public class MyUDTF extends GenericUDTF {
private List<String> dataList=new ArrayList<>();
//alt+enter不会重写,要自己写,定义输出列名和数据类型。不写会报异常
@Override
public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
//ArrayList说明可以炸裂出多个列
List<String> fieldNames=new ArrayList<>();
//定义输出数据的列名
fieldNames.add("word");
List<ObjectInspector> fieldOIs=new ArrayList<>();
//设置函数的返回值的类型,这里是String
fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
}
@Override
public void process(Object[] objects) throws HiveException {
//1获取数据
String data = objects[0].toString();
//2获取分割符
String splitKey = objects[1].toString();
//3切分数据
String[] words = data.split(splitKey);
//4遍历写出
for (String word : words) {
//5将数据放到集合
dataList.clear();
dataList.add(word);
//6写出数据的操作
forward(dataList);
}
}
@Override
public void close() throws HiveException {
}
}
后面依然是打jar包,放入linux,再从hive创function
集体步骤这里就省略了,和创建UDF没什么区别。
执行成功!
UDAF(User- Defined Aggregation Funcation)多进一出
package udaf;
import org.apache.hadoop.hive.ql.exec.UDAF;
import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;
public class MyUDAF extends UDAF {
/*
在函数类中准备自定义函数需要的东西
当前例子是用来做计数的因此准备一个计数用变量
这里可能会有人疑问:只是计数为什么不直接用一个值变量,而是要写一个类
别着急往下看
*/
public static class MyCount{
//最好不要直接写int count=0
int count;
}
/**
* 计算类实现UDAFEvaluator接口
* 重写init、iterate、terminatePartial、merge、terminate
* 但是说是重写,可以有时候出不来,大家手敲一下就好
*/
public static class MyEvaluator implements UDAFEvaluator {
//为了节省资源最好使用public static修饰一下
public static MyCount count;
public MyEvaluator() {
/*
函数运行时计算类会被实例化
我们要在计算类的构造器中初始化
但是注意这里初始化的不是我们一般理解的赋具体值
而是用来初始化资源,如实例化对象等相关操作
具体的值操作在init中执行,这就是为什么我前面写了一个类
就是为了在这里让大家知道计算类的构造器和init方法到底初始化的是什么
*/
count=new MyCount();
/*
init方法个人感觉不是必须调用,它执行的是函数内部的初始化工作
只是因为书写规范,所以需要在构造器中调用一下
这里说的内部我给大家说一个例子大家就明白了:
如果你运行的是group之后的计数操作,那么init方法就是在
一组数据计数之后做计数归零的那个角色
怎么样看到这里是不是突然想来一句 搜嘎斯内
*/
init();
}
/**
* 这里进行初始化
*/
public void init() {
count.count=0;
}
/**
* iterate接收传入的参数,被函数内部多次调用,它的地位和MapReducer的map阶段等价
* 我们的聚合计算操作就在这里实现
* 但是它的返回值类型是个布尔
* 作用是对聚合做成功与否判断
* 大家写的时候根据当下的业务需要书写
* 接收的参数类型也是按照因为需要类
* 本例不是很需要具体值所有这里随便写了一个Double型
*/
public boolean iterate(Double o) {
if (o != null) {
count.count++;
}
return true;
}
/**
* terminatePartial无参数
* 它做的工作是用来操作iterate的结果的
* 类似于hadoop的小文件合并哪一类的
* 过滤不必要数据的操作
* 如果没有需要那么直接返回就需要值好
* 但是工作中为了团队合作的我们还是会书写该方法
* 在这里简单的做一个结果验证操作
* 因为MR的map不止有一个同理iterate也可能不止一个
* 他们的结果我们要过滤一下
*/
public MyCount terminatePartial() {
return count.count == 0 ? null : count;
}
/**
* merge处理terminatePartial的结果做合并结果操作
*/
public boolean merge(MyCount c) {
if (c != null) {
count.count+=c.count;
}
return true;
}
/**
* terminate处理并返回最终的聚集函数结果
*/
public int terminate() {
return count.count == 0 ? null : count.count;
}
}
}