一、自定义函数
1、步骤
(1)在idea中写好的函数打包成jar包,把包放至hive中 add jar “包在Linux中的路径”
(2)创造一个函数用来运行包和传参:
create temporary function myudtf as "com.shujia.udf.MyUDTFDemo"; //后面是Java文价在包中的位置
(3)当函数去使用,传入表的数据
select myudf(word,",") from wordcount;
2、实战
(1)UDF:表示传入一行数据,在通过数据计算逻辑传出一行数据
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
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.primitive.PrimitiveObjectInspectorFactory;
/**
* 需求:
* 传入一列数据,返回该列数据中每行的字符串长度
*/
public class MyNewUDFDemo extends GenericUDF {
/**
* initialize :实例化UDF对象
*
* 参数 arguments 该参数为HIVE注册后的方法传入参数,是一个检查器对象的数组
* ObjectInspector 返回的类型是指 我们数据输出的数据类型。
*/
@Override
public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
// 如果传入参数长度不为1 那么就返回UDF长度异常,并提示异常信息
if(arguments.length != 1){
throw new UDFArgumentLengthException("传入参数长度不够...");
}
// 获取一个参数,比对传入参数的数据类型
/**
*ObjectInspector.Category.
* PRIMITIVE : hive中String、int、float、double、boolean等基础数据类型
* LIST : 数组类型
* MAP : Map类型
* STRUCT : 结构体数据类型
*/
if(!arguments[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)){
/**
*
* UDFArgumentTypeException(int argumentId, String message)
* 异常对象需要传入两个参数:
* int argumentId 表示 参数的位置 ObjectInspector中的下标
* String message 表示 异常提示信息
*/
throw new UDFArgumentTypeException(0,"参数类型不正确...");
}
// 可以通过查看GenericUDF 其他子类实现去比对查看继承关系快捷键 Ctrl + H
return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
}
/**
* evaluate方法可以去实现数据的处理逻辑
*/
@Override
public Object evaluate(DeferredObject[] arguments) throws HiveException {
// 获取第0位参数的值,注意需要通过get()获取
String columns1 = arguments[0].get().toString();
return columns1.length();
}
@Override
public String getDisplayString(String[] children) {
return "展现执行计划及流程...";
}
}
使用命令
HIVE中的命令:
* ① 添加打包好的jar 至hive lib中 add jar /usr/local/soft/myjar/HiveCode15-1.0-SNAPSHOT.jar;
* ② create temporary function getlen as "com.shujia.udf.MyNewUDFDemo";
* ③ select getlen("abcd"); 或者 show functions like "*len*";
(2)UDTF: 传入一行参数,之后通过forward传出多行数据
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
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.StructField;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.io.Text;
import java.util.ArrayList;
import java.util.List;
/**
* 需求:
* 通过wordCount 去实现一个 切分数据,并将一行数据变成多行数据
* 类似于: explode(split(word,','))
* spark,hadoop,Java变成
*spark
*hadoop
*Java
*/
public class MyUDTFDemo extends GenericUDTF {
//实例化UDTF
@Override
public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
// 获取所有字段的列表
List<? extends StructField> fieldRefs = argOIs.getAllStructFieldRefs();
// 判断传入参数的长度
if (fieldRefs.size() != 2) {
throw new UDFArgumentLengthException("传入参数长度不对...");
}
// 判断传入的数据类型
// 注意:UDTF是没有自己的类型异常类,可以通过UDF的类型异常类
for (int i = 0; i < fieldRefs.size(); i++) {
if (!fieldRefs.get(i).getFieldObjectInspector().getCategory().equals(ObjectInspector.Category.PRIMITIVE)) {
throw new UDFArgumentTypeException(i, "传入参数类型不对...");
}
}
// 输出数据的字段名称
ArrayList<String> fieldNames = new ArrayList<String>();
fieldNames.add("outWord");
// 输出数据的字段类型
ArrayList<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>();
// 增加数据的数据类型,writableStringObjectInspector表示Hadoop中的writable序列化的String -> Text
fieldOIs.add(PrimitiveObjectInspectorFactory.writableStringObjectInspector);
// 增加数据的数据类型 javaStringObjectInspector 表示 输出数据类型为Java中的String类型 具体需要跟ArrayList<Text> lineRes 中的数据类型保存一致
//fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
}
/**
* process:具体的数据处理对象
*
* forward中的处理方法:collector.collect(o); 表示通过收集器的收集方法收集一行数据,
*/
@Override
public void process(Object[] args) throws HiveException {
ArrayList<Text> lineRes = new ArrayList<>();
// 判断数据是否为null 如果不做判断,那么数据中如果有null 那么会直接报空指针异常
if (args[0] == null) {
lineRes.add(null);
forward(lineRes);
}
String line = args[0].toString();
String regex = args[1].toString();
for (String s : line.split(regex)) {
lineRes.clear();
lineRes.add(new Text(s));
// 提交每行数据
forward(lineRes);
}
}
@Override
public void close() throws HiveException {
System.out.println("Nothing to do...");
}
}
使用命令:
hive 中的创建语句:
* ① 添加打包好的jar 至hive lib中 add jar /usr/local/soft/myjar/HiveCode15-1.0-SNAPSHOT.jar;
* ② create temporary function myudtf as "com.shujia.udf.MyUDTFDemo";
* ③ select myudf(word,",") from wordcount;
UDTF:
流程: * ① 实例化UDFT,并且在实例化方法initialize中去判断传入参数的数据长度 以及 数据类型 * ② 创建输出字段名称的数组以及字段数据类型的数组 * ③ process中编辑处理的逻辑 * ④ 将处理好的数据通过forward方法将数据按行写出 * ⑤ 将项目打包至Linux中hive中创建临时函数,并使用 * * 实现过程: * 创建数据库 * create database learn4; * 创建表: * create table learn4.movie(json_str String Comment "电影JSON") STORED AS TEXTFILE; * 上传数据: * load data local inpath "/usr/local/soft/hive-3.1.2/data/UDTF.txt" into table learn4.movie; * 查看数据: * select get_json_object(json_str,"$.movie") from learn4.movie; * * 上传jar包: * add jar /usr/local/soft/myjar/HiveCode15-1.0-SNAPSHOT.jar * * 创建临时函数: * create temporary function myudtf as "com.shujia.udf.MyUDTFDemo2"; * * 测试函数: * select myudtf(get_json_object(json_str,"$.movie")) from learn4.movie;
/**
*
* 需求:
* {"movie": [{"movie_name": "肖申克的救赎", "MovieType": "犯罪" }, {"movie_name": "肖申克的救赎", "MovieType": "剧情" }]}
* 为一行数据,类型为JSON,需要从JSON中取出 movie_name MovieType 两个Key对应的Value值
* 并且数据输出的格式为:
* movie_name MovieType
* 肖申克的救赎 犯罪
* 肖申克的救赎 剧情
* ...
*/
public class MyUDTFDemo2 extends GenericUDTF {
@Override
public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
// 实例化UDFT,并且在实例化方法initialize中去判断传入参数的数据长度 以及 数据类型
List<? extends StructField> fieldRefs = argOIs.getAllStructFieldRefs();
if(fieldRefs.size() != 1){
throw new UDFArgumentLengthException("长度不是1,不符合要求");
}
if(!fieldRefs.get(0).getFieldObjectInspector().getCategory().equals(ObjectInspector.Category.PRIMITIVE)){
throw new UDFArgumentTypeException(0,"数据类型不正确");
}
// ② 创建输出字段名称的数组以及字段数据类型的数组
ArrayList<String> columNames = new ArrayList<>();
ArrayList<ObjectInspector> columType = new ArrayList<>();
columNames.add("movie_name");
columType.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
columNames.add("MovieType");
columType.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
return ObjectInspectorFactory.getStandardStructObjectInspector(columNames, columType);
}
@Override
public void process(Object[] args) throws HiveException {
// ③ process中编辑处理的逻辑
String[] outline = new String[2];
if(args[0] != null){
JSONArray jsonArray = new JSONArray(args[0].toString());
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
outline[0] = jsonObject.getString("movie_name");
outline[1] = jsonObject.getString("MovieType");
// ④ 将处理好的数据通过forward方法将数据按行写出
forward(outline);
}
}else {
outline[0] = null;
outline[1] = null;
forward(outline);
}
}
@Override
public void close() throws HiveException {
}
}