目錄:
一柒昏、繼承GenericUDTF抽象類
二、重寫方法initialize()
三熙揍、實現(xiàn)抽象方法process()
四职祷、實現(xiàn)抽象方法close()
五、自定義將一行字符串轉(zhuǎn)多行代碼
UDTF(User-Defined Table-Generating Functions)是一進多出函數(shù)届囚,如hive中的explode()函數(shù)有梆。
在學習自定義UDTF函數(shù)時,一定要知道hive中的UDTF函數(shù)如何使用意系,不會的先看這篇文章:hive中UDTF函數(shù)explode詳解 + explode與lateral view 3套案例練習泥耀。
自定義UDF函數(shù)步驟如下:
自定義函數(shù)、實現(xiàn)UDTF一進多出功能昔字,我們主要關心的是要繼承什么類爆袍,實現(xiàn)什么方法首繁。
1)繼承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF
類
2)重寫initialize、process陨囊、close
方法
備注:我們在繼承這個類的時候弦疮,只需要關心它能實現(xiàn)什么功能、我們需要處理什么業(yè)務邏輯蜘醋。就像在使用sql函數(shù)也是這樣胁塞,我以前還糾結為什么它可以實現(xiàn)這樣的功能。
不過繼承的這個類可以實現(xiàn)什么功能压语,怎么使用一定一定要清楚啸罢、熟練掌握。
一胎食、繼承GenericUDTF抽象類
繼承GenericUDTF抽象類時扰才,我們需要重寫initialize
方法、并實現(xiàn)2個抽象方法(process厕怜、close)
.
在Alt + Enter回車時衩匣,只提示我們實現(xiàn)兩個方法抽象方法process、close
粥航。initialize方法不是抽象方法不用實現(xiàn)琅捏,但是該方法需要重寫,不然會報錯递雀。
- initialize初始化:UDTF首先會調(diào)用initialize方法柄延,此方法返回UDTF的返回行的信息(返回個數(shù),類型缀程,名稱)搜吧。initialize針對任務調(diào)一次
- process:初始化完成后,會調(diào)用process方法杠输,對傳入的參數(shù)進行處理赎败,可以通過forword()方法把結果寫出。
process
傳入一行數(shù)據(jù)寫出去多次
蠢甲,與mapreduce中的map方法很像僵刮,也是一行一行的數(shù)據(jù)傳入,傳入一行數(shù)據(jù)輸出多行數(shù)據(jù)鹦牛,如:mapreduce單詞計數(shù)
搞糕。process針對每行數(shù)據(jù)調(diào)用一次該方法
- process:初始化完成后,會調(diào)用process方法杠输,對傳入的參數(shù)進行處理赎败,可以通過forword()方法把結果寫出。
- close:最后close()方法調(diào)用,對需要清理的方法進行清理曼追,close()方法針對整個任務調(diào)一次
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方法是針對整個任務調(diào)一次,initialize作用是定義輸出字段的列名礼殊、和輸出字段的數(shù)據(jù)類型驹吮,重寫該方法時里面有一些知識點需要我們記
- 在定義輸出字段
(fieldNames)
的數(shù)據(jù)類型(ieldOIs)
時针史,該處定義的數(shù)據(jù)類型跟常用的Java數(shù)據(jù)類型不一致,需要格外去記碟狞。
比如string
數(shù)據(jù)類型這里用的是javaStringObjectInspector
;int
數(shù)據(jù)類型這里用的是javaIntObjectInspector
- 在定義輸出字段
- 輸出的字段名是一個集合啄枕,而不是一個字段。也就是炸裂這個方法可以輸出多個列族沃。如:hive中explode對數(shù)組炸裂時返回一個字段,explode對map數(shù)據(jù)炸裂時返回2個字段.
- 輸出的列數(shù)據(jù)類型也是一個集合频祝。
- 返回列字段名和列數(shù)據(jù)類型時,是返回的是一個工廠數(shù)據(jù)類型
ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs)
脆淹,記住就好了常空。
這里語言描述有問題,底層的還沒理解到盖溺,就是定義好輸出的字段和字段數(shù)據(jù)類型漓糙,然后把這兩個參數(shù)塞到getStandardStructObjectInspector
方法里面去。
- 返回列字段名和列數(shù)據(jù)類型時,是返回的是一個工廠數(shù)據(jù)類型
@Override
/**
* 返回數(shù)據(jù)類型:StructObjectInspector
* 定義輸出數(shù)據(jù)的列名咐柜、和數(shù)據(jù)類型兼蜈。
*/
public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
//fieldNames字段名,函數(shù)定義字段名拙友,關心輸入和輸出。應該為輸出的字段名
List<String> fieldNames = new ArrayList<String>();//問題歼郭?為什么函數(shù)輸出的字段名是一個集合遗契,而不是一個字段?
//也就是炸裂這個方法可以輸出多個列病曾,我們使用hive默認的explode函數(shù)炸裂的時候是炸裂一個列牍蜂,
//但是UDTF炸裂可以有多個列
fieldNames.add("world");
List<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>(); //類型,列輸出類型
fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
}
三、實現(xiàn)抽象方法process()
process方法是一行數(shù)據(jù)調(diào)用一次process方法泰涂,即有多少行數(shù)據(jù)就會調(diào)用多少次process方法鲫竞。主要作用是對傳入的每一行數(shù)據(jù)寫出去多次,調(diào)用forward()將數(shù)據(jù)寫入到一個緩沖區(qū)。
有2個點需要記妆泼伞:
- 1)在
initialize初始化
的時候从绘,定義輸出字段的數(shù)據(jù)類型是集合,我們調(diào)用forward()將數(shù)據(jù)寫入到一個緩沖區(qū)是牢,寫入緩沖區(qū)的數(shù)據(jù)也要是集合僵井。 -
2)如果只定義了一個集合,每次調(diào)用forward()寫數(shù)據(jù)之前驳棱,需要將集合中的數(shù)據(jù)給清空批什。
//數(shù)據(jù)的集合
private List<String> dataList = new ArrayList<String>();
/**
* process(Object[] objects) 參數(shù)是一個數(shù)組,但是hive中的explode函數(shù)接受的是一個社搅,一進多出
* @param args
* @throws HiveException
*/
public void process(Object[] args) throws HiveException {
//我們現(xiàn)在的需求是傳入一個數(shù)據(jù)驻债,在傳入一個分割符
//1.獲取數(shù)據(jù)
String data = args[0].toString();
//2.獲取分割符
String splitKey = args[1].toString();
//3.切分數(shù)據(jù),得到一個數(shù)組
String[] words = data.split(splitKey);
//4.想把words里面的數(shù)據(jù)全部寫出去乳规。類似在map方法中,通過context.write方法
// 定義是集合合呐、寫出去是一個string暮的,類型不匹配,寫出也要寫出一個集合
for (String word : words) {
//5.將數(shù)據(jù)放置集合合砂,EG:傳入"hello,world,hdfs"---->寫出需要寫n次青扔,hello\world
dataList.clear();//清空數(shù)據(jù)集合
dataList.add(word);
//5.寫出數(shù)據(jù)的操作
forward(dataList);
}
}
四、實現(xiàn)抽象方法close()
這里沒有io流的操作所以不需要關閉翩伪。
關于是否有IO流以及是否關閉IO流不清楚微猖。
public void close() throws HiveException {
}
五、自定義將一行字符串轉(zhuǎn)多行代碼
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;
/**
* 類兩邊有一些陰影是抽象類缘屹,繼承抽象類就要實現(xiàn)抽象方法
*/
public class SplitUDTF extends GenericUDTF {
//數(shù)據(jù)的集合
private List<String> dataList = new ArrayList<String>();
@Override
/**
* 返回數(shù)據(jù)類型:StructObjectInspector
* 定義輸出數(shù)據(jù)的列名凛剥、和數(shù)據(jù)類型。
*/
public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
//fieldNames字段名轻姿,函數(shù)定義字段名犁珠,關心輸入和輸出。應該為輸出的字段名
List<String> fieldNames = new ArrayList<String>();//問題互亮?為什么函數(shù)輸出的字段名是一個集合犁享,而不是一個字段?
//也就是炸裂這個方法可以輸出多個列豹休,我們使用hive默認的explode函數(shù)炸裂的時候是炸裂一個列炊昆,
//但是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) 參數(shù)是一個數(shù)組,但是hive中的explode函數(shù)接受的是一個威根,一進多出
* @param args
* @throws HiveException
*/
public void process(Object[] args) throws HiveException {
//我們現(xiàn)在的需求是傳入一個數(shù)據(jù)凤巨,在傳入一個分割符
//1.獲取數(shù)據(jù)
String data = args[0].toString();
//2.獲取分割符
String splitKey = args[1].toString();
//3.切分數(shù)據(jù),得到一個數(shù)組
String[] words = data.split(splitKey);
//4.想把words里面的數(shù)據(jù)全部寫出去。類似在map方法中洛搀,通過context.write方法
// 定義是集合敢茁、寫出去是一個string,類型不匹配留美,寫出也要寫出一個集合
for (String word : words) {
//5.將數(shù)據(jù)放置集合彰檬,EG:傳入"hello,world,hdfs"---->寫出需要寫n次,hello\world
dataList.clear();//清空數(shù)據(jù)集合
dataList.add(word);
//5.寫出數(shù)據(jù)的操作
forward(dataList);
}
}
public void close() throws HiveException {
}
}
后記
最后文章里面独榴,還有很多描述不清楚的地方僧叉,以及我不明白的地方,大家也可以去看看其他的文章棺榔。