hbase java api 的使用

[TOC]

1 hbase java api的使用

hbase的java api在windows中使用,第一次连接时,会有点慢,大概需要二十秒左右,连接上去就很快了,在linux没有这个问题

1.1 初始化连接

private Connection connection;
private HTable table;
HBaseAdmin admin;
 
@Before
public void init() throws IOException {
    Configuration configuration = HBaseConfiguration.create();
    //设置zookeeper的地址,可以有多个,以逗号分隔
    configuration.set("hbase.zookeeper.quorum","server1,server2");
    //设置zookeeper的端口
    configuration.set("hbase.zookeeper.property.clientPort","2181");
    //创建hbase的连接,这是一个分布式连接
    connection = ConnectionFactory.createConnection(configuration);
    //获取hbase中的表
    table = (HTable) connection.getTable(TableName.valueOf("user"));
 
    //这个admin是管理table时使用的,比如说创建表
    admin = (HBaseAdmin) connection.getAdmin();
}

1.2 创建表

创建表只需要指定列族名就行了,不需要指定列名,列名可以动态添加

    /**
     * 创建表,创建表只需要指定列族,不需要指定列
     * 其实用命令真的会更快,create 'user','info1','info2'
     */
    @Test
    public void createTable() throws IOException {
        //声明一个表名
        TableName tableName = TableName.valueOf("user");
        //构造一个表的描述
        HTableDescriptor desc = new HTableDescriptor(tableName);
        //创建列族
        HColumnDescriptor family1 = new HColumnDescriptor("info1");
        HColumnDescriptor family2 = new HColumnDescriptor("info2");
        //添加列族
        desc.addFamily(family1);
        desc.addFamily(family2);
        //创建表
        admin.createTable(desc);
    }

1.3 插入数据(修改数据)

这里之所以还带了修改数据,是因为对于同一个key的指定列族中的列重新插入数据,就是修改数据

    /**
     * 添加数据
     * 对同一个row key进行重新put同一个cell就是修改数据
     */
    @Test
    public void insertUpdate() throws IOException {
        //构造参数是row key,必传
        Put put = new Put(Bytes.toBytes("zhangsan_123" + i));
        //put.add()已经被弃用了
        //这里的参数依次为,列族名,列名,值
        put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("name"),Bytes.toBytes("lisi" + i));
        put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("age"),Bytes.toBytes(22 + i));
        put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("sex"),Bytes.toBytes("男"));
        put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("address"),Bytes.toBytes("天堂" + i));
        table.put(put);
        //table.put(List<Put>); //通过一个List集合,可以添加一个集合
 
    }

1.4 删除数据

删除数据使用Delete对象就行了,我们可以删除一行,或者删除一个列族,或者删除一个列族中的指定列

    /**
     * 删除数据
     */
    @Test
    public void delete() throws IOException {
        Delete deleteRow = new Delete(Bytes.toBytes("zhangsan_1235")); //删除一个行
 
        Delete delete = new Delete(Bytes.toBytes("zhangsan_1235"));
        delete.addFamily(Bytes.toBytes("info1"));//删除该行的指定列族
        delete.addColumn(Bytes.toBytes("info1"),Bytes.toBytes("name"));//删除指定的一个单元
 
 
        table.delete(deleteRow);
 
        //table.delete(List<Delete>); //通过添加一个list集合,可以删除多个
    }

1.5 查询单条数据

    /**
     * 查询单条数据
     * @throws IOException
     */
    @Test
    public void queryByKey() throws IOException {
        String rowKey = "zhangsan_1235";
        Get get = new Get(Bytes.toBytes(rowKey));
        Result result = table.get(get);
        byte[] address = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("address")); //读取单条记录
        byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name")); //读取单条记录
        byte[] sex = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("sex")); //读取单条记录
        byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age")); //读取单条记录
        System.out.print(Bytes.toString(name) + ",");
        System.out.print(Bytes.toString(sex) + ",");
        System.out.print(Bytes.toString(address) + ",");
        System.out.print(Bytes.toInt(age) + ",");
        System.out.println();
    }

1.6 全表扫描

完整的全表扫描要慎用,不过全表扫描中可以指定很多过滤器,我们可以很好的使用它

    /**
     * 全表扫描
     */
    @Test
    public void scanData() throws IOException {
        Scan scan = new Scan();
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
    }

我们看到上方扫描后得到一个ResultScanner,这是一个扫描结果,我们来看一下要如何使用它

    private void printResult(ResultScanner resultScanner) {
        for (Result result : resultScanner) {
            byte[] address = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("address")); //读取单条记录
            byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name")); //读取单条记录
            byte[] sex = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("sex")); //读取单条记录
            byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age")); //读取单条记录
            byte[] rowKey = result.getRow(); //获取rowKey
            System.out.print(Bytes.toString(rowKey) + ",");
            System.out.print(Bytes.toString(name) + ",");
            System.out.print(Bytes.toString(sex) + ",");
            System.out.print(Bytes.toString(address) + ",");
            System.out.print((age == null ? null : Bytes.toInt(age)) + ",");
            System.out.println();
        }
    }

我们已经把这个打印结果的代码封装成为了一个方法了,我们接下来很多地方都会用到它

1.7 区间扫描

区间扫描我们用处也很多,因为row key是按照字典序来排列的,我们可以根据这个特性,查找某一个用户指定时间段的数据,比如查询用户A昨天到今天的数据

在查询的过程中,我们还可以指定返回的结果,比如指定返回一个列族,以及返回指定的列,这样可以增加查询速度

    /**
     * 区间扫描
     */
    @Test
    public void areaScanData() throws IOException {
        Scan scan = new Scan();
        scan.withStartRow(Bytes.toBytes("zhangsan_1232")); //设置开始行
        scan.withStopRow(Bytes.toBytes("zhangsan_12352")); //设置结束行
        //scan.addColumn(Bytes.toBytes("info1"),Bytes.toBytes("name"));//查询指定列
        scan.addFamily(Bytes.toBytes("info1"));//查询指定列族
 
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
    }

1.8 列值过滤器

列值过滤器也是我们很常用的操作,它提供了一种甚至值的条件查询。类似sql中的where field = 'xxx',这极大的扩展了hbase的查询方式,因为如果只是根据row key来查询,那么很多产景都不适用hbase

    /**
     * 全表扫描时加过滤器 --> 列值过滤器
     */
    @Test
    public void scanDataByFilter1() throws IOException {
        Scan scan = new Scan();
        /*
         * 第一个参数: 列族
         * 第二个参数: 列名
         * 第三个参数: 是一个枚举类型
         *              CompareOp.EQUAL  等于
         *              CompareOp.LESS  小于
         *              CompareOp.LESS_OR_EQUAL  小于或等于
         *              CompareOp.NOT_EQUAL  不等于
         *              CompareOp.GREATER_OR_EQUAL  大于或等于
         *              CompareOp.GREATER  大于
         */
        SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(Bytes.toBytes("info1"), Bytes.toBytes("name"), CompareFilter.CompareOp.GREATER_OR_EQUAL, Bytes.toBytes("zhangsan8"));
    //这个方法很重要,需要注意,当此过滤器过滤时,如果遇到该列值为NULL的情况,如果设置的参数为true,则会过滤掉这一行,如果设置的参数为false,那么则会把这一行的结果返回,默认为false
    singleColumnValueFilter.setFilterIfMissing(true);
        scan.setFilter(singleColumnValueFilter);
 
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
 
    }

由上面的代码可以看到,我们使用的的是SingleColumnValueFilter,它传入了四个参数,我们分别介绍一下

  • 第一个:列族
  • 第二个:列名
  • 第三个:是一个枚举类型,告诉列值匹配条件,类型如下
    • CompareOp.EQUAL 等于
    • CompareOp.LESS 小于
    • CompareOp.LESS_OR_EQUAL 小于或等于
    • CompareOp.NOT_EQUAL 不等于
    • CompareOp.GREATER_OR_EQUAL 大于或等于
    • CompareOp.GREATER 大于
  • 第四个:是匹配的值

1.9 前缀过滤器

前缀过滤器和表里面的内容没有关系,它只是用来匹配指定的列的,,比如有这样两个列 name1 和name2 ,通过这个过滤器,就会查询这两个列的所有数据,当然,其实这个方式和scan.addColumn差不多,并且它会匹配到多个列族

 
    /**
     * 全表扫描时加过滤器 --> 前缀过滤器
     *
     * 这里的查询和单元内容没有关系,仅仅是匹配列名,比如有这样两个列 name1 和name2  ,通过这个过滤器,就会查询这两个列的所有数据,当然,其实这个方式和scan.addColumn差不多,
     * 且并会匹配到其它列族的列名
     *
     */
    @Test
    public void scanDataByFilter2() throws IOException {
        ColumnPrefixFilter columnPrefixFilter = new ColumnPrefixFilter(Bytes.toBytes("name"));
        Scan scan = new Scan();
        scan.setFilter(columnPrefixFilter);
 
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
    }

1.10 多列值前缀过滤器

好吧,这个过滤器和上面的前缀过滤器ColumnPrefixFilter差不多,区别就是可以匹配多个,其实作用并不大

    /**
     * 全表扫描时添加过滤器  --> 多个列值前缀过滤器
     *
     *  这个过滤器和 ColumnPrefixFilter 过滤器差不多,但是能匹配多个前缀
     */
 
    @Test
    public void scanDataFilter3() throws IOException {
        Scan scan = new Scan();
        byte[][] prefix = new byte[][]{Bytes.toBytes("name"),Bytes.toBytes("add")};
        MultipleColumnPrefixFilter multipleColumnPrefixFilter = new MultipleColumnPrefixFilter(prefix);
 
        scan.setFilter(multipleColumnPrefixFilter);
 
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
    }

1.11 按row key正则表达式查找

这个也是hbase查找中经常用到的功能

    /**
     * row key查找
     */
    @Test
    public void scanByRowKey() throws IOException {
        //查找以指定内容开头的
        Filter rowKeyFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^zhangsan_1239"));
        Scan scan = new Scan();
        scan.setFilter(rowKeyFilter);
 
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
    }

1.12 同时使用多个过滤器

我们可能有这样的情况,我们想扫描指定row key范围的,又想指定address为深圳的,或者是更多的一些条件,我们要怎么做呢?我们需要组合一些过滤器

    /**
     * 使用多个过滤器,并合查找,可以同时设置多个过滤器
     *
     * 这里查找row key中,name列等于指定值的
     */
    @Test
    public void scanByFilterList() throws IOException {
        /*
        * 我们需要注意Operator这个参数,这是一个枚举类型,里面有两个类型
        *   Operator.MUST_PASS_ALL   需要通过全部的条件,也就是并且,and &&
        *   Operator.MUST_PASS_ONE   任何一个条件满足都可以,也就是或者,or ||
        */
        FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
 
        //row key正则表达式的过滤器
        Filter rowKeyFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^zhangsan_1239"));
        //列值过滤器
        SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(Bytes.toBytes("info1"), Bytes.toBytes("name"), CompareFilter.CompareOp.EQUAL, Bytes.toBytes("zhangsan9"));
 
 
        //把两个filter添加进filterList中
        filterList.addFilter(rowKeyFilter);
        filterList.addFilter(singleColumnValueFilter);
 
 
        Scan scan = new Scan();
        scan.setFilter(filterList);
 
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
    }

我们可以通过FilterList来添加多个过滤器,然后把这个对象设置进去,我们看下这个对象的构造方法,Operator也是一个枚举类型,类型如下

  • Operator.MUST_PASS_ALL 需要通过全部的条件,也就是并且,and &&
  • Operator.MUST_PASS_ONE 任何一个条件满足都可以,也就是或者,or ||

1.13 完整代码

之前所有代码的java类如下

package com.xiaojiezhu.hbase.demo;
 
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Before;
import org.junit.Test;
 
import java.io.IOException;
import java.util.List;
 
/**
* @author xiaojie.zhu
*/
public class HbaseTest {
    private Connection connection;
    private HTable table;
    HBaseAdmin admin;
    @Before
    public void init() throws IOException {
        Configuration configuration = HBaseConfiguration.create();
        //设置zookeeper的地址,可以有多个,以逗号分隔
        configuration.set("hbase.zookeeper.quorum","dockerServer");
        //设置zookeeper的端口
        configuration.set("hbase.zookeeper.property.clientPort","2181");
        //创建hbase的连接,这是一个分布式连接
        connection = ConnectionFactory.createConnection(configuration);
        //获取hbase中的表
        table = (HTable) connection.getTable(TableName.valueOf("user"));
 
        //这个admin是管理table时使用的,比如说创建表
        admin = (HBaseAdmin) connection.getAdmin();
    }
 
    /**
     * 创建表,创建表只需要指定列族,不需要指定列
     * 其实用命令真的会更快,create 'user','info1','info2'
     */
    @Test
    public void createTable() throws IOException {
        //声明一个表名
        TableName tableName = TableName.valueOf("user");
        //构造一个表的描述
        HTableDescriptor desc = new HTableDescriptor(tableName);
        //创建列族
        HColumnDescriptor family1 = new HColumnDescriptor("info1");
        HColumnDescriptor family2 = new HColumnDescriptor("info2");
        //添加列族
        desc.addFamily(family1);
        desc.addFamily(family2);
        //创建表
        admin.createTable(desc);
    }
 
 
    /**
     * 添加数据
     * 对同一个row key进行重新put同一个cell就是修改数据
     */
    @Test
    public void insertUpdate() throws IOException {
        //构造参数是row key,必传
        for(int i = 0 ; i < 100 ; i ++){
            Put put = new Put(Bytes.toBytes("zhangsan_123" + i));
            //put.add()已经被弃用了
            //这里的参数依次为,列族名,列名,值
            put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("name"),Bytes.toBytes("lisi" + i));
            put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("age"),Bytes.toBytes(22 + i));
            put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("sex"),Bytes.toBytes("男"));
            put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("address"),Bytes.toBytes("天堂" + i));
            table.put(put);
            //table.put(List<Put>); //通过一个List集合,可以添加一个集合
        }
 
    }
 
    /**
     * 删除数据
     */
    @Test
    public void delete() throws IOException {
        Delete deleteRow = new Delete(Bytes.toBytes("zhangsan_1235")); //删除一个行
 
        Delete delete = new Delete(Bytes.toBytes("zhangsan_1235"));
        delete.addFamily(Bytes.toBytes("info1"));//删除该行的指定列族
        delete.addColumn(Bytes.toBytes("info1"),Bytes.toBytes("name"));//删除指定的一个单元
 
 
        table.delete(deleteRow);
 
        //table.delete(List<Delete>); //通过添加一个list集合,可以删除多个
    }
 
 
    /**
     * 查询单条数据
     * @throws IOException
     */
    @Test
    public void queryByKey() throws IOException {
        String rowKey = "zhangsan_1235";
        Get get = new Get(Bytes.toBytes(rowKey));
        Result result = table.get(get);
        byte[] address = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("address")); //读取单条记录
        byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name")); //读取单条记录
        byte[] sex = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("sex")); //读取单条记录
        byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age")); //读取单条记录
        System.out.print(Bytes.toString(name) + ",");
        System.out.print(Bytes.toString(sex) + ",");
        System.out.print(Bytes.toString(address) + ",");
        System.out.print(Bytes.toInt(age) + ",");
        System.out.println();
    }
 
    /**
     * 全表扫描
     */
    @Test
    public void scanData() throws IOException {
        Scan scan = new Scan();
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
    }
 
 
    /**
     * 区间扫描
     */
    @Test
    public void areaScanData() throws IOException {
        Scan scan = new Scan();
        scan.withStartRow(Bytes.toBytes("zhangsan_1232")); //设置开始行
        scan.withStopRow(Bytes.toBytes("zhangsan_12352")); //设置结束行
        //scan.addColumn(Bytes.toBytes("info1"),Bytes.toBytes("name"));//查询指定列
        scan.addFamily(Bytes.toBytes("info1"));//查询指定列族
 
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
    }
 
 
    /**
     * 全表扫描时加过滤器 --> 列值过滤器
     */
    @Test
    public void scanDataByFilter1() throws IOException {
        Scan scan = new Scan();
        /*
         * 第一个参数: 列族
         * 第二个参数: 列名
         * 第三个参数: 是一个枚举类型
         *              CompareOp.EQUAL  等于
         *              CompareOp.LESS  小于
         *              CompareOp.LESS_OR_EQUAL  小于或等于
         *              CompareOp.NOT_EQUAL  不等于
         *              CompareOp.GREATER_OR_EQUAL  大于或等于
         *              CompareOp.GREATER  大于
         */
        SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(Bytes.toBytes("info1"), Bytes.toBytes("name"), CompareFilter.CompareOp.GREATER_OR_EQUAL, Bytes.toBytes("zhangsan8"));
        scan.setFilter(singleColumnValueFilter);
 
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
 
    }
 
 
    /**
     * 全表扫描时加过滤器 --> 前缀过滤器
     *
     * 这里的查询和单元内容没有关系,仅仅是匹配列名,比如有这样两个列 name1 和name2  ,通过这个过滤器,就会查询这两个列的所有数据,当然,其实这个方式和scan.addColumn差不多,
     * 且并会匹配到其它列族的列名
     *
     */
    @Test
    public void scanDataByFilter2() throws IOException {
        ColumnPrefixFilter columnPrefixFilter = new ColumnPrefixFilter(Bytes.toBytes("name"));
        Scan scan = new Scan();
        scan.setFilter(columnPrefixFilter);
 
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
    }
 
    /**
     * 全表扫描时添加过滤器  --> 多个列值前缀过滤器
     *
     *  这个过滤器和 ColumnPrefixFilter 过滤器差不多,但是能匹配多个前缀
     */
 
    @Test
    public void scanDataFilter3() throws IOException {
        Scan scan = new Scan();
        byte[][] prefix = new byte[][]{Bytes.toBytes("name"),Bytes.toBytes("add")};
        MultipleColumnPrefixFilter multipleColumnPrefixFilter = new MultipleColumnPrefixFilter(prefix);
 
        scan.setFilter(multipleColumnPrefixFilter);
 
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
    }
 
 
    /**
     * row key查找
     */
    @Test
    public void scanByRowKey() throws IOException {
        //查找以指定内容开头的
        Filter rowKeyFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^zhangsan_1239"));
        Scan scan = new Scan();
        scan.setFilter(rowKeyFilter);
 
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
    }
 
 
    /**
     * 使用多个过滤器,并合查找,可以同时设置多个过滤器
     *
     * 这里查找row key中,name列等于指定值的
     */
    @Test
    public void scanByFilterList() throws IOException {
        /*
        * 我们需要注意Operator这个参数,这是一个枚举类型,里面有两个类型
        *   Operator.MUST_PASS_ALL   需要通过全部的条件,也就是并且,and &&
        *   Operator.MUST_PASS_ONE   任何一个条件满足都可以,也就是或者,or ||
        */
        FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
 
        //row key正则表达式的过滤器
        Filter rowKeyFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^zhangsan_1239"));
        //列值过滤器
        SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(Bytes.toBytes("info1"), Bytes.toBytes("name"), CompareFilter.CompareOp.EQUAL, Bytes.toBytes("zhangsan9"));
 
 
        //把两个filter添加进filterList中
        filterList.addFilter(rowKeyFilter);
        filterList.addFilter(singleColumnValueFilter);
 
 
        Scan scan = new Scan();
        scan.setFilter(filterList);
 
        ResultScanner resultScanner = table.getScanner(scan);
        printResult(resultScanner);
    }
 
 
    private void printResult(ResultScanner resultScanner) {
        for (Result result : resultScanner) {
            byte[] address = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("address")); //读取单条记录
            byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name")); //读取单条记录
            byte[] sex = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("sex")); //读取单条记录
            byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age")); //读取单条记录
            byte[] rowKey = result.getRow(); //获取rowKey
            System.out.print(Bytes.toString(rowKey) + ",");
            System.out.print(Bytes.toString(name) + ",");
            System.out.print(Bytes.toString(sex) + ",");
            System.out.print(Bytes.toString(address) + ",");
            System.out.print((age == null ? null : Bytes.toInt(age)) + ",");
            System.out.println();
        }
    }
}
 
    原文作者:昨夜今夕
    原文地址: https://www.jianshu.com/p/046ed8cac2f1
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞