开源工具 | hbase-sdk ORM框架
hbase-sdk
基于hbase-client的相关API开发而来的一款轻量级的HBase ORM框架。 😋
hbase-sdk分为spring-boot-starter-hbase和hbase-sdk-core两部分。
SpringBoot项目中引入spring-boot-starter-hbase,在普通的Java项目中则可以使用hbase-sdk-core。
hbase-sdk
hbase-sdk
是一款轻量级的 ORM 框架,封装了对 HBase 的数据读写和对集群的运维管理等操作。
功能特性
当前功能
-
在保留原有 hbase-client 功能 API 的基础上提供了 ORM 的特性 -
与 SpringBoot 集成,很好地利用了 SpringBoot 的优良特性 -
JDK8+
未来想要新增的功能
-
主备集群 API 层面的熔断机制 -
读写流量控制 -
... ...
快速开始
1. 普通项目
Maven
配置:
创建一个基础的 Maven
工程,HBase SDK API 开发时基于的 HBase 版本主要是 1.4.3 和 2.1.0。
所以,如果你的 HBase 版本是 1.x,可以使用如下依赖。
<dependency>
<groupId>com.github.CCweixiao</groupId>
<artifactId>hbase-sdk-core_1.4</artifactId>
<version>1.0.5</version>
</dependency>
如果你的 HBase 版本是 2.x,则可以使用如下依赖。
<dependency>
<groupId>com.github.CCweixiao</groupId>
<artifactId>hbase-sdk-core_2.1</artifactId>
<version>1.0.5</version>
</dependency>
hbase-sdk
目前最新的版本是1.0.5
。你可以在 maven 仓库中搜索 CCweixiao 来获取 hbase-sdk 相关 jar 包的最新版本。
https://mvnrepository.com/artifact/com.github.CCweixiao
当然,如果你想重新编译,以适配你自己的 HBase 版本,也可以选择下载源码,修改项目 pom.xml 文件中的 hbase.version 来运行如下编译命令:
git clone https://github.com/CCweixiao/hbase-sdk.git
git clone https://gitee.com/weixiaotome/hbase-sdk.git
cd hbase-sdk
mvn clean install -Dmaven.test.skip=true
2. 项目结构
3. 在 SpringBoot 项目中使用
Maven
配置:
创建一个基于Maven
的 spring boot 工程。
<dependency>
<groupId>com.github.CCweixiao</groupId>
<artifactId>spring-boot-starter-hbase_1.4</artifactId>
<version>1.0.5</version>
</dependency>
spring-boot-starter-hbase 这个模块中已经包含了 hbase-sdk-core。
4. 引入 hbase-client 的依赖
除了引入hbase-sdk
的相关依赖之外,你还需要引入hbase-client
的依赖,hbase-client
的版本目前建议为1.2.x
、1.4.x
、2.1.x
。其实 hbase-client 新旧 API 有所差异。未来,hbase-sdk
在对 hbase 的版本支持方面会更加完善。
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.4.3</version>
</dependency>
5. 配置 HBase 数据库连接
普通项目
// 数据读写操作
HBaseTemplate hBaseTemplate = new HBaseTemplate("node1", "2181");
//集群管理操作
HBaseAdminTemplate hBaseAdminTemplate = new HBaseAdminTemplate("node1", "2181");
spring boot 项目
application.yaml
spring:
data:
hbase:
quorum: node1
@Service
public class UserService {
@Autowired
private HBaseTemplate hBaseTemplate;
@Autowired
private HBaseAdminTemplate hBaseAdminTemplate;
}
集群管理
目前,HBaseAdminTemplate 只提供了 HBaseAdmin 的常用操作,比如 namespace 的管理、表的管理等等,与原生 HBaseAdmin 的 API 相比,它的功能可能不是很全面,但以后一定会更加完善。
创建 namespace
HBase 管理员操作 API 与原 API 其实差异并不大。
@Test
public void testCreateNamespace() {
String namespaceName = "LEO_NS2";
Map<String, String> para = new HashMap<>();
para.put("tag", "测试命名空间");
para.put("createBy", "leo");
para.put("updateBy", "");
NamespaceDescriptor namespaceDescriptor = NamespaceDescriptor.create(namespaceName)
.addConfiguration(para)
.build();
hBaseTemplate.createNamespace(namespaceDescriptor);
}
创建表
@Test
public void testCreateTable() {
String tableName = "LEO_NS:USER";
HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));
tableDescriptor.setValue("tag", "测试用户表");
tableDescriptor.setValue("createUser", "leo");
HColumnDescriptor columnDescriptor = new HColumnDescriptor("INFO");
columnDescriptor.setScope(1);
columnDescriptor.setCompressionType(Compression.Algorithm.SNAPPY);
columnDescriptor.setTimeToLive(2147483647);
columnDescriptor.setMaxVersions(3);
HColumnDescriptor columnDescriptor2 = new HColumnDescriptor("INFO2");
columnDescriptor2.setScope(0);
columnDescriptor2.setTimeToLive(864000);
columnDescriptor2.setMaxVersions(3);
tableDescriptor.addFamily(columnDescriptor).addFamily(columnDescriptor2);
hBaseTemplate.createTable(tableDescriptor, Bytes.toBytes(0), Bytes.toBytes(100), 10);
}
更多操作
可以参考测试用例中的相关API。
数据读写
类似于 Hibernate,你也可以使用 hbase-sdk 框架所提供的 ORM 特性,来实现对 HBase 的读写操作。
创建数据模型类
public class ModelEntity {
private String createBy;
private Long createTime;
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
}
@HBaseTable(schema = "TEST", name = "LEO_USER", uniqueFamily = "info1")
public class UserEntity extends ModelEntity{
@HBaseRowKey
private String userId;
private String username;
private int age;
private List<String> addresses;
private Map<String,Object> contactInfo;
private Double pay;
@HBaseColumn(name = "is_vip", family = "INFO2", toUpperCase = true)
private boolean isVip;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isVip() {
return isVip;
}
public void setVip(boolean vip) {
isVip = vip;
}
public List<String> getAddresses() {
return addresses;
}
public void setAddresses(List<String> addresses) {
this.addresses = addresses;
}
public Map<String, Object> getContactInfo() {
return contactInfo;
}
public void setContactInfo(Map<String, Object> contactInfo) {
this.contactInfo = contactInfo;
}
public Double getPay() {
return pay;
}
public void setPay(Double pay) {
this.pay = pay;
}
@Override
public String toString() {
return "UserEntity{" +
"userId='" + userId + '\'' +
", username='" + username + '\'' +
", age=" + age +
", addresses=" + addresses +
", contactInfo=" + contactInfo +
", pay=" + pay +
", isVip=" + isVip +
'}';
}
}
@HBaseTable(schema = "TEST", name = "LEO_USER", uniqueFamily = "info1")
HBaseTable 注解用于定义 HBase 的表信息,schema 用于定义该表的命名空间,如果不指定,默认是 default, name 用于定义该表的表名,如果不指定,表名则为类名的组合单词拆分加'_'拼接,如:UserEntity 对应的表名为:user_entity。 uniqueFamily 用于定义如果所有的字段不特配置列簇名,则使用此处配置的列簇名。
@HBaseRowKey
private String userId;
该注解表示 userId 字段为 rowKey 字段。
@HBaseColumn(name = "is_vip", family = "INFO2", toUpperCase = true)
private boolean isVip;
该注解用于定义一个字段信息,name 用于定义字段名,如果不指定,则默认使用字段名的组合单词拆分加'_'拼接,如:isVip,对应的字段名是:is_vip. family 用于定义该字段属于 INFO2 列簇,toUpperCase 表示字段名是否转大写,默认 false,不做操作。
保存数据
@Test
public void testSaveUser() {
UserEntity userEntity = new UserEntity();
userEntity.setUserId("10001");
userEntity.setUsername("leo");
userEntity.setAge(18);
userEntity.setVip(true);
userEntity.setAddresses(Arrays.asList("北京", "上海"));
userEntity.setCreateBy("admin");
userEntity.setCreateTime(System.currentTimeMillis());
Map<String, Object> contactInfo = new HashMap<>(2);
contactInfo.put("email", "[email protected]");
contactInfo.put("phone", "18739577988");
contactInfo.put("address", "浦东新区");
userEntity.setContactInfo(contactInfo);
userEntity.setPay(100000.0d);
try {
hBaseTemplate.save(userEntity);
System.out.println("用户数据保存成功!");
} catch (Exception e) {
e.printStackTrace();
}
}
除此之外,保存数据时也可以不必构造数据模型类,而直接构造 map 数据模型。
@Test
public void testToSave() {
Map<String, Object> data = new HashMap<>();
data.put("info1:addresses", Arrays.asList("广州", "深圳"));
data.put("info1:username", "leo");
data.put("info1:age", 18);
data.put("INFO2:IS_VIP", true);
data.put("info1:pay", 10000.1d);
data.put("info1:create_by", "tom");
data.put("info1:create_time", System.currentTimeMillis());
Map<String, Object> contactInfo = new HashMap<>(2);
contactInfo.put("email", "[email protected]");
contactInfo.put("phone", "18739577988");
contactInfo.put("address", "浦东新区");
data.put("info1:contact_info", contactInfo);
hBaseTemplate.save("TEST:LEO_USER", "10002", data);
System.out.println("用户数据保存成功!");
}
批量保存数据
@Test
public void testToSaveBatch() {
Map<String, Map<String, Object>> data = new HashMap<>();
Map<String, Object> data1 = new HashMap<>();
data1.put("info1:username", "kangkang");
data1.put("info1:age", 18);
data1.put("INFO2:IS_VIP", true);
Map<String, Object> data2 = new HashMap<>();
data2.put("info1:username", "jane");
data2.put("info1:age", 18);
data2.put("INFO2:IS_VIP", false);
data.put("12003", data1);
data.put("11004", data2);
hBaseTemplate.saveBatch("TEST:LEO_USER", data);
System.out.println("用户数据批量保存成功!");
}
查询数据
根据 RowKey 查询
@Test
public void testGet() {
UserEntity userEntity = hBaseTemplate.getByRowKey("10001", UserEntity.class);
final UserEntity userEntity1 = hBaseTemplate.getByRowKey("10002", UserEntity.class);
System.out.println("用户数据获取成功!");
System.out.println(userEntity);
}
@Test
public void testGetToMap() {
Map<String, Object> userInfo = hBaseTemplate.getByRowKey("TEST:LEO_USER", "10001");
System.out.println(Boolean.valueOf(userInfo.get("INFO2:IS_VIP").toString()));
System.out.println(userInfo);
}
scan 查询
@Test
public void testFind() {
final List<UserEntity> userEntities = hBaseTemplate.findAll(10, UserEntity.class);
System.out.println(userEntities);
System.out.println("用户数据批量查询");
}
@Test
public void testFindByPrefix() {
final List<UserEntity> userEntities = hBaseTemplate.findByPrefix("11", 10, UserEntity.class);
System.out.println("用户数据批量查询");
}
删除数据
@Test
public void testDeleteData() {
hBaseTemplate.delete("TEST:LEO_USER", "12003");
hBaseTemplate.delete("TEST:LEO_USER", "11004", "INFO2");
hBaseTemplate.delete("TEST:LEO_USER", "10001", "info1", "addresses");
System.out.println("数据删除完成");
}
@Test
public void testDeleteBatch() {
hBaseTemplate.deleteBatch("TEST:LEO_USER", Arrays.asList("10001", "10002"));
hBaseTemplate.deleteBatch("TEST:LEO_USER", Collections.singletonList("10003"), "INFO2");
hBaseTemplate.deleteBatch("TEST:LEO_USER", Collections.singletonList("10004"),
"info1", "age", "username");
}
总结
基于 hbase-sdk 的 ORM 特性,可以很方便地实现 Java 对象与 HBase 数据模型之间的绑定,目前代码整体的实现还有些粗糙,或许还存在没有考虑到的性能方面的问题,但这些瓶颈一定会在后续的努力中一一改善。