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