写在前面
这篇文章上接Hbase搭建和Shell命令,咕咕咕了好久,最近终于有空歇下来总结一下了。
基本API——增删改
导入依赖
首先新建一个maven项目,导入如下的依赖:
<dependencies>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
这里的版本要和服务器里的版本相对应。
准备工作
为了方便以后的连接,我们直接把连接HBase的配置写到静态代码块里,这样每次这个类运行的时候都会执行这些代码:
private static Connection connection = null;
private static Admin admin = null;
static {
try {
// 1.获取配置信息
Configuration configuration = HBaseConfiguration.create();
configuration.set("hbase.zookeeper.quorum","hadoop03,hadoop04,hadoop05");
// 2.创建连接对象
connection = ConnectionFactory.createConnection(configuration);
// 3.创建admin对象
admin = connection.getAdmin();
} catch (Exception e) {
e.printStackTrace();
}
}
这里的configuration里set的值要换成你自己的hbase地址。我这里是在windows里配置了hosts文件映射,所以可以直接写主机名。建议都去配一下映射,不然好像会报错。
检验表是否存在
先来看代码:
/**
* 1.判断表是否存在
* @return
*/
public static boolean isTableExist(String tableName) throws IOException {
// 1.判断表是否存在
boolean exists = admin.tableExists(TableName.valueOf(tableName));
// 2.关闭连接
admin.close();
// 3.返回结果
return exists;
}
这里要注意的点是,tableExists()方法只能传入一个TableName对象,我们可以通过TableName.valueOf()来获得一个TableName对象。
创建表
/**
* 2. 创建表
*/
public static void createTable(String tableName,String... cfs) throws IOException {
// 1.判断是否存在列族信息
if(cfs.length <=0){
System.out.println("请设置列族信息!");
return;
}
// 2.判断表是否存在
if(isTableExist(tableName)){
System.out.println(tableName + "表已存在!");
return;
}
// 3.创建表描述器
HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));
// 4.循环添加列族信息
for (String cf : cfs) {
// 5.创建列族描述器
HColumnDescriptor hColumnDescriptor = new HColumnDescriptor(cf);
// 6.添加具体的列族信息
hTableDescriptor.addFamily(hColumnDescriptor);
}
// 7.创建表
admin.createTable(hTableDescriptor);
}
这里要注意的点是,我们通过面向对象的思想来创建一个表,首先创建一个表描述器,然后对表描述器添加列族描述器。这里只是一个例子,可以传入的类型不止这些,可以再细看API。
删除表
删除表,就跟我们用shell操作一样,先让表失效,再去删除表:
/**
* 3.删除表
*/
public static void dropTable(String tableName) throws IOException {
// 1.判断表是否存在
if(!isTableExist(tableName)){
System.out.println(tableName + "表不存在!!");
return;
}
// 2.使表下线
admin.disableTable(TableName.valueOf(tableName));
// 3.删除表
admin.deleteTable(TableName.valueOf(tableName));
}
向表插入(修改)数据
向表插入或者修改数据,我们使用Put对象即可。创建一个Put对象,然后对其赋值即可:
/**
* 5.向表插入数据
*/
public static void putData(String tableName,String rowKey,String cf,String cn,String value) throws IOException {
// 1.获取表对象
Table table = connection.getTable(TableName.valueOf(tableName));
// 2.创建put对象
Put put = new Put(Bytes.toBytes(rowKey));
// 3.给Put对象赋值
put.addColumn(Bytes.toBytes(cf),Bytes.toBytes(cn),Bytes.toBytes(value));
// 4.插入数据
table.put(put);
// 5.关闭表连接
table.close();
}
删除表中数据
/**
* 8.删除数据
*/
public static void deleteData(String tableName,String rowKey,String cf,String cn) throws IOException {
// 1.获取表对象
Table table = connection.getTable(TableName.valueOf(tableName));
// 2.构建删除对象
Delete delete = new Delete(Bytes.toBytes(rowKey));
// 2.1设置删除的列
// delete.addColumn(Bytes.toBytes(cf),Bytes.toBytes(cn));
// delete.addColumns(Bytes.toBytes(cf),Bytes.toBytes(cn));
// 2.2 删除指定的列族
delete.addFamily(Bytes.toBytes(cf));
// 3.执行删除操作
table.delete(delete);
// 4.关闭连接
table.close();
}
这里我们在删除时,可以指定删除的列或者列族,进而达到指定的列或者列族的目的。
查询API——get和scan
get
/**
* 6.获取数据 (get)
*/
public static void getData(String tableName,String rowKey,String cf,String cn) throws IOException {
// 1.获取表对象
Table table = connection.getTable(TableName.valueOf(tableName));
// 2.创建get对象
Get get = new Get(Bytes.toBytes(rowKey));
// 2.1指定获取的列族
get.addFamily(Bytes.toBytes(cf));
// 2.2指定列族和列
get.addColumn(Bytes.toBytes(cf),Bytes.toBytes(cn));
// 2.3设置获取数据的版本数
get.setMaxVersions();
// 3.获取数据
Result result = table.get(get);
// 4.解析result并打印
for (Cell cell : result.rawCells()) {
// 5.打印数据
System.out.println("cf:" + Bytes.toString(CellUtil.cloneFamily(cell)) +
",CN:" + Bytes.toString(CellUtil.cloneQualifier(cell)) +
",Value:" + Bytes.toString(CellUtil.cloneValue(cell)));
}
// 6.关闭表连接
table.close();
}
get查询方法和scan查询方法的区别在于,get查询方法获得指定的值,而scan方法获得某一范围内的值。
scan
/**
* 7.获取数据(scan)
*/
public static void scanTable(String tableName) throws IOException {
// 1.获取表对象
Table table = connection.getTable(TableName.valueOf(tableName));
// 2.构建Scan对象 左闭右开
Scan scan = new Scan(Bytes.toBytes("1001"),Bytes.toBytes("1003"));
// 3.扫描表
ResultScanner scanner = table.getScanner(scan);
// 4.解析scanner
for (Result result : scanner) {
// 5.解析Result并打印
for (Cell cell : result.rawCells()) {
// 6.打印数据
System.out.println("RoleKey:" + Bytes.toString(CellUtil.cloneRow(cell))+
",cf:" + Bytes.toString(CellUtil.cloneFamily(cell)) +
",CN:" + Bytes.toString(CellUtil.cloneQualifier(cell)) +
",Value:" + Bytes.toString(CellUtil.cloneValue(cell)));
}
}
// 7.关闭连接
table.close();
}
进阶查询——过滤器和比较器
这里我通过一个案例来举例:
假设现在有这样一个需求:根据用户输入的对应字段来做一个多条件查询,例如目前要查询的字段有jgmc(机构名称),szdy(所在地域),jsxqmc(技术需求名称)。假设已经把数据存入Hbase(一个列族名为dcwjxx,字段名为对应列),如何使用Hbase实现该查询?
案例中的需求十分明确,我们要根据用户输入的各个字段来拼接查询条件。这种事用mysql十分容易实现,那么用hbase呢?还是先来看代码:
public List<Dcwjxx> searchByCondition(String jgmc, String szdy,String jsxqmc) {
// 根据条件是不是空来封装过滤器
Scan scan = new Scan();
// 按或条件查询
FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ONE);
if (!TextUtils.isEmpty(jgmc)) {
SubstringComparator jgmcSubstringComparator = new SubstringComparator(jgmc);
SingleColumnValueFilter jgmcFilter = new SingleColumnValueFilter(Bytes.toBytes("dcwjxx"),Bytes.toBytes(
"jgmc"), CompareFilter.CompareOp.EQUAL,jgmcSubstringComparator);
filterList.addFilter(jgmcFilter);
}
if (!TextUtils.isEmpty(szdy)) {
SubstringComparator szdySubstringCom = new SubstringComparator(szdy);
SingleColumnValueFilter szdyFilter = new SingleColumnValueFilter(Bytes.toBytes("dcwjxx"),Bytes.toBytes(
"szdy"), CompareFilter.CompareOp.EQUAL,szdySubstringCom);
filterList.addFilter(szdyFilter);
}
if (!TextUtils.isEmpty(jsxqmc)) {
SubstringComparator jsxqmcSubstringCom = new SubstringComparator(jsxqmc);
SingleColumnValueFilter jsxqmcFilter = new SingleColumnValueFilter(Bytes.toBytes("dcwjxx"),Bytes.toBytes(
"jsxqmc"), CompareFilter.CompareOp.EQUAL,jsxqmcSubstringCom);
filterList.addFilter(jsxqmcFilter);
}
scan.setFilter(filterList);
return (List<Dcwjxx>) getResult(scan);
}
这里的getResult方法把返回的结果封装成了我们需要的对象,不需要在意这个方法,我们来看看如何用HBase的API拼接条件查询。
- 首先我们要明确,我们这是一个多条件的过滤,且是或的关系。
- 我们先定义一个FilterList对象,这个对象可以存储多个过滤器,这样我们就可以存储多个过滤条件了。这里传入一个或运算符
- 之后,根据条件是否为空来拼接filter,这里我们使用了SubstringComparator比较器,这是Hbase自带的比较器中的一种,其作用在类名上写的十分明确了,即判断传入的字符串是不是要查询的字符串的子串。
- 然后我们定义一个SingleColumnValueFilter过滤器,代表我们要对一个单元格数据进行处理,传入列族,列名和比较器运算符以及比较器,最后添加到filterList即可。
- 这样一来,我们就实现了类似mysql中的where语句的查询了。
总结
总的来说,Hbase作为一个非关系型分布式数据库和mysql这类关系型数据库差别还是比较大的,操作起来也有诸多不适应。还是希望能多多理解。