Skip to content

Commit e1e3431

Browse files
committed
Merged branch 'v116'. Following changes:
* Methods to check whether row key exists * Method to 'enable' table. * Removed deprecated rowkey annotation * Fixed generic type for persist and delete methods
1 parent 9f3ea66 commit e1e3431

File tree

9 files changed

+165
-82
lines changed

9 files changed

+165
-82
lines changed

README.md

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22

33
[![Build Status](https://api.travis-ci.org/flipkart-incubator/hbase-orm.svg?branch=master&status=passed)](https://travis-ci.org/github/flipkart-incubator/hbase-orm)
44
[![Coverage Status](https://coveralls.io/repos/github/flipkart-incubator/hbase-orm/badge.svg?branch=master)](https://coveralls.io/github/flipkart-incubator/hbase-orm?branch=master)
5-
[![Maven Central](https://img.shields.io/badge/sonatype-1.15-orange.svg)](https://oss.sonatype.org/content/repositories/releases/com/flipkart/hbase-object-mapper/1.15/)
5+
[![Maven Central](https://img.shields.io/badge/sonatype-1.16-orange.svg)](https://oss.sonatype.org/content/repositories/releases/com/flipkart/hbase-object-mapper/1.15/)
66
[![License](https://img.shields.io/badge/License-Apache%202-blue.svg)](./LICENSE.txt)
77

88
## Introduction
9-
An ultra-light-weight HBase ORM library that enables:
9+
HBase ORM is a light-weight, thread-safe and performant library that enables:
1010

1111
1. object-oriented access of HBase rows (Data Access Object) with minimal code and good testability
1212
2. reading from and/or writing to HBase tables in Hadoop MapReduce jobs
1313

14-
1514
## Usage
1615
Let's say you've an HBase table `citizens` with row-key format of `country_code#UID`. Now, let's say this table is created with three column families `main`, `optional` and `tracked`, which may have columns (qualifiers) `uid`, `name`, `salary` etc.
1716

@@ -115,14 +114,15 @@ See source files [Citizen.java](./src/test/java/com/flipkart/hbaseobjectmapper/t
115114

116115
### Serialization / Deserialization mechanism
117116

117+
* Serialization and deserialization are handled through 'codecs'.
118118
* The default codec (called [BestSuitCodec](./src/main/java/com/flipkart/hbaseobjectmapper/codec/BestSuitCodec.java)) included in this library has the following behavior:
119119
* uses HBase's native methods to serialize objects of data types `Boolean`, `Short`, `Integer`, `Long`, `Float`, `Double`, `String` and `BigDecimal` (see: [Bytes](https://hbase.apache.org/2.0/devapidocs/org/apache/hadoop/hbase/util/Bytes.html))
120120
* uses [Jackson's JSON serializer](https://en.wikipedia.org/wiki/Jackson_(API)) for all other data types
121121
* serializes `null` as `null`
122122
* To customize serialization/deserialization behavior, you may define your own codec (by implementing the [Codec](./src/main/java/com/flipkart/hbaseobjectmapper/codec/Codec.java) interface) or you may extend the default codec.
123123
* The optional parameter `codecFlags` (supported by both `@HBColumn` and `@HBColumnMultiVersion` annotations) can be used to pass custom flags to the underlying codec. (e.g. You may want your codec to serialize field `Integer id` in `Citizen` class differently from field `Integer id` in `Employee` class)
124124
* The default codec class `BestSuitCodec` takes a flag `BestSuitCodec.SERIALIZE_AS_STRING`, whose value is "serializeAsString" (as in the above `Citizen` class example). When this flag is set to `true` on a field, the default codec serializes that field (even numerical fields) as strings.
125-
* Your custom codec may take other such flags to customize serialization/deserialization behavior at a **class field level**.
125+
* Your custom codec may take other such flags as inputs to customize serialization/deserialization behavior at a **class field level**.
126126

127127
## Using this library for database access (DAO)
128128
This library provides an abstract class to define your own [data access object](https://en.wikipedia.org/wiki/Data_access_object). For example, you can create one for `Citizen` class in the above example as follows:
@@ -132,7 +132,7 @@ import org.apache.hadoop.hbase.client.Connection;
132132
import java.io.IOException;
133133

134134
public class CitizenDAO extends AbstractHBDAO<String, Citizen> {
135-
// in above, String is the row type of Citizen
135+
// in above, String is the 'row type' of Citizen
136136

137137
public CitizenDAO(Connection connection) throws IOException {
138138
super(connection); // if you need to customize your codec, you may use super(connection, codec)
@@ -235,10 +235,10 @@ Read data from HBase using HBase's native `Get`:
235235

236236
```java
237237
Get get1 = citizenDao.getGet("IND#2"); // returns object of HBase's Get corresponding to row key "IND#2", to enable advanced read patterns
238-
counterDAO.getOnGets(get1);
238+
Counter counter1 = counterDAO.getOnGet(get1);
239239

240240
Get get2 = citizenDao.getGet("IND#2").setTimeRange(1, 5).setMaxVersions(2); // Advanced HBase row fetch
241-
counterDAO.getOnGets(get2);
241+
Counter counter2 = counterDAO.getOnGet(get2);
242242
```
243243

244244
Manipulate and persist an object back to HBase:
@@ -307,15 +307,15 @@ Once instantiated, you may do the following DDL operations:
307307
hbAdmin.createTable(Citizen.class);
308308
// Above statement creates table with name and column families specification as per the @HBTable annotation on the Citizen class
309309

310-
hbAdmin.tableExists(Citizen.class); // returns true
310+
hbAdmin.tableExists(Citizen.class); // returns true/false
311311

312312
hbAdmin.disableTable(Citizen.class);
313313

314314
hbAdmin.deleteTable(Citizen.class);
315315

316316
```
317317

318-
Note that **all** of the above are very heavy and time-consuming operations.
318+
Note that DDL operations on HBase are typically heavy and time-consuming.
319319

320320
## Using this library in MapReduce jobs
321321

@@ -325,6 +325,7 @@ If your MapReduce job is reading from an HBase table, in your `map()` method, HB
325325
```java
326326
T readValue(ImmutableBytesWritable rowKey, Result result, Class<T> clazz)
327327
```
328+
where `T` is your bean-like class that extends this library's `HBRecord` interface (e.g. `Citizen` class above).
328329

329330
For example:
330331

@@ -336,11 +337,13 @@ Citizen e = hbObjectMapper.readValue(key, value, Citizen.class);
336337
If your MapReduce job is writing to an HBase table, in your `reduce()` method, object of your bean-like class can be converted to HBase's `Put` (for row contents) and `ImmutableBytesWritable` (for row key) using below methods:
337338

338339
```java
339-
ImmutableBytesWritable getRowKey(HBRecord<R> obj)
340+
ImmutableBytesWritable getRowKey(T record)
340341
```
341342
```java
342-
Put writeValueAsPut(HBRecord<R> obj)
343+
Put writeValueAsPut(T record)
343344
```
345+
where `T` is your bean-like class that extends this library's `HBRecord` interface (e.g. `Citizen` class above).
346+
344347
For example, below code in Reducer writes your object as one HBase row with appropriate column families and columns:
345348

346349
```java
@@ -353,11 +356,13 @@ If your MapReduce job is reading from an HBase table, you would want to unit-tes
353356
Object of your bean-like class can be converted to HBase's `Result` (for row contents) and `ImmutableBytesWritable` (for row key) using below methods:
354357

355358
```java
356-
ImmutableBytesWritable getRowKey(HBRecord<R> obj)
359+
ImmutableBytesWritable getRowKey(T record)
357360
```
358361
```java
359-
Result writeValueAsResult(HBRecord<R> obj)
362+
Result writeValueAsResult(T record)
360363
```
364+
where `T` is your bean-like class that extends this library's `HBRecord` interface (e.g. `Citizen` class above).
365+
361366
Below is an example of unit-test of a Mapper using [MRUnit](https://attic.apache.org/projects/mrunit.html):
362367

363368
```java
@@ -382,6 +387,8 @@ HBase's `Put` object can be converted to your object of you bean-like class usin
382387
```java
383388
T readValue(ImmutableBytesWritable rowKey, Put put, Class<T> clazz)
384389
```
390+
where `T` is your bean-like class that extends this library's `HBRecord` interface (e.g. `Citizen` class above).
391+
385392

386393
Below is an example of unit-test of a Reducer using [MRUnit](https://attic.apache.org/projects/mrunit.html):
387394

@@ -398,11 +405,11 @@ CitizenSummary citizenSummary = hbObjectMapper.readValue(
398405
```
399406

400407
## Advantages
401-
* Your application code will be clean and minimal.
408+
* Your application code will be **clean** and **minimal**.
402409
* Your code need not worry about HBase methods or serialization/deserialization at all, thereby helping you maintain clear [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns).
403-
* Classes are **thread-safe**. You just have to instantiate your DAO classes once at the start of your application and use them anywhere!
404-
* Light weight: This library depends on just HBase Client and few other small libraries. It has very low overhead and hence is very fast.
405-
* Customizability/Extensibility: Want to use HBase native methods directly in some cases? No problem. Want to customize ser/deser in general or for a given class field? No problem. This library is high flexible.
410+
* Classes are **thread-safe**. You just have to instantiate your DAO classes once at the start of your application and use them anywhere throughout the life-cycle of your application!
411+
* **Light weight**: This library depends on just [hbase-client](https://mvnrepository.com/artifact/org.apache.hbase/hbase-client) and few other small libraries. It has very low overhead and hence is very fast.
412+
* Customizability/Extensibility: Want to use HBase's native methods directly in some cases? You can do that. Want to customize serializatoin/deserialization for a given type or for a specific given class field? You can do that too. This library is highly flexible.
406413

407414
## Limitations
408415
Being an *object mapper*, this library works for pre-defined columns only. For example, this library doesn't provide ways to fetch:
@@ -417,7 +424,7 @@ Add below entry within the `dependencies` section of your `pom.xml`:
417424
<dependency>
418425
<groupId>com.flipkart</groupId>
419426
<artifactId>hbase-object-mapper</artifactId>
420-
<version>1.15</version>
427+
<version>1.16</version>
421428
</dependency>
422429
```
423430

@@ -428,7 +435,7 @@ See artifact details: [com.flipkart:hbase-object-mapper on **Maven Central**](ht
428435
To build this project, follow below simple steps:
429436

430437
1. Do a `git clone` of this repository
431-
2. Checkout latest stable version `git checkout v1.15`
438+
2. Checkout latest stable version `git checkout v1.16`
432439
3. Execute `mvn clean install` from shell
433440

434441
### Please note:

pom.xml

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
55
<name>HBase ORM</name>
66
<description>
7-
An ultra-light-weight HBase ORM library that enables:
7+
HBase ORM is a light-weight, thread-safe and performant library that enables:
88
[1] object-oriented access of HBase rows (Data Access Object) with minimal code and good testability
99
[2] reading from and/or writing to HBase tables in Hadoop MapReduce jobs
1010
</description>
1111
<modelVersion>4.0.0</modelVersion>
1212
<groupId>com.flipkart</groupId>
1313
<artifactId>hbase-object-mapper</artifactId>
14-
<version>1.15</version>
14+
<version>1.16</version>
1515
<url>https://github.com/flipkart-incubator/hbase-orm</url>
1616
<scm>
1717
<url>https://github.com/flipkart-incubator/hbase-orm</url>
@@ -84,12 +84,6 @@
8484
<version>${version.hbase}</version>
8585
<scope>test</scope>
8686
</dependency>
87-
<dependency>
88-
<groupId>org.apache.hadoop</groupId>
89-
<artifactId>hadoop-mapreduce-client-core</artifactId>
90-
<version>${version.hadoop}</version>
91-
<scope>test</scope>
92-
</dependency>
9387
<dependency>
9488
<groupId>org.apache.hbase</groupId>
9589
<artifactId>hbase-testing-util</artifactId>
@@ -105,13 +99,13 @@
10599
<dependency>
106100
<groupId>org.junit.jupiter</groupId>
107101
<artifactId>junit-jupiter-engine</artifactId>
108-
<version>5.5.2</version>
102+
<version>5.6.2</version>
109103
<scope>test</scope>
110104
</dependency>
111105
<dependency>
112106
<groupId>org.mockito</groupId>
113107
<artifactId>mockito-core</artifactId>
114-
<version>2.23.4</version>
108+
<version>3.3.3</version>
115109
<scope>test</scope>
116110
</dependency>
117111
</dependencies>
@@ -129,7 +123,7 @@
129123
<plugin>
130124
<groupId>org.apache.maven.plugins</groupId>
131125
<artifactId>maven-javadoc-plugin</artifactId>
132-
<version>3.1.1</version>
126+
<version>3.2.0</version>
133127
<executions>
134128
<execution>
135129
<id>attach-javadocs</id>

src/main/java/com/flipkart/hbaseobjectmapper/AbstractHBDAO.java

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ public Get getGet(R rowKey) {
170170
/**
171171
* Fetch an HBase row for a given {@link Get} object
172172
*
173-
* @param get HBase's Get object, typically formed using the {@link #getGet(Serializable) getGet} method
173+
* @param get HBase's Get object, typically formed using the {@link #getGet(Serializable) getGet(R)} method
174174
* @return HBase row, deserialized as object of your bean-like class (that implements {@link HBRecord})
175175
* @throws IOException When HBase call fails
176176
*/
@@ -507,7 +507,7 @@ public Increment getIncrement(R rowKey) {
507507
* Performs HBase {@link Table#increment} on the given {@link Increment} object <br>
508508
* <br>
509509
* <b>Note</b>: <ul>
510-
* <li>You may construct {@link Increment} object using the {@link #getIncrement(Serializable) getIncrement} method</li>
510+
* <li>You may construct {@link Increment} object using the {@link #getIncrement(Serializable) getIncrement(R)} method</li>
511511
* <li>Unlike the {@link #increment(Serializable, String, long)} methods, this method skips some validations (hence, be cautious)</li>
512512
* </ul>
513513
*
@@ -533,7 +533,7 @@ public T increment(Increment increment) throws IOException {
533533
* @return <b>Partial object</b> containing (only) value of field that was appended
534534
* @throws IOException When HBase call fails
535535
* @see Table#append(Append)
536-
* @see #append(Serializable, Map)
536+
* @see #append(Serializable, Map) append(R, Map)
537537
*/
538538
public T append(R rowKey, String fieldName, Object valueToAppend) throws IOException {
539539
Map<String, Object> one = new HashMap<>(1);
@@ -551,10 +551,10 @@ public T append(R rowKey, String fieldName, Object valueToAppend) throws IOExcep
551551
* @return <b>Partial object</b> containing (only) values of fields that were appended
552552
* @throws IOException When HBase call fails
553553
* @see Table#append(Append)
554-
* @see #append(Serializable, String, Object)
554+
* @see #append(Serializable, String, Object) append(R, String, Object)
555555
*/
556556
public T append(R rowKey, Map<String, Object> valuesToAppend) throws IOException {
557-
Append append = new Append(toBytes(rowKey));
557+
Append append = getAppend(rowKey);
558558
for (Map.Entry<String, Object> e : valuesToAppend.entrySet()) {
559559
String fieldName = e.getKey();
560560
Field field = getField(fieldName);
@@ -567,10 +567,7 @@ public T append(R rowKey, Map<String, Object> valuesToAppend) throws IOException
567567
hbObjectMapper.valueToByteArray((Serializable) value, hbColumn.codecFlags())
568568
);
569569
}
570-
try (Table table = getHBaseTable()) {
571-
Result result = table.append(append);
572-
return hbObjectMapper.readValueFromResult(result, hbRecordClass);
573-
}
570+
return append(append);
574571
}
575572

576573

@@ -588,8 +585,8 @@ public Append getAppend(R rowKey) {
588585
* Performs HBase's {@link Table#append} on the given {@link Append} object <br>
589586
* <br>
590587
* <b>Note</b>: <ul>
591-
* <li>You may construct {@link Append} object using the {@link #getAppend(Serializable) getAppend} method</li>
592-
* <li>Unlike the {@link #append(Serializable, String, Object)} and related methods, this method skips some validations. So, use this only if you need access to HBase's native methods.</li>
588+
* <li>You may construct {@link Append} object using the {@link #getAppend(Serializable) getAppend(R)} method</li>
589+
* <li>Unlike the {@link #append(Serializable, String, Object) append(R, String, Object)} and related methods, this method skips some validations. So, use this only if you need access to HBase's native methods.</li>
593590
* </ul>
594591
*
595592
* @param append HBase's {@link Append} object
@@ -622,7 +619,7 @@ public List<T> get(R startRowKey, R endRowKey) throws IOException {
622619
* @return Row key of the persisted object, represented as a {@link String}
623620
* @throws IOException When HBase call fails
624621
*/
625-
public R persist(HBRecord<R> record) throws IOException {
622+
public R persist(T record) throws IOException {
626623
Put put = hbObjectMapper.writeValueAsPut0(record);
627624
try (Table table = getHBaseTable()) {
628625
table.put(put);
@@ -640,7 +637,7 @@ public R persist(HBRecord<R> record) throws IOException {
640637
public List<R> persist(List<T> records) throws IOException {
641638
List<Put> puts = new ArrayList<>(records.size());
642639
List<R> rowKeys = new ArrayList<>(records.size());
643-
for (HBRecord<R> record : records) {
640+
for (T record : records) {
644641
puts.add(hbObjectMapper.writeValueAsPut0(record));
645642
rowKeys.add(record.composeRowKey());
646643
}
@@ -670,7 +667,7 @@ public void delete(R rowKey) throws IOException {
670667
* @param record Object to delete
671668
* @throws IOException When HBase call fails
672669
*/
673-
public void delete(HBRecord<R> record) throws IOException {
670+
public void delete(T record) throws IOException {
674671
this.delete(record.composeRowKey());
675672
}
676673

@@ -698,7 +695,7 @@ public void delete(R[] rowKeys) throws IOException {
698695
*/
699696
public void delete(List<T> records) throws IOException {
700697
List<Delete> deletes = new ArrayList<>(records.size());
701-
for (HBRecord<R> record : records) {
698+
for (T record : records) {
702699
deletes.add(new Delete(toBytes(record.composeRowKey())));
703700
}
704701
try (Table table = getHBaseTable()) {
@@ -851,7 +848,7 @@ public NavigableMap<R, NavigableMap<Long, Object>> fetchFieldValues(R startRowKe
851848
}
852849

853850
/**
854-
* Fetch column values for a given array of row keys (bulk variant of method {@link #fetchFieldValue(Serializable, String)})
851+
* Fetch column values for a given array of row keys (bulk variant of method {@link #fetchFieldValue(Serializable, String) fetchFieldValue(R, String)})
855852
*
856853
* @param rowKeys Array of row keys to fetch
857854
* @param fieldName Name of the private variable of your bean-like object (of a class that implements {@link HBRecord}) whose corresponding column needs to be fetched
@@ -882,7 +879,7 @@ public Map<R, NavigableMap<Long, Object>> fetchFieldValues(R[] rowKeys, String f
882879
get.addColumn(hbColumn.familyBytes(), hbColumn.columnBytes());
883880
gets.add(get);
884881
}
885-
Map<R, NavigableMap<Long, Object>> map = new HashMap<>(rowKeys.length, 1.0f);
882+
Map<R, NavigableMap<Long, Object>> map = new LinkedHashMap<>(rowKeys.length, 1.0f);
886883
try (Table table = getHBaseTable()) {
887884
Result[] results = table.get(gets);
888885
for (Result result : results) {
@@ -901,4 +898,38 @@ public Map<R, NavigableMap<Long, Object>> fetchFieldValues(R[] rowKeys, String f
901898
public byte[] toBytes(R rowKey) {
902899
return hbObjectMapper.rowKeyToBytes(rowKey, hbTable.getCodecFlags());
903900
}
901+
902+
/**
903+
* Check whether a row exists or not
904+
*
905+
* @param rowKey Row key
906+
* @return <code>true</code> if row with given row key exists
907+
* @throws IOException When HBase call fails
908+
*/
909+
public boolean exists(R rowKey) throws IOException {
910+
try (Table table = getHBaseTable()) {
911+
return table.exists(new Get(
912+
toBytes(rowKey)
913+
));
914+
}
915+
}
916+
917+
/**
918+
* Check whether specified rows exist or not
919+
*
920+
* @param rowKeys Row keys
921+
* @return Array with <code>true</code>/<code>false</code> values corresponding to whether row with given row keys exist
922+
* @throws IOException When HBase call fails
923+
*/
924+
public boolean[] exists(R[] rowKeys) throws IOException {
925+
List<Get> gets = new ArrayList<>(rowKeys.length);
926+
for (R rowKey : rowKeys) {
927+
gets.add(new Get(
928+
toBytes(rowKey)
929+
));
930+
}
931+
try (Table table = getHBaseTable()) {
932+
return table.exists(gets);
933+
}
934+
}
904935
}

0 commit comments

Comments
 (0)