Hive反射函数的使用-程序员是怎么学UDF函数的

前言

学习udf的时候,程序员给人感觉是知道会快些,这个答案是肯定的,因为常规的视角都是udf驱动效率,但是程序员的世界是反过来的–程序员驱动udf的效率。

udf的几座大山

学习udf函数几乎需要回答下面几个问题:

  1. 我要做这个操作有什么函数
  2. 这个函数怎么使用
  3. 我咋自己去写一个函数
  4. hive里面还有哪些骚操作

源码引入

写程序肯定是需要源码的,hive是开源的,源码可以随时查阅,很多人因为编译不过就放弃,其实源码只要可以导入idea搜索就可以了,当然能调试更好,有了源码,我们就可以各种搜索,这个就是我们学习宝库!
在这里插入图片描述

函数的入口

udf其实就是一堆堆的类执行函数,一般常规这种灵活的扩展性,hive是提供了注册进制来做的,我们需要找到我们的注册类:

package org.apache.hadoop.hive.ql.exec;
 ......若干引入包
/**
 * FunctionRegistry.
 */
public final class FunctionRegistry {

  private static final Logger LOG = LoggerFactory.getLogger(FunctionRegistry.class);
  ......若干代码

我们可以看到源码部分都是很有规律的函数引入,第一个参数便是函数名,第二个参数便是实现类了,不光如此,udf注册的时候在代码上面做了一个很好的分类,比如字符操作相关的会放在一起:

   system.registerGenericUDF("concat", GenericUDFConcat.class);
   system.registerUDF("substr", UDFSubstr.class, false);
   ...... 

时间处理相关的,也会放在一起

  system.registerUDF("day", UDFDayOfMonth.class, false);
  system.registerUDF("dayofmonth", UDFDayOfMonth.class, false);
  ...... 

我们按照有规律的排布,可以很方便找到我们要的函数。

函数的实现类

在函数头部有一段描述,比较有耐心的程序员会把这段写得很详细,这段在函数注册时候我们可以通过desc 命令看得到。
在这里插入图片描述

hive> desc function concat;
Begin to execute:
desc function concat;
OK
concat(str1, str2, ... strN) - returns the concatenation of str1, str2, ... strN or concat(bin1, bin2, ... binN) - returns the concatenation of bytes in binary data  bin1, bin2, ... binN
Time taken: 0.029 seconds, Fetched: 1 row(s)

测试代码

hive这种代码都是会经过大量的测试,测试代码中我们有个很关键的东西就是里面会直接写这个函数的用法,因为测试的是sql,源码中放在了.g的文件中,我们找到(通过搜索去找):
在这里插入图片描述
关于udf的第一手使用基本来自这个地方,在这里会覆盖各种场景和用法,基本在这个地方摸索就可以了。

反射函数

反射操作其实是程序员喜欢,但是市面上听说很少的操作,我们直接从源码找到这几个函数:

system.registerGenericUDF("reflect", GenericUDFReflect.class);
system.registerGenericUDF("reflect2",GenericUDFReflect2.class;
system.registerGenericUDF("java_method",GenericUDFReflect.clas;

我们在测试代码中找到对应的使用方法:
在这里插入图片描述

reflect使用

我们捡关键的部分:

SELECT reflect("java.lang.String", "valueOf", 1),
       reflect("java.lang.String", "isEmpty"),
       reflect("java.lang.Math", "max", 2, 3),
       reflect("java.lang.Math", "min", 2, 3),
       reflect("java.lang.Math", "round", 2.5D),
       round(reflect("java.lang.Math", "exp", 1.0D), 6),
       reflect("java.lang.Math", "floor", 1.9D),
       reflect("java.lang.Integer", "valueOf", key, 16)
FROM src tablesample (1 rows)

这种操作基本就秒懂,所以这个udf瞬间学会了不是么,当然,我们还是稍加解释。
reflect其实就是通过反射的形式去调用我们java中的代码,hive是java写的,调用java代码也完全不是事,这样子操作其实意味着,我们在程序中写的函数可以通过这种方式引用,一般情况下我们jdk在lang下的包我们不会主动去引用,实际上来说我们主动写上引用也是ok的:
效果一样的代码:

   System.out.println(String.valueOf("2"));
   System.out.println(Math.max(2, 3));
 System.out.println(java.lang.String.valueOf("2"));
 System.out.println(java.lang.Math.max(2,3));

到了udf上面反射是要加全路径的:
比如一段求最大值的操作

select reflect("java.lang.Math", "max", 2, 3);
3

reflect2

这个函数做啥的,因为我们其实已经有了reflect函数,我们还是找的测试的代码:

SELECT key,
       reflect2(key,   "byteValue"),
       reflect2(key,   "shortValue"),
       .....
       reflect2(value, "concat", "_concat"),
       reflect2(value, "contains", "86"),
       reflect2(value, "startsWith", "v"),
       reflect2(value, "endsWith", "6"),
       reflect2(value, "equals", "val_86"),
       reflect2(value, "equalsIgnoreCase", "VAL_86")
       ts,
       reflect2(ts, "getYear"),
       reflect2(ts, "getMonth"),
       reflect2(ts, "getDay"),
       .....
FROM (select cast(key as int) key, value, cast('2013-02-15 19:41:20' as timestamp) ts from src) a LIMIT 5;

事实上,就算看了这个测试代码其实也不大清楚做啥的,我们找到源码上面去看看,源码在对应类:
org.apache.hadoop.hive.ql.udf.generic.GenericUDFReflect2中,从138行开始我们找到关键逻辑:

......
 switch (returnOI.getPrimitiveCategory()) {
      case VOID:
        return null;
      case BOOLEAN:
        ((BooleanWritable)returnObj).set((Boolean)result);
        return returnObj;
      case BYTE:
        ((ByteWritable)returnObj).set((Byte)result);
        return returnObj;
      case SHORT:
        ((ShortWritable)returnObj).set((Short)result);
        return returnObj;
      case INT:
        ((IntWritable)returnObj).set((Integer)result);
        return returnObj;
     ......

根据前面的函数调用情况,我们其实可以看到,这个函数其实是对应的数据类型不同可以调用到不同的方法,比如字符串操作的startwith,日期函数对应的getYear,这个函数是可以根据类型来适配的。
我们操作一把:对应日期类型提前年月日的操作:

select  Reflect2(ts, "getYear"),
       reflect2(ts, "getMonth"),
       reflect2(ts, "getDay"),
       reflect2(ts, "getHours"),
       reflect2(ts, "getMinutes"),
       reflect2(ts, "getSeconds")
	   from (select cast('2013-02-15 19:41:20' as timestamp) ts ) t; 
	   
 113     1       5       19      41      20  

这里发现了我们的2013的年结果是113,月份是1,这个我们也是要看源码去解释:

在这里插入图片描述
在这里插入图片描述
我们看到年份其实是从1900开始计算的,月份其实是从0开始算的,计算器一点,我们没问题。
在这里插入图片描述
再来一个字符串替换操作:

select     reflect2("原始值[val]", "replace", "val", "新值") 
原始值新值

java_method

反射函数最后的一个函数,我们还是按照老路子去找,我们找到测试的代码:

SELECT java_method("java.lang.String", "valueOf", 1),
       java_method("java.lang.String", "isEmpty"),
       java_method("java.lang.Math", "max", 2, 3),
       java_method("java.lang.Math", "min", 2, 3),
       java_method("java.lang.Math", "round", 2.5D),
       round(java_method("java.lang.Math", "exp", 1.0D), 6),
       java_method("java.lang.Math", "floor", 1.9D)
FROM src tablesample (1 rows);

这个看了之后发现好像和reflect也没啥差别啊,我们只能心里觉得一样,但是总每个说服力,我们继续寻寻觅觅,发现这么一句话:
在这里插入图片描述
这架势,百度有道谷歌啥的通通来一遍,我们java_method和reflect其实是同义词的概念,我摸一下注册代码,发生其实对应的实现类其实就是同一个!
在这里插入图片描述
不用看了,玩跑跑去~
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值