Docker下HBase学习,三部曲之三:java开发

前两章《Docker下HBase学习,三部曲之一:极速体验》《Docker下HBase学习,三部曲之二:集群HBase搭建》我们学习了HBase的单机和集群环境搭建,本章我们继续实战,学习在java应用中操作HBase;

完整的Demo源码

本次实战的完整的源码地址是:git@github.com:zq2599/blog_demos.git,里面有多个工程,本次Demo所在目录如下图红框所示:

网络规划

本次实战,会启动两个docker容器:hbase单机版和tomcat,java web应用部署在tomcat上,对hbase进行操作;

启动hbase容器

执行以下命令启动hbase单机版容器:

docker run --name=hbasestandalone -idt -p 60010:60010 bolingcavalry/centos7-hbase126-standalone:0.0.1

容器启动后要执行如下命令进入容器:

docker exec -it hbasestandalone /bin/bash

进入容器后,执行start-hbase.sh即可启动hbase服务,如下图:

启动tomcat容器

执行以下命令启动一个定制版的tomcat容器,该容器启动后,可以在线部署war包:

docker run --name=tomcat001 --link=hbasestandalone:hbaseserver -p 8080:8080 -idt bolingcavalry/online_deploy_tomcat:0.0.1

link参数表明了可以通过hbaseserver确定hbasestandalone的ip(/etc/hosts文件);

关于tomcat支持在线部署war包的更多信息,请参考文章《实战docker,编写Dockerfile定制tomcat镜像,实现web应用在线部署》;

开发web应用:pom.xml

本次开发的java web应用是基于maven构建的,所以在pom中出了spring,sl4j等常用的库,还要加入hbase的依赖,如下:

<dependency>
      <groupId>org.apache.hadoop</groupId>
      <artifactId>hadoop-hdfs</artifactId>
      <version>2.7.4</version>
    </dependency>

    <dependency>
      <groupId>org.apache.hbase</groupId>
      <artifactId>hbase-client</artifactId>
      <version>1.2.6</version>
    </dependency>

关于Demo

本次实战的demo是围绕着学生类展开的,这个学生类只有三个字段:id、name、age,源码如下:

public class Student {
    /**
     * 学号
     */
    private long id;

    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private int age;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

服务接口

本次我们只是简单的实战,只涉及到新增、删除、查找这些最基本的功能,服务接口定义如下:

public interface StudentService {

    /**
     * 新增
     * @param student
     * @return
     */
    String insert(Student student);

    /**
     * 根据学号删除
     * @param id
     * @return
     */
    String delete(long id);

    /**
     * 根据学号查找
     * @param id
     * @return
     */
    StudentDTO find(long id);
}

初始化、创建表

通过spring的@PostConstruct注解,我们可以定义一个实例在构造方法完成后被spring主动调用执行的方法,这里我们用这个方法来检查有没有student方法,如果没有就在此创建,以保证后续的操作时表是存在的,源码如下:

private Configuration configuration;

    private HBaseAdmin hBaseAdmin;

    @PostConstruct
    public void init(){
        LOGGER.info("start init");
        configuration = HBaseConfiguration.create();
        configuration.set("hbase.zookeeper.quorum", "hbaseserver");
        try {
            hBaseAdmin = new HBaseAdmin(configuration);

            //检查表是否存在,如果不存在就创建
            if (!hBaseAdmin.tableExists(TABLE_NAME)) {
                LOGGER.info("table is not exist");

                HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf(TABLE_NAME));
                for (String columnName : COLUMNS) {
                    HColumnDescriptor column = new HColumnDescriptor(columnName);
                    hTableDescriptor.addFamily(column);
                }

                hBaseAdmin.createTable(hTableDescriptor);
            }
        }catch (Exception e){
            LOGGER.error("init error", e);
            e.printStackTrace();
        }

        LOGGER.info("finish init");
    }

如上所示,通过@PostConstruct注释,在业务调用StudentService提供的服务之前,init方法就已经执行过了,里面对configuration、hBaseAdmin等变量都做了实例化,还会检查student表是否存在,如果不存在就主动去创建student表;

注意这段代码configuration.set(“hbase.zookeeper.quorum”, “hbaseserver”),”hbaseserver”是启动docker容器时候的link参数,这里可以用来代表hbase服务器的ip(被docker服务写入了tomcat001容器的/etc/hosts文件);

新增一条记录

student表有两个family:id和info,id是学号,info包含了两个qualifier:name和age,并且每一条记录的rowkey也用学号表示,所以新增一条记录的关键代码如下:

        //主键
        byte[] rowIdBytes = Bytes.toBytes(rowId);

        String errorStr = null;
        HTable hTable = htable();

        if(null==hTable){
            return ERROR_CREATE_HTABLE_FAIL;
        }

        Put p1 = new Put(rowIdBytes);

        Map<String, String> map = new HashMap<String, String>();
        map.put("id", id);
        map.put("info:name", student.getName());
        map.put("info:age", String.valueOf(student.getAge()));

        for (String columnName : map.keySet()) {
            byte[] value = Bytes.toBytes(map.get(columnName));
            String[] str = columnName.split(":");
            byte[] family = Bytes.toBytes(str[0]);
            byte[] qualifier = null;
            if (str.length > 1) {
                qualifier = Bytes.toBytes(str[1]);
            }
            p1.addColumn(family, qualifier, value);
        }

        try {
            hTable.put(p1);
        }catch(Exception e){
            LOGGER.error("insert error : " + e);
            e.printStackTrace();
        }

删除一条记录

根据rowkey删除记录的代码如下所示:

List<Delete> list = new ArrayList<Delete>();
        Delete delete = new Delete(Bytes.toBytes(String.valueOf(id)));
        list.add(delete);

        String errorStr = null;

        try {
            hTable.delete(list);
        }catch(Exception e){
            errorStr = "delete error : " + e;
            LOGGER.error("delete error");
        }

根据rowkey查询记录

根据rowkey查询记录的代码如下所示:

        String errorStr = null;
        Result result = null;
        Get get = new Get(Bytes.toBytes(String.valueOf(id)));

        try {
            result = hTable.get(get);
        }catch(Exception e){
            errorStr = "find error : " + e;
        }

        StudentDTO studentDTO = new StudentDTO();
        studentDTO.setErrorStr(errorStr);

        if(StringUtils.isBlank(errorStr) && null!=result){
            studentDTO.setStudent(buildStudent(result.rawCells()));
            LOGGER.info("after build : " + JSON.toJSONString(studentDTO));
        }

        return studentDTO;

查找到的结果被封装在result对象中,result.rawCells()方法返回的Cell数组中有所有查询结果数据,这里专门做了个buildStudent方法来处理Cell数组,转成Student对象:

        Student student = null;

        for(Cell cell : cellArray){
            String row = new String(CellUtil.cloneRow(cell));
            String family = new String(CellUtil.cloneFamily(cell));
            String qualifier = new String(CellUtil.cloneQualifier(cell));
            String value = new String(CellUtil.cloneValue(cell));

            LOGGER.info("row [{}], family [{}], qualifier [{}], value [{}]", row, family, qualifier, value);

            if(!StringUtils.isNumeric(row)){
                LOGGER.error("invalid row for build student");
                return null;
            }

            if(null==student){
                student = new Student();
                student.setId(Long.valueOf(row));
            }

            if("info".equals(family)){
                if("age".equals(qualifier)){
                    student.setAge(Integer.valueOf(value));
                } else if("name".equals(qualifier)){
                    student.setName(value);
                }
            }
        }

如上所示,从Cell中可以取出row、family、qualifier、value这些信息,综合起来就能凑够student所需的字段了;

Controller

做好了StudentService及其对应的实现,我们再实现一个Controller,以便从浏览器输入url来体验StudentService的服务:

@RequestMapping("/insert")
    public String insert(HttpServletRequest request, Model model) {
        String id = get(request, "id");
        String name = get(request, "name");
        String age = get(request, "age");

        Student student = new Student();
        student.setId(Long.valueOf(id));
        student.setName(name);
        student.setAge(Integer.valueOf(age));

        String errorStr = studentService.insert(student);

        LOGGER.info("student service response [{}]", errorStr);

        return StringUtils.isEmpty(errorStr) ? "success" : buileFilePageInfo(errorStr, model);
    }


    @RequestMapping("/delete")
    public String delete(HttpServletRequest request, Model model) {
        String id = get(request, "id");

        String errorStr = studentService.delete(Long.valueOf(id));

        LOGGER.info("student service response [{}]", errorStr);

        return StringUtils.isEmpty(errorStr) ? "success" : buileFilePageInfo(errorStr, model);
    }

    @RequestMapping("/find")
    @ResponseBody
    public String find(HttpServletRequest request, Model model) {
        String id = get(request, "id");

        StudentDTO studentDTO = studentService.find(Long.valueOf(id));

        LOGGER.info("find result : {}", JSON.toJSONString(studentDTO));

        return StringUtils.isEmpty(studentDTO.getErrorStr())
                ? JSON.toJSONString(studentDTO.getStudent())
                : studentDTO.getErrorStr();
    }

部署&验证

将应用打包成war,在线部署到tomcat001容器上,开始验证: 1. 在浏览器输入“http://localhost:8080/hbasedemo/insert?id=9009&name=Tom&age=16”,验证新增一条记录,页面提示“操作成功”后,去hbase容器上执行hbase shell命令进入控制台,再执行scan ‘student’查看student表的内容,如下图,已经新增了一条记录:

2. 在浏览器输入“http://localhost:8080/hbasedemo/find?id=9009”,根据学号查找记录,服务端直接返回json格式数据,如下图:

3. 在浏览器输入“http://localhost:8080/hbasedemo/delete?id=9009”,根据学号删除记录,操作成功后,不论是执行查询请求还是去hbase控制台执行scan ‘student’查看student表的内容,都看不到9009这条记录了;

至此,在java应用中操作HBase的实战就结束了,完整的源码地址是:git@github.com:zq2599/blog_demos.git,里面有多个工程,本次Demo所在目录如下图红框所示:

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏张善友的专栏

.NET 4.0 的Web Form和EF的例子 Employee Info Starter Kit (v4.0.0)

ASP.NET 4.0改进了许多不同的场景集(set of scenarios),如Webforms ,Dynamic Data以及基于AJAX的Web开发。此...

17310
来自专栏学习力

《Java从入门到放弃》框架入门篇:springMVC数据传递

1224
来自专栏技术博客

MVC项目开发中那些用到的知识点(Jquery ajax提交Json后台处理)

  jQuery提供的ajax方法能很方便的实现客户端与服务器的异步交互,在asp.net mvc 框架使用jQuery能很方便地异步获取提交数据,给用户提供更...

992
来自专栏Java 技术分享

Web 小案例 -- 网上书城(三)

27010
来自专栏小筱月

springboot 整合 MongoDB 实现登录注册,html 页面获取后台参数的方法

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而...

530
来自专栏葡萄城控件技术团队

ASP.NET MVC 5 - 控制器

MVC代表: 模型-视图-控制器 。MVC是一个架构良好并且易于测试和易于维护的开发模式。基于MVC模式的应用程序包含: · Models: 表示该应用程序的数...

1768
来自专栏java沉淀

java实现Excel导入实战篇(迭代二)

823
来自专栏dotnet core相关

WCF入门(10)

公司是做乙方的,工资还凑合,主要是项目基本都已完成,进去就是干维护,体会不到那种从头彻尾的成就感。项目中具体用了EF+Ado.net+WCF+WPF+(VB.n...

452
来自专栏PHP技术

PHP使用SOAP调用.net的WebService问题

项目的需求,需要和一个.net系统进行数据交换,合作方提供了一个WebService接口。这个与一般的PHP POST或GET传值再查库拿数据的思路有点不一样,...

3126
来自专栏向治洪

Android 几种网络请求的区别与联系

HttpUrlConnection 最开始学android的时候用的网络请求是HttpUrlConnection,当时很多东西还不知道,但是在android...

2035

扫码关注云+社区