對於數據操作,HBase支持四類主要的數據操作,分別是:
- Put :增加一行,修改一行
- Delete :刪除一行,刪除指定列族,刪除指定column的多個版本,刪除指定column的制定版本等
- Get :獲取指定行的所有信息,獲取指定行和指定列族的所有colunm,獲取指定column,獲取指定column的幾個版本, 獲取指定column的指定版本等
- Scan :獲取所有行,獲取指定行鍵范圍的行,獲取從某行開始的幾行,獲取滿足過濾條件的行等
1. 命名空間NameSpace
在關系數據庫系統中,命名空間NameSpace指的是一個表的邏輯分組 ,同一分組中的各個表有類似的用途。命名空間的概念為即將到來的多租戶特性打下基礎:
配額管理(Quota Management (HBASE-8410)):限制一個NameSpace可以使用的資源,資源包括region和table等
命名空間安全管理(Namespace Security Administration (HBASE-9206)):提供了另一個層面的多租戶安全管理
Region服務器組(Region server groups (HBASE-6721)):一個命名空間或一張表,可以被固定到一組 RegionServers上,從而保證了數據隔離性。
1.1.命名空間管理
命名空間可以被創建、移除、修改。表和命名空間的隸屬關系 在在創建表時決定,通過以下格式指定:<namespace>:<table>
Example:hbase shell中相關命令:
//Create a namespace create_namespace 'my_ns' //create my_table in my_ns namespace create ' my_ns:my_table', 'fam' //drop namespace drop_namespace 'my_ns' //alter namespace alter_namespace 'my_ns', {METHOD => 'set', 'PROPERTY_NAME' => 'PROPERTY_VALUE'}
1.2. 預定義的命名空間
有兩個系統內置的預定義命名空間:
hbase:系統命名空間,用於包含hbase的內部表
default:所有未指定命名空間的表都自動進入該命名空間(默認的)
Example:指定命名空間和默認命名空間
//namespace=foo and table qualifier=bar create 'foo:bar', 'fam' //namespace=default and table qualifier=bar create 'bar', 'fam'
2.創建表
private static void createTable() throws MasterNotRunningException, ZooKeeperConnectionException, IOException { /** * create()方法有兩個靜態方法,無參和有參數 * 無參數:hadoop的core-site.xml配置的參數會被后面的覆蓋 * 有參數:hadoop的core-site.xml配置的參數無法修改 */ Configuration conf = HBaseConfiguration.create();
// the location of hbase on hdfs conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); // the node of zookeeper conf.set("hbase.zookeeper.quorum", "ncst");
HBaseAdmin hba = new HBaseAdmin(conf);
// create namespace named 'ns' hba.createNamespace(NamespaceDescriptor.create("ns").build());
// create table named 'ns:t1' TableName tname = TableName.valueOf("ns:t1");
// Analyzing table exists if(!hba.tableExists(tname)){ HTableDescriptor htd = new HTableDescriptor(tname);
// add column 'f1' to table 't1' HColumnDescriptor hcd = new HColumnDescriptor("f1"); htd.addFamily(hcd);
//create table now hba.createTable(htd); } hba.close(); }
注意:
1). 必須將HBase集群的hbase-site.xml文件添加進工程的classpath中,否則 Configuration conf = HBaseConfiguration. create()代碼獲取不到需要的集群相關信息,也就無法找到集群,運行程序時會報錯。即conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); 和 conf.set("hbase.zookeeper.quorum", "ncst"); 不可缺少!
2). 代碼HTableDescriptor htd = new HTableDescriptor(TableName.valueOf("ns:t1"));是描述表t1,並將t1添加到ns命名空間中,前提是該命名空間已存在,如果不存在則會報錯NamespaceNotFoundException
3). 命名空間一般在建模階段通過命令行創建,在java代碼中通過hba.createNamespace(NamespaceDescriptor.create("ns").build());創建的機會不多
4). 創建 HBaseAdmin 對象時就已經建立了客戶端程序與HBase集群的connection,所以在程序執行完成后,務必通過 hba.close(); 關閉connection
5). 可以通過 HTableDescriptor 對象設置Table的相關特性 ,比如:
//日志flush的時候是同步寫,還是異步寫 htd.setDurability(Durability.SYNC_WAL);
//region size大小,當一個region中的最大store文件達到這個size時,region就開始分裂 htd.setMaxFileSize(1024*1024*1024);
//MemStore大小,當memstore達到這個值時,開始往磁盤中刷數據 htd.setMemStoreFlushSize(256*1024*1024);
6). 由於HBase的數據是先寫入內存,數據累計達到內存閥值時才往磁盤中flush數據,所以,如果在數據還沒有flush進硬盤時,RegionServer down掉了,內存中的數據將丟失。想解決這個場景的問題就需要用到WAL(Write-Ahead-Log),htd.setDurability(Durability.SYNC_WAL); 就是設置寫WAL日志的級別,示例中設置的是同步寫WAL,該方式安全性較高,但無疑會一定程度影響性能,請根據具體場景選擇使用
同步交互:指發送一個請求,需要等待返回,然后才能夠發送下一個請求,有個等待過程
異步交互:指發送一個請求,不需要等待返回,隨時可以再發送下一個請求,即不需要等待
兩者區別:一個需要等待,一個不需要等待,在部分情況下,我們的項目開發中都會優先選擇不需要等待的異步交互方式。
7). setDurability(Durability d)方法可以在相關的三個對象中使用,分別是:HTableDescriptor,Delete,Put。其中Delete和Put的該方法都是繼承自父類org.apache.hadoop.hbase.client.Mutation。分別針對表、插入操作、刪除操作設定WAL日志寫入級別。需要注意的是,Delete和Put並不會繼承Table的Durability級別(已實測驗證)。Durability是一個枚舉變量,如果不通過該方法指定WAL日志級別,則為默認USE_DEFAULT級別
8). 可以通過 HColumnDescriptor 對象設置ColumnFamily的特性 ,比如:
//壓縮內存中和存儲文件中的數據,默認NONE(不壓縮) //PREFIX-TREE算法以后詳述 hcd.setDataBlockEncoding(DataBlockEncoding.PREFIX); //bloom過濾器:NONE,ROW(默認值)和ROWCOL.ROWCOL除了過濾ROW還要過濾列族 hcd.setBloomFilterType(BloomType.ROW); //集群間復制的時候,如果被設置成REPLICATION_SCOPE_LOCAL(默認值)就不能被復制了 hcd.setScope(HConstants.REPLICATION_SCOPE_GLOBAL); //數據保存的最大版本數.默認是Long.MAX hcd.setMaxVersions(3); //數據保存的最小版本數.默認是1.配合TTL使用 hcd.setMinVersions(1); //數據保存的最長時間,即TTL,單位是ms hcd.setTimeToLive(18000); //設定數據存儲的壓縮類型.默認無壓縮(NONE) hcd.setCompressionType(Algorithm.SNAPPY); //是否保存那些已經刪除掉的cell hcd.setKeepDeletedCells(false); //設置數據保存在內存中以提高響應速度 hcd.setInMemory(true); //塊緩存,保存着每個HFile數據塊的startKey hcd.setBlockCacheEnabled(true); //塊的大小,默認值是65536 hcd.setBlocksize(64*1024);
2.1.列出所有的表
//listTableNames例子 private static void listTable() throws MasterNotRunningException, ZooKeeperConnectionException, IOException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); conf.set("hbase.zookeeper.quorum", "ncst");
HBaseAdmin hba = new HBaseAdmin(conf);
TableName[] listTableNames = hba.listTableNames(); for (TableName tableName : listTableNames) { System.out.println(tableName.toString()); } hba.close(); }
3.刪除表
說明:刪除表前必須先disable表
private static void dropTable() throws MasterNotRunningException, ZooKeeperConnectionException, IOException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); conf.set("hbase.zookeeper.quorum", "ncst");
HBaseAdmin hba = new HBaseAdmin(conf);
TableName tableName = TableName.valueOf("t1"); if(hba.tableExists(tableName)){
//Must disable table at first hba.disableTable(tableName);
//delete table hba.deleteTable(tableName); } hba.close(); }
4.修改表
4.1.刪除列族 and 新增列族
通過 HTableDescriptor htd = hba.getTableDescriptor(tablename.getBytes()); 取得目標表的描述對象,通過 hba.modifyTable(tablename, htd); 將修改后的描述對象應用到目標表
private static void modifyCF() throws MasterNotRunningException, ZooKeeperConnectionException, IOException, TableNotFoundException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); conf.set("hbase.zookeeper.quorum", "ncst");
HBaseAdmin hba = new HBaseAdmin(conf);
String tablename = "nc:t1"; if(hba.tableExists(tablename)){ //disable table hba.disableTable(tablename); HTableDescriptor htd = hba.getTableDescriptor(tablename.getBytes()); //delete column family named 'f1' htd.removeFamily("f1".getBytes()); //add new column family HColumnDescriptor hcd = new HColumnDescriptor("info"); hcd.setMaxVersions(3); hcd.setInMemory(true); htd.addFamily(hcd); //modify target table hba.modifyTable(tablename, htd); //enable table hba.enableTable(tablename); } hba.close(); }
4.2.修改現有列族的屬性(setMaxVersions)
private static void ModifyCFAttribute() throws MasterNotRunningException, ZooKeeperConnectionException, IOException, TableNotFoundException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); conf.set("hbase.zookeeper.quorum", "ncst");
HBaseAdmin hba = new HBaseAdmin(conf);
String tablename = "nc:t1"; if(hba.tableExists(tablename)){ hba.disableTable(tablename); HTableDescriptor htd = hba.getTableDescriptor(tablename.getBytes()); //get target column family HColumnDescriptor family = htd.getFamily("info".getBytes()); family.setMaxVersions(5); //modify table hba.modifyTable(tablename, htd); } hba.close(); }
5.插入數據Put
5.1.常用構造函數:
指定行鍵:public Put(byte[] row)
指定行鍵和時間戳:public Put(byte[] row, long ts)
從目標字符串中提取子串,作為行鍵:Put(byte[] rowArray, int rowOffset, int rowLength)
從目標字符串中提取子串,作為行鍵,並加上時間戳:Put(byte[] rowArray, int rowOffset, int rowLength, long ts)
5.2.Put常用方法:
指定列族和限定符,添加值:add(byte[] family, byte[] qualifier, byte[] value)
指定列族,限定符和時間戳,添加值:add(byte[] family, byte[] qualifier, long ts, byte[] value)
設置寫WAL(Write-Ahead-Log)的級別:public void setDurability(Durability d)
參數是一個枚舉值,可以有以下幾種選擇:
- ASYNC_WAL : 當數據變動時,異步寫WAL日志
- SYNC_WAL : 當數據變動時,同步寫WAL日志
- FSYNC_WAL : 當數據變動時,同步寫WAL日志,並且,強制將數據寫入磁盤
- SKIP_WAL : 不寫WAL日志
- USE_DEFAULT : 使用HBase全局默認的WAL寫入級別,即 SYNC_WAL
private static void putOneRecord() throws IOException, InterruptedIOException, RetriesExhaustedWithDetailsException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); conf.set("hbase.zookeeper.quorum", "ncst");
HTable ht = new HTable(conf, "nc:t1"); //RowKey Put put = new Put("xiaoming".getBytes()); //column family, column, value put.add("info".getBytes(), "age".getBytes(), "18".getBytes()); put.setDurability(Durability.SYNC_WAL); //put a record ht.put(put); ht.close(); }
注意:
1). Put的構造函數都需要指定行鍵,如果是全新的行鍵,則新增一行;如果是已有的行鍵,則更新現有行
2). 創建Put對象及put.add(***)過程都是在構建一行的數據:創建Put對象時相當於創建了行對象,add的過程就是往目標行里添加cell,直到table.put(***)才將數據插入表格
3). Put的其他構造函數 Put put = new Put("100001_100002".getBytes(),7,6); 第二個參數是偏移量,也就是從第一個參數的第幾個字符開始截取RowKey,第三個參數是截取長度。這個代碼實際是從 100001_100002 中截取了100002子串作為目標行的RowKey。
6.刪除數據Delete
Delete類用於刪除表中的一行數據,通過HTable.delete來執行該動作。
在執行Delete操作時,HBase並不會立即刪除數據,而是對需要刪除的數據打上一個“墓碑”標記,直到當Storefile合並時,再清除這些被標記上“墓碑”的數據。
如果希望刪除整行,用行鍵來初始化一個Delete對象即可。如果希望進一步定義刪除的具體內容,可以使用以下這些Delete對象的方法:
- 為了刪除指定的列族,可以使用 deleteFamily
- 為了刪除指定列的多個版本,可以使用 deleteColumns
- 為了刪除指定列的指定版本 ,可以使用 deleteColumn,這樣的話就只會刪除版本號與指定版本相同的列。如果不指定時間戳,默認只刪除最新的版本
6.1.構造函數
1). 指定要刪除的行鍵:Delete(byte[] row)
刪除行鍵指定行的數據。如果沒有進一步的操作,使用該構造函數將刪除行鍵指定的行中所有列族中所有列的所有版本 !
2). 指定要刪除的行鍵和時間戳:Delete(byte[] row, long timestamp)
刪除行鍵和時間戳共同確定行的數據。如果沒有進一步的操作,使用該構造函數將刪除行鍵指定的行中,所有列族中所有列的時間戳【小於等於】指定時間戳的數據版本 !
注意 :該時間戳僅僅和刪除行有關,如果需要進一步指定列族或者列,你必須分別為它們指定時間戳。
3). 給定一個字符串,目標行鍵的偏移,截取的長度:Delete(byte[] rowArray, int rowOffset, int rowLength)
4).給定一個字符串,目標行鍵的偏移,截取的長度,時間戳:Delete(byte[] rowArray, int rowOffset, int rowLength, long ts)
6.2.常用方法
1).刪除指定列的 最新版本 的數據:Delete deleteColumn (byte[] family, byte[] qualifier)
2).刪除指定列的 指定版本 的數據:Delete deleteColumn (byte[] family, byte[] qualifier, long timestamp )
3).刪除指定列的 所有版本 的數據:Delete deleteColumns (byte[] family, byte[] qualifier)
4).刪除指定列的,時間戳 小於等於 給定時間戳的 所有版本 的數據:Delete deleteColumns (byte[] family, byte[] qualifier, long timestamp )
5).刪除指定列族的所有列的 所有版本 數據:Delete deleteFamily (byte[] family)
6).刪除指定列族的所有列中 時間戳 小於等於 指定時間戳 的所有數據:Delete deleteFamily (byte[] family, long timestamp)
7).刪除指定列族中 所有列的時間戳 等於 指定時間戳的版本數據:Delete deleteFamilyVersion (byte[] family, long timestamp)
8).為Delete對象設置時間戳:void setTimestamp (long timestamp)
private static void deleteColumn() throws IOException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); conf.set("hbase.zookeeper.quorum", "ncst"); HTable ht = new HTable(conf, "nc:t1"); //RowKey Delete delete = new Delete("xiaoming".getBytes()); //Column delete.deleteColumn("info".getBytes(), "age".getBytes()); ht.delete(delete); ht.close(); }
7.獲取單行Get
如果希望獲取整行數據,用行鍵初始化一個Get對象就可以,如果希望進一步縮小獲取的數據范圍,可以使用Get對象的的方法.
7.1.構造函數
Get的構造函數很簡單,只有一個構造函數: Get(byte[] row) 參數是行鍵。
7.2.Get對象常用方法
1). Get addFamily(byte[] family) 指定希望獲取的列族
2). Get addColumn(byte[] family, byte[] qualifier) 指定希望獲取的列
3). Get setTimeRange(long minStamp, long maxStamp) 設置獲取數據的 時間戳范圍
4). Get setTimeStamp(long timestamp) 設置獲取數據的時間戳
5). Get setMaxVersions(int maxVersions) 設定獲取數據的版本數
6). Get setMaxVersions() 設定獲取數據的所有版本
7). Get setFilter(Filter filter) 為Get對象添加過濾器,過濾器詳解請參見:HBase API Filter過濾器
8). void setCacheBlocks(boolean cacheBlocks) 設置該Get獲取的數據是否緩存在內存中
//獲取指定RowKey,指定列 的最新版本數據 private static void getColumn() throws IOException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); conf.set("hbase.zookeeper.quorum", "ncst");
HTable ht = new HTable(conf, "users"); Get get = new Get("xiaoming".getBytes()); // Column Family and Qualifier get.addColumn("address".getBytes(), "city".getBytes()); Result result = ht.get(get); for(Cell cell : result.rawCells()){ System.out.println(new String(CellUtil.cloneRow(cell))+"\t" +new String(CellUtil.cloneFamily(cell))+"\t" +new String(CellUtil.cloneQualifier(cell))+"\t" +new String(CellUtil.cloneValue(cell))+"\t" +cell.getTimestamp()); } ht.close(); }
//獲取指定RowKey,指定 時間戳 的數據 private static void getTimeStamp() throws IOException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); conf.set("hbase.zookeeper.quorum", "ncst"); HTable ht = new HTable(conf, "users"); Get get = new Get("xiaoming".getBytes()); // TimeStamp get.setTimeStamp(1441997498939L); Result result = ht.get(get); for(Cell cell : result.rawCells()){ System.out.println(new String(CellUtil.cloneRow(cell))+"\t" +new String(CellUtil.cloneFamily(cell))+"\t" +new String(CellUtil.cloneQualifier(cell))+"\t" +new String(CellUtil.cloneValue(cell))+"\t" +cell.getTimestamp()); } ht.close(); }
8.獲取多行Scan
Scan對象可以返回滿足給定條件的多行數據。如果希望獲取所有的行,直接初始化一個Scan對象即可。如果希望限制掃描的行范圍,可以使用Scan對象的方法
8.1.Scan構造函數
1). 創建掃描所有行的Scan:Scan()
2). 創建Scan,從指定行開始掃描:Scan(byte[] startRow)
注意:如果指定行不存在,從下一個最近的行開始
3). 創建Scan,指定起止行:Scan(byte[] startRow, byte[] stopRow)
注意: startRow <= 結果集 < stopRow
4). 創建Scan,指定起始行和過濾器:Scan(byte[] startRow, Filter filter)
注意:過濾器的功能和構造參見 http://blog.csdn.net/u010967382/article/details/37653177
8.2.Scan對象常用方法
- Scan setStartRow (byte[] startRow) 設置Scan的開始行,默認 結果集 包含該行。如果希望結果集不包含該行,可以在行鍵末尾加上0。
- Scan setStopRow (byte[] stopRow) 設置Scan的結束行,默認 結果集 不包含該行。如果希望結果集包含該行,可以在行鍵末尾加上0。
- Scan setBatch(int batch) 指定最多返回的Cell數目.用於防止一行中有過多的數據,導致OutofMemory錯誤
- Scan setTimeRange (long minStamp, long maxStamp) 掃描指定 時間范圍 的數據
- Scan setTimeStamp (long timestamp) 掃描 指定時間 的數據
- Scan addColumn (byte[] family, byte[] qualifier) 指定掃描的列
- Scan addFamily (byte[] family) 指定掃描的列族
- Scan setFilter (Filter filter) 為Scan設置過濾器,詳見HBase API Filter過濾器
- Scan setReversed (boolean reversed) 設置Scan的掃描順序,默認是正向掃描(false),可以設置為逆向掃描(true)。注意:該方法0.98版本以后才可用!!
- Scan setMaxVersions () 獲取所有版本的數據
- Scan setMaxVersions (int maxVersions) 設置獲取的最大版本數! 不調用上下兩個setMaxVersions() 方法,只返回最新版本數據
- void setCaching (int caching) 設定緩存在內存中的行數,緩存得越多,以后查詢結果越快,同時也消耗更多內存
- void setRaw (boolean raw) 激活或者禁用raw模式。如果raw模式被激活,Scan將返回 所有已經被打上刪除標記但尚未被真正刪除 的數據。該功能僅用於激活了 KEEP_DELETED_ROWS的列族,即列族開啟了 hcd.setKeepDeletedCells(true)
- Scan激活raw模式后,只能瀏覽所有的列,而不能指定任意的列,否則會報錯
//掃描表中的 所有行 的最新版本數據 private static void scanAll() throws IOException, UnsupportedEncodingException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); conf.set("hbase.zookeeper.quorum", "ncst");
HTable ht = new HTable(conf, "users"); // Scan All Scan scan = new Scan(); ResultScanner rs = ht.getScanner(scan); for(Result result : rs){ for(Cell cell : result.rawCells()){ System.out.println(new String(CellUtil.cloneRow(cell))+"\t" +new String(CellUtil.cloneFamily(cell))+"\t" +new String(CellUtil.cloneQualifier(cell))+"\t" +new String(CellUtil.cloneValue(cell),"UTF-8")+"\t" +cell.getTimestamp()); } }
ht.close(); }
//掃描指定RowKey范圍,通過末尾加0,使得結果集包含StopRow
private static void scanRange() throws IOException, UnsupportedEncodingException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); conf.set("hbase.zookeeper.quorum", "ncst");
HTable ht = new HTable(conf, "users"); // Range Scan scan = new Scan("xiaoming".getBytes(),"xiaoming030".getBytes()); ResultScanner rs = ht.getScanner(scan); for(Result result : rs){ for(Cell cell : result.rawCells()){ System.out.println(new String(CellUtil.cloneRow(cell))+"\t" +new String(CellUtil.cloneFamily(cell))+"\t" +new String(CellUtil.cloneQualifier(cell))+"\t" +new String(CellUtil.cloneValue(cell),"UTF-8")+"\t" +cell.getTimestamp()); } } ht.close(); }
//返回 所有已經被打上刪除標記但尚未被真正刪除 的數據 private static void scanRaw() throws IOException, UnsupportedEncodingException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); conf.set("hbase.zookeeper.quorum", "ncst");
HTable ht = new HTable(conf, "users"); Scan scan = new Scan();
//開啟raw模式 scan.setRaw(true);
//默認值long.Max scan.setMaxVersions(); ResultScanner rs = ht.getScanner(scan); for(Result result : rs){ for(Cell cell : result.rawCells()){ System.out.println(new String(CellUtil.cloneRow(cell))+"\t" +new String(CellUtil.cloneFamily(cell))+"\t" +new String(CellUtil.cloneQualifier(cell))+"\t" +new String(CellUtil.cloneValue(cell),"UTF-8")+"\t" +cell.getTimestamp()); } } ht.close(); }
//結合過濾器,獲取所有age在15到30之間的行 private static void scanFilter() throws IOException, UnsupportedEncodingException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://ncst:9000/hbase"); conf.set("hbase.zookeeper.quorum", "ncst");
HTable ht = new HTable(conf, "users"); // And FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL); // >= SingleColumnValueFilter filter1 = new SingleColumnValueFilter("info".getBytes(), "age".getBytes(), CompareOp.GREATER_OR_EQUAL, "15".getBytes()); // =< SingleColumnValueFilter filter2 = new SingleColumnValueFilter("info".getBytes(), "age".getBytes(), CompareOp.LESS_OR_EQUAL, "30".getBytes()); filterList.addFilter(filter1); filterList.addFilter(filter2); Scan scan = new Scan(); // set Filter scan.setFilter(filterList); ResultScanner rs = ht.getScanner(scan); for(Result result : rs){ for(Cell cell : result.rawCells()){ System.out.println(new String(CellUtil.cloneRow(cell))+"\t" +new String(CellUtil.cloneFamily(cell))+"\t" +new String(CellUtil.cloneQualifier(cell))+"\t" +new String(CellUtil.cloneValue(cell),"UTF-8")+"\t" +cell.getTimestamp()); } } ht.close(); }