Cris 小哥哥的大数据项目之 HBase 模拟微博核心功能
Author:Cris
文章目录
0. 序
- 通过使用 HBase 模拟完成微博核心业务的后台数据操作,对 HBase 的 API 操作更加熟练
- 熟悉需要使用 HBase 的业务场景,以及使用 HBase 的流程
- 项目代码满足阿里代码规约,每个功能点的实现代码均经过 junit 测试以及测试效果图
- 封装一些有用的类,避免重复造轮子,提高工作效率
1. 需求分析
先思考一下,微博的核心功能有哪些?
每天打开微博,第一件事肯定就是你关注的人又发了哪些微博吧;
然后你刷微博看见有意思的用户就会关注他吧,再顺便进去他的空间看看他之前发的微博吧;
心情不错,想发个微博让自己的粉丝点赞;
发现自己关注的明星又出轨了 or 吸毒了,取消关注!
然后一天就这么刷刷刷的过去了…
虽然 Cris 不玩微博,但是基本的微博功能还是知道的,从上面的场景,我们可以分析出微博的哪些核心功能呢~
- 发微博,这是微博用户内容输出最核心的功能点
- 关注和取消关注,这是微博用户之间连接的核心
- 刷或者是查看微博内容,这是微博用户花费时间最多的功能点
以上三点,基本涵盖了微博的核心功能,让我们重新做一个微博项目,这肯定不现实,但是从某些方面模拟微博的核心功能点,这还是没有问题的,接下来我们使用 HBase 完成微博核心功能点的数据层操作,跟着 Cris 小哥哥来,带你装B带你飞~
2. 项目流程
2.1 项目结构设计
先说点题外话,我们都知道开发常用的架构都是三层架构体系(Controller,Service,Dao),然后这个架构上的表现层一个经典的实现就是 MVC(Model,View,Controller)
本项目则是基于三层架构体系完成,其中 Controller 层并没有实现 MVC,悉知
关于三层架构和 MVC 之间的区别,建议参考这篇文章,个人觉得介绍的很不错哦
先来创建我们的项目,这里 Cris 选择创建一个模块来开发
然后选择父模块导入 pom.xml
项目结果图如下:
pom.xml 如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>testParent</artifactId>
<groupId>com.cris</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../testParent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>weibo</artifactId>
</project>
当然你可以重新创建一个 Proj 来完成改项目
接下来就是完成代码模块分析(这里没有使用 Spring / Spring MVC /Spring Boot,因为该项目核心是使用 HBase 完成后台的数据处理
)
先导入 log4j.properties 和 hdfs-site.xml
我的 hdfs-site.xml 如下:
<configuration>
<property>
<name>hbase.rootdir</name>
<value>hdfs://hadoop101:9000/hbase</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<!-- 0.98后的新变动,之前版本没有.port,默认端口为60000 -->
<property>
<name>hbase.master.port</name>
<value>16000</value>
</property>
<property>
<name>hbase.zookeeper.quorum</name>
<value>hadoop101:2181,hadoop102:2181,hadoop103:2181</value>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/opt/module/zookeeper/zkdata</value>
</property>
</configuration>
其实不导入也是也可以的,只要你的 Linux 服务器上的 HBase 配置文件都已经配置了以上内容
然后建包
TestWb 用于测试我们的功能
WbService 层用于写具体的逻辑
WbController 作为控制器
dao 层则是我们的核心层,和 HBase 做直接交互的都是这层,所以这层设计尤为重要,我们结合数据库的建设来分析
-
WbContentDao
主要用于记录当用户发微博时的微博内容,HBase 需要有专门的表来存储,rowkey 设计为用户 id + 时间戳,列族设计则为发微博的内容
-
WbRelationDao
记录被关注者和粉丝之间的关系(这里我们使用 star 来表示被关注者,用 fan 表示粉丝),rowkey 为用户 id,列族设计为该用户的 fan 的 id 以及该用户关注的 star 的 id
-
WbInboxDao
表示用户的微博收件箱,用于存储用户可以接收的微博,可以自动获取该用户关注的 start 发布的微博,rowkey 为用户 id,列族设计为接收的微博,列族每个列为(star 的id及其发布的微博 id)
-
WbHbaseDao
和 HBase 的基本操作类,例如创建连接,关闭连接,获取 Admin 服务器等
备注:用户信息通常存储在关系型数据库,这里不涉及用户信息,在测试中直接给出即可
2.2 项目代码完成
① 项目所用表初始化
-
首先完成我们的工具类 WbHbaseDao
/** * 和 HBase 交互的工具类 * * @author cris * @version 1.0 **/ @SuppressWarnings("SpellCheckingInspection") public class WbHbaseDao { /** * 协助当前线程存储数据到一个map中,key 就是当前 ThreadLocal 对象,值就是要存储的数据 **/ private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>(); /** * 默认列数据的最大版本数为1,HBase 默认就是1 **/ private static final int DEFAULT_MAX_VERSION = 1; public static final String WB_CONTENT_TABLENAME = "cris:wb_content"; public static final String WB_RELATION_TABLENAME = "cris:wb_relation"; public static final String WB_INBOX_TABLENAME = "cris:wb_inbox"; public static final String WB_NAMESPACENAME = "cris"; /** * 获取连接,结合 Optional 可以确保得到的连接一定不为空!!!如果连接有问题就直接报错了~ * * @return 连接 */ public static Connection getConnection() { Optional<Connection> optionalConnection = Optional.ofNullable(THREAD_LOCAL.get()); /*如果 optionalConnection 有值,那么返回容器中的值,否则为当前线程的 ThreadLocalMap 存储一个 connection*/ return optionalConnection.orElseGet(() -> { try { /*创建 Connection 过程中报错就直接抛出空指针异常,确保每个当前线程都应该有 connection * 并且需要保证从当前线程得到的 onnection 一定是非空值*/ Connection conn = ConnectionFactory.createConnection(); THREAD_LOCAL.set(conn); return conn; } catch (IOException e) { e.printStackTrace(); throw new NullPointerException(); } }); } /** * 关闭连接 */ public static void closeConnection() throws IOException { Connection connection = THREAD_LOCAL.get(); closeAdmin(connection.getAdmin()); connection.close(); /*注意:一定要移除线程中绑定的 Connection 对象*/ THREAD_LOCAL.remove(); } public static void closeAdmin(Admin admin) throws IOException { if (admin != null) { admin.close(); } } public static void closeTable(Table table) throws IOException { if (table != null) { table.close(); } } /** * 初始化命名空间 * * @param nameSpaceName 命名空间名字 * @throws IOException */ public static void initNameSpace(String nameSpaceName) throws IOException { Connection connection = getConnection(); Admin admin = connection.getAdmin(); // 先要判断 nameSpace 是否存在,如果不存在那么创建 if (!existNameSpace(nameSpaceName)) { NamespaceDescriptor namespaceDescriptor = NamespaceDescriptor.create(nameSpaceName).build(); admin.createNamespace(namespaceDescriptor); } } /** * 判断命名空间是否存在 * * @param nameSpace 命名空间名字 * @return 命名空间存在返回 true * @throws IOException */ public static boolean existNameSpace(String nameSpace) throws IOException { Connection connection = getConnection(); Admin admin = connection.getAdmin(); try { /*如果命名空间不存在将会抛出 NamespaceNotFoundException */ NamespaceDescriptor namespaceDescriptor = admin.getNamespaceDescriptor(nameSpace); return true; } catch (IOException e) { e.printStackTrace(); return false; } } /** * 移除命名空间 * * @param nameSpaceName 命名空间名字 * @throws IOException */ public static void removeNameSpace(String nameSpaceName) throws IOException { Connection connection = getConnection(); Admin admin = connection.getAdmin(); // 先要判断 nameSpace 是否存在,如果存在那么删除 boolean existNameSpace = existNameSpace(nameSpaceName); if (existNameSpace) { admin.deleteNamespace(nameSpaceName); } } /** * 移除 HBase 表 * * @param tableName 表名 * @throws IOException */ public static void removeTable(String tableName) throws IOException { Connection connection = getConnection(); Admin admin = connection.getAdmin(); TableName name = TableName.valueOf(tableName); boolean tableExists = admin.tableExists(name); if (tableExists) { admin.disableTable(name); admin.deleteTable(name); } } /** * 初始化项目所需要的 HBase 表 * * @param tableName * @param maxVersion * @param columnNames * @throws IOException */ public static void initTables(String tableName, int maxVersion, String... columnNames) throws IOException { Connection connection = getConnection(); Admin admin = connection.getAdmin(); createTable(admin, tableName, maxVersion, columnNames); } /** * 初始化项目所需要的 HBase 表 * * @param tableName * @param columnNames * @throws IOException */ public static void initTables(String tableName, String... columnNames) throws IOException { initTables(tableName, DEFAULT_MAX_VERSION, columnNames); } /** * 实际的创建 HBase 表的方法 * * @param admin HBase 集群的主机(HMaster 节点),负责协调 HBase 集群 * @param tableName 表名 * @param columnNames 列名(可能多个) * @param maxVersion 可手动设置最大版本号 * @throws IOException */ public static void createTable(Admin admin, String tableName, int maxVersion, String... columnNames) throws IOException { TableName name = TableName.valueOf(tableName); boolean tableExists = admin.tableExists(name); if (!tableExists) { HTableDescriptor hTableDescriptor = new HTableDescriptor(name); for (String columnName : columnNames) { HColumnDescriptor hColumnDescriptor = new HColumnDescriptor(columnName); hColumnDescriptor.setMaxVersions(maxVersion); hTableDescriptor.addFamily(hColumnDescriptor); } admin.createTable(hTableDescriptor); } } }
这里使用了 ThreadLocal 类来完成将 Connection 连接存储在当前线程的功能(Java 框架底层大都如此实现,当然比我写的这个要复杂多了)?
然后尝试引入了 Java 8 的 Optional 类完成对对象的封装,确保得到的 Connection 对象必须非空,否则就会报错
本工具类参考以下博客:
静态方法本来使用静态变量是有线程安全问题的,但是这里并没有修改静态变量,所以个人感觉是不会产生线程安全问题,也就没有加锁
HBase 表的草图如下:
然后是测试代码
/** * 测试代码 * * @author cris * @version 1.0 **/ @SuppressWarnings("JavaDoc") public class WbTest { private static final Logger LOGGER = LoggerFactory.getLogger(String.valueOf(WbTest.class)); private WbController wbController = new WbController(); @Before public void testStart() { wbController.getWbService().start(); } @After public void testEnd() throws IOException { wbController.getWbService().end(); } /** * 测试 ok * * @throws IOException */ @Test public void testInitNameSpace() throws IOException { WbHbaseDao.initNameSpace(WbHbaseDao.WB_NAMESPACENAME); LOGGER.warn("命名空间创建成功!"); } /** * 测试 ok * * @throws IOException */ @Test public void testRemoveNameSpace() throws IOException { WbHbaseDao.removeNameSpace(WbHbaseDao.WB_NAMESPACENAME); LOGGER.warn("命名空间删除成功"); } /** * 初始化项目所需要的三张表:微博内容表,微博用户关系表,微博用户收件箱表 * 测试 ok * * @throws IOException */ @Test public void testInitTables() throws IOException { WbHbaseDao.initTables(WbHbaseDao.WB_CONTENT_TABLENAME, "info"); WbHbaseDao.initTables(WbHbaseDao.WB_RELATION_TABLENAME, WbRelationDao.COLUMN_FAMILY_ATTENDS, WbRelationDao.COLUMN_FAMILY_FANS); WbHbaseDao.initTables(WbHbaseDao.WB_INBOX_TABLENAME, 5, "info"); LOGGER.warn("创建表成功!"); } /** * 删除 HBase 表(测试ok,记得带上命名空间) * * @throws IOException */ @Test public void testDeleteTables() throws IOException { WbHbaseDao.removeTable("cris:wb_content"); WbHbaseDao.removeTable("cris:wb_relation"); WbHbaseDao.removeTable("cris:wb_inbox"); LOGGER.warn("删除表成功!"); }
测试效果如下:
② 发布微博功能实现
a、微博内容表中添加1条数据
b、微博收件箱表对所有粉丝用户添加数据
- Controller 层
/**
* 控制层
*
* @author cris
* @version 1.0
**/
@SuppressWarnings("JavaDoc")
public class WbController {
private WbService wbService = new WbService();
public WbService getWbService() {
return wbService;
}
/**
* star 发布微博
*
* @param star
* @param content
* @throws IOException
*/
public void publishWb(String star, String content) throws IOException {
wbService.publishWb(star, content);
}
- Service 层
/**
* 业务逻辑层
*
* @author cris
* @version 1.0
**/
@SuppressWarnings("JavaDoc")
@Data
@AllArgsConstructor
@Accessors(chain = true)
public class WbService {
private final WbContentDao wbContentDao = new WbContentDao();
private final WbRelationDao wbRelationDao = new WbRelationDao();
private final WbInboxDao wbInboxDao = new WbInboxDao();
public void start() {
WbHbaseDao.getConnection();
}
public void end() throws IOException {
WbHbaseDao.closeConnection();
}
/**
* star 发布微博 ok
*
* @param star 明星
* @param content 发布的微博内容
*/
public void publishWb(String star, String content) throws IOException {
Connection connection = WbHbaseDao.getConnection();
// 微博内容表增加一条记录 ok
byte[] rowkey = wbContentDao.addContent(connection, star, content);
// 获取该 star 的所有粉丝 fans ok
List<String> fans = wbRelationDao.getAllFans(connection, star);
// 向这些粉丝的收件箱发送微博消息 ok
wbInboxDao.receiveMessage(connection, star, rowkey, fans);
}
- Dao 层
- WbContentDao
/**
* 微博内容表交互类
*
* @author cris
* @version 1.0
**/
public class WbContentDao {
public static final String COLUMN_FAMILY_NAME = "info";
public static final String COLUMN_NAME = "content";
/**
* 微博内容表增加一条记录
*
* @param connection
* @param star
* @param content
*/
public byte[] addContent(Connection connection, String star, String content) throws IOException {
Table table = connection.getTable(TableName.valueOf(WbHbaseDao.WB_CONTENT_TABLENAME));
/*HBase 表存储数据默认按照 rowkey 的字符串大小比较排序,即字符串小的 rowkey 排在上面
* 为了取出 star 最新发布的微博内容,这里需要使用 Long 的最大值减去时间戳得到 rowkey:
* 微博发布时间越新,时间戳越大,Long 的最大值减去时间戳得到的 rowkey 就越小,在 Hbase 表排序越往上*/
byte[] rowkey = Bytes.toBytes(star + (Long.MAX_VALUE - System.currentTimeMillis()));
Put put = new Put(rowkey);
put.addColumn(Bytes.toBytes(COLUMN_FAMILY_NAME), Bytes.toBytes(COLUMN_NAME), Bytes.toBytes(content));
table.put(put);
WbHbaseDao.closeTable(table);
return rowkey;
}
}
- WbRelationDao
/**
* 用来表示用户之间关系的数据表,这里使用用户名字作为 rowkey,使用用户名字作为 fans 或者 attends 的列
* 实际开发中应该使用用户 id,这里为了方便展示,故使用用户姓名
*
* @author cris
* @version 1.0
**/
@SuppressWarnings("JavaDoc")
public class WbRelationDao {
public static final String COLUMN_FAMILY_FANS = "fans";
public static final String COLUMN_FAMILY_ATTENDS = "attends";
/**
* 根据 star 的名字获取他的所有粉丝名字
*
* @param connection
* @param star
* @return 粉丝列表
* @throws IOException
*/
public List<String> getAllFans(Connection connection, String star) throws IOException {
Table table = connection.getTable(TableName.valueOf(WbHbaseDao.WB_RELATION_TABLENAME));
// star 名字作为 rowkey ,搜索对应的所有粉丝列表
Get get = new Get(Bytes.toBytes(star));
get.addFamily(Bytes.toBytes(COLUMN_FAMILY_FANS));
//Result result = table.get(get);
//这里使用 Optional 包装一下,空指针直接报错
Optional<Result> result = Optional.of(table.get(get));
/*如果 star 没有粉丝列表也不会报错,返回的是一个没有数据的 cell[]*/
Cell[] cells = result.get().rawCells();
List<String> list = new ArrayList<>();
for (Cell cell : cells) {
list.add(new String(CellUtil.cloneQualifier(cell), StandardCharsets.UTF_8));
}
return list;
}
- WbInboxDao
/**
* 和 inbox 包交互类
*
* @author cris
* @version 1.0
**/
@SuppressWarnings("JavaDoc")
public class WbInboxDao {
private static final String COLUMN_FAMILY_INFO = "info";
private static final int DEFAULT_MESSAGE_COUNT = 5;
/**
* 通知所有粉丝的收件箱查看关注的 star 新发布的微博
*
* @param connection 连接
* @param star star 名字
* @param rowkey star 新发布的微博 rowkey
* @param fans 粉丝列表们
*/
public void receiveMessage(Connection connection, String star, byte[] rowkey, List<String> fans) throws IOException {
Table table = connection.getTable(TableName.valueOf(WbHbaseDao.WB_INBOX_TABLENAME));
Put put;
for (String fan : fans) {
put = new Put(Bytes.toBytes(fan));
put.addColumn(Bytes.toBytes(COLUMN_FAMILY_INFO), Bytes.toBytes(star), rowkey);
table.put(put);
}
WbHbaseDao.closeTable(table);
}
- 测试
/**
* 获取指定 star 的所有粉丝列表,测试 ok
*
* @throws IOException
*/
@Test
public void testGetAllFans() throws IOException {
List<String> allFans = wbController.getWbService().getWbRelationDao().getAllFans(WbHbaseDao.getConnection(), "james");
allFans.forEach(System.out::println);
// List<String> allFans = wbController.getWbService().getWbRelationDao().getAllFans(WbHbaseDao.getConnection(), "curry");
// allFans.forEach(System.out::println);
}
/**
* 整合功能测试(发布微博) ok
*
* @throws IOException
*/
@Test
public void testPublishWb() throws IOException {
wbController.publishWb("james", "i like f");
LOGGER.warn("发布成功!");
}
③ 添加关注用户功能实现
a、在微博用户关系表中,对当前主动操作的用户添加新关注的好友
b、在微博用户关系表中,对被关注的用户添加新的粉丝
c、微博收件箱表中添加所关注的用户发布的微博
- Service 层
/**
* 用户关注 star
*
* @param star 名字
* @param fan 粉丝
* @throws IOException
*/
public void attend(String star, String fan) throws IOException {
Connection connection = WbHbaseDao.getConnection();
// star 的粉丝列表更新 ok
wbRelationDao.addFans(connection, star, fan);
// fan 的 attend 列表更新 ok
wbRelationDao.addAttend(connection, star, fan);
// fan 的收件箱收到 star 最近更新的 5 条微博
wbInboxDao.flush(connection, star, fan);
}
- Dao 层
- WbRelationDao
/**
* 用户的 fans 列组添加粉丝
*
* @param connection
* @param star 明星
* @param fan 粉丝
* @throws IOException
*/
public void addFans(Connection connection, String star, String fan) throws IOException {
addFansOrStar(connection, fan, star, COLUMN_FAMILY_FANS);
}
/**
* 用户的 attends 列组添加 star
*
* @param connection
* @param star
* @param fan
* @throws IOException
*/
public void addAttend(Connection connection, String star, String fan) throws IOException {
addFansOrStar(connection, star, fan, COLUMN_FAMILY_ATTENDS);
}
/**
* 实际的添加 fan 或者 star 的方法
*
* @param connection 连接
* @param column star 或 fan 的名字
* @param rowkey 用户名字
* @param columnFamily 列族(每个人的 fans 列族或者 attends 列族)
* @throws IOException
*/
private void addFansOrStar(Connection connection, String column, String rowkey, String columnFamily) throws IOException {
Table table = connection.getTable(TableName.valueOf(WbHbaseDao.WB_RELATION_TABLENAME));
Put put = new Put(Bytes.toBytes(rowkey));
put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column), null);
table.put(put);
WbHbaseDao.closeTable(table);
}
- WbInboxDao
/**
* 用户关注某个用户后,收件箱自动获取该 star 最新的 5 条微博数据
*
* @param connection 连接
* @param star 明星名字
* @param fan 粉丝名字
*/
public void flush(Connection connection, String star, String fan) throws IOException {
Table table = connection.getTable(TableName.valueOf(WbHbaseDao.WB_CONTENT_TABLENAME));
Scan scan = new Scan();
scan.setStartRow(Bytes.toBytes(star));
/*这里使用倒数第三大的 | 符号来组装 rowkey,需要根据开始的 rowkey 和 结束的 rowkey 得到该明星发布的最新的 5 条微博*/
scan.setStopRow(Bytes.toBytes(star + "|"));
ResultScanner resultScanner = table.getScanner(scan);
List<Put> puts = new ArrayList<>(DEFAULT_MESSAGE_COUNT);
/*最好在添加每条微博数据的时候自定义时间戳,否则极易因为程序执行太快导致时间戳相同而无法插入多条数据*/
int i = 0;
/*这里得到的每一个 result 其实就是每一个 rowkey 对应的数据*/
for (Result result : resultScanner) {
String message = new String(result.getRow(), StandardCharsets.UTF_8);
Put put = new Put(Bytes.toBytes(fan));
put.addColumn(Bytes.toBytes(COLUMN_FAMILY_INFO), Bytes.toBytes(star),
System.currentTimeMillis() + (++i), Bytes.toBytes(message));
puts.add(put);
if (puts.size() == 5) {
break;
}
}
table = connection.getTable(TableName.valueOf(WbHbaseDao.WB_INBOX_TABLENAME));
table.put(puts);
WbHbaseDao.closeTable(table);
}
- 测试
/**
* 测试 star 的粉丝添加 ok
*
* @throws IOException
*/
@Test
public void testAddFans() throws IOException {
wbController.getWbService().getWbRelationDao().addFans(WbHbaseDao.getConnection(), "james", "cris");
wbController.getWbService().getWbRelationDao().addFans(WbHbaseDao.getConnection(), "james", "curry");
wbController.getWbService().getWbRelationDao().addFans(WbHbaseDao.getConnection(), "james", "harden");
LOGGER.warn("star 的粉丝列表添加成功!");
}
/**
* 测试粉丝的 attend 添加 ok
*
* @throws IOException
*/
@Test
public void testAddAttend() throws IOException {
wbController.getWbService().getWbRelationDao().addAttend(WbHbaseDao.getConnection(), "james", "cris");
wbController.getWbService().getWbRelationDao().addAttend(WbHbaseDao.getConnection(), "james", "curry");
wbController.getWbService().getWbRelationDao().addAttend(WbHbaseDao.getConnection(), "james", "harden");
LOGGER.warn("用户的关注列表添加成功");
}
/**
* 整合功能测试(关注 star) ok
*
* @throws IOException
*/
@Test
public void testAttend() throws IOException {
wbController.attend("james", "durant");
LOGGER.warn("关注成功!");
}
④ 移除(取关)用户功能实现
a、在微博用户关系表中,对当前主动操作的用户移除取关的好友(attends)
b、在微博用户关系表中,对被取关的用户移除粉丝
c、微博收件箱中删除取关的用户发布的微博
- Controller 层
/**
* 用户取消关注 star
*
* @param star
* @param fan
* @throws IOException
*/
public void removeAttend(String star, String fan) throws IOException {
wbService.removeAttend(star, fan);
}
- Service 层
/**
* 用户取消关注
*
* @param star
* @param fan
*/
public void removeAttend(String star, String fan) throws IOException {
Connection connection = WbHbaseDao.getConnection();
wbRelationDao.removeFan(connection, star, fan);
wbRelationDao.removeAttend(connection, star, fan);
wbInboxDao.removeFlush(connection, star, fan);
}
- Dao 层
- wbRelationDao
/**
* star 的粉丝列表移除粉丝
*
* @param connection
* @param star
* @param fan
* @throws IOException
*/
public void removeFan(Connection connection, String star, String fan) throws IOException {
removeFansOrStar(connection, fan, star, COLUMN_FAMILY_FANS);
}
/**
* fan 的 attend 列表移除 star
*
* @param connection
* @param star
* @param fan
* @throws IOException
*/
public void removeAttend(Connection connection, String star, String fan) throws IOException {
removeFansOrStar(connection, star, fan, COLUMN_FAMILY_ATTENDS);
}
/**
* 实际的移除 fan 或者 star 的方法
*
* @param connection 连接
* @param column star 或 fan 的名字
* @param rowkey 用户名字
* @param columnFamily 列族(每个人的 fans 列族或者 attends 列族)
* @throws IOException
*/
private void removeFansOrStar(Connection connection, String column, String rowkey, String columnFamily) throws IOException {
Table table = connection.getTable(TableName.valueOf(WbHbaseDao.WB_RELATION_TABLENAME));
Delete delete = new Delete(Bytes.toBytes(rowkey));
delete.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column));
table.delete(delete);
WbHbaseDao.closeTable(table);
}
- wbInboxDao
/**
* 粉丝取消关注后自动移除该粉丝收件箱,关于取消关注的 star 的所有消息
*
* @param connection 连接
* @param star 明星名字
* @param fan 粉丝名字
* @throws IOException
*/
public void removeFlush(Connection connection, String star, String fan) throws IOException {
Table table = connection.getTable(TableName.valueOf(WbHbaseDao.WB_INBOX_TABLENAME));
Delete delete = new Delete(Bytes.toBytes(fan));
delete.addColumn(Bytes.toBytes(COLUMN_FAMILY_INFO), Bytes.toBytes(star));
table.delete(delete);
WbHbaseDao.closeTable(table);
}