vlambda博客
学习文章列表

开源工具 | 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. 项目结构

project

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.x1.4.x2.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 数据模型之间的绑定,目前代码整体的实现还有些粗糙,或许还存在没有考虑到的性能方面的问题,但这些瓶颈一定会在后续的努力中一一改善。


据说,关注我的人都找到了对象👇