美文网首页hbase
HBASE之JAVA API学习笔记

HBASE之JAVA API学习笔记

作者: 我是老薛 | 来源:发表于2018-12-09 09:43 被阅读0次

本文是对如何使用Hbase提供的Java Api编写Hbase客户端程序的一个学习总结,共包括如下章节的内容:

  • 概述
  • 更新和查询数据
  • 创建和更新表结构
  • 编译和运行
  • 远程访问Hbase

参考资料:

1、如果需要了解Hbase的基本知识,可参见《HBASE学习笔记》一文。

一、概述

Hbase提供了一套Java API,可以让编写java程序来访问Hbase。从HBase 1.0开始,Hbase提供了新的Api替代以前的老的Api,新的Api更加干净和直观。
新的Api使用接口,而不是特定的类来操作API。新的Api,使用ConnectionFactory的工厂方法来获取一个Connection对象,然后调用getAdmin()和getTable()方法来获取Admin和Table实例,接着使用实例完成具体操作,最后关闭实例和连接。
本文用的hbase版本是hbase2.0.2,将使用新的API进行介绍。新旧API一个非常直观的区别是,带H开头的是旧的API,如Htable,HBaseAdmin,HTableDescriptor,HColumnDescriptor等。
java客户端是通过zookeeper找到hbase服务的,因此,需要在代码中配置zookeeper的信息。

二、更新和查询数据

我们通过具体例子来说明,假设hbase中已经有一张表test,表中有一个列族data。关键的API是Table接口。

(一)插入数据

本例子代码是往表中插入数据,完整的代码如下:

package com.hbaseexample;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.util.Bytes;
public class Example {
    private Connection connection = null;
    private Table table = null;
    public static void main(String[] args) {
        Example example = new Example();
        try {
            example.conn();
            example.addData();
            System.out.println("finish");
        } catch (IOException e) {e.printStackTrace();
        } finally {example.close();}
    }
    public void conn() throws IOException {
        Configuration config = HBaseConfiguration.create();
        config.set("hbase.zookeeper.quorum", "127.0.0.1");//zk地址
        config.set("hbase.zookeeper.property.clientPort", "2181");//zk端口
        connection = ConnectionFactory.createConnection(config);
        table = connection.getTable(TableName.valueOf("test"));
    }
    public void close() {
        if (table != null)
            try {table.close();} catch (IOException e) {}
        if (connection != null)
            try {connection.close();    } catch (IOException e) {}
    }
    public void addData() throws IOException {
        Put p = new Put(Bytes.toBytes("rk_100001")); //行键 
        //addColumn第1个参数是列族,第2个参数是列名,第3个参数是插入的值
        p.addColumn(Bytes.toBytes("data"), Bytes.toBytes("uname"), Bytes.toBytes("tom"));
        p.addColumn(Bytes.toBytes("data"), Bytes.toBytes("age"), Bytes.toBytes("25"));
        table.put(p);
    }
}

上面代码是一个完整的带main方法的java类,首先获得一个Connection对象,然后获取到Table对象,再创建一个Put对象保存要插入的数据,最后利用Table对象提交数据。

下面例子代码都是在这个代码基础上运行。

(二)按键值查询数据

如果我们要查询指定的数据,可以利用Get对象来完成,代码如下(这里只给出了片段代码,只需把这代码加入到上面插入数据的完整例子代码中就可以使用):

    public void getData() throws IOException{
        Get g = new Get(Bytes.toBytes("rk_100001"));
        Result r = table.get(g);
        byte[] value = r.getValue(Bytes.toBytes("data"), Bytes.toBytes("uname"));
        String valueStr = Bytes.toString(value);
        System.out.println("uname:"+valueStr);
        value = r.getValue(Bytes.toBytes("data"), Bytes.toBytes("age"));
        System.out.println("age:"+Bytes.toString(value));
    }

上面代码根据表的键值创建Get对象,然后利用Table对象获取到结果集,最后通过结果集获取各单元值。

(三)扫描表

在某项场景下,我们不知道键值的时候,可以利用Scan对象扫描整个表,遍历所需的数据。例子如:

public void scanData() throws IOException{
    Scan s = new Scan();
    ResultScanner scanner = table.getScanner(s);
    try {
         for (Result rr : scanner) {
             byte[] row = rr.getRow();
             byte[] uname = rr.getValue(Bytes.toBytes("data"), Bytes.toBytes("uname"));
             byte[] age = rr.getValue(Bytes.toBytes("data"), Bytes.toBytes("age"));
             System.out.println("Found:" + Bytes.toString(row)+","+Bytes.toString(uname)+","+Bytes.toString(age));
         }
    } finally {
        scanner.close();
    }
}

上面代码对整个表进行扫描。我们也可以给Scan对象增加条件,只获取指定的列族或指定列的数据。

如果指定获取列族的数据,可以如下面代码:

public void scanData() throws IOException{
    Scan s = new Scan();
    s.addFamily(Bytes.toBytes("data"));
    ResultScanner scanner = table.getScanner(s);
    try {
         for (Result rr : scanner) {
             byte[] row = rr.getRow();
             byte[] uname = rr.getValue(Bytes.toBytes("data"), Bytes.toBytes("uname"));
             byte[] age = rr.getValue(Bytes.toBytes("data"), Bytes.toBytes("age"));
             System.out.println("Found:" + Bytes.toString(row)+","+Bytes.toString(uname)+","+Bytes.toString(age));
         }
    } finally {
        scanner.close();
    }
}

上面代码,调用Scan对象的addFamily方法限定只获取指定列族的数据,可以调用多次,增加多个列族。

我们也可以指定到具体的列,如下面代码:

public void scanData() throws IOException{
    Scan s = new Scan();
    s.addColumn(Bytes.toBytes("data"), Bytes.toBytes("uname"));
    s.addColumn(Bytes.toBytes("data"), Bytes.toBytes("age"));
    ResultScanner scanner = table.getScanner(s);
    try {
         for (Result rr : scanner) {
             byte[] row = rr.getRow();
             byte[] uname = rr.getValue(Bytes.toBytes("data"), Bytes.toBytes("uname"));
             byte[] age = rr.getValue(Bytes.toBytes("data"), Bytes.toBytes("age"));
             System.out.println("Found:" + Bytes.toString(row)+","+Bytes.toString(uname)+","+Bytes.toString(age));
         }
    } finally {
        scanner.close();
    }
}

(四)获取表的列族信息

有时我们可能需要通过代码获取hbase表定义的列族信息,例子代码如下:

public void getCfInfo() throws IOException{
    ColumnFamilyDescriptor[] columnFamilyDescriptors = table.getDescriptor().getColumnFamilies();
    for (ColumnFamilyDescriptor columnFamilyDescriptor : columnFamilyDescriptors) {
        String familyName = columnFamilyDescriptor.getNameAsString();
        System.out.println(familyName);
    }
}

(五)删除表中数据

我们可以按照键值来删除数据,如下面代码。

public void deleteData() throws IOException{
        String[] rowKeys={"row1","row3"};
        List<Delete> deleteList = new ArrayList<>(rowKeys.length);
        Delete delete;
        for (String rowKey : rowKeys) {
            delete = new Delete(Bytes.toBytes(rowKey));
            deleteList.add(delete);
        }
        table.delete(deleteList);
    }

三、创建和更新表结构

我们可以编写java代码创建表、删除表、修改表的结构,类似关系数据库的DDL语句。关键的Api是Admin接口。

(一)查看所有的表

类似hbase命令行中list命令,显示当前用户下所有的hbase表,例子代码如下。

package com.hbaseexample;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
public class AdminExample {
    private Connection connection = null;
    private Admin admin = null;
    public static void main(String[] args) {
        AdminExample example = new AdminExample();
        try {
            example.conn();
            example.listTable();
            System.out.println("finish");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            example.close();
        }
    }
    public void conn() throws IOException {
        Configuration config = HBaseConfiguration.create();
        config.set("hbase.zookeeper.quorum", "127.0.0.1");//zk地址
        config.set("hbase.zookeeper.property.clientPort", "2181");//zk端口
        connection = ConnectionFactory.createConnection(config);
        admin = connection.getAdmin();
    }
    public void close() {
        if (admin != null)
            try {admin.close(); } catch (IOException e) {}
        if (connection != null)
            try {connection.close();} catch (IOException e) {}
    }
    public void listTable() throws IOException{
        TableName[] names = admin.listTableNames();
        for(TableName name:names){
            System.out.println(name.getNameAsString());
        }
    }
}

上面代码是一个完整的带main方法的java类,首先获得一个Connection对象,然后获取到Admin对象,然后调用Admin对象的方法。下面的例子都是在上面代码基础上运行。

(二)创建/删除表

下面代码演示了检查一个表是否存在,如果存在,则先使之无效,再删除,然后创建一个新表。代码如下(不是完整代码,可放到上面完整例子代码中运行):

public void doDDL() throws IOException {
    TableName tableName = TableName.valueOf("test");
    boolean re = admin.tableExists(tableName );
    if(re){
        admin.disableTable(tableName);
        admin.deleteTable(tableName);
    }
    TableDescriptorBuilder tableDesc = TableDescriptorBuilder.newBuilder(tableName);
    tableDesc.setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf1")).build());
    tableDesc.setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf2")).build());
    admin.createTable(tableDesc.build());
}

(三)删除表的列族

例子代码如下

public void deleteCF() throws IOException{
    TableName tableName = TableName.valueOf("test");
    admin.deleteColumnFamily(tableName, Bytes.toBytes("cf2"));
}

(四)添加表的列族

例子代码如下

public void addCF() throws IOException{
    TableName tableName = TableName.valueOf("test");
    ColumnFamilyDescriptor cf =ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf2")).build();
    admin.addColumnFamily(tableName, cf );
    cf =ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf3")).build();
    admin.addColumnFamily(tableName, cf );
}

(五)修改已存在的列族

例子代码如下

public void modifyCF() throws IOException{
    TableName tableName = TableName.valueOf("test");
    ColumnFamilyDescriptorBuilder builder = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf1"));
    builder.setMaxVersions(3);
    admin.modifyColumnFamily(tableName, builder.build());
}

创建列族时,默认列族支持的最多存储的版本数是1,上面例子代码将列族cf1支持的最多存储版本数改为3。

四、编译和运行

上面的例子只是给出了程序的源代码,那怎么编译和执行呢?因为代码中引入了很多Hbase JAVA API的接口和类,需要引入相应的jar包。编写的java hbase客户端程序所依赖的jar包都位于hbase安装目录的lib目录下。
想要成功编译上面代码,只需引入如下三个jar包即可(均在lib目录下可找到):

hadoop-common-2.7.7.jar
hbase-common-2.0.2.jar
hbase-client-2.0.2.jar

但要想运行程序,需要依赖更多的jar包。这有两种方式:
1、将lib根目录下所有的jar包加入到classpath路径中(如果在ide中运行,则全部引入),这样可以在IDE(如eclipse)中直接运行。

2、把程序编译后的class打成jar包。然后利用hbase提供的命令行客户端脚本程序hbase来执行,这样就不需要关心要引入哪些jar包了。
执行hbase脚本前,需要先将打成的jar包加入到HBASE_CLASSPATH环境变量中。然后运行hbase,后面跟着可执行类名。如在linux下,在控制台下执行如下两条语句即可:

export HBASE_CLASSPATH= 上面打成的jar包
hbase com.hbaseexample.Example

如在windows下,在dos命令行执行,需要将export命令改成set命令。

五、远程访问hbase

如果我们希望编写的hbase客户端程序(位于windows系统下)能操作安装在远程的Linux机器上的hbase服务。这时需要做一些额外的配置。
当然前提是hbase客户端程序将运行的机器上已经有hbase客户端可运行的环境。

需要做如下配置:
1、配置远程hbase服务器支持远程连接。这时需要在hbase-site.xml中添加一个属性配置,属性名 hbase.zookeeper.quorum ,属性值为远程服务器主机的主机名(注意,一定不是IP地址,而是主机名)。如下面配置例子:

  <property>
    <name>hbase.zookeeper.quorum</name>
    <value>master</value>
  </property>

上面的master是远程服务器的主机名,不能配置成IP地址。

2、修改hbase客户端程所在的windows机器上的配置文件:C:\Windows\System32\drivers\etc\hosts 在其中添加远程Linux机器IP地址和主机名的对应关系。如:
192.168.2.6 master
上面的192.168.2.6是远程Linux主机的IP地址,master是远程Linux主机的主机名,要与hbase-site.xml中的配置一致。

3、修改java代码
将上面例子中的代码中的如下语句
config.set("hbase.zookeeper.quorum", "127.0.0.1");
改成如下的语句
config.set("hbase.zookeeper.quorum", "192.168.2.6");
上面的192.168.2.6是Linux服务器的IP地址,也可以用主机名master来替换IP地址,如:
config.set("hbase.zookeeper.quorum", "master");
经过上面的设置后,java程序在windows下运行,就可以连接运行在远程linux上的hbase服务了。

下面简单介绍下linux下查看和修改主机名的方法,我们这里用的是版本是Centos7的linux操作系统。方法如下:

1、查看主机名的命令 hostname,默认情况下,Linxu的主机名叫localhost.localdomain

2、修改主机名,可以临时修改(机器重启将无效),或永久生效。
临时修改只需执行如下命令:
sudo hostname 设置的主机名
如果要永久生效,就需要修改Linux下的配置文件** /etc/hostname**,将其中的内容改成要设置的主机名,如master。根据需要,我们还可以修改 /etc/hosts文件,添加IP地址与主机名的对应关系。

相关文章

网友评论

    本文标题:HBASE之JAVA API学习笔记

    本文链接:https://www.haomeiwen.com/subject/bjjhhqtx.html