一行输入,返回一个结果

可以满足日常工作中的大部分需求

UDF的实现方法

Hive 提供了两个实现 UDF 的方式:

第一种:继承 UDF
  • 优点:

    • 实现简单
    • 支持Hive的基本类型、数组和Map
    • 支持函数重载
  • 缺点:
    • 逻辑较为简单,只适合用于实现简单的函数

这种方式编码少,代码逻辑清晰,可以快速实现简单的UDF

第二种:继承 GenericUDF
  • 优点:

    • 支持任意长度、任意类型的参数
    • 可以根据参数个数和类型实现不同的逻辑
    • 可以实现初始化和关闭资源的逻辑(initialize、close)
  • 缺点:
    • 实现比继承UDF要复杂一些

与继承 UDF 相比,GenericUDF 更加灵活,可以实现更为复杂的函数

关于两者的选择

如果函数具有以下特点,优先继承 UDF 类:

  1. 逻辑简单,比如英文转小写函数
  2. 参数和返回值类型简单,都是Hive的基本类型、数组或Map
  3. 没有初始化或关闭资源的需求

否则考虑继承 GenericUDF 类

接下来介绍这两种实现方式的具体步骤

继承 UDF 类

第一种方式的代码实现最为简单,只需新建一个类 继承UDF,然后编写 evaluate() 即可

import org.apache.hadoop.hive.ql.exec.UDF;/*** 继承 org.apache.hadoop.hive.ql.exec.UDF*/
public class SimpleUDF extends UDF {/*** 编写一个函数,要求如下:* 1. 函数名必须为 evaluate* 2. 参数和返回值类型可以为:Java基本类型、Java包装类、org.apache.hadoop.io.Writable等类型、List、Map* 3. 函数一定要有返回值,不能为 void*/public int evaluate(int a, int b) {return a + b;}/*** 支持函数重载*/public Integer evaluate(Integer a, Integer b, Integer c) {if (a == null || b == null || c == null)return 0;return a + b + c;}
}

继承UDF类的方式非常简单,但还有一些需要注意的地方:

  1. evaluate() 方法并不是继承自 UDF 类(编写代码时名字容易打错,哈哈哈~)
  2. evaluate() 的返回值类型不能为 void(你这个函数返回值都没有,我要你干嘛?)

支持的参数和返回值类型

支持 hive基本类型、数组和Map

Hive基本类型

Java可以使用Java原始类型、Java包装类或对应的Writable类

PS:对于基本类型,最好不要使用 Java原始类型,当 null 传给 Java原始类型 参数时,UDF 会报错。Java包装类还可以用于null值判断

Hive类型 Java原始类型 Java包装类 hadoop.io.Writable
tinyint byte Byte ByteWritable
smallint short Short ShortWritable
int int Integer IntWritable
bigint long Long LongWritable
string String - Text
boolean boolean Boolean BooleanWritable
float float Float FloatWritable
double double Double DoubleWritable
数组和Map
Hive类型 Java类型
array List
Map<K, V> Map<K, V>

继承 GenericUDF

这种方式最为灵活,但实现起来也比上一种方法要复杂一些

继承 GenericUDF 后,必须实现其三个方法:

  1. initialize()
  2. evaluate()
  3. getDisplayString()

initialize()

/*** 初始化 GenericUDF,每个 GenericUDF 示例只会调用一次初始化方法** @param arguments*          自定义UDF参数的 ObjectInspector 实例* @throws UDFArgumentException*           参数个数或类型错误时,抛出该异常* @return 函数返回值类型
*/
public abstract ObjectInspector initialize(ObjectInspector[] arguments)throws UDFArgumentException;

initialize() 在函数在 GenericUDF 初始化时被调用一次,执行一些初始化操作,包括:

  1. 判断函数参数个数
  2. 判断函数参数类型
  3. 确定函数返回值类型

除此之外,用户在这里还可以做一些自定义的初始化操作,比如初始化HDFS客户端等

其一:判断函数参数个数

可通过 arguments 数组的长度来判断函数参数的个数

判断函数参数个数示例:

// 1. 参数个数检查,只有一个参数
if (arguments.length != 1) // 函数只接受一个参数throw new UDFArgumentException("函数需要一个参数"); // 当自定义UDF参数与预期不符时,抛出异常
其二:判断函数参数类型

ObjectInspector 可用于侦测参数数据类型,其内部有一个枚举类 Category,代表了当前 ObjectInspector 的类型

public interface ObjectInspector extends Cloneable {public static enum Category {PRIMITIVE, // Hive原始类型LIST, // Hive数组MAP, // Hive MapSTRUCT, // 结构体UNION // 联合体};
}

Hive原始类型又细分了多种子类型,PrimitiveObjectInspector 实现了 ObjectInspector,可以更加具体的表示对应的Hive原始类型

public interface PrimitiveObjectInspector extends ObjectInspector {/*** The primitive types supported by Hive.*/public static enum PrimitiveCategory {VOID, BOOLEAN, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, STRING,DATE, TIMESTAMP, BINARY, DECIMAL, VARCHAR, CHAR, INTERVAL_YEAR_MONTH, INTERVAL_DAY_TIME,UNKNOWN};
}

关于 PrimitiveCategory 的枚举类型就不一一解释了

参数类型判断示例:

// 2. 参数类型检查,参数类型为String
if (arguments[0].getCategory() != ObjectInspector.Category.PRIMITIVE // 参数是Hive原始类型|| !PrimitiveObjectInspector.PrimitiveCategory.STRING.equals(((PrimitiveObjectInspector)arguments[0]).getPrimitiveCategory())) // 参数是Hive的string类型throw new UDFArgumentException("函数第一个参数为字符串"); // 当自定义UDF参数与预期不符时,抛出异常
其三:确定函数返回值类型

initialize() 需要 return 一个 ObjectInspector 实例,用于表示自定义UDF返回值类型。initialize() 的返回值决定了 evaluate() 的返回值类型

ObjectInspector 的源码中,有这么一段注释,其大意是 ObjectInspector 的实例应该由对应的工厂类获取,以保证实例的单例等属性

/*** An efficient implementation of ObjectInspector should rely on factory, so* that we can make sure the same ObjectInspector only has one instance. That* also makes sure hashCode() and equals() methods of java.lang.Object directly* works for ObjectInspector as well.*/
public interface ObjectInspector extends Cloneable { }

对于基本类型(byte、short、int、long、float、double、boolean、string),可以通过 PrimitiveObjectInspectorFactory 的静态字段直接获取

Hive类型 writable类型 Java包装类型
tinyint writableByteObjectInspector javaByteObjectInspector
smallint writableShortObjectInspector javaShortObjectInspector
int writableIntObjectInspector javaIntObjectInspector
bigint writableLongObjectInspector javaLongObjectInspector
string writableStringObjectInspector javaStringObjectInspector
boolean writableBooleanObjectInspector javaBooleanObjectInspector
float writableFloatObjectInspector javaFloatObjectInspector
double writableDoubleObjectInspector javaDoubleObjectInspector

注意:基本类型返回值有两种:Writable类型Java包装类型

  • 在 initialize 指定的返回值类型为 Writable类型 时,在 evaluate() 中 return 的就应该是对应的 Writable实例
  • 在 initialize 指定的返回值类型为 Java包装类型 时,在 evaluate() 中 return 的就应该是对应的 Java包装类实例

Array、Map<K, V>等复杂类型,则可以通过 ObjectInspectorFactory 的静态方法获取

Hive类型 ObjectInspectorFactory的静态方法 evaluate()返回值类型
Array getStandardListObjectInspector(T t) List
Map<K, V> getStandardMapObjectInspector(K k, V v); Map<K, V>

返回值类型为 Map<String, int> 的示例:

// 3. 自定义UDF返回值类型为 Map<String, int>
return ObjectInspectorFactory.getStandardMapObjectInspector(PrimitiveObjectInspectorFactory.javaStringObjectInspector, // Key 是 StringPrimitiveObjectInspectorFactory.javaIntObjectInspector // Value 是 int
);
完整的 initialize() 函数如下:
/*** 初始化 GenericUDF,每个 GenericUDF 示例只会调用一次初始化方法** @param arguments*          自定义UDF参数的 ObjectInspector 实例* @throws UDFArgumentException*           参数个数或类型错误时,抛出该异常* @return 函数返回值类型
*/
@Override
public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {// 1. 参数个数检查,只有一个参数if (arguments.length != 1) // 函数只接受一个参数throw new UDFArgumentException("函数需要一个参数"); // 当自定义UDF参数与预期不符时,抛出异常// 2. 参数类型检查,参数类型为Stringif (arguments[0].getCategory() != ObjectInspector.Category.PRIMITIVE // 参数是Hive原始类型|| !PrimitiveObjectInspector.PrimitiveCategory.STRING.equals(((PrimitiveObjectInspector)arguments[0]).getPrimitiveCategory())) // 参数是Hive的string类型throw new UDFArgumentException("函数第一个参数为字符串"); // 当自定义UDF参数与预期不符时,抛出异常// 3. 自定义UDF返回值类型为 Map<String, int>return ObjectInspectorFactory.getStandardMapObjectInspector(PrimitiveObjectInspectorFactory.javaStringObjectInspector, // Key 是 StringPrimitiveObjectInspectorFactory.javaIntObjectInspector // Value 是 int);
}

evaluate()

核心方法,自定义UDF的实现逻辑

代码实现步骤可以分为三部分:

  1. 参数接收
  2. 自定义UDF核心逻辑
  3. 返回处理结果
第一步:参数接收

evaluate() 的参数就是 自定义UDF 的参数,

/*** Evaluate the GenericUDF with the arguments.** @param arguments*          The arguments as DeferedObject, use DeferedObject.get() to get the*          actual argument Object. The Objects can be inspected by the*          ObjectInspectors passed in the initialize call.* @return The*/
public abstract Object evaluate(DeferredObject[] arguments)throws HiveException;

通过源码注释可知,DeferedObject.get() 可以获取参数的值

/*** A Defered Object allows us to do lazy-evaluation and short-circuiting.* GenericUDF use DeferedObject to pass arguments.*/
public static interface DeferredObject {void prepare(int version) throws HiveException;Object get() throws HiveException;
};

再看看 DeferredObject 的源码,DeferedObject.get() 返回的是 Object,传入的参数不同,会是不同的Java类型,以下是Hive常用参数类型对应的Java类型

对于Hive基本类型,传入的都是 Writable类型

Hive类型 Java类型
tinyint ByteWritable
smallint ShortWritable
int IntWritable
bigint LongWritable
string Text
boolean BooleanWritable
float FloatWritable
double DoubleWritable
Array ArrayList
Map<K, V> HashMap<K, V>

参数接收示例:

// 只有一个参数:Map<String, int>// 1. 参数为null时的特殊处理
if (arguments[0] == null)return ...// 2. 接收参数
Map<Text, IntWritable> map = (Map<Text, IntWritable>)arguments[0].get();
第二步:自定义UDF核心逻辑

获取参数之后,到这里就是自由发挥了~

第三步:返回处理结果

这一步和 initialize() 的返回值一一对应

基本类型返回值有两种:Writable类型Java包装类型

  • 在 initialize 指定的返回值类型为 Writable类型 时,在 evaluate() 中 return 的就应该是对应的 Writable实例
  • 在 initialize 指定的返回值类型为 Java包装类型 时,在 evaluate() 中 return 的就应该是对应的 Java包装类实例

Hive数组和Map的返回值类型如下:

Hive类型 Java类型
Array List
Map<K, V> Map<K, V>

getDisplayString()

getDisplayString() 返回的是 explain 时展示的信息

/*** Get the String to be displayed in explain.*/
public abstract String getDisplayString(String[] children);

注意:这里不能return null,否则可能在运行时抛出空指针异常,而且这个出现这个问题还不容易排查~

ERROR [b1c82c24-bfea-4580-9a0c-ff47d7ef4dbe main] ql.Driver: FAILED: NullPointerException null
java.lang.NullPointerExceptionat java.util.regex.Matcher.getTextLength(Matcher.java:1283)...at org.apache.hadoop.util.RunJar.main(RunJar.java:136)

close()

资源关闭回调函数

不是抽象方法,可以不实现

/*** Close GenericUDF.* This is only called in runtime of MapRedTask.*/
@Override
public void close() throws IOException { }

自定义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;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.io.Text;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;public class SimpleGenericUDF extends GenericUDF {@Overridepublic ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {// 1. 参数个数检查if (arguments.length != 1) // 函数只接受一个参数throw new UDFArgumentException("函数需要一个参数"); // 当自定义UDF参数与预期不符时,抛出异常// 2. 参数类型检查if (arguments[0].getCategory() != ObjectInspector.Category.PRIMITIVE // 参数是Hive原始类型|| !PrimitiveObjectInspector.PrimitiveCategory.STRING.equals(((PrimitiveObjectInspector)arguments[0]).getPrimitiveCategory())) // 参数是Hive的string类型throw new UDFArgumentException("函数第一个参数为字符串"); // 当自定义UDF参数与预期不符时,抛出异常// 3. 自定义UDF返回值类型为 Map<String, int>return ObjectInspectorFactory.getStandardMapObjectInspector(PrimitiveObjectInspectorFactory.javaStringObjectInspector, // Key 是 StringPrimitiveObjectInspectorFactory.javaIntObjectInspector // Value 是 int);}@Overridepublic Object evaluate(DeferredObject[] arguments) throws HiveException {// 1. 参数接收if (arguments[0] == null)return new HashMap<>();String str = ((Text) arguments[0].get()).toString();// 2. 自定义UDF核心逻辑// 统计字符串中每个字符的出现次数,并将其记录在Map中Map<String, Integer> map = new HashMap<>();for (char ch : str.toCharArray()) {String key = String.valueOf(ch);Integer count = map.getOrDefault(key, 0);map.put(key, count + 1);}// 3. 返回处理结果return map;}@Overridepublic String getDisplayString(String[] children) {return "这是一个简单的测试自定义UDF~";}
}

UDF开发手册 - UDF相关推荐

  1. Hive UDF 开发手册

    文档目的 笔者在工作中有接触到 Hive UDF 的开发任务,大部分 UDF 开发并不困难,困难的往往是: 不清楚 UDF 代码的编写逻辑(UDF.UDTF.UDAF) 不清楚如何传入特定类型的参数 ...

  2. PI Function Library 应用,公共UDF开发

    PI Function Library 应用,公共UDF开发 PI7.1以前开发时每一个SWCV namespace 都需要独立开发,不能实现程序员所常用的方法共用,7.1之后Function Lib ...

  3. 6、HIVE JDBC开发、UDF、体系结构、Thrift服务器、Driver、元数据库Metastore、数据库连接模式、单/多用户模式、远程服务模式、Hive技术原理解析、优化等(整理的笔记)

    目录: 5 HIVE开发 5.1 Hive JDBC开发 5.2 Hive UDF 6 Hive的体系结构 6.2 Thrift服务器 6.3 Driver 6.4 元数据库Metastore 6.5 ...

  4. MaxCompute Studio使用心得系列6——一个工具完成整个Python UDF开发

    摘要: 2017/12/20 北京云栖大会上阿里云MaxCompute发布了最新的功能Python UDF,万众期待的功能终于支持啦,我怎么能不一试为快,今天就分享如何通过Studio进行Python ...

  5. Hive UDF开发

    Hive进行UDF开发十分简单,此处所说UDF为Temporary的function,所以需要hive版本在0.4.0以上才可以. Hive的UDF开发只需要重构UDF类的evaluate函数即可.例 ...

  6. udf开发入门(python udf、hive udf)

    开发前的声明         udf开发是在数据分析的时候如果内置的函数解析不了的情况下去做的开发,比方说你只想拆分一个字段,拼接一个字段之类的,就不要去搞udf了,这种基本的需求自带函数完全支持,具 ...

  7. 【若泽大数据实战第十九天】Hive 函数UDF开发以及永久注册udf函数

    前言: 回顾上期课程,上次课我们讲了聚合函数,多进一出, 分组函数  group by,出现在select里面的字段除了分组函数之外,其他都要出现在group by里面,分组函数的过滤必须使用hivi ...

  8. Hive 简单UDF开发

    因为业务需要,需要udf辅助开发,就仿照官网案例和参考了一些博客,自己试着开发了一个简单的时间戳转日期字符串的函数,用来简化hql代码. 总结一下开发hive udf的几个步骤: 1. 用java开发 ...

  9. Hive 函数UDF开发以及永久注册UDF函数

    explode: (把一串数据转换成多行的数据) 创建一个文本: [hadoop@ruozehadoop000 data]$ vi hive-wc.txt hello,world,welcome he ...

最新文章

  1. windows server 2012 application control policy
  2. 真的,千万不要给女朋友解释 什么是 “羊群效应”
  3. 性能测试通过几种方式造数据
  4. javaweb如何定位
  5. Docker 存储选型,这些年我们遇到的坑
  6. OpenMP变量的私有与共享
  7. 赚钱只要找到方法,就如吸空气
  8. DB2造数据存储过程
  9. UBUNTU安装,不建议最小安装,因为有很多问题
  10. 3d Max修改器中英文对照表
  11. 我做过的最好的$ 4.90
  12. 【汇正财经】短线交易行为的构成问题
  13. 一些奇怪的东西以及寄几需要注意的地方
  14. 如何理解CPU卡内部认证与外部认证
  15. 计算机毕业设计ssm+vue基本微信小程序的拼车自助服务小程序
  16. HTML显示json字符串并且进行格式化
  17. 告别交通拥堵和数据孤岛,区块链成智慧交通发展新基石
  18. 反病毒工具入门系列-楔子
  19. 【华人学者风采】周裕 哈尔滨工业大学深圳研究院
  20. JS-Web-API

热门文章

  1. 人工势场算法 Matlab版源码
  2. 数据清洗的一般方法和步骤
  3. Java类名的命名规则
  4. java多线程设计模式详解[推荐]
  5. feded计算机音乐,Romeo And Jazzie/Alan Walker《Faded Refix》[FLAC/MP3-320K]
  6. 冒泡排序图解-Java实现
  7. 【深度之眼Python基础+数据科学入门训练营】第八章 文件、异常和模块
  8. C语言_宏函数_换行符
  9. MarkDown首行缩进和换行
  10. C语言基本数据类型之整型变量