暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

hive自定义函数UDF开发

大数据架构之道 2021-05-03
917

目         录

1、背景描述

2、环境信息

3、新建java项目

4、编写java类

5、导出jar包

6、注册jar包

7、使用自定义函数

8、删除自定义函数


1、背景描述

Apache Hive用户自定义函数(UDF)有两个不同的接口:

  • org.apache.hadoop.hive.ql.exec.UDF
  • org.apache.hadoop.hive.ql.udf.generic.GenericUDF

前者简单,用于函数读取和返回都是基础数据类型,后者复杂,用于操作嵌套数据结构,比如Map,List,Set等。下面将用简单API介绍一个自定义函数从java开发到部署到集群中使用的流程。


2、环境信息

本文在编写时,hive采用的是华为大数据平台FusionInsight HD 6.5(现在已经改名为MRS),不同大数据平台涉及到的差异主要是用户认证,权限和一些bug的修复,其他大体一致。

UDF开发语言采用java,jdk1.8。


3、新建java项目

创建一个名字随便取,符合规范的Project。

添加以下两个jar包,可以在下载的客户端中找到:FusionInsight_Services_ClientConfig\FusionInsight_Services_ClientConfig\Hive\jdbc

  • hive-exec-1.3.0.java

  • hadoop-common-2.7.2.jar

配置build path


4、编写java类


简单API只需要编写一个 java 类,继承 UDF,并重载 evaluate 方法即可
,代码如下:
    package com.xxx.xxx;


    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.Date;


    import org.apache.hadoop.hive.ql.exec.UDF;


    /**
    *
    * @author 作 者:chenyunliang
     * @note 实现功能: 
     * @note 此类实现检查身份证和转换身份证功能
     */
    public class ValidateIDCardUDF extends UDF {
    // 中国公民身份证号码最小长度
    public static final int CHINA_ID_MIN_LENGTH = 15;


    // 中国公民身份证号码最大长度
    public static final int CHINA_ID_MAX_LENGTH = 18;


    // 中国公民身份证号码最大长度
    public static final int CHINA_ID_TWHKMK_LENGTH = 10;


    // 每位加权因子
    public static final int power[] = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };


    /**
    *
    * @author 作 者:chenyunliang
    * @note 实现功能:
    * @note 此方法实现测试功能
    */
    public static void main(String[] args) {
    ValidateIDCardUDF validateIDCardUDF = new ValidateIDCardUDF();
    System.out.println(validateIDCardUDF.evaluate("43061119850113118X"));
    // 370725881105149, 37072519881105149x
    }


    /**
    *
    * @author 作 者:chenyunliang
    * @note 实现功能:
    * @note 此方法实现将15位身份证号码转换为18位功能
    */
    public String evaluate(String cardID) {
    String cardID_18 = "";


    // 检查输入参数,必须是15位
    if (cardID.trim().length() == CHINA_ID_MAX_LENGTH) {
    return cardID.trim();
    } else if (cardID.trim().length() != CHINA_ID_MIN_LENGTH || !isNum(cardID)) {
    return null;
    }


    // 获取出生年月日
    String birthDayString = cardID.substring(6, 12);
    Date birthDate = null;
    try {
    birthDate = (Date) new SimpleDateFormat("yyMMdd").parse(birthDayString);
    } catch (Exception e) {
    e.printStackTrace();
    }


    // 通过日历来获取年份等
    Calendar calendar = Calendar.getInstance();
    if (null != birthDate) {
    calendar.setTime(birthDate);
    }


    String yearString = String.valueOf(calendar.get(Calendar.YEAR));
    cardID_18 = cardID.substring(0, 6) + yearString + cardID.substring(8);


    // 转换字符数组
    char[] cardID_18_chars = cardID_18.toCharArray();
    if (null != cardID_18_chars) {
    int[] iCard = convertCharToInt(cardID_18_chars);
    int iSum17 = getPowerSum(iCard);


    // 获取校验位
    String checksumString = getCheckCode18(iSum17);
    if (checksumString.length() > 0) {
    cardID_18 += checksumString;
    } else {
    return null;
    }
    }


    return cardID_18;


    }


    /**
    *
    * @author 作 者:chenyunliang
    * @note 实现功能:
    * @note 此方法实现将power和值与11取模获得余数进行校验码判断功能
    */
    private String getCheckCode18(int iSum) {
    String sCode = "";
    switch (iSum % 11) {
    case 10:
    sCode = "2";
    break;
    case 9:
    sCode = "3";
    break;
    case 8:
    sCode = "4";
    break;
    case 7:
    sCode = "5";
    break;
    case 6:
    sCode = "6";
    break;
    case 5:
    sCode = "7";
    break;
    case 4:
    sCode = "8";
    break;
    case 3:
    sCode = "9";
    break;
    case 2:
    sCode = "x";
    break;
    case 1:
    sCode = "0";
    break;
    case 0:
    sCode = "1";
    break;
    }
    return sCode;
    }


    /**
    *
    * @author 作 者:chenyunliang
    * @note 实现功能:
    * @note 此方法实现 将身份证的每位和对应位的加权因子相乘之后,再得到和值 功能
    */
    private int getPowerSum(int[] iArr) {
    int iSum = 0;
    if (power.length == iArr.length) {
    for (int i = 0; i < iArr.length; i++) {
    for (int j = 0; j < power.length; j++) {
    if (i == j) {
    iSum = iSum + iArr[i] * power[j];
    }
    }
    }
    }
    return iSum;
    }


    /**
    *
    * @author 作 者:chenyunliang
    * @note 实现功能:
    * @note 此方法实现 将字符数组转换成数字数组 功能
    */
    private int[] convertCharToInt(char[] ca) {
    int len = ca.length;
    int[] iArr = new int[len];
    try {
    for (int i = 0; i < len; i++) {
    iArr[i] = Integer.parseInt(String.valueOf(ca[i]));
    }
    } catch (NumberFormatException e) {
    e.printStackTrace();
    }
    return iArr;
    }


    /**
    *
    *
    * @author 作 者:chenyunliang
    * @note 实现功能:
    * @note 此方法实现检查是否是数字功能
    */
    private boolean isNum(String val) {
    return val == null || "".equals(val) ? false : val.matches("^[0-9]*{1}");
    }
    }


    5、导出jar包
    导出时只需要勾选代码即可。
    上传到linux客户端服务器指定目录:

    6、注册jar包

    登陆认证,参考不同大数据平台的各自认证方法,如果是kerberos认证就使用 kinit xxxx。

    创建hdfs目录:
      hdfs dfs -mkdir /user/hive/udflib
      将jar包上传到hdfs目录,前提是需要有hdfs的读写权限:
        hdfs dfs -put hive-udf.jar  /user/hive/udflib
        注册jar包:
          add jar hdfs:///user/hive/udflib/hive-udf.jar;
          create function dcf.validateidcardudf as 'com.audaque.gldm.ValidateIDCardUDF' using jar 'hdfs:///user/hive/udflib/hive-udf.jar';


          7、使用自定义函数

          在beeline中通过下面语句使用:

            select dcf.validateIDCardUDF('370725881105149');
            注意:当前session会立即生效,全局生效需要等待5-10分钟,需要将jar包同步到其他节点。

            8、删除自定义函数

            当需要替换jar包,或者不需要函数时,可以删除函数并清理jar包

            删除函数:
              drop function if exists dcf.validateIDCardUDF;
              找到需要删除的jar包:
                list jars;
                删除jar包
                  delete jar /opt/huawei/Bigdata/tmp/hivelocaltmp/session_resources/c03ed7ff-1b7b-43e3-b9cc-e76f45b7d397_resources/hive-udf.jar;

                  文章转载自大数据架构之道,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                  评论