第十四天 - JavaWeb结合Hive - Hive外部表 - Hive内置函数 - Hive自定义函数


  • 第十四天 - JavaWeb结合Hive - Hive外部表 - Hive内置函数 - Hive自定义函数
  • 一、JavaWeb结合Hive(二)
  • 完善(一)中的功能
  • 创建表、导入数据
  • 二、Hive外部表
  • 三、Hive函数操作
  • 关系运算
  • 数学运算
  • 逻辑运算
  • 数值运算
  • 日期函数
  • 字符串函数
  • 四、Hive自定义函数
  • 准备工作
  • 编写代码
  • 打包上传
  • 运行测试
  • 其他事项


一、JavaWeb结合Hive(二)

完善(一)中的功能
  • JavaWeb结合Hive(一)
  • 实现功能:点击预览数据能获得表内的前十条数据,点击结构信息能获得表结构信息,并且都是在当前页面展示
  • datasource.js
$(function() {
$(".showTables").click(function() {
    var databaseName = "test";
    $.ajax({
        url : "DataSourceServlet",
        type : "post",
        data : {
            databaseName : databaseName
        },
        dataType : "json",
        success : function(data) {
            var content = $(".tableList");
            for (index in data) {
                var tableName = data[index];
                content.append("<div><span>" + tableName + "</span><input class='info' type='button' value='结构信息' data-name='" + tableName + "' /><input class='data' type='button' value='预览数据' data-name='" + tableName + "' /></div>");
            }
        }
    })
})
// 对于动态添加的元素绑定事件 - on方式实现需要在jQuery的1.7版本以后,live,bind
// $(父级选择器).on(事件名称,需要绑定事件的元素-即动态添加进来的元素,方法体-触发事件后执行的内容)
// 如果动态添加的元素没有父级元素可以使用document或者body
// 此时$(this)依然代表触发事件的元素
$(document).on("click",".info",function(){
    // 通过data("xxx")方法可以获得当前元素通过data-xxx属性定义的值
    var tableName = $(this).data("name");
    // 通过触发事件元素与需要获取信息的元素之间的层级关系进行查找
    // siblings能够获取到同级元素的集合,也可以直接传入选择器作为参数,如siblings(".className"),会返回同级中匹配的元素
    //alert($(this).siblings().eq(0).html());
    $.ajax({
        url : "TableInfoServlet",
        type : "post",
        data : {
            tableName : tableName
        },
        dataType : "json",
        success:function(data){
            var content = $(".tableInfo");
            // 保证指定区域只显示当前表信息,添加信息前先清空
            content.html("");
            for(index in data){
                content.append("<div>" + data[index] + "</div>")
            }
        }
    })
})
$(document).on("click",".data",function(){
    var tableName = $(this).data("name");
    $.ajax({
        url : "TableDataServlet",
        type : "post",
        data : {
            tableName : tableName
        },
        dataType : "json",
        success:function(data){
            var content = $(".tableData");
            // 保证指定区域只显示当前表信息,添加信息前先清空
            content.html("");
            for(index in data){
                content.append("<div>" + data[index] + "</div>")
            }
        }
    })
})
})

TableInfoServlet.java

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.sand.util.HiveUtil;

import net.sf.json.JSONArray;

/**
* Servlet implementation class TableInfoServlet
*/
@WebServlet("/TableInfoServlet")
public class TableInfoServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

  /**
   * @see HttpServlet#HttpServlet()
   */
  public TableInfoServlet() {
      super();
      // TODO Auto-generated constructor stub
  }

/**
 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
 */
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setCharacterEncoding("UTF-8");
    PrintWriter out = response.getWriter();
    String tableName = request.getParameter("tableName");
    HiveUtil hiveUtil = new HiveUtil();
    hiveUtil.changeDatabase("test");
    // 当需要获取表信息时,如果只传入表名称会在当前数据库中搜索该表,需要先切换数据库
    // 如果传入库名.表名 -> 在指定的数据库下进行搜索
    List<String> list = hiveUtil.getTableInfo(tableName);
    out.print(JSONArray.fromObject(list).toString());
    out.close();
}

/**
 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
 */
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doGet(request, response);
}

}

TableDataServlet.java

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.sand.util.HiveUtil;

import net.sf.json.JSONArray;

/**
* Servlet implementation class TableDataServlet
*/
@WebServlet("/TableDataServlet")
public class TableDataServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

  /**
   * @see HttpServlet#HttpServlet()
   */
  public TableDataServlet() {
      super();
      // TODO Auto-generated constructor stub
  }

/**
 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
 */
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setCharacterEncoding("UTF-8");
    PrintWriter out = response.getWriter();
    String tableName = request.getParameter("tableName");
    HiveUtil hiveUtil = new HiveUtil();
    hiveUtil.changeDatabase("test");
    // 当需要获取表信息时,如果只传入表名称会在当前数据库中搜索该表,需要先切换数据库
    // 如果传入库名.表名 -> 在指定的数据库下进行搜索
    List<String> list = hiveUtil.getTableData(tableName);
    out.print(JSONArray.fromObject(list).toString());
    out.close();
}

/**
 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
 */
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
    doGet(request, response);
}

}

运行效果

hivesql把字符串20240506转换为日期 hive sql字符串转日期格式_hive内置函数

创建表、导入数据

load.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="js/load.js"></script>
</head>
<body>
    <form action="LoadDataServlet" method="post"
        enctype="multipart/form-data">
        <span>数据表名称:</span><input type="text" name="tableName" /><br />
        <label><input type="checkbox" name="auto" value="auto" />从首行中读取字段信息</label><br />
        <label><input type="checkbox" name="overwrite" value="overwrite" />是否覆盖导入</label><br />
        <span>列分隔符:</span><input type="text" name="format" /><br />
        <input type="button" value="添加" class="add" />
        <div class="columnInfo"></div>
        <input type="file" name="data" /> <input type="submit" value="上传" />
    </form>

    <div class="column" style="display: none">
        <span class="columnNameLabel">列名:</span><input type="text" name="columnName" /> <span>数据类型:</span>
        <select name="columnType">
            <option value="int">int</option>
            <option value="string">String</option>
        </select> <span class="delete">-</span>
    </div>
</body>
</html>

load.js

$(function(){
    $(".add").click(function(){
        var column = $(".column").html();
        $(".columnInfo").append("<div>" + column + "<br /><br /></div>");
    })
    $(document).on("click",".delete",function(){
        $(this).parent().remove();
    })
    $("input[name='auto']").click(function(){
        if($(this).attr("checked") == "checked"){
            $(".columnNameLabel").hide();
            $("input[name='columnName']").hide();
        }else{
            $(".columnNameLabel").show();
            $("input[name='columnName']").show();
        }
    })
    $("input[type='checkbox']").removeAttr("checked");
})

LoadDataServlet.java

此Servlet功能仅实现了拼接字符串,真正执行的代码待完善

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

/**
 * Servlet implementation class LoadDataServlet
 */
@WebServlet("/LoadDataServlet")
@MultipartConfig
public class LoadDataServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public LoadDataServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 设置编码
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/plain; charset=UTF-8");
        // 接收参数
        String tableName = request.getParameter("tableName");
        boolean isAuto = request.getParameter("auto") == null ? false : true;
        boolean isOverwrite = request.getParameter("overwrite") == null ? false : true;
        String format = request.getParameter("format");
        if (!isAuto) {
            String[] columnNames = request.getParameterValues("columnName");
        }
        String[] columnTypes = request.getParameterValues("columnType");
        for (String columnType : columnTypes) {
            System.out.println(columnType);
        }
        // 使用Part对象接收文件
        Part part = request.getPart("data");
        // 取出文件名(如果需要)
        String path = "/tmp";
        // 可以使用自定义的名称,也可以使用UUID
        String fileName = UUID.randomUUID().toString();
        // 从登陆信息中获取当前用户的唯一标识
        String userId = "1";
        String filePath = path + File.separator + fileName;
        // 使用write方法向路径中写入文件
        part.write(filePath);
        Reader reader = new FileReader(new File(filePath));
        BufferedReader bf = new BufferedReader(reader);
        String tableInfo = bf.readLine();
        String createTable = "create table " + tableName + "(";
        for(int i = 0;i < columnTypes.length;i ++) {
            createTable += tableInfo.split(format)[i] + " " + columnTypes[i] + ",";
        }
        createTable = createTable.substring(0,createTable.length() - 1);
        createTable += ") row format delimited fields terminated by '" + format + "' tblproperties(\"skip.header.line.count\"=\"1\")";
        bf.close();
        reader.close();
        System.out.println(createTable);
        String loadToHive = "load data local inpath '"+ filePath +"' " + (isOverwrite ? "overwrite " : "") +"into table " + tableName;
        System.out.println(loadToHive);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

将项目打包成war文件,并且用Xftp上传至CentOS中apache安装目录下的webapps目录下

hivesql把字符串20240506转换为日期 hive sql字符串转日期格式_hive自定义函数_02

su root切换至root用户,执行./startup.sh启动apache服务,启动后会自动解压war包并部署项目

hivesql把字符串20240506转换为日期 hive sql字符串转日期格式_hive内置函数_03

启动后就可以通过浏览器访问http://sz01/WebProject/load.jsp

待完善

二、Hive外部表

内部表和外部表区别:内部表即完全交给hive管理表,在创建时会将数据移动到数据仓库所
在的路径,删除时会删除数据源文件。外部表即增加hive管理的数据文件,创建时需要记录
数据所在的路径,不会移动数据源文件,删除时不会删除数据源文件

  1. 创建外部表
    创建时需要用location关键字指定数据所在的路径

create table {tableName}(

{columnName} {columnType},

{columnName} {columnType},

)[row format delimited fields terminated by ‘\t’]

[location ‘{HDFS_path}’];

  1. 如果指定的文件夹下已经有数据文件,则只要结构匹配就可以直接使用
  2. 导入数据即将数据导放置在创建表时指定的目录下
  3. 删除外部表,只会删除结构,文件将会保留

hivesql把字符串20240506转换为日期 hive sql字符串转日期格式_hive外部表_04

hivesql把字符串20240506转换为日期 hive sql字符串转日期格式_hive内置函数_05

注:在mysql创建的hive库中有两张数据元表,其中SDS用于记录表结构对应的路径信息,TBLS用于记录表的基本信息(所属的数据库,数据表类型)

三、Hive函数操作

函数的执行,通过select关键字进行调用;函数之间可以相互嵌套使用,只需要一个select关键字。

根据函数的作用,传入的参数可以是某个固定的值,也可以指表中某个列的字段名称;

传入单个数据时,返回单个的结果,和表产生关联时,返回的是逐条数据调用后返回的结果。

查看可用函数列表

show functions;

查看函数描述信息

desc function {functionName};

关系运算
  • 等值比较

select num1=num2;

  • 不等值比较

select num1>num2;

select num1

数学运算
  • 四则运算

select 6+3;

select 6-3;

select 6*3;

select 6/3;


hivesql把字符串20240506转换为日期 hive sql字符串转日期格式_Hive_06

  • 取余运算

select 25%3;

  • 按位运算

与:select 6&9; 0110&1001=0000

或:select 6|9; 0110|1001=1111

异或:select 6^9; 0110^1001=1111

取反:select ~4;

hivesql把字符串20240506转换为日期 hive sql字符串转日期格式_Hive_07

逻辑运算

如果结果为真,则返回TRUE,否则返回FALSE

逻辑与(AND)、逻辑或(OR)、逻辑非(NOT)

hivesql把字符串20240506转换为日期 hive sql字符串转日期格式_hive外部表_08

数值运算
  • 取整函数
  1. 四舍五入法round:1~4舍,5~9进,传入两个参数时可指定精度

select round (5.369,2);

  1. 银行家舍入法bround:1~4舍,6~9进,5->前一位是偶数则舍,前一位是奇数则进,传入两个参数时可指定精度

select bround(5.365,2);

  • 向下取整函数

select floor (5.9);

  • 向上取整函数

select ceil/ceiling (6.1);

  • 生成随机数
    rand ([{seed}]):返回一个0到1范围内的随机数,传入参数时可生成稳定的随机数

select rand;

  • 自然指数函数
    自然指数e的n次方:exp ({n})
  • 对数函数
  1. 以10为底的对数函数:log10 ({value})
  2. 以2为底的对数函数:log2 ({value})
  3. 以e为底的对数函数:ln ({value})
  4. 对数函数:log ({base},{value})
  • 幂函数
    pow/power ({base},{exponent})
  • 平方根函数
    sqrt ({value})
  • 立方根函数
    cbrt ({value})
  • 进制函数
  1. 转二进制函数:bin ({value})
  2. 转十六进制函数:hex ({value})
  3. 反转十六进制函数:unhex ({value})
  4. 进制转换函数:conv ({value},{fromBase},{toBase})
  • 绝对值函数
    abs ({value})
日期函数
  • 获取日期函数
    unix_timestamp()
  • 时间戳转换函数,在进行日期转换时,可以自定义日期格式
    UNIX时间戳转日期:from_unixtime ({unixTime}[,{formatString}])
    日期转UNIX时间戳:unix_timestamp ({timeString}[,{formatString}])
  • 日期截取函数,使用日期截取函数时,必须针对字符串日期操作
  1. 返回日期部分:to_date ({timeString})
  2. 返回日期的年:year ({timeString})
  3. 返回日期的月:month ({timeString})
  4. 返回日期的天:day ({timeString})
  5. 返回日期的时:hour ({timeString})
  6. 返回日期的分:minute ({timeString})
  7. 返回日期的秒:second ({timeString})
  8. 返回日期的周:weekofyear ({timeString})
  • 日期计算函数
  1. 日期比较函数:datediff ({endDate},{startDate})
  2. 日期增加函数:date_add ({startData},{days})
  3. 日期减少函数:date_sub ({startData},{days})
字符串函数
  • 字符串长度函数
    length ({stringValue})
  • 字符串翻转函数
    reverse ({stringValue})
  • 字符串连接函数
  1. 无分隔符连接函数:concat ({stringValues})
  2. 分隔符连接函数:concat_ws ({separator},{stringValues})
  • 字符串截取函数
  1. substr/substring ({stringValue},{index}):当index为正数时,截取从index至结尾的字符串,当index为负数时,截取后index个字符,index的值不能超过字符串长度
  2. substr/substring ({stringValue},{index},{length}):截取从index开始,长度为length的字符,index为正数时,索引从左边开始,index为负数时,索引从右边开始
  • 大小写转换函数
  1. 转大写函数:upper/ucase ({stringValue})
  2. 转小写函数:lower/lcase ({stringValue})
  • 去空格函数
  1. 两边去空格函数:trim ({stringValue})
  2. 左边去空格函数:ltrim ({stringValue})
  3. 右边去空格函数:rtrim ({stringValue})
  • 正则表达式函数
    正则替换函数:regexp_replace ({stringValue},{regexpString},{replaceValue})
    正则解析函数:regexp_extract ({stringValue},{regexpString},{index})
  • URL解析函数(重要)
    parse_url ({stringValue},’{extractPart}’)[,’{extractKey}’])

HOST:获取主机名(域名)

PATH:获取访问路径

QUERY:参数解析,需要配合extractKey一起使用

REF:获取锚点信息

PROTOCOL:获取网络协议

FILE:获取路径及参数信息

select parse_url (‘https://www.baidu.com/s?keyword=111‘,’HOST’);

hivesql把字符串20240506转换为日期 hive sql字符串转日期格式_hive自定义函数_09

select parse_url (‘https://www.baidu.com/s?keyword=111‘,’QUERY’,’keyword’);

hivesql把字符串20240506转换为日期 hive sql字符串转日期格式_hive内置函数_10

  • JSON解析函数(重要)
    json解析地址get_json_object({jsonString},’$.{jsonObjectKey}’)

select get_json_object(‘{“name”:”cry”,”age”:20}’,’$.name’);

  • 字符串生成函数
  1. 空格字符串函数:space (n)
  2. 重复字符串函数:repeat ({stringValue},n)
  • 首字符ASCII码函数
    ascii ({stringValue})
  • 字符串补足函数
    将原字符串用指定的追加字符串补足为指定长度的字符串
  1. 左补足函数:lpad ({stringValue},{length},{appendValue})
  2. 右补足函数:rpad ({stringValue},{length},{appendValue})
  • 字符串分割函数
    split ({stringValue},{splitValue})

四、Hive自定义函数

准备工作
  • 在Eclipse中新建普通java项目
  • 新建文件夹lib,将hive-exec.1.2.2.jar(jar包在hive安装包的lib目录下)复制到lib中,并且添加至构建路径
  • 由于编写了一个自定义解析JSON对象类,所以需要将json依赖的两个jar包导入到hive安装路径的lib目录中

ezmorph-1.0.6.jar

json-lib-2.2.2-jdk15.jar

编写代码
  • 获取子字符串类SubString.java,需要继承UDF类,并且需要创建方法evaluate,可以自定义返回值类型和参数列表,但是方法名必须是evaluate
import org.apache.hadoop.hive.ql.exec.UDF;

public class SubString extends UDF{
public String evaluate(String str, int start, int end) {
    return str.substring(start, end);
}
}
  • 自定义解析JSON对象类JsonParse.java
import org.apache.hadoop.hive.ql.exec.UDF;

import net.sf.json.JSONObject;

public class JsonParse extends UDF{
public static String evaluate(String jsonStr, String key) { 
    return JSONObject.fromObject(jsonStr).get(key).toString();
}
}
打包上传

右键项目选择导出,类型为jar file,只需要打包src目录下的内容即可,命名udf.jar

使用Xftp将udf.jar上传至CentOS

运行测试

注意:自定义函数加载仅对当前会话有效

  1. 启动hive客户端
  2. 加载jar包,将当前jar包添加至构建路径(类的搜索加载路径)中

add jar /home/bigdata/udf.jar

  1. 创建函数

create temporary function sub as ‘com.cry.udf.SubString’;

create temporary function jsonParse as ‘com.cry.udf.JsonParse’;

  1. 可以通过以下命令查看函数是否创建成功

show functions;

  1. 根据定义的函数名使用select执行,并传入正确的参数

select sub(‘abcdefg’,0,7);

select sub(‘abcdefg’,1,6);

select jsonParse(‘{\”name\”:\”cry\”,\”age\”:20}’,’name’);

hivesql把字符串20240506转换为日期 hive sql字符串转日期格式_java_11

其他事项
  • 删除函数

drop temporary function {functionName};

  • 配置文件加载
    在hive-site.xml中配置指定目录,可以省略导入jar包操作,需要将jar包放在指定目录下
<property>
    <name>hive.aux.jars.path</name>
    <value>$HIVE_HOME/auxlib</value>
</property>
  • 初始化文件加载
    启动hive时指定初始化文件,在文件中添加jar包,创建函数

vi init-hive

add jar /home/bigdata/udf.jar;
create temporary function sub as 'com.cry.udf.SubString';
create temporary function jsonParse as 'com.cry.udf.JsonParse';
  • JDBC操作Hive时可以在初始化工具类时加上初始化方法,即可自动加载jar包,创建函数
public void init() {
    try {
        statement.execute("add jar /home/bigdata/udf.jar");
        statement.execute("create temporary function sub as 'com.sand.udf.SubString'");
        statement.execute("create temporary function jsonParse as 'com.sand.udf.JsonParse'");
    } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

将以上函数添加至构造函数中

public HiveUtil() {
    open();
    init();
}