第十四天 - 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);
    }
    
    }

    运行效果

    1536925854179

创建表、导入数据

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目录下

1536926438358

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

1536926497986

启动后就可以通过浏览器访问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}’];

    如果指定的文件夹下已经有数据文件,则只要结构匹配就可以直接使用

  2. 导入数据即将数据导放置在创建表时指定的目录下

  3. 删除外部表,只会删除结构,文件将会保留

1536905795185

1536905839727

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

三、Hive函数操作

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

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

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

查看可用函数列表

show functions;

查看函数描述信息

desc function {functionName};

关系运算
  • 等值比较

    select num1=num2;

    1536912512566

  • 不等值比较

    select num1>num2;

    select num1

数学运算
  • 四则运算

    select 6+3;

    select 6-3;

    select 6*3;

    select 6/3;

    1536913143074

    1536915866956

  • 取余运算

    select 25%3;

    1536915934759

  • 按位运算

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

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

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

    1536916575134

    取反:select ~4;

    1536916714880

逻辑运算

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

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

1536916802496

数值运算
  • 取整函数

    1. 四舍五入法round:1~4舍,5~9进,传入两个参数时可指定精度

      select round (5.369,2);

      1536916954138

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

      select bround(5.365,2);

  • 向下取整函数

    select floor (5.9);

    1536917308714

  • 向上取整函数

    select ceil/ceiling (6.1);

    1536917772443

  • 生成随机数

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

    select rand;

    1536917857281

  • 自然指数函数

    自然指数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’);

    1536919229262

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

    1536919271563

  • JSON解析函数(重要)

    json解析地址

    get_json_object({jsonString},’$.{jsonObjectKey}’)

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

    1536921257896

  • 字符串生成函数

    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

  3. 创建函数

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

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

    1536922593743

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

    show functions;

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

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

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

    1536922668601

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

    1536914348420

其他事项
  • 删除函数

    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();
    }

猜你喜欢

转载自blog.csdn.net/cry970795248/article/details/82708249