hive自定義UDTF函數,步驟講解

目錄:
一、繼承GenericUDTF抽象類
二、重寫方法initialize()
三、實現抽象方法process()
四、實現抽象方法close()
五、自定義將一行字符串轉多行代碼

UDTF(User-Defined Table-Generating Functions)是一進多出函數,如hive中的explode()函數。
在學習自定義UDTF函數時,一定要知道hive中的UDTF函數如何使用,不會的先看這篇文章:hive中UDTF函數explode詳解 + explode與lateral view 3套案例練習

自定義UDF函數步驟如下:

自定義函數、實現UDTF一進多出功能,我們主要關心的是要繼承什麼類,實現什麼方法。
1)繼承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF
2)重寫initialize、process、close方法

備註:我們在繼承這個類的時候,只需要關心它能實現什麼功能、我們需要處理什麼業務邏輯。就像在使用sql函數也是這樣,我以前還糾結爲什麼它可以實現這樣的功能。
不過繼承的這個類可以實現什麼功能,怎麼使用一定一定要清楚、熟練掌握。

一、繼承GenericUDTF抽象類

繼承GenericUDTF抽象類時,我們需要重寫initialize方法、並實現2個抽象方法(process、close).
在Alt + Enter回車時,只提示我們實現兩個方法抽象方法process、closeinitialize方法不是抽象方法不用實現,但是該方法需要重寫,不然會報錯。

    1. initialize初始化:UDTF首先會調用initialize方法,此方法返回UDTF的返回行的信息(返回個數,類型,名稱)。initialize針對任務調一次
    1. process:初始化完成後,會調用process方法,對傳入的參數進行處理,可以通過forword()方法把結果寫出。
      process傳入一行數據寫出去多次,與mapreduce中的map方法很像,也是一行一行的數據傳入,傳入一行數據輸出多行數據,如:mapreduce單詞計數process針對每行數據調用一次該方法
    1. close:最後close()方法調用,對需要清理的方法進行清理,close()方法針對整個任務調一次
public abstract class GenericUDTF {
…
…
    /** @deprecated */
    @Deprecated
    public StructObjectInspector initialize(ObjectInspector[] argOIs) throws UDFArgumentException {
        throw new IllegalStateException("Should not be called directly");
    }

    public abstract void process(Object[] var1) throws HiveException;

    public abstract void close() throws HiveException;
…
…
}

二、重寫方法initialize()

initialize方法是針對整個任務調一次,initialize作用是定義輸出字段的列名、和輸出字段的數據類型,重寫該方法時裏面有一些知識點需要我們記

    1. 在定義輸出字段(fieldNames)數據類型(ieldOIs)時,該處定義的數據類型跟常用的Java數據類型不一致,需要格外去記。
      比如string數據類型這裏用的是javaStringObjectInspector;int 數據類型這裏用的是javaIntObjectInspector
    1. 輸出的字段名是一個集合,而不是一個字段。也就是炸裂這個方法可以輸出多個列。如:hive中explode對數組炸裂時返回一個字段,explode對map數據炸裂時返回2個字段.
    1. 輸出的列數據類型也是一個集合。
    1. 返回列字段名和列數據類型時,是返回的是一個工廠數據類型ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs),記住就好了。
      這裏語言描述有問題,底層的還沒理解到,就是定義好輸出的字段和字段數據類型,然後把這兩個參數塞到getStandardStructObjectInspector方法裏面去。
 @Override
    /**
     * 返回數據類型:StructObjectInspector
     * 定義輸出數據的列名、和數據類型。
     */
    public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
        //fieldNames字段名,函數定義字段名,關心輸入和輸出。應該爲輸出的字段名

        List<String> fieldNames = new ArrayList<String>();//問題?爲什麼函數輸出的字段名是一個集合,而不是一個字段?
        //也就是炸裂這個方法可以輸出多個列,我們使用hive默認的explode函數炸裂的時候是炸裂一個列,
        //但是UDTF炸裂可以有多個列
        fieldNames.add("world");

        List<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>(); //類型,列輸出類型
        fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);

        return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
    }

三、實現抽象方法process()

process方法是一行數據調用一次process方法,即有多少行數據就會調用多少次process方法。主要作用是對傳入的每一行數據寫出去多次,調用forward()將數據寫入到一個緩衝區。

有2個點需要記住:

  • 1)在initialize初始化的時候,定義輸出字段的數據類型是集合,我們調用forward()將數據寫入到一個緩衝區,寫入緩衝區的數據也要是集合
  • 2)如果只定義了一個集合,每次調用forward()寫數據之前,需要將集合中的數據給清空。


    //數據的集合
    private List<String> dataList = new ArrayList<String>();

 /**
     * process(Object[] objects) 參數是一個數組,但是hive中的explode函數接受的是一個,一進多出
     * @param args
     * @throws HiveException
     */
    public void process(Object[] args) throws HiveException {
        //我們現在的需求是傳入一個數據,在傳入一個分割符

        //1.獲取數據
        String data = args[0].toString();

        //2.獲取分割符
        String splitKey = args[1].toString();

        //3.切分數據,得到一個數組
        String[] words = data.split(splitKey);

        //4.想把words裏面的數據全部寫出去。類似在map方法中,通過context.write方法
        // 定義是集合、寫出去是一個string,類型不匹配,寫出也要寫出一個集合
        for (String word : words) {
            //5.將數據放置集合,EG:傳入"hello,world,hdfs"---->寫出需要寫n次,hello\world
            dataList.clear();//清空數據集合

            dataList.add(word);

            //5.寫出數據的操作
            forward(dataList);
        }
    }

四、實現抽象方法close()

這裏沒有io流的操作所以不需要關閉。

關於是否有IO流以及是否關閉IO流不清楚。

    public void close() throws HiveException {

    }

五、自定義將一行字符串轉多行代碼

package com.atguigu.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 SplitUDTF extends GenericUDTF {
    //數據的集合
    private List<String> dataList = new ArrayList<String>();

    @Override
    /**
     * 返回數據類型:StructObjectInspector
     * 定義輸出數據的列名、和數據類型。
     */
    public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
        //fieldNames字段名,函數定義字段名,關心輸入和輸出。應該爲輸出的字段名

        List<String> fieldNames = new ArrayList<String>();//問題?爲什麼函數輸出的字段名是一個集合,而不是一個字段?
        //也就是炸裂這個方法可以輸出多個列,我們使用hive默認的explode函數炸裂的時候是炸裂一個列,
        //但是UDTF炸裂可以有多個列
        fieldNames.add("world");

        List<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>(); //類型,列輸出類型
        fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
//        fieldOIs.add(PrimitiveObjectInspectorFactory.javaIntObjectInspector);

        return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
    }

    /**
     * process(Object[] objects) 參數是一個數組,但是hive中的explode函數接受的是一個,一進多出
     * @param args
     * @throws HiveException
     */
    public void process(Object[] args) throws HiveException {
        //我們現在的需求是傳入一個數據,在傳入一個分割符

        //1.獲取數據
        String data = args[0].toString();

        //2.獲取分割符
        String splitKey = args[1].toString();

        //3.切分數據,得到一個數組
        String[] words = data.split(splitKey);

        //4.想把words裏面的數據全部寫出去。類似在map方法中,通過context.write方法
        // 定義是集合、寫出去是一個string,類型不匹配,寫出也要寫出一個集合
        for (String word : words) {
            //5.將數據放置集合,EG:傳入"hello,world,hdfs"---->寫出需要寫n次,hello\world
            dataList.clear();//清空數據集合

            dataList.add(word);

            //5.寫出數據的操作
            forward(dataList);
        }
    }

    public void close() throws HiveException {

    }
}

後記

最後文章裏面,還有很多描述不清楚的地方,以及我不明白的地方,大家也可以去看看其他的文章。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章