HBase回顾六、蛇皮项目实战演练

HBase回顾六、 HBase实战

参考自尚硅谷HBase教程http://www.atguigu.com/

需求分析

  1. 微博内容的浏览,数据库表设计

  2. 用户社交体现:关注用户,取关用户

  3. 拉取关注的人的微博内容

在这里插入图片描述 在这里插入图片描述

代码实现

1 代码设计总览:

  1. 创建命名空间以及表名的定义

  2. 创建微博内容表

  3. 创建用户关系表

  4. 创建用户微博内容接收邮件表

  5. 发布微博内容

  6. 添加关注用户

  7. 获取用户的初始化页面信息

  8. 获取用户微博详情

  9. 测试


2 创建命名空间以及各个表

content表结构:

方法名creatTableeContent
Table Nameweibo:content
RowKey用户ID_时间戳
ColumnFamilyinfo
ColumnLabel标题,内容,图片
Version1个版本

relation表结构:

方法名createTableRelations
Table Nameweibo:relations
RowKey用户ID
ColumnFamilyattends、fans
ColumnLabel关注用户ID,粉丝用户ID
ColumnValue用户ID
Version1个版本

inbox表结构:

方法名createTableReceiveContentEmails
Table Nameweibo:receive_content_email
RowKey用户ID
ColumnFamilyinfo
ColumnLabel用户ID
ColumnValue取微博内容的RowKey
Version1000

测试代码

package com.chanzany.test;

import com.chanzany.constants.Constants;
import com.chanzany.utils.HBaseUtil;

import java.io.IOException;

public class TestCreateTable {
   public static void main(String[] args) throws IOException {
       HBaseUtil.createNameSpace(Constants.NAMESPACE);
       HBaseUtil.createTable(Constants.CONTENT_TABLE,Constants.CONTENT_TABLE_VERSIONS,Constants.CONTENT_TABLE_FAMILY);
       HBaseUtil.createTable(Constants.RELATION_TABLE,Constants.RELATION_TABLE_VERSIONS,Constants.RELATION_TABLE_FAMILY1,Constants.RELATION_TABLE_FAMILY2);
       HBaseUtil.createTable(Constants.INBOX_TABLE,Constants.INBOX_TABLE_VERSIONS,Constants.INBOX_TABLE_FAMILY);

   }
}

工具类

package com.chanzany.utils;

import com.chanzany.constants.Constants;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;

import java.io.IOException;

/**
 * 操作HBase的工具类
 * 1. 创建命名空间
 * 2. 判断表是否存在
 * 3. 表的创建(三张表)
 */
public class HBaseUtil {
    private static ThreadLocal<Connection> connHolder = new ThreadLocal<Connection>();
    private static ThreadLocal<Admin> adminHolder = new ThreadLocal<Admin>();

    static {
        Connection conn = connHolder.get();
        Admin admin =adminHolder.get() ;
        if (conn == null) {
            try {
                conn = ConnectionFactory.createConnection(Constants.CONFIGURATION);
                connHolder.set(conn);
                if (admin == null){
                    admin=conn.getAdmin();
                    adminHolder.set(admin);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }



    /**
     * 创建命名空间
     * @param nameSpace
     */
    public static void createNameSpace(String nameSpace) throws IOException {
        Admin admin = adminHolder.get();
        NamespaceDescriptor namespaceDescriptor = NamespaceDescriptor.create(nameSpace).build();
        admin.createNamespace(namespaceDescriptor);
    }

    /**
     * 判断表是否存在
     * @param tableName
     * @return
     */
    private static boolean isTableExist(String tableName) throws IOException {
        Admin admin = adminHolder.get();
        boolean flag = admin.tableExists(TableName.valueOf(tableName));
        admin.close();
        return flag;
    }

    public static void createTable(String tableName,int versions,String... columnFamilys) throws IOException {
        //判断是否传入了列族信息
        if (columnFamilys.length<=0){
            System.out.println("Column Family information are not set!");
            return;
        }

        if (isTableExist(tableName)){
            System.out.println(tableName+"has already exists!");
            return;
        }
        Admin admin = adminHolder.get();
        //设置表的信息,创建表
        HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));
        for (String cf : columnFamilys) {
            //设置每个列族的信息
            HColumnDescriptor columnDescriptor = new HColumnDescriptor(cf);
            columnDescriptor.setMaxVersions(versions);
            //为表描述对象添加列族描述对象
            tableDescriptor.addFamily(columnDescriptor);
        }
        //创建表
        admin.createTable(tableDescriptor);

    }



    public static void close() throws IOException {
        Connection conn = connHolder.get();
        Admin admin = adminHolder.get();
        if (conn != null) {
            conn.close();
            connHolder.remove();
        }
        if (admin!=null){
            admin.close();
            adminHolder.remove();
        }
    }

}

常量类

package com.chanzany.constants;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;

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

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

    //微博内容表信息
    public static String CONTENT_TABLE="weibo:content";
    public static String CONTENT_TABLE_FAMILY="info";
    public static int CONTENT_TABLE_VERSIONS= 1;

    //用户关系表信息
    public static String RELATION_TABLE="weibo:relation";
    public static String RELATION_TABLE_FAMILY1="attends";
    public static String RELATION_TABLE_FAMILY2="fans";
    public static int RELATION_TABLE_VERSIONS= 1;

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

}

3 发布微博内容

需要添加数据的表:

a、微博内容表中添加1条数据

b、微博收件箱表对所有粉·数据

示意图:

在这里插入图片描述

代码实现方法

public static void publishBlog(String uid,String content) throws IOException {
    //操作微博内容表
    Connection conn = ConnectionFactory.createConnection(Constants.CONFIGURATION);
    Table contentTable = conn.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
    //rowKey
    long ts = System.currentTimeMillis();
    String rowKey = uid+"_"+ts;
    //Put对象
    Put contentPut = new Put(Bytes.toBytes(rowKey));
    contentPut.addColumn(Bytes.toBytes(Constants.CONTENT_TABLE_FAMILY),
            Bytes.toBytes("content"),
            Bytes.toBytes(content));

    contentTable.put(contentPut);

    //操作关联表并修改微博收件箱表
    // 先获取用户关系表,从关系表的fans列族中找到所有列(粉丝)
    Table relationTable = conn.getTable(TableName.valueOf(Constants.RELATION_TABLE));
    Get get = new Get(Bytes.toBytes(uid));
    get.addFamily(Bytes.toBytes(Constants.RELATION_TABLE_FAMILY2));
    Result result = relationTable.get(get);

    // 构建一个容器,存放需要放入IndexBox表的Put对象
    ArrayList<Put> inBoxPuts = new ArrayList<Put>();
    for (Cell cell : result.rawCells()) {
        Put inBoxPut = new Put(CellUtil.cloneQualifier(cell));
        //列族-列-内容
        inBoxPut.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_FAMILY),
                Bytes.toBytes(uid),
                Bytes.toBytes(rowKey));
        inBoxPuts.add(inBoxPut);
    }

    //判断是否有粉丝
    if (inBoxPuts.size()>0){
        //获取收件箱表,将inBoxPuts的数据插入其中
        Table inboxTable = conn.getTable(TableName.valueOf(Constants.INBOX_TABLE));
        inboxTable.put(inBoxPuts);
        inboxTable.close();
    }

    //释放资源
    relationTable.close();
    contentTable.close();
    conn.close();

}

测试结果

@Test
public void testPublish() throws IOException {
    HBaseDao.publishBlog("1001","no time to waste!");
}

在这里插入图片描述

4 添加关注用户

a、在微博用户关系表中,对当前主动操作的用户添加新关注的好友

b、在微博用户关系表中,对被关注的用户添加新的粉丝

c、微博收件箱表中添加所关注的用户发布的微博

示意图:

在这里插入图片描述

代码实现

/**
 * 关注用户逻辑
 * a、在微博用户关系表中,对当前主动操作的用户添加新的关注的好友
 * b、在微博用户关系表中,对被关注的用户添加粉丝(当前操作的用户)
 * c、当前操作用户的微博收件箱添加所关注的用户发布的微博rowkey
 */
public static void addAttends(String uid,String... attends ) throws IOException {
    if (attends.length<=0){
        System.out.println("no attends were checked!");
        return;
    }
    //操作relation表
    Connection conn = ConnectionFactory.createConnection(Constants.CONFIGURATION);
    Table relationTable = conn.getTable(TableName.valueOf(Constants.RELATION_TABLE));
    //创建集合存放所有需要添加的列对应的Put对象
    ArrayList<Put> relationPuts = new ArrayList<Put>();
    //创建操作者的Put对象
    Put userPut = new Put(Bytes.toBytes(uid));
    //遍历关注者为userPut赋值并创建attendPut对象
    for (String attend : attends) {
        //为userPut赋值
        userPut.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_FAMILY1),
                Bytes.toBytes(attend),
                Bytes.toBytes(attend));
        //为对应多个列的各个attendPut对象赋值并添加到列表
        Put attendPut = new Put(Bytes.toBytes(attend));
        attendPut.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_FAMILY2),
                Bytes.toBytes(uid),
                Bytes.toBytes(uid));
        relationPuts.add(attendPut);
    }

    relationPuts.add(userPut);
    relationTable.put(relationPuts);

    //操作inbox表
    //获取content内容表对象
    Table contentTable = conn.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
    //创建需要放入收件箱表的Put对象
    Put inboxPut = new Put(Bytes.toBytes(uid));
    //循环attends,获取每个被关注者的近期发布微博
    for (String attend : attends) {
        //获取当前被关注对象的近期发布微博,由于rowKey带时间戳是未知的。所有要用scan{startRow,stopRow}来获取
        Scan scan = new Scan();
        scan.setStartRow(Bytes.toBytes(attend+"_"));
        scan.setStopRow(Bytes.toBytes(attend+"|"));
        ResultScanner resultScanner = contentTable.getScanner(scan);

        //定义时间戳为保证当前被关注对象所发布的微博时间戳不同
        long ts = System.currentTimeMillis();

        //给需要放入收件箱表的Put对象赋值
        for (Result result : resultScanner) {
            //content表里的rowKey作为inbox的value,被关注对象attends作为Inbox的多个column
            inboxPut.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_FAMILY),Bytes.toBytes(attend),ts++,result.getRow());

        }
        resultScanner.close();
    }
    //把inboxPut对象放入收件箱表
    if (!inboxPut.isEmpty()){
        Table inboxTable = conn.getTable(TableName.valueOf(Constants.INBOX_TABLE));
        inboxTable.put(inboxPut);
        inboxTable.close();
    }

    relationTable.close();
    contentTable.close();
    conn.close();
}

测试结果

@Test
public void testPublish() throws IOException {
    HBaseDao.publishBlog("1001","no time to waste!");
    HBaseDao.publishBlog("1001","no time to waste1!");
    HBaseDao.publishBlog("1001","no time to waste2!");
    HBaseDao.publishBlog("1001","no time to waste3!");
    HBaseDao.publishBlog("1002","no time to waste!");
    HBaseDao.publishBlog("1003","no time to waste!");
    HBaseDao.publishBlog("1003","no time to waste1!");
    HBaseDao.publishBlog("1003","no time to waste2!");
}

@Test
public void testAddAttends()throws Exception{
    HBaseDao.addAttends("1001","1002","1003");
}

在这里插入图片描述

5 移除(取关)用户

a、在微博用户关系表中,对当前主动操作的用户移除取关的好友(attends)

b、在微博用户关系表中,对被取关的用户移除粉丝

c、微博收件箱中删除取关的用户发布的微博

示意图

在这里插入图片描述

代码实现

/**
 * 取消关注(remove)
 * a、在微博用户关系表中,对当前主动操作的用户删除对应取关的好友
 * b、在微博用户关系表中,对被取消关注的人删除粉丝(当前操作人)
 * c、从收件箱中,删除取关的人对应的列
 */
public static void removeAttends(String uid, String... delAttends) throws IOException {
    Connection conn = ConnectionFactory.createConnection(Constants.CONFIGURATION);
    //操作关系表
    Table relationTable = conn.getTable(TableName.valueOf(Constants.RELATION_TABLE));
    //操作者需要通过Delete对象来删除attend列族下取关的对象列
    Delete userDelete = new Delete(Bytes.toBytes(uid));
    ArrayList<Delete> deleteList = new ArrayList<Delete>();
    for (String delAttend : delAttends) {
        //需要拿到每个被取关的对象,每个被取关的对象删除fans列族下的当前用户列
        Delete deleted = new Delete(Bytes.toBytes(delAttend));
        deleted.addColumns(Bytes.toBytes(Constants.RELATION_TABLE_FAMILY2),Bytes.toBytes(uid));
        deleteList.add(deleted);
        //为操作者的delete对象赋值-各个需要取关的对象uid列
        userDelete.addColumns(Bytes.toBytes(Constants.RELATION_TABLE_FAMILY1),Bytes.toBytes(delAttend));
    }
    deleteList.add(userDelete);
    relationTable.delete(deleteList);

    //操作收件箱表
    Table inboxTable = conn.getTable(TableName.valueOf(Constants.INBOX_TABLE));
    //将当前用户(rowKey)需要取关的用户对应的列删除
    Delete inboxDelete = new Delete(Bytes.toBytes(uid));
    for (String delAttend : delAttends) {
       inboxDelete.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_FAMILY),Bytes.toBytes(delAttend));
    }
    inboxTable.delete(inboxDelete);

    //释放资源
    relationTable.close();
    inboxTable.close();
    conn.close();
}

测试

在这里插入图片描述 在这里插入图片描述

运行测试代码

@Test
public void testRemoveAttends()throws Exception{
    HBaseDao.removeAttends("1001","1002","1003");
}

在这里插入图片描述 在这里插入图片描述

测试结果符合期待,成功。


6 获取用户的初始化页面信息

a. 从Inbox表中拿到所关注的所有好友的列下的value(作为content表的rowKey)

b. 根据获取的RowKey,得到微博的内容

示意图:
在这里插入图片描述

代码实现

/**
 * 获取某个人的初始化页面数据
 * @param uid
 */
public static void getInitInfo(String uid) throws IOException {
    Connection conn = ConnectionFactory.createConnection(Constants.CONFIGURATION);
    //操作收件箱表
    Table inboxTable = conn.getTable(TableName.valueOf(Constants.INBOX_TABLE));
    //创建收件箱表Get对象,获取uid(rowKey)对应的数据(设置最大版本)
    Get userGet = new Get(Bytes.toBytes(uid));
    userGet.addFamily(Bytes.toBytes(Constants.INBOX_TABLE_FAMILY));
    userGet.setMaxVersions();
    Result result = inboxTable.get(userGet);
    //创建一个存放contentId的集合
    ArrayList<String> contentIds = new ArrayList<String>();
    for (Cell cell : result.rawCells()) {
        String contentId= Bytes.toString(CellUtil.cloneValue(cell));
        contentIds.add(contentId);
    }

    //操作微博内容表
    Table contentTable = conn.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
    ArrayList<Get> contentGets = new ArrayList<Get>();
    for (String contentId : contentIds) {
        Get contentGet = new Get(Bytes.toBytes(contentId));
        contentGet.addFamily(Bytes.toBytes(Constants.CONTENT_TABLE_FAMILY));
        contentGets.add(contentGet);
    }

    Result[] detailResults = contentTable.get(contentGets);
    //解析内容并打印
    for (Result detailResult : detailResults) {
        for (Cell cell : detailResult.rawCells()) {
            System.out.print("rowKey:"+Bytes.toString(CellUtil.cloneRow(cell))+"\t");
            System.out.print("columnFamily:"+Bytes.toString(CellUtil.cloneFamily(cell))+"\t");
            System.out.print("column:"+Bytes.toString(CellUtil.cloneQualifier(cell))+"\t");
            System.out.print("value:"+Bytes.toString(CellUtil.cloneValue(cell)));
        }
        System.out.println();
    }

    //释放资源
    contentTable.close();
    inboxTable.close();
    conn.close();
}

测试

在这里插入图片描述

执行测试代码

@Test
public void testGetInitInfo()throws Exception{
    HBaseDao.getInitInfo();
}

在这里插入图片描述 在这里插入图片描述
因为在Inbox表设计的时候指定的是两个版本,所以只会显示最新发布的两条微博内容,符合我们的期待,测试成功。

7 获取用户的所有微博

a、设计过滤器,将传入的uid在content表中对应的微博rowKey拿到

b、遍历拿到的所有微博id,然后进行消费,在真实生成环境中,往往把这些查询结果返回给提交申请的用户。

示意图:
在这里插入图片描述

代码实现

/**
 * 获取某个人的所有微博详情
 *
 * @param uid
 * @throws Exception
 */
public static List getBlogs(String uid) throws Exception {
    //只需要操作content表
    Connection conn = ConnectionFactory.createConnection(Constants.CONFIGURATION);
    Table contentTable = conn.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
    Scan scan = new Scan();
    //content表的RowKey是以uid_ts的形式生成的。所以要根据传入的uid找到其对应的所有rowKey
    RowFilter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator(uid));
    scan.setFilter(rowFilter);

    ResultScanner resultScanner = contentTable.getScanner(scan);
    ArrayList<Cell> cellResults = new ArrayList<Cell>();
    for (Result result : resultScanner) {
        cellResults.addAll(Arrays.asList(result.rawCells()));
    }

    contentTable.close();
    conn.close();
    return cellResults;
}

测试

@Test
public void testGetBlogs() throws Exception{
    List blogs = HBaseDao.getBlogs("1003");
    for (Object cell : blogs) {
        System.out.print("rowKey:" + Bytes.toString(CellUtil.cloneRow((Cell) cell)) + "\t");
        System.out.print("columnFamily:" + Bytes.toString(CellUtil.cloneFamily((Cell) cell)) + "\t");
        System.out.print("column:" + Bytes.toString(CellUtil.cloneQualifier((Cell) cell)) + "\t");
        System.out.println("value:" + Bytes.toString(CellUtil.cloneValue((Cell) cell)));
    }
}

测试结果: 在这里插入图片描述

满足预期,测试成功

至此,所有需求已实现,代码已上传https://download.csdn.net/download/qq_41819729/12551598

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值