一、本文描述
前文已经搭建好Hadoop和Hbase的伪分布式环境,现在我们就使用Hbase Java客户端来编写Hbase的增删改查示例代码
二、添加pom依赖
我们使用的Hbase版本为1.2.0-cdh5.7.0,使用1.3.0版本的客户端,在pom文件中添加如下文件即可
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.3.0</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
这里将slf4j的jar包排除是因为我在项目中使用的是logback打印的日志,排除是为了解决jar包冲突导致日志无法打印的问题。这里还有一个问题就是hbase-client的版本问题,原本我使用的是1.2.6的jar包版本,但是在实际运行程序的过程中发现会报如下异常:
org.apache.hadoop.hbase.DoNotRetryIOException: java.lang.IllegalAccessError: tried to access method com.google.common.base.Stopwatch.<init>()V from class org.apache.hadoop.hbase.zookeeper.MetaTableLocator
这个问题我也没有仔细去研究,将版本换成1.3.0就可以了。
三、Hbase Java客户端代码
Hbase本质上就是一个数据库,最常用的其实就是增删改查,这里我们就写一个比较通用的HbaseClient,既可以用来自己测试使用,改造后也可以用在工作中
我把代码放在了自己的github上:源代码地址
package com.russell.bigdata.hbase.util;
import com.russell.bigdata.hbase.common.RowKeyDO;
import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author liumenghao
* @Date 2019/3/5
*/
@Slf4j
public class HbaseClient {
private volatile static HbaseClient instance;
private Admin admin;
/**
* Hbase连接
*/
private Connection connection;
private HbaseClient(Connection conn, Admin ad) {
this.connection = conn;
this.admin = ad;
}
/**
* 创建表
*
* @param tableName 表名
* @param cols 需要创建的family
* @throws IOException
*/
public void createTable(String tableName, String[] cols) throws IOException {
TableName table = TableName.valueOf(tableName);
if (admin.tableExists(table)) {
System.out.println("table is exists!");
} else {
HTableDescriptor hTableDescriptor = new HTableDescriptor(table);
for (String col : cols) {
HColumnDescriptor hColumnDescriptor = new HColumnDescriptor(col);
hTableDescriptor.addFamily(hColumnDescriptor);
}
admin.createTable(hTableDescriptor);
}
}
/**
* 列出所有的表
*
* @throws IOException
*/
public List<String> listTables() throws IOException {
HTableDescriptor[] hTableDescriptor = admin.listTables();
List<String> tables = new ArrayList<>();
for (HTableDescriptor tableDescriptor : hTableDescriptor) {
tables.add(tableDescriptor.getTableName().getNameAsString());
}
return tables;
}
/**
* 删除指定的表
*
* @param tableName 表名
* @throws IOException
*/
public void deleteTable(String tableName) throws IOException {
TableName tn = TableName.valueOf(tableName);
if (admin.tableExists(tn)) {
admin.disableTable(tn);
admin.deleteTable(tn);
}
}
/**
* 批量插入
*
* @param tableName 表名
* @param rowKeyDOList 插入的内容
*/
public void putData(String tableName, List<RowKeyDO> rowKeyDOList) throws IOException {
Table table = connection.getTable(TableName.valueOf(tableName));
List<Put> puts = new ArrayList<>();
for (RowKeyDO rowKeyDo : rowKeyDOList) {
Put put = new Put(Bytes.toBytes(rowKeyDo.getRowKey()));
put.addColumn(Bytes.toBytes(rowKeyDo.getColFamily()), Bytes.toBytes(rowKeyDo.getQualifier()),
Bytes.toBytes(rowKeyDo.getValue()));
puts.add(put);
}
table.put(puts);
}
/**
* 根据单个rowkey获取数据
*
* @param tableName
* @param rowKeyDo
* @return
* @throws IOException
*/
public Result getData(String tableName, RowKeyDO rowKeyDo) throws IOException {
Table table = connection.getTable(TableName.valueOf(tableName));
Get get = new Get(Bytes.toBytes(rowKeyDo.getRowKey()));
String colFamily = rowKeyDo.getColFamily();
String qualifier = rowKeyDo.getQualifier();
if (colFamily == null) {
return table.get(get);
}
if (qualifier != null) {
get.addColumn(Bytes.toBytes(colFamily), Bytes.toBytes(qualifier));
} else {
get.addFamily(Bytes.toBytes(colFamily));
}
return table.get(get);
}
/**
* 批量查询数据(前闭后开)
*
* @param tableName
* @param startRow 起始rowKey
* @param stopRow 结束rowkey
*/
public ResultScanner scanData(String tableName, String startRow, String stopRow) throws IOException {
Table table = connection.getTable(TableName.valueOf(tableName));
Scan scan = new Scan();
scan.setStartRow(Bytes.toBytes(startRow));
scan.setStopRow(Bytes.toBytes(stopRow));
ResultScanner results = table.getScanner(scan);
return results;
}
/**
* 测试使用
*
* @param result
*/
public void showCell(Result result) {
Cell[] cells = result.rawCells();
for (Cell cell : cells) {
System.out.println("RowName:" + new String(CellUtil.cloneRow(cell)) + " ");
System.out.println("Timetamp:" + cell.getTimestamp() + " ");
System.out.println("column Family:" + new String(CellUtil.cloneFamily(cell)) + " ");
System.out.println("row Name:" + new String(CellUtil.cloneQualifier(cell)) + " ");
System.out.println("value:" + new String(CellUtil.cloneValue(cell)) + " ");
}
}
/**
* 使用单例模式获取client实例
*
* @return
*/
public static HbaseClient getInstance() {
if (instance == null) {
synchronized (HbaseClient.class) {
if (instance == null) {
try {
Configuration conf = new Configuration();
conf.addResource("hbase-site.xml");
Configuration hbaseConfig = HBaseConfiguration.create(conf);
Connection connection = ConnectionFactory.createConnection(hbaseConfig);
Admin admin = connection.getAdmin();
instance = new HbaseClient(connection, admin);
} catch (Exception e) {
log.error("获取hbase 实例失败", e);
}
}
}
}
return instance;
}
}
代码实现起来没有什么难点,无外乎就是利用hbase-client提供的api接口包装一层,这样在实际工作中使用更方便。代码中主要实现了批量插入数据,查单个rowKey的数据点,以及批量查询的功能。为了插入数据方便,代码中还抽象了一个对象:
package com.russell.bigdata.hbase.common;
import lombok.Data;
/**
* @author liumenghao
* @Date 2019/3/5
*/
@Data
public class RowKeyDO {
/**
* 插入内容的rowKey
*/
private String rowKey;
/**
* 列族
*/
private String colFamily;
/**
* 列名
*/
private String qualifier;
/**
* 插入的内容
*/
private String value;
}
四、测试
写完代码之后需要进行测试,这样可以更加直观的对hbase有一个了解
1、首先启动ZK、Hadoop、Hbase
安装ZK,Hadoop和Hbase可以参考我的其他文章:
## 启动zk(由于我都设置了环境变量,所以不用到特定目录去启动)
zkStart.sh start
## 启动Hadoop(由于我们只是使用Hadoop的hdfs,所以只用启动hdfs即可)
start-dfs.sh
## 启动Hbase
start-hbase.sh
## 如果没有添加环境变量,需要进入到安装文件中的bin或者sbin目录下执行启动命令
全部启动完成之后,可以先使用jps命令查看下是否所有进行都有启动
jps
40643 HRegionServer (hbase regoin server进程)
40118 NameNode (hadoop NameNode进程)
50584 QuorumPeerMain (ZK进程)
40315 SecondaryNameNode (hadoop SecondaryNameNode进程)
40523 HMaster (hbase master进程)
40205 DataNode (hadoop dataNode进程)
## 启动Hbase console控制台
http://localhost:60010
下面我们就可以编写测试代码了,这个代码就没有必要特别说明了,就是首先在habse中创建表,然后写入数据,最后再将数据查出来,看是否符合预期。整个过程可以配置hbase管理控制台进行
package com.russell.bigdata.hbase.example;
import com.russell.bigdata.hbase.common.RowKeyDO;
import com.russell.bigdata.hbase.util.HbaseClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import java.util.ArrayList;
import java.util.List;
/**
* 测试接口使用
*
* @author liumenghao
* @Date 2019/3/6
*/
@Slf4j
public class TestMain {
private static String tableName = "hbase_user";
private static String[] colFamily = {"userInfo"};
public static void main(String[] args) throws Exception {
HbaseClient hbaseClient = HbaseClient.getInstance();
// 测试创建表
log.info("创建表开始......");
hbaseClient.createTable(tableName, colFamily);
List<String> tables = hbaseClient.listTables();
log.info("=======================================");
log.info("插入单rowkey,多列的数据");
hbaseClient.putData(tableName, generateSingleRows());
RowKeyDO query = new RowKeyDO();
query.setRowKey("hbase_rowkey_0");
Result result = hbaseClient.getData(tableName, query);
hbaseClient.showCell(result);
log.info("=======================================");
log.info("插入多rowkey,多列的数据");
hbaseClient.putData(tableName, generateMuilteRows());
log.info("批量查询数据......");
String startRow = "hbase_rowkey_0";
String stopRow = "hbase_rowkey_3";
ResultScanner scanner = hbaseClient.scanData(tableName, startRow, stopRow);
for (Result result1 : scanner) {
hbaseClient.showCell(result1);
}
log.info("测试完成......");
}
public static List<RowKeyDO> generateSingleRows() {
List<RowKeyDO> rows = new ArrayList<>();
RowKeyDO rowKeyDO = new RowKeyDO();
rowKeyDO.setRowKey("hbase_rowkey_0");
rowKeyDO.setColFamily(colFamily[0]);
rowKeyDO.setQualifier("userName");
rowKeyDO.setValue("russell");
rows.add(rowKeyDO);
RowKeyDO rowKeyDO1 = new RowKeyDO();
rowKeyDO1.setRowKey("hbase_rowkey_0");
rowKeyDO1.setColFamily(colFamily[0]);
rowKeyDO1.setQualifier("userAge");
rowKeyDO1.setValue("25");
rows.add(rowKeyDO1);
return rows;
}
public static List<RowKeyDO> generateMuilteRows() {
List<RowKeyDO> rows = new ArrayList<>();
RowKeyDO rowKeyDO = new RowKeyDO();
rowKeyDO.setRowKey("hbase_rowkey_1");
rowKeyDO.setColFamily(colFamily[0]);
rowKeyDO.setQualifier("userName");
rowKeyDO.setValue("russell");
rows.add(rowKeyDO);
RowKeyDO rowKeyDO1 = new RowKeyDO();
rowKeyDO1.setRowKey("hbase_rowkey_2");
rowKeyDO1.setColFamily(colFamily[0]);
rowKeyDO1.setQualifier("userName");
rowKeyDO1.setValue("darview");
rows.add(rowKeyDO1);
return rows;
}
}
五、总结
现在大数据越来越火,相关的技术栈也让人眼花缭乱。我也是一步一步从自学走过来的,幸运的是我现在从事了大数据相关的工作。写这一系列文章的原因就是希望初学者可以真实的触摸到大数据相关的技术栈,从搭建环境到写demo示例,最后到运行成功。大数据并不是什么高深莫测的技术,不要对其有恐惧心理。
如果文章对你有帮助,请点个喜欢。如果有任何问题,请留言与我交流,或者直接加我微信:as82984895ok