Hive——UDF、UDTF、UDAF

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;
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值