HBase案例-谷粒微博(尚硅谷)

1 需求分析

模拟微博运行过程,可以实现发布微博、关注/取关用户、查看用户微博内容等功能。

2 数据表设计

共设计3张表,分别是微博内容表、用户关系表、微博收件箱表。

2.1 微博内容表

设计一个列族info,包含一个列Content,用于存放发布的微博内容。行键设计为用户id+时间戳,每行数据表示某个用户在某一时刻发布的微博内容。所有用户发布的所有微博内容均存储在一张表中。

2.2 用户关系表

设计两个列族attends、fans,表示当前用户的关注者和粉丝。每个列族可以有很多列,每个列表示一个具体的用户,列的值与列名保持相同,仅起到标识用户的作用。行键设计为用户id。如果某个用户没有关注任何其他用户,则该行数据的attends列族为空;同样地,如果没有粉丝,则fans列族为空。

2.3 微博收件箱表(初始化页面表)

设计为一个列族info,可以包括多个列,分别表示当前用户关注的其他用户。行键设计为用户id,每行数据表示当前用户关注的其他用户所发布的微博内容,每列的值设置3个版本,内容为关注者所发布的微博内容的行键(uid+ts)。即列的值是第一张表(微博内容表)的外键。

数据表示例如下:

3 项目架构

创建Maven工程,在pom.xml文件中添加项目所需要的依赖。

    <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>

在src/main/resources文件夹下添加配置文件hbase-site.xml,其中包含项目要连接的集群信息,这样就不用在代码里再设置了。

在src/main/java下创建4个Java包:constants、utils、dao、test,分别表示常量、工具类、数据操作、测试。

4 定义常量

在constants包下创建Constants类,存放项目需要用到的常量。包括HBase配置信息、命名空间、微博内容表及其列族信息、用户关系表及其列族信息、收件箱表及其列族信息。

public class Constants {

    // HBase配置信息
    public static final Configuration CONFIGURATION = HBaseConfiguration.create();

    // 命名空间
    public static final String NAMESPACE = "weibo";

    // 微博内容表
    public static final String CONTENT_TABLE = "weibo:content";
    public static final String CONTENT_TABLE_CF = "info";
    public static final int CONTENT_TABLE_VERSIONs = 1;

    // 用户关系表
    public static final String RELATION_TABLE = "weibo:relation";
    public static final String RELATION_TABLE_CF1 = "attends";
    public static final String RELATION_TABLE_CF2 = "fans";
    public static final int RELATION_TABLE_VERSIONs = 1;

    // 收件箱表
    public static final String INBOX_TABLE = "weibo:inbox";
    public static final String INBOX_TABLE_CF = "info";
    public static final int INBOX_TABLE_VERSIONS = 2;
}

5 HBaseUtil封装

在utils包下创建HBaseUtil类,提供项目通用的功能,包括创建命名空间、判断表是否存在、创建表。

public class HBaseUtil {

    // 1 创建命名空间
    public static void createNamespace(String nameSpace) throws IOException {
        // 1 获取Connection对象
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
        // 2 获取Admin对象
        Admin admin = connection.getAdmin();
        // 3 构建命名空间描述器
        NamespaceDescriptor namespaceDescriptor = NamespaceDescriptor.create(nameSpace).build();
        // 4 创建命名空间
        admin.createNamespace(namespaceDescriptor);
        // 5 关闭资源
        admin.close();
        connection.close();
    }

    // 2 判断表是否存在
    private static boolean isTableExist(String tableName) throws IOException {
        // 1 获取Connection对象
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
        // 2 获取Admin对象
        Admin admin = connection.getAdmin();
        // 3 判断是否存在
        boolean exists = admin.tableExists(TableName.valueOf(tableName));
        // 4 关闭资源
        admin.close();
        connection.close();
        // 5 返回结果
        return exists;
    }

    // 3 创建表
    public static void createTable(String tableName, int versions, String... cfs) throws IOException {
        // 1 判断是否传入了列族信息
        if(cfs.length <= 0){
            System.out.println("请传入列族信息");
            return;
        }
        // 2 判断表是否存在
        if(isTableExist(tableName)){
            System.out.println("表" + tableName + "已存在");
            return;
        }
        // 3 获取Connection对象
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
        // 4 获取Admin对象
        Admin admin = connection.getAdmin();
        // 5 创建表描述器
        HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));
        // 6 添加列族信息
        for (String cf : cfs) {
            HColumnDescriptor hColumnDescriptor = new HColumnDescriptor(cf);
            // 7 设置版本
            hColumnDescriptor.setMaxVersions(versions);
            hTableDescriptor.addFamily(hColumnDescriptor);
        }
        // 8 创建表对象
        admin.createTable(hTableDescriptor);
        // 9 关闭资源
        admin.close();
        connection.close();
    }
}

6 HBaseDao封装

在dao包下创建HBaseDao类,实现与业务相关的数据操作功能。

6.1 发布微博

    // 1 发布微博
    public static void publishWeiBo(String uid, String content) throws IOException {
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);

        // 第一部分:操作微博内容表
        Table contTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
        long ts = System.currentTimeMillis();
        String rowKey = uid + "_" + ts;
        Put contPut = new Put(Bytes.toBytes(rowKey));
        contPut.addColumn(Bytes.toBytes(Constants.CONTENT_TABLE_CF), Bytes.toBytes("content"), Bytes.toBytes(content));
        contTable.put(contPut);

        // 第二部分:操作微博收件箱表
        Table relaTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
        Get relaGet = new Get(Bytes.toBytes(uid));
        relaGet.addFamily(Bytes.toBytes(Constants.RELATION_TABLE_CF2));
        Result result = relaTable.get(relaGet);

        ArrayList<Put> inboxPuts = new ArrayList<>();

        for (Cell cell : result.rawCells()) {
            Put inboxPut = new Put(CellUtil.cloneQualifier(cell));
            inboxPut.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(uid), Bytes.toBytes(rowKey));
            inboxPuts.add(inboxPut);
        }
        // 判断当前uid是否有粉丝
        if (inboxPuts.size() > 0){
            Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
            inboxTable.put(inboxPuts);
            inboxTable.close();
        }

        // 关闭资源
        relaTable.close();
        contTable.close();
        connection.close();
    }

难点是要同时操作3张表,要处理3张表之间的联系。某个uid用户发布了一条微博,系统做了这些事情:在微博内容表中,添加一条数据;在用户关系表中,查找uid的fans用户;在微博收件箱表中,为所有fans用户更新当前uid用户的微博发布信息。

微博内容表中,行键设计为uid+时间戳。

        Table contTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
        long ts = System.currentTimeMillis();
        String rowKey = uid + "_" + ts;
        Put contPut = new Put(Bytes.toBytes(rowKey));
        contPut.addColumn(Bytes.toBytes(Constants.CONTENT_TABLE_CF), Bytes.toBytes("content"), Bytes.toBytes(content));
        contTable.put(contPut);

在用户关系表中,查找当前uid的fans信息。遍历每一个粉丝,在微博收件箱表中为每个粉丝更新uid用户的微博发布信息。构建Get对象,行键为uid,列族为fans,传入用户关系表中,返回一个result对象,其中包含uid的所有粉丝信息。

        Table relaTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
        Get relaGet = new Get(Bytes.toBytes(uid));
        relaGet.addFamily(Bytes.toBytes(Constants.RELATION_TABLE_CF2));
        Result result = relaTable.get(relaGet);

解析result对象,获取每一个cell对象,每个cell对象包含列族fans中的各列的列名及列的值。将获取到的粉丝信息作为新的行键,构建微博收件箱表的Put对象,列族为info,列名为当前用户的uid,值是当前新添加微博的行键(uid+时间戳)。每一个Put对象存放在创建的ArrayList列表中。循环遍历每一个粉丝,进行如上操作。

        ArrayList<Put> inboxPuts = new ArrayList<>();

        for (Cell cell : result.rawCells()) {
            Put inboxPut = new Put(CellUtil.cloneQualifier(cell));
            inboxPut.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(uid), Bytes.toBytes(rowKey));
            inboxPuts.add(inboxPut);
        }

如果当前uid用户没有粉丝(ArrayList列表为空),则不对微博收件箱表做任何操作。否则,获取微博收件箱表对象,并将ArrayList列表中的Put对象传入到微博收件箱表中。

        // 判断当前uid是否有粉丝
        if (inboxPuts.size() > 0){
            Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
            inboxTable.put(inboxPuts);
            inboxTable.close();
        }

最后,关闭资源。

        // 关闭资源
        relaTable.close();
        contTable.close();
        connection.close();

6.2 关注用户

    // 2 关注用户
    public static void addAttends(String uid, String... attends) throws IOException {
        if (attends.length <= 0) {
            System.out.println("请传入要关注的人");
            return;
        }

        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);

        // 添加uid用户的关注人,且同步更新关注人的粉丝信息
        Table relaTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
        Put attendPut = new Put(Bytes.toBytes(uid));
        ArrayList<Put> relaPuts = new ArrayList<>();
        for (String attend : attends) {  // 对于每一个attend对象,要同时做两件事情
            attendPut.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_CF1), Bytes.toBytes(attend), Bytes.toBytes(attend));

            Put fanPut = new Put(Bytes.toBytes(attend));
            fanPut.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_CF2), Bytes.toBytes(uid), Bytes.toBytes(uid));
            relaPuts.add(fanPut);
        }
        relaPuts.add(attendPut);  // 对于同一行数据,可以一次添加多个列
        relaTable.put(relaPuts);

        // 更新微博收件箱表信息
        Table contTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
        Put inboxPut = new Put(Bytes.toBytes(uid));
        for (String attend : attends) {
            Scan scan = new Scan(Bytes.toBytes(attend + "_"), Bytes.toBytes(attend + "|"));
            ResultScanner resultScanner = contTable.getScanner(scan);

            // 定义一个时间戳,以解决多条数据同一时间戳的问题
            long ts = System.currentTimeMillis();

            for (Result result : resultScanner) {
                inboxPut.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(attend), ts++, result.getRow());
            }
        }
        // 判断uid所关注的人是否有发过微博
        if (!inboxPut.isEmpty()) {
            Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
            inboxTable.put(inboxPut);
            inboxTable.close();
        }

        // 关闭资源
        relaTable.close();
        contTable.close();
        connection.close();
    }

关注用户同样需要操作3张表。在用户关系表中,当前uid用户在attends列族中添加若干个用户,同时这些用户在fans列族中添加当前uid用户。此外,在微博收件箱表中,以当前uid用户为行键,在列族info中添加若干个列,每列表示一个关注人,每列的值存放此关注人发布的微博的行键(版本存放2条)。

循环遍历传入的attends参数,在每一次遍历中要同时做两件事情。一是为uid添加关注人,二是为关注人添加粉丝(uid)。这个地方要注意,对于同一行数据,Put对象可以分步骤添加多个列。而对于不同行数据,需要分别创建Put对象并添加到ArrayList<Put>列表中,之后再传入至用户关系表对象中。

        // 添加uid用户的关注人,且同步更新关注人的粉丝信息
        Table relaTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
        Put attendPut = new Put(Bytes.toBytes(uid));
        ArrayList<Put> relaPuts = new ArrayList<>();
        for (String attend : attends) {  // 对于每一个attend对象,要同时做两件事情
            attendPut.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_CF1), Bytes.toBytes(attend), Bytes.toBytes(attend));

            Put fanPut = new Put(Bytes.toBytes(attend));
            fanPut.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_CF2), Bytes.toBytes(uid), Bytes.toBytes(uid));
            relaPuts.add(fanPut);
        }
        relaPuts.add(attendPut);  // 对于同一行数据,可以一次添加多个列
        relaTable.put(relaPuts);

在微博收件箱表中,对于当前uid用户,为其添加新关注的人,并更新这些人发布的微博信息。这两个信息分别由列名和列值承载,所以可以通过一个Put对象同时添加。新关注人发布的微博信息通过Scan对象获取,指定起始和终止的行键,根据字典序的排序规则以及微博内容表中“uid+时间戳”的行键设计规则,设计起始行键为“attend_”,终止行键为“attend|”,其中attend是要关注的人。这样设计可以获取到某个用户所发布的全部微博的行键(字典序,有比没有大,而且字符|大于字符_)。微博内容表的getScanner()方法返回ResultScanner对象,其中包含若干个Result对象。每一个Result对象表示一行数据,包含所需要的行键信息。手动定义一个时间戳,以解决多条数据同一时间戳的问题。最后,如果当前uid用户新关注的人都没有发布过微博,则不用对微博收件箱表做任何操作;当至少有一条微博时,才进行后续操作。

        // 更新微博收件箱表信息
        Table contTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
        Put inboxPut = new Put(Bytes.toBytes(uid));
        for (String attend : attends) {
            Scan scan = new Scan(Bytes.toBytes(attend + "_"), Bytes.toBytes(attend + "|"));
            ResultScanner resultScanner = contTable.getScanner(scan);

            // 定义一个时间戳,以解决多条数据同一时间戳的问题
            long ts = System.currentTimeMillis();

            for (Result result : resultScanner) {
                inboxPut.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(attend), ts++, result.getRow());
            }
        }
        // 判断uid新关注的人是否有发过微博
        if (!inboxPut.isEmpty()) {
            Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
            inboxTable.put(inboxPut);
            inboxTable.close();
        }

        // 关闭资源
        relaTable.close();
        contTable.close();
        connection.close();

6.3 取关用户

    // 3 取关用户
    public static void deleteAttends(String uid, String... dels) throws IOException {
        if (dels.length <= 0) {
            System.out.println("请传入要取关的用户");
            return;
        }

        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);

        // 更新用户关系表
        Table relaTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
        Delete uidDelete = new Delete(Bytes.toBytes(uid));

        ArrayList<Delete> attendDeletes = new ArrayList<>();

        for (String del : dels) {
            uidDelete.addColumns(Bytes.toBytes(Constants.RELATION_TABLE_CF1), Bytes.toBytes(del));

            Delete attendDelete = new Delete(Bytes.toBytes(del));
            attendDelete.addColumns(Bytes.toBytes(Constants.RELATION_TABLE_CF2), Bytes.toBytes(uid));
            attendDeletes.add(attendDelete);
        }
        relaTable.delete(uidDelete);
        relaTable.delete(attendDeletes);

        // 更新微博收件箱表
        Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
        Delete inboxDelete = new Delete(Bytes.toBytes(uid));
        for (String del : dels) {
            inboxDelete.addColumns(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(del));
        }
        inboxTable.delete(inboxDelete);

        // 关闭资源
        inboxTable.close();
        relaTable.close();
        connection.close();
    }

与关注用户逻辑类似,但更简单些,因为只涉及到用户关系表和微博收件箱表两张表。其中,调用了Delete对象的addColumns()方法而不是addColumn()方法,addColumns()方法可以同时删掉多个版本。

6.4 获取某个人的初始化页面数据

    // 4 获取某个人的初始化页面数据
    public static void getInit(String uid) throws IOException {
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);

        Table contTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
        Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
        Get inboxGet = new Get(Bytes.toBytes(uid));
        inboxGet.setMaxVersions();  // 为Get对象设置最大版本
        Result result = inboxTable.get(inboxGet);

        for (Cell cell : result.rawCells()) {
            Get contGet = new Get(CellUtil.cloneValue(cell));
            Result contResult = contTable.get(contGet);
            for (Cell contCell : contResult.rawCells()) {
                System.out.println("RK:" + Bytes.toString(CellUtil.cloneRow(contCell))+
                        ", ColumnFamily:" + Bytes.toString(CellUtil.cloneFamily(contCell)) +
                        ", ColumnName:" + Bytes.toString(CellUtil.cloneQualifier(contCell)) +
                        ", value:" + Bytes.toString(CellUtil.cloneValue(contCell)));
            }
        }

        // 关闭资源
        inboxTable.close();
        contTable.close();
        connection.close();
    }

根据当前uid用户遍历微博收件箱表,找到其关注的所有人,打印出这些人发布的若干条微博数据(由版本数控制)。

创建Get对象,传入uid作为行键,并设置最大版本。传入收件箱表对象,返回一个Result对象,其中包含uid所关注的所有用户的若干条微博的行键。

        Table contTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
        Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
        Get inboxGet = new Get(Bytes.toBytes(uid));
        inboxGet.setMaxVersions();  // 为Get对象设置最大版本
        Result result = inboxTable.get(inboxGet);

获取result对象的所有Cell对象,每一个Cell对象包含一条微博数据的一个行键。遍历所有Cell对象,获取微博数据的行键,之后再微博内容表中根据行键找到相应微博数据,此时返回一个Result对象。这个Result对象只包含一个Cell对象(Cell是HBase中的概念,是由列族、列名、时间戳唯一确定的单元),获取这个Cell对象,进一步地获取行键、列族、列名、值的信息。这里的难点在于对Result和Cell的理解。

        for (Cell cell : result.rawCells()) {
            Get contGet = new Get(CellUtil.cloneValue(cell));
            Result contResult = contTable.get(contGet);
            for (Cell contCell : contResult.rawCells()) {
                System.out.println("RK:" + Bytes.toString(CellUtil.cloneRow(contCell))+
                        ", ColumnFamily:" + Bytes.toString(CellUtil.cloneFamily(contCell)) +
                        ", ColumnName:" + Bytes.toString(CellUtil.cloneQualifier(contCell)) +
                        ", value:" + Bytes.toString(CellUtil.cloneValue(contCell)));
            }
        }

        // 关闭资源
        inboxTable.close();
        contTable.close();
        connection.close();

6.5 获取某个人的所有微博详情

    // 5 获取某个人的所有微博详情
    public static void getWeiBo(String uid) throws IOException {
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);

        Table contTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));

        // 使用Scan起始终止行键的方法
        // Scan scan = new Scan(Bytes.toBytes(uid + "_"), Bytes.toBytes(uid + "|"));
        Scan scan = new Scan();
        // 构建过滤器
        RowFilter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator(uid + "_"));
        scan.setFilter(rowFilter);
        ResultScanner resultScanner = contTable.getScanner(scan);

        for (Result result : resultScanner) {
            for (Cell contCell : result.rawCells()) {
                System.out.println("RK:" + Bytes.toString(CellUtil.cloneRow(contCell))+
                        ", ColumnFamily:" + Bytes.toString(CellUtil.cloneFamily(contCell)) +
                        ", ColumnName:" + Bytes.toString(CellUtil.cloneQualifier(contCell)) +
                        ", Value:" + Bytes.toString(CellUtil.cloneValue(contCell)));
            }
        }

        // 关闭资源
        contTable.close();
        connection.close();
    }

可以使用Scan起始终止行键的方法,也可以使用过滤器的方法。这里创建一个行键过滤器RowFilter,第一个参数传入比较符,第二个参数传入要比较的对象。之后通过setFilter()方法为Scan对象添加过滤器。

        Scan scan = new Scan();
        // 构建过滤器
        RowFilter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator(uid + "_"));
        scan.setFilter(rowFilter);
        ResultScanner resultScanner = contTable.getScanner(scan);

7 测试代码

在test包下新建TestWeiBo类,存放程序主函数。

public class TestWeiBo {
    public static void init() {
        try {
            // 创建命名空间
            HBaseUtil.createNamespace(Constants.NAMESPACE);
            // 创建微博内容表
            HBaseUtil.createTable(Constants.CONTENT_TABLE, Constants.CONTENT_TABLE_VERSIONS, Constants.CONTENT_TABLE_CF);
            // 创建用户关系表
            HBaseUtil.createTable(Constants.RELATION_TABLE, Constants.RELATION_TABLE_VERSIONS, Constants.RELATION_TABLE_CF1, Constants.RELATION_TABLE_CF2);
            // 创建微博收件箱表
            HBaseUtil.createTable(Constants.INBOX_TABLE, Constants.INBOX_TABLE_VERSIONS, Constants.INBOX_TABLE_CF);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        // 初始化
        init();

        // 1001发布微博
        HBaseDao.publishWeiBo("1001", "1001的第一条微博");
        // 1002关注1001和1003
        HBaseDao.addAttends("1002", "1001", "1003");
        // 获取1002初始化页面
        HBaseDao.getInit("1002");
        System.out.println("------------111------------");
        // 1003发布3条微博,同时1001发布2条微博
        Thread.sleep(10);
        HBaseDao.publishWeiBo("1003", "1003的第一条微博");
        Thread.sleep(10);
        HBaseDao.publishWeiBo("1001", "1001的第二条微博");
        Thread.sleep(10);
        HBaseDao.publishWeiBo("1003", "1003的第二条微博");
        Thread.sleep(10);
        HBaseDao.publishWeiBo("1001", "1001的第三条微博");
        Thread.sleep(10);
        HBaseDao.publishWeiBo("1003", "1003的第三条微博");
        // 获取1002初始化页面
        HBaseDao.getInit("1002");
        System.out.println("------------222------------");
        // 1002取关1003
        HBaseDao.deleteAttends("1002", "1003");
        // 获取1002初始化页面
        HBaseDao.getInit("1002");
        System.out.println("------------333------------");
        // 1002再次关注1003
        HBaseDao.addAttends("1002", "1003");
        // 获取1002初始化页面
        HBaseDao.getInit("1002");
        System.out.println("------------444------------");
        // 获取1001微博详情
        HBaseDao.getWeiBo("1001");
        System.out.println("------------555------------");
        // 获取1003微博详情
        HBaseDao.getWeiBo("1003");
    }
}

程序运行结果如下:(结果有点不对,还没有解决这个问题..


参考:尚硅谷HBase教程(hbase框架快速入门)

原文地址:https://www.cnblogs.com/wangmengdx/p/15140498.html