Hbase Java API简单实践(附源代码解释)
来源:互联网 发布:mac重命名快捷键 编辑:程序博客网 时间:2024/05/19 12:16
- 详细代码及链接
- 注释
- 标注1
- 标注2
- 标注3
- 标注4
- 运行截图
- 报错及解决过程
- 参考资源
详细代码及链接
- maven依赖:hbase-client,slf4j-api,slf4j-nop(不需要hbase-server包)
- resource中加入hdfs-site.xml配置文件(不需要什么core-site.xml和hdfs-site.xml)
- resource中放置log4j.properties文件(HBase安装目录下conf文件中的log4j.properties)
- 完整项目链接 ————(https://github.com/AtTops/Practice-Item/tree/master/HbaseTest)
- 完整代码如下(SomeHbaseAPI类与APITest类)
SomeHbaseAPI.java
import org.apache.hadoop.hbase.*;import org.apache.hadoop.hbase.client.*;import org.apache.hadoop.hbase.util.Bytes;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.io.IOException;import java.util.ArrayList;import java.util.List;/** * @author 王海 * @version V1.0 * @package per.wanghai * @Description * @Date 2017/10/29 23:23 */public class SomeHbaseAPI { private final Logger logger = LoggerFactory.getLogger(SomeHbaseAPI.class); protected void listTable(Admin admin) throws IOException { // 获得HTableDescriptors // (所有namespace的表,相当于scan META) HTableDescriptor[] tableDescriptor = admin.listTables(); System.out.println("您的HBase有以下表:"); for (int i = 0; i < tableDescriptor.length; i++) { System.out.println("表" + i + ":" + tableDescriptor[i].getNameAsString()); } } /** * @param columnFamilies(这是一个变长参数,“Varargs”机制只允许一个变长参数,且必须放在最后)详见参考2 * @throws IOException * @Description 该方法创建一个table实例 */ protected void createTable(Admin admin, TableName tableName, String... columnFamilies) throws IOException { try { if (admin.tableExists(tableName)) { // "{}"是slf4j的占位符(其一大特色) // DEBUG < INFO < WARN < ERROR < FATAL logger.warn("表:{}已经存在!", tableName.getNameAsString()); } else { // 标注2:关于HTableDescriptor: HTableDescriptor tableDescriptor = new HTableDescriptor(tableName); for (String columnFamily : columnFamilies) { tableDescriptor.addFamily(new HColumnDescriptor(columnFamily)); } admin.createTable(tableDescriptor); logger.info("表:{}创建成功!", tableName.getNameAsString()); } } finally { if (admin != null) { admin.close(); } } } /** * @throws IOException * @Description 一行一行的插入数据 * TODO:批量插入可以使用 Table.put(List<Put> list) */ protected void putOneByOne(Connection connection, TableName tableName, byte[] rowKey, String columnFamily, String column, String data) throws IOException { Table table = null; try { // 创建一个table实例 table = connection.getTable(tableName); // HBase中所有的数据最终都被转化为byte[] // (rowKey已经在testCurd方法中转换为byte[]) Put p = new Put(rowKey); // 查看源码知:put的add方法已经被弃用 p.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column), Bytes.toBytes(data)); table.put(p); logger.info("表:{}已更新!!!", tableName.getNameAsString()); } finally { if (table != null) { table.close(); } } } /** * @param connection * @param tableName * @param str:一个字符串数组(rowkey,family,qualifier,value.循环) * @throws IOException */ protected void putList(Connection connection, TableName tableName, String[] str) throws IOException { // 每个put操作,我们放入四个数据 int count = str.length / 4; // 我们希望数据量是4的倍数,因为剩下的我们将不会写入 int remainder = str.length % 4; if (remainder > 0) { logger.warn("数据可能并不会像您预期的那样完全写入,如有必要,请检查下您的数据量!"); } Table table = null; try { // 创建一个table实例 table = connection.getTable(tableName); List<Put> puts = new ArrayList<>(); for (int i = 0; i < count; i++) { Put put = new Put(Bytes.toBytes(str[4 * i])); put.addColumn(Bytes.toBytes(str[4 * i + 1]), Bytes.toBytes(str[4 * i + 2]), Bytes.toBytes(str[4 * i + 3])); puts.add(put); } table.put(puts); logger.info("表:{}已使用putList方法更新!!!", tableName.getNameAsString()); } finally { if (table != null) { table.close(); } } } /** * @throws IOException * @Description 扫描表 * 想获取部分行的数据,与putList方法类似,用List<Get>即可 */ protected void scan(Connection connection, TableName tableName) throws IOException { Table table = null; try { table = connection.getTable(tableName); /* 行的数目很大时,同时在一次请求中发送大量数据,会占用大量的系统资源并消耗很长时间, 所以ResultScanner类把扫描操作转换为类似的get操作,它将每一行数据封装成一个Result实例, 并将所有的Result实例放入一个迭代器中 */ ResultScanner rsScan1; ResultScanner rsScan2; // 这次操作返回表中所有的数据 Scan scan1 = new Scan(); rsScan1 = table.getScanner(scan1); for (Result r : rsScan1) { System.out.println(r); // 打印出来的Value是bytes类型 } rsScan1.close(); // 注:扫描器也使用同样的租约超时机制,保护其不被失效的客户单阻塞太久 // 超时时间配置:hbase.regionserver.lease.period // 同样,也可以addfamily: Scan scan2 = new Scan(); scan2.addFamily(Bytes.toBytes("commonInfo")); rsScan2 = table.getScanner(scan2); for (Result r : rsScan2) { System.out.println(r); } rsScan2.close(); } finally { if (table != null) { table.close(); } } } /** * @throws IOException * @Description 根据row key获取表中的该行数据 */ protected void getOneRow(Connection connection, TableName tableName, byte[] rowKey) throws IOException { Table table = null; try { table = connection.getTable(tableName); // 这种方法获取指定rowkey的所有信息(然后可以使用不同的方法获取指定信息) // 用rowKey来实例化get对象, Get all = new Get(rowKey); // Result类不是线程安全的 // 更多的使用方法见标注4 Result result = table.get(all); // 可以使用addColumn指定columnFamily与qualifier // 标注3:更多缩小获取范围的方法 /* 这里使用addFamily获取指定列族的所有列的信息(一行) Get part = new Get(rowKey); part.addFamily(Bytes.toBytes("commonInfo")); Result result = table.get(part); ... ... */ /*通过getValue获取指定信息 不推荐用字符串拼接的方式,字符串拼接会不断的创建新的对象, 而原来的对象就会变为垃圾被GC回收掉,如果拼接得次数多,这样执行效率会很低底。 (见下方Cell中使用StringBuffer) String city = Bytes.toString(result.getValue(Bytes.toBytes("commonInfo"),Bytes.toBytes("city"))); String age = Bytes.toString(result.getValue(Bytes.toBytes("concelInfo"),Bytes.toBytes("age"))); System.out.println("city: " + city + "\t" + "age: " + age); */ // rawCells()返回cell[];注意:Cell接口中的getFamily、getValue等方法已经被废弃 // 推荐:使用CellUtil中的一些列方法 for (Cell cell : result.rawCells()) { /* 与上方的String拼接不同,这样的String拼接不会创建多个String对象 System.out.println( "RowNum : " + "\t" + Bytes.toString(CellUtil.cloneRow(cell)) + ", Family : " + "\t" + Bytes.toString(CellUtil.cloneFamily(cell)) + ", Qualifier : " + "\t" + Bytes.toString(CellUtil.cloneQualifier(cell)) + ", Value : " + "\t" + Bytes.toString(CellUtil.cloneValue(cell)) ); */ // 采用StringBuffer:(因为其是可变的字符串对象,所以不会再创建新变量) StringBuffer sbuffer = new StringBuffer() .append("RowNum : \t") .append(Bytes.toString(CellUtil.cloneRow(cell))) .append(", Family : \t") .append(Bytes.toString(CellUtil.cloneFamily(cell))) .append(", Qualifier : \t") .append(Bytes.toString(CellUtil.cloneQualifier(cell))) .append(", Value : \t") .append(Bytes.toString(CellUtil.cloneValue(cell))); System.out.println(sbuffer); } } finally { if (table != null) { table.close(); } } } /** * @throws IOException * @Description 删除表中的数据 */ protected void myDeleteTable(Admin admin, TableName tableName) throws IOException { try { if (admin.tableExists(tableName)) { // 必须先disable, 再delete myDisableTable(admin, tableName); // admin的很多方法在子类HBaseAdmin中实现 // TODO:没看出该父类通过何种方式调用的子类方法 admin.deleteTable(tableName); logger.info("表:{}已删除!!!", tableName.getNameAsString()); } else { logger.info("表:{}并不存在!!!", tableName.getNameAsString()); } } finally { if (admin != null) { admin.close(); } } } protected void myDisableTable(Admin admin, TableName tableName) throws IOException { try { // admin的很多方法在子类HBaseAdmin中实现 if (admin.tableExists(tableName)) { admin.disableTable(tableName); logger.info("表:{}已禁用!!!", tableName.getNameAsString()); } } finally { if (admin != null) { admin.close(); } } }}
APITest.java
import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.hbase.HBaseConfiguration;import org.apache.hadoop.hbase.TableName;import org.apache.hadoop.hbase.client.*;import org.apache.hadoop.hbase.util.Bytes;import java.io.IOException;/** * @author 王海[https://github.com/AtTops] * @version V1.0 * @package PACKAGE_NAME * @Description * @Date 2017/10/31 11:43 */public class APITest { public static void main(String[] args) { new APITest().testCrud(); } private void testCrud() { SomeHbaseAPI caller = new SomeHbaseAPI(); /* * 标注1:详解HBaseConfiguration */ // 创建一个configuration对象 —— 告诉客户端必要的配置信息 Configuration config = HBaseConfiguration.create(); // 创建一个连接到集群的connection Connection connection = null; /* Admin是一个接口类,其很多方法在子类HBaseAdmin中实现 0.99版本开始:HBaseAdmin不再是客户端API。它被标记为InterfaceAudience.Private, 表示是一个HBase内部类。 使用Connection.getAdmin()获取Admin的实例,而不是直接构建一个HBaseAdmin。 可以用来create、drop、list、enabl、disable表;add、drop 表的column families,以及一些其他的管理操作。*/ try { connection = ConnectionFactory.createConnection(config); // Admin admin = connection.getAdmin(); // 该方法传递一个String类型参数,返回TableName实例 TableName tableName = TableName.valueOf("myHBaseTable"); // 表不存在会报:TableNotFoundException // 获取lists of table caller.listTable(admin); // 创建HBase表 caller.createTable(admin, tableName, "commonInfo", "concelInfo"); // rowkey要设计得尽量的短,数据的持久化文件HFile中是按照KeyValue存储的, // 如果rowkey过长,会极大影响HFile的存储效率 byte[] rowkey_bytes = Bytes.toBytes("ROW4"); /* 一行一行的插入数据,每一次put操作都是一次有效的RPC( 所以数据量大时不要这样使用, 而应该使用BufferedMutator来实现批量的异步写操作。) 这里两个列族,commonInfo列族两个“小列”,concelInfo一个“小列”*/ caller.putOneByOne(connection, tableName, rowkey_bytes, "commonInfo", "city", "Ziyang"); caller.putOneByOne(connection, tableName, rowkey_bytes, "commonInfo", "password", "000000"); caller.putOneByOne(connection, tableName, rowkey_bytes, "concelInfo", "age", "100"); // 删除表// caller.myDeleteTable(admin, tableName); // 获取指定的数据 caller.getOneRow(connection, tableName, rowkey_bytes); // 批量put数据 String[] str = new String[]{"ROW5", "commonInfo", "city", "Shanghai", "ROW5" , "concelInfo", "age", "35", "ROW6", "concelInfo", "age", "120", "Illegal_Value"}; caller.putList(connection, tableName, str); // 删除两行数据 Delete delete1 = new Delete(Bytes.toBytes("ROW5")); Delete delete = new Delete(Bytes.toBytes("ROW6")); /*也可以定义删除的列族: 其中addCaddColumn是删除最新版本,addCaddColumns 是删除所有版本*/ Table table = connection.getTable(tableName); table.delete(delete1); table.delete(delete); // scan表 caller.scan(connection, tableName); } catch (IOException e) { e.printStackTrace(); } finally { try { // 最后记得关闭集群 if (connection != null) { connection.close(); } } catch (IOException e) { e.printStackTrace(); } } }}
注:代码不用上传到集群运行,类似于JDBC,配置文件中已经有了各种配置信息,数据通过网络套接字进行传输(确定编写代码的网络与集群网络能通信,否则可能会报Will not attempt to authenticate using SASL)
注释
标注1
- HBaseConfiguration 继承了 hadoop.conf.Configuration
- 该类的HBaseConfiguration()和HBaseConfiguration(Configuration c)
构造方法已经被弃用,建议使用create方法
LOG.warn(“instantiating HBaseConfiguration() is deprecated. Please use HBaseConfiguration.create() to construct a plain Configuration”);- HBaseConfiguration.create()方法首先调用hadoop的Configuration()构造conf对象;然后将该conf对象传递给addHbaseResources方法,该方法返回最终的“知道”各种配置信息的conf对象。
*
从这里我们可知:resource文件夹只需要放入hbase-default.xml和hbase-site.xml这两个配置文件即可,然后将resource文件夹添加到classpath
fjksv
标注2
- Hbase2.0.0中,把HTableDescriptor标记为@Deprecated,并会在3.0.0版本被移除
- HTableDescriptor包含有关HBase表的详细信息,例如所有列族的描述符、获取列族数量、列族名字等等
标注3
要进一步缩小要获取的范围,请使用以下方法:
- 要从特定列族获取所有列,请为每个列族执行addFamily进行检索。
- 要获取特定列(qualifier),请对要检索的每个列执行addColumn。
- 要仅在特定范围的版本时间戳内检索列,请执行setTimeRange。
- 要仅检索具有特定时间戳的列,请执行setTimestamp。
- 要限制要返回的每列的版本数,请执行setMaxVersions。
- 要添加过滤器,请调用setFilter。
标注4
Result类(可以直接返回各种Map结构和值)更多的使用方法:
- 要获取Result中所有单元格的完整映射,包括多个系列和多个版本,使用getMap()。
- 要获取每个 family到其列(qualifiers和values)的映射,仅包括每个列的最新版本,请使用getNoVersionMap()。
- 要获得一个个别 family的限定符到最新值的映射,使用getFamilyMap(byte [])。
- 要获取特定family和qualifiers的最新值,使用getValue(byte [],byte [])。返回的结果是Cell对象数组,每个对象包含row, family, qualifier, timestamp, 和value.
- 可以通过方法listCells()访问底层的Cell对象。这将从内部Cell []创建一个列表。
运行截图:
创建表,插入三行数据:
查看表(验证):
list:
通过getValue获取指定信息 & rawCells() 获取指定行所有信息
删除与禁用表:
Scan全表 & Scan特定列
putList方法一次put多条数据:
报错及解决过程
1.Failed to load class “org.slf4j.impl.StaticLoggerBinder
在类路径上放置一个(只有一个)slf4j-nop.jar,slf4j-simple.jar,slf4j-log4j12.jar,slf4j-jdk14.jar或logback-classic.jar可以解决问题(直接在maven中加入)
2.WARN No appenders could be found for logger (org.apache.hadoop.security.Groups).
没有配置文件log4j.xml或者log4j.properties,亦或者路径不对
3.Will not attempt to authenticate using SASL
网络ping不通的原因
4.警告:java.io.IOException: No FileSystem for scheme: hdfs
试过添加core-site.xml和hdfs-site.xml,无效(但是并不影响使用)
参考资源
参考1:HBase官方apidocs(1.2.6)
参考2:《HBase The Definitive Guide》 2nd Edition
参考3:java的变长参数机制
参考4:为什么要使用SLF4J而不是Log4J&SLF4j使用
参考5:易百HBase教程
- Hbase Java API简单实践(附源代码解释)
- JAVA实现的一个简单的死锁(附解释)
- JAVA实现的一个简单的死锁(附解释)
- JAVA实现的一个简单的死锁(附解释)
- 简单TCP服务器端和客户端(源代码)附详细解释
- java桌面程序-简单ATM附源代码
- hbase命令实践与java api代码实践
- Hbase 1.2.1 Java API简单demo
- [hbase]使用HBase的Java API查询HBase(2)
- JAVA 语言实现简单的聊天室,附源代码
- java 文件输入输出(附源代码)
- java 文件输入输出(附源代码)
- 百钱买百鸡问题(附java源代码)
- java记事本(附源代码)
- HBase简单API操作
- HBase Client API简单
- hBase-thrift 实践(java)
- 使用Java Api 对HBase进行简单操作
- 选Apache还是Nginx?
- 增广贤文
- 数字时钟
- 中国可重复使用火箭方案首次公布,已突破一些关键技术
- 摩拜将支持 iPhone 相机扫码解锁;芝麻分 700 以上,日本 5 年签免流水 | 雷锋早报
- Hbase Java API简单实践(附源代码解释)
- pyenv安装python报出警告
- 朱刘算法详解?
- uboot1.1.6顶层makefile详解
- 刘作虎自曝一加 5 真机图,将上 8GB RAM?
- 苹果替Google和Facebook做了件大好事
- 乐视云盘关停,网盘模式的出路在哪?
- 独角兽榜单又被ofo抢走,另一家又要哭了
- 联想、富士通PC业务整合谈判接近尾声