hbase-site.xml
和hdfs-site.xml
以及日志配置文件下载下来,放入根目录,如下准备好这些,还需要你配置好机器的maven环境,下面是用到的pom文件
junit
junit
4.12
org.apache.hbase
hbase-client
2.1.1
C:\Windows\System32\drivers\etc\host
,输入你的{ip} {hostname}
配置映射即可,第二种办法,就是你直接更改你的配置文件中的hostname为Linux的ip就可以了对于我这个版本来说,参考书上的代码已经是部分过期不推荐使用了,而是换成了xxxBuilder来更灵活的创建对象,这次我们创建test表,列族依旧是cf,如下
public void createTable() throws IOException {
//使用HBase配置创建新连接
Connection connection = ConnectionFactory.createConnection();
//或者HBase集群的管理员实例,实例不是线程安全的
Admin admin = connection.getAdmin();
//创建表名描述
TableName tableName = TableName.valueOf("default:test");
//创建表描述,并赋予表名
TableDescriptorBuilder tableBuilder = TableDescriptorBuilder.newBuilder(tableName);
//Builder构建列族信息
ColumnFamilyDescriptor cf = ColumnFamilyDescriptorBuilder.newBuilder("cf".getBytes()).build();
//将列族信息也赋予表描述对象
TableDescriptor table = tableBuilder.setColumnFamily(cf).build();
//创建表
admin.createTable(table);
//关闭
admin.close();
//关闭
connection.close();
}
TableName.valueOf("default:test");
中的default,就类似RDBMS中的库的概念,我们昨天在shell操作中,并没有指定哪个库,那么默认就会有一个default库供我们使用,库的名次在HBase中叫做namespaceVERSIONS => '1'
修改cf列族的VERSIONS属性为5
public void modifyCF() throws IOException {
Connection connection = ConnectionFactory.createConnection();
Admin admin = connection.getAdmin();
TableName tableName = TableName.valueOf("default:test");
//修改列族的信息,肯定要得到列族描述对象
//需要注意的是,由于我们不指定VERSIONS的话,默认最大版本和最小版本都是1
//所以如果你像下面在修改VERSIONS的时候,需要遵循MinVersions < MaxVersions
//最终修改到HBase中去的值是最大值
ColumnFamilyDescriptor cf = ColumnFamilyDescriptorBuilder.newBuilder("cf".getBytes())
.setMinVersions(4).setMaxVersions(5).build();
//执行修改
admin.modifyColumnFamily(tableName,cf);
connection.close();
admin.close();
}
desc 'test'
就会看到VERSIONS => '5'
,上面的修改操作是sync的,也有一个异步修改的方法,只是方法名不一样而已modifyColumnFamilyAsync(x,x)
,会修改了VERSIONS,那么修改别的属性只是在找方法名而已put没什么特别需要注意的,如下代码
public void putData() throws IOException {
Connection connection = ConnectionFactory.createConnection();
Table table = connection.getTable(TableName.valueOf("default:test"));
//创建put对象,一个put肯定要对应一行的,所以参数是必须的,但是有重载的参数,自己可以去看一下
Put putObj = new Put("row1".getBytes());
//上面行指定完后,就需要指定列族,列,值,这样才是完整的
putObj.addColumn("cf".getBytes(), "name".getBytes(), "wangziqiang".getBytes());
//执行~
table.put(putObj);
connection.close();
table.close();
}
xx.getBytes()
显得很麻烦,那么有什么办法解决吗?还真..没有,既然不能解决,HBase就自带了一个Bytes类,使用也很简单就是Bytes.toXXX(ss)
就可以了,这也说明了HBase中所有数据都是bytes数组,一切可以序列化为bytes的对象都可以作为rowkey,所以你就可以使用任何的序列化工具来保存在HBase中,比如Avro之类的Put类每次调用addColumn都会返回Put,所以我们就可以使用Builder模式来写代码了,如下
putObj.addColumn(xx)
.addColumn(xx)
.addColumn(xx);
checkAndMutate方法只是把检查和写入这两个步骤合二为一了.checkAndMutate方法在写入 前会先比较目前存在的数据是否与你传入的数据一致,如果一致则进行 put操作,并返回true.如果不一致,则返回false,但不写入数据(原子操作).那我们试试,首先HBase中的数据我并没有改,依旧是上面插入的wangziqiang,然后我们编写代码
public void checkAndPut() throws IOException {
Connection connection = ConnectionFactory.createConnection();
Table table = connection.getTable(TableName.valueOf("test"));
Put putObj = new Put("row1".getBytes());
putObj.addColumn("cf".getBytes(), "name".getBytes(), "wzq".getBytes());
//如果你对链式编程很熟悉,那么下面会很简单
//猜测thenPut方法是一个"触发方法",因为java的stream就是这样的
//指定行和列族
boolean b = table.checkAndMutate("row1".getBytes(), "cf".getBytes())
//指定列
.qualifier("name".getBytes())
//判断是否与这个串相等
.ifEquals("wangziqiang".getBytes())
//相等就put返回true,否则不put返回false
.thenPut(putObj);
System.out.println(b);
table.close();
connection.close();
}
在跑之前,我们将name值用shell改掉,看看他是否还是会put
hbase(main):022:0> scan 'test'
ROW COLUMN+CELL
row1 column=cf:name, timestamp=1543680836035, value=xgl
1 row(s)
Took 0.0130 seconds
执行结果就是false,当我们在手动改回去的时候,再次执行,就会返回true,不仅仅只能判断是否相等,还可以有比较器啊,或者删除操作,那么比较器是怎么用呢?
boolean b = table.checkAndMutate("row1".getBytes(), "cf".getBytes())
//指定列
.qualifier("name".getBytes())
//判断是否比1小
.ifMatches(CompareOperator.LESS, Bytes.toBytes(1))
//相等就put返回true,否则不put返回false
.thenPut(putObj);
对于枚举类可选参数有如下
/** less than */
LESS,
/** less than or equal to */
LESS_OR_EQUAL,
/** equals */
EQUAL,
/** not equal */
NOT_EQUAL,
/** greater than or equal to */
GREATER_OR_EQUAL,
/** greater than */
GREATER,
/** no operation */
NO_OP,
就是在你指定位置数据上追加一段数据,比如我们在name上随意追加一串字符,如下
public void appendData() throws IOException {
Connection connection = ConnectionFactory.createConnection();
Table table = connection.getTable(TableName.valueOf("default:test"));
Append append = new Append("row1".getBytes());
//指定列族列
append.addColumn("cf".getBytes(),"name".getBytes(),"NP".getBytes());
table.append(append);
table.close();
connection.close();
}
get方法跟shell作用的一样的,那么我们就可以得到一个版本的cell,或者全部版本的cell,如下
public void getData() throws IOException {
Connection connection = ConnectionFactory.createConnection();
Table table = connection.getTable(TableName.valueOf("default:test"));
//指定行
Get getObj = new Get("row1".getBytes());
//根据行查询出行中信息,封装为result对象
Result result = table.get(getObj);
for (Cell cell : result.listCells()) {
System.out.println(Bytes.toString(cell.getRowArray()));
System.out.println("family:" + Bytes.toString(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength()));
System.out.println("qualifier:" + Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()));
System.out.println("value:" + Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
System.out.println("Timestamp:" + cell.getTimestamp());
}
table.close();
connection.close();
}
执行结果
row1cfage gl��20
family:cf
qualifier:age
value:20
Timestamp:1543706228633
row1cfname glj;wangziqiang
family:cf
qualifier:name
value:wangziqiang
Timestamp:1543706208827
当然上面的代码中getObj也可以添加列条件,比如
...
Get getObj = new Get("row1".getBytes());
getObj.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("name"));
...
那么执行结果就只是有name的cell了,到这我们只是返回了一个版本的cell,我们之前可是put了很多的name的,那么如何查看这些信息呢?
//代码几乎是一致的,只是将get对象设置一个属性即可
public void getData() throws IOException {
....
//指定行
Get getObj = new Get("row1".getBytes());
getObj.readVersions(5); //变化!!!!
//根据行查询出行中信息,封装为result对象
Result result = table.get(getObj);
for (Cell cell : result.getColumnCells(Bytes.toBytes("cf"),Bytes.toBytes("name"))) {
System.out.println(Bytes.toString(cell.getValueArray()));
System.out.println(Bytes.toString(CellUtil.cloneValue(cell)));
}
...
}
上面有两个输出,他们是这样的
row1cfname glK�wangziqiang
wangziqiang
一看名字就知道,他肯定是原子性的并且是数字操作,好了我的表中现在我是20岁,我需要将它加十岁
public void incrementData() throws IOException {
Connection connection = ConnectionFactory.createConnection();
Table table = connection.getTable(TableName.valueOf("default:test"));
Increment increment = new Increment(Bytes.toBytes("row1"));
increment.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("age"),10L);
table.increment(increment);
table.close();
connection.close();
}
需要注意的是,当你查看HBase中的Long型数据的时候,他并不能直接看到,我们需要用到上面get方法才能取得我们能看得懂的数据,我找了一下没有发现shell中直接查看这个数据的内置命令,只能通过这个
//以后在找到了方便的在更新吧,如果您知道怎么做,请您不吝啬的抛给我哈哈
org.apache.hadoop.hbase.util.Bytes.toLong("\x00\x00\x00\x00\x00\x00\x00\x1E".to_java_bytes)
也不一定是数据,还可以是行啊或者列族
public void delete() throws IOException {
Connection connection = ConnectionFactory.createConnection();
Table table = connection.getTable(TableName.valueOf("default:test"));
Delete del = new Delete(Bytes.toBytes("row1"));
del.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("name"));
del.addColumns(Bytes.toBytes("cf"),Bytes.toBytes("age"));
table.delete(del);
table.close();
connection.close();
}
addColumns
和addColumn
一个只删除最新版本,一个是全部版本都删除delete也有一个类似put方法的check版本,其使用的方法都是一样的,就是checkAndMutate
,之后后续调用的是thenDelete而已
public void checkAndDelete() throws IOException {
Connection connection = ConnectionFactory.createConnection();
Table table = connection.getTable(TableName.valueOf("default:test"));
Delete del = new Delete(Bytes.toBytes("row1"));
del.addColumns(Bytes.toBytes("cf"),Bytes.toBytes("name"));
table.checkAndMutate(Bytes.toBytes("row1"),Bytes.toBytes("name"))
.ifEquals(Bytes.toBytes("wangziqiang"))
.thenDelete(del);
table.close();
connection.close();
}
RowMutations
类可以让你完成这个想法这个类的继承关系
public class RowMutations implements Row {}
public interface Row extends Comparable {}
public abstract class Mutation extends OperationWithAttributes implements Row, CellScannable,HeapSize {}
而RowMutations,Append,Delete,Put,Increment,Get等都是这个类的实现,既然是他们的共同父类,所以就可以达到一起使用了
使用
public void test() throws IOException {
Connection connection = ConnectionFactory.createConnection();
Table table = connection.getTable(TableName.valueOf("default:test"));
//将name全部版本cell删除
Delete delete = new Delete(Bytes.toBytes("row1"))
.addColumns(Bytes.toBytes("cf"), Bytes.toBytes("name"));
//添加addr:beijing
Put put = new Put(Bytes.toBytes("row1"))
.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("addr"),Bytes.toBytes("beijing"));
List list = new ArrayList<>();
list.add(delete);
list.add(put);
RowMutations rowMutations = new RowMutations(Bytes.toBytes("row1"));
rowMutations.add(list);
//执行
table.mutateRow(rowMutations);
table.close();
connection.close();
}
checkAndMutation
,具体自己可以试一下,就不贴出来了批量操作的api依旧是对表的操作,所以还是在我们的Table对象上用批量操作
default void batch(final List extends Row> actions, final Object[] results)..{}
类型 | 说明 |
---|---|
null | 操作与服务器端的通信失败 |
EmptyResult | put和delete操作成功之后的返回结果 |
Result | Get操作成功之后的返回结果,如果没有匹配get的条件,那么就返回空的Result |
Throwable | 操作在服务器端出现异常了,服务端会将错误返回回来 |
RetriesExhaustedWithDetailsException
,这个异常包含了有多少操作失败了以及失败的原因之类的详细信息下面就只剩下代码实现了
public void test() throws IOException, InterruptedException {
Connection connection = ConnectionFactory.createConnection();
Table table = connection.getTable(TableName.valueOf("default:test"));
Put put = new Put(Bytes.toBytes("row1"));
put.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("name"),Bytes.toBytes("wangziqiang"));
put.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("addr"),Bytes.toBytes("hebei"));
put.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("heigth"),Bytes.toBytes(182));
put.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("garden"),Bytes.toBytes("nan"));
ArrayList list = new ArrayList<>();
list.add(put);
Object[] result = new Object[list.size()];
table.batch(list,result);
table.close();
connection.close();
}
对于失败get
弃用理由
所以综上所述:setAutoFlush被废弃了,每个表自带的 writeBuffer也被废弃了,但是客户端写缓冲区还是存在的,只是结构 和调用方式并不是之前那样了,而是转而使用BufferedMutator对象
Connection connection = ConnectionFactory.createConnection();
BufferedMutator bufferedMutator = connection.getBufferedMutator(TableName.valueOf("test"));
bufferedMutator.mutate(put); //用此对象提交put操作
//然后调用flush或者close将请求批量交给服务器