HBase项目案例之微博
HBase项目案例之微博
笔者配置
Hadoop3.1.3+HBase2.2.2
IDEA本地运行
没有写运行配置,我是在resorces里添加文件,可以看另一篇文章的步骤一
HBase整合MR本地IDEA运行
思路
三张表用来存储数据
微博内容表:CONTENT_TABLE
用户关系表:RELATION_TABLE
收件箱表:INBOX_TABLE
微博内容表:content
用来存储用户发布的内容
//微博内容表
public static final String CONTENT_TABLE = "weibo:content";
public static final String CONTENT_TABLE_CF = "info";
public static final int CONTENT_TABLE_VERSION = 1;
用户关系表:relation
用来存储用户的关注和粉丝,分为两个列族,列名和值是一样的
//用户关系表
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;
收件箱表:inbox
用来存储用户的关注的人的微博,rowkey是用户,info:name,name是指关注的人,value是关注的人的最新微博,根据name去查找content表,找最新的内容的rowkey,然后放进inbox表的value值里。
INBOX_TABLE_VERSIONS是设置保存的版本数,所以即使覆盖了,还是可以看同一个用户的两条微博。
//收件箱表
public static final String INBOX_TABLE = "weibo:inbox";
public static final String INBOX_TABLE_CF = "info";
public static final int INBOX_TABLE_VERSIONS = 2;
代码结构
Constants.java
package com.lmr.constants;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
public class Constants {
//HBase的配置信息
public static final Configuration CONFIGURATION = HBaseConfiguration.create();
//命名空间
public static final String NAMESPACE = "weibo1";
//微博内容表
public static final String CONTENT_TABLE = "weibo1:content";
public static final String CONTENT_TABLE_CF = "info";
public static final int CONTENT_TABLE_VERSION = 1;
//用户关系表
public static final String RELATION_TABLE = "weibo1: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 = "weibo1:inbox";
public static final String INBOX_TABLE_CF = "info";
public static final int INBOX_TABLE_VERSIONS = 2;
}
HBaseUtil.java
package com.lmr.utils;
import com.lmr.constants.Constants;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.NamespaceDescriptor;
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;
import java.io.IOException;
/**
* 1.创建命名空间
* 2.判断表是否存在
* 3.创建表(三张表)
*/
public class HBaseUtil {
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();
//返回结果
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();
}
}
HBaseDao.java
package com.lmr.dao;
import com.lmr.constants.Constants;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.CompareFilter;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.filter.SubstringComparator;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IOException;
import java.util.ArrayList;
/**
* 1.发布微博
* 2.删除微博
* 3.关注用户
* 4.取关用户
* 5.获取用户的初始化页面
* 6.获取用户微博详情
*/
public class HBaseDao {
//1.发布微博
public static void publishWeiBo(String uid, String content) throws IOException {
//获取Connection对象
Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
//第一部分:操作微博内容表
//1.获取微博内容表对象
Table contTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
//2.获取当前时间戳
long ts = System.currentTimeMillis();
//3.获取RowKey
String rowKey = uid + "_" + ts;
//4.创建Put对象
Put contPut = new Put(Bytes.toBytes(rowKey));
//5.给Put对象赋值
contPut.addColumn(Bytes.toBytes(Constants.CONTENT_TABLE_CF), Bytes.toBytes("content"), Bytes.toBytes(content));
//6.执行插入数据操作
contTable.put(contPut);
//第二部分:操作微博收件箱表
//1.获取用户关系表对象
Table relaTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
//2.获取当前发布微博人的fans列族数据
Get get = new Get(Bytes.toBytes(uid));
get.addFamily(Bytes.toBytes(Constants.RELATION_TABLE_CF2));
Result result = relaTable.get(get);
//3.创建一个集合,用于存放微博内容表的Put对象
ArrayList<Put> inboxPuts = new ArrayList<>();
//4.遍历粉丝
for (Cell cell : result.rawCells()) {
//5.构建微博收件箱表的Put对象
Put inboxPut = new Put(CellUtil.cloneQualifier(cell));
//6.给收件箱表的Put对象赋值
inboxPut.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(uid), Bytes.toBytes(rowKey));
//7.将收件箱表的Put对象存入集合
inboxPuts.add(inboxPut);
}
//8.判断是否有粉丝
if (inboxPuts.size() > 0){
//获取收件箱对象
Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
//执行收件箱表数据插入操作
inboxTable.put(inboxPuts);
//关闭收件箱表
inboxTable.close();
}
//关闭资源
relaTable.close();
contTable.close();
connection.close();
}
//2.关注用户
public static void addAttends(String uid, String... attends) throws IOException {
//校验是否添加了待关注的人
if (attends.length <= 0){
System.out.println("请选择待关注的人!!!");
return;
}
//获取Connection对象
Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
//第一部分:操作用户关系表
//1.获取用户关系表对象
Table relaTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
//2.创建一个集合,用于存放用户关系表的Put对象
ArrayList<Put> relaPuts = new ArrayList<>();
//3.创建操作者的Put对象
Put uidPut = new Put(Bytes.toBytes(uid));
//4.循环创建被关注者的Put对象
for (String attend : attends) {
//5.给操作者的Put对象赋值
uidPut.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_CF1), Bytes.toBytes(attend), Bytes.toBytes(attend));
//6.创建被关注着的Put对象
Put attendPut = new Put(Bytes.toBytes(attend));
//7.给被关注者的Put对象赋值
attendPut.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_CF2), Bytes.toBytes(uid), Bytes.toBytes(uid));
//8.将被关注者的Put对象放入集合
relaPuts.add(attendPut);
}
//9.将操作者的Put对象添加至集合
relaPuts.add(uidPut);
//10.执行用户关系表的插入数据操作
relaTable.put(relaPuts);
//第二部分:操作收件箱表
//1.获取微博内容表对象
Table contTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
//2.创建收件箱表的Put对象
Put inboxPut = new Put(Bytes.toBytes(uid));
//3.循环attends,获取每个被关注者的近期发布的微博
for (String attend : attends) {
//4.获取当前被关注者的近期发布的微博(scan)-> 集合ResultScanner
Scan scan = new Scan(Bytes.toBytes(attend + "_"), Bytes.toBytes(attend + "|"));
ResultScanner resultScanner = contTable.getScanner(scan);
//定义一个时间戳
long ts = System.currentTimeMillis();
//5.对获取的值进行遍历
for (Result result : resultScanner) {
//6.给收件箱表的Put对象赋值
inboxPut.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(attend), ts++, result.getRow());
}
}
//7.判断当前的Put对象是否为空
if (!inboxPut.isEmpty()){
//获取收件箱表的对象
Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
//插入数据
inboxTable.put(inboxPut);
//关闭收件箱表连接
inboxTable.close();
}
//关闭资源
relaTable.close();
contTable.close();
connection.close();
}
//3.取关
public static void deleteAttends(String uid, String... dels) throws IOException {
if (dels.length <= 0){
System.out.println("请添加待取关的用户!!!");
return;
}
//获取Connection对象
Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
//第一部分
//1.获取用户关系表对象
Table relaTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
//2.创建一个集合,用于存放用户关系表的Delete对象
ArrayList<Delete> relaDeletes = new ArrayList<>();
//3.创建操作者的Delete对象
Delete uidDelete = new Delete(Bytes.toBytes(uid));
//4.循环创建被取关者的Delete对象
for (String del : dels) {
//5.给操作者的Delete对象赋值
uidDelete.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_CF1), Bytes.toBytes(del));
//6.创建被取关者的Delete对象
Delete delDelete = new Delete(Bytes.toBytes(del));
//7.给被取关者的Delete对象赋值
delDelete.addColumns(Bytes.toBytes(Constants.RELATION_TABLE_CF2), Bytes.toBytes(del));
//8.将被取关者的Delete对象添加至集合
relaDeletes.add(delDelete);
}
//9.将操作者的Delete对象添加至集合
relaDeletes.add(uidDelete);
//10.执行用户关系表的删除操作
relaTable.delete(relaDeletes);
//第二部分:操作收件箱表
//1.获取收件箱表对象
Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
//2.创建操作者的Delete对象
Delete inboxDelete = new Delete(Bytes.toBytes(uid));
//3.给操作者的Delete对象赋值
for (String del : dels) {
inboxDelete.addColumns(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(del));
}
//4.执行收件箱表的删除操作
inboxTable.delete(inboxDelete);
//关闭资源
relaTable.close();
inboxTable.close();
connection.close();
}
//4.获取某个人的初始化页面数据
public static void getInit(String uid) throws IOException {
//1.获取Connection对象
Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
//2.获取收件箱表对象
Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
//3.获取微博内容表对象
Table contTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
//4.创建收件箱表Get对象,并获取数据(设置最大版本)
Get inboxGet = new Get(Bytes.toBytes(uid));
inboxGet.setMaxVersions();
Result result = inboxTable.get(inboxGet);
//5.遍历获取的数据
for (Cell cell : result.rawCells()) {
//6.构建微博内容表Get对象
Get contGet = new Get(CellUtil.cloneValue(cell)); //获取关注的人的微博内容的值
//7.获取该Get对象的数据内容
Result contResult = contTable.get(contGet); //再把关注的人微博内容的值的数据内容
//8.解析内容并打印
for (Cell contCell : contResult.rawCells()) {
System.out.println("RK:" + Bytes.toString(CellUtil.cloneRow(contCell)) +
",CF:" + Bytes.toString(CellUtil.cloneFamily(contCell)) +
",CN:" + Bytes.toString(CellUtil.cloneQualifier(contCell)) +
",Value:" + Bytes.toString(CellUtil.cloneValue(contCell)));
}
}
//9.关闭资源
inboxTable.close();
contTable.close();
connection.close();
}
//5.获取某个人的所有微博详情
public static void getWeiBo(String uid) throws IOException {
//1.获取Connection对象
Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
//2.获取微博内容对象
Table table = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
//3.构建Scan对象
Scan scan = new Scan();
//构建过滤器
RowFilter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator(uid + "_"));
scan.setFilter(rowFilter);
//4.获取数据
ResultScanner resultScanner = table.getScanner(scan);
//5.解析数据并打印
for (Result result : resultScanner) {
for (Cell cell : result.rawCells()) {
System.out.println("RK:" + Bytes.toString(CellUtil.cloneRow(cell)) +
",CF:" + Bytes.toString(CellUtil.cloneFamily(cell)) +
",CN:" + Bytes.toString(CellUtil.cloneQualifier(cell)) +
",Value:" + Bytes.toString(CellUtil.cloneValue(cell)));
}
}
//6.关闭资源
table.close();
connection.close();
}
}
TestWeiBo
package com.lmr.test;
import com.lmr.constants.Constants;
import com.lmr.dao.HBaseDao;
import com.lmr.utils.HBaseUtil;
import java.io.IOException;
public class TestWeiBo {
public static void init(){
try {
//创建命名空间
HBaseUtil.createNameSpace(Constants.NAMESPACE);
//创建微博内容表
HBaseUtil.createTable(Constants.CONTENT_TABLE, Constants.CONTENT_TABLE_VERSION, 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条微博
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第三的微博!!!");
Thread.sleep(10);
//获取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");
}
}
代码结果
RK:1001_1642943474683,CF:info,CN:content,Value:1001第一的微博!!!
*******************111*******************
RK:1001_1642943484562,CF:info,CN:content,Value:1001第三的微博!!!
RK:1001_1642943478157,CF:info,CN:content,Value:1001第二的微博!!!
RK:1003_1642943487776,CF:info,CN:content,Value:1003第三的微博!!!
RK:1003_1642943481379,CF:info,CN:content,Value:1003第二的微博!!!
*******************222*******************
RK:1001_1642943484562,CF:info,CN:content,Value:1001第三的微博!!!
RK:1001_1642943478157,CF:info,CN:content,Value:1001第二的微博!!!
*******************333*******************
RK:1001_1642943484562,CF:info,CN:content,Value:1001第三的微博!!!
RK:1001_1642943478157,CF:info,CN:content,Value:1001第二的微博!!!
RK:1003_1642943487776,CF:info,CN:content,Value:1003第三的微博!!!
RK:1003_1642943481379,CF:info,CN:content,Value:1003第二的微博!!!
*******************444*******************
RK:1001_1642943474683,CF:info,CN:content,Value:1001第一的微博!!!
RK:1001_1642943478157,CF:info,CN:content,Value:1001第二的微博!!!
RK:1001_1642943484562,CF:info,CN:content,Value:1001第三的微博!!!