diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Append.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Append.java index 90dc411b5fa..922f46703eb 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Append.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Append.java @@ -23,6 +23,8 @@ import java.util.Map; import java.util.NavigableMap; import java.util.UUID; import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellBuilder; +import org.apache.hadoop.hbase.CellBuilderType; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.io.TimeRange; import org.apache.hadoop.hbase.security.access.Permission; @@ -224,4 +226,9 @@ public class Append extends Mutation { public Append setTTL(long ttl) { return (Append) super.setTTL(ttl); } + + @Override + public CellBuilder getCellBuilder(CellBuilderType type) { + return getCellBuilder(type, Cell.Type.Put); + } } diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Delete.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Delete.java index 1df710f1279..ccda14f6d16 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Delete.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Delete.java @@ -25,6 +25,8 @@ import java.util.Map; import java.util.NavigableMap; import java.util.UUID; import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellBuilder; +import org.apache.hadoop.hbase.CellBuilderType; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.security.access.Permission; @@ -324,4 +326,9 @@ public class Delete extends Mutation { public Delete setPriority(int priority) { return (Delete) super.setPriority(priority); } + + @Override + public CellBuilder getCellBuilder(CellBuilderType type) { + return getCellBuilder(type, Cell.Type.Delete); + } } diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Increment.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Increment.java index 60304ccd5cc..df448eb91b6 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Increment.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Increment.java @@ -25,6 +25,8 @@ import java.util.NavigableMap; import java.util.TreeMap; import java.util.UUID; import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellBuilder; +import org.apache.hadoop.hbase.CellBuilderType; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.io.TimeRange; @@ -311,4 +313,9 @@ public class Increment extends Mutation { public Increment setPriority(int priority) { return (Increment) super.setPriority(priority); } + + @Override + public CellBuilder getCellBuilder(CellBuilderType type) { + return getCellBuilder(type, Cell.Type.Put); + } } diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Mutation.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Mutation.java index a561632960a..bfdf29f363b 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Mutation.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Mutation.java @@ -32,6 +32,9 @@ import java.util.TreeMap; import java.util.UUID; import java.util.stream.Collectors; import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellBuilder; +import org.apache.hadoop.hbase.CellBuilderFactory; +import org.apache.hadoop.hbase.CellBuilderType; import org.apache.hadoop.hbase.CellScannable; import org.apache.hadoop.hbase.CellScanner; import org.apache.hadoop.hbase.CellUtil; @@ -784,6 +787,104 @@ public abstract class Mutation extends OperationWithAttributes implements Row, C return this; } + /** + * get a CellBuilder instance that already has relevant Type and Row set. + * @param cellBuilderType e.g CellBuilderType.SHALLOW_COPY + * @return CellBuilder which already has relevant Type and Row set. + */ + public abstract CellBuilder getCellBuilder(CellBuilderType cellBuilderType); + + /** + * get a CellBuilder instance that already has relevant Type and Row set. + * the default CellBuilderType is CellBuilderType.SHALLOW_COPY + * @return CellBuilder which already has relevant Type and Row set. + */ + public CellBuilder getCellBuilder() { + return getCellBuilder(CellBuilderType.SHALLOW_COPY); + } + + /** + * get a CellBuilder instance that already has relevant Type and Row set. + * @param cellBuilderType e.g CellBuilderType.SHALLOW_COPY + * @param cellType e.g Cell.Type.Put + * @return CellBuilder which already has relevant Type and Row set. + */ + protected CellBuilder getCellBuilder(CellBuilderType cellBuilderType, Cell.Type cellType) { + CellBuilder builder = CellBuilderFactory.create(cellBuilderType).setRow(row).setType(cellType); + return new CellBuilder() { + @Override + public CellBuilder setRow(byte[] row) { + return this; + } + + @Override + public CellBuilder setType(Cell.Type type) { + return this; + } + + @Override + public CellBuilder setRow(byte[] row, int rOffset, int rLength) { + return this; + } + + @Override + public CellBuilder setFamily(byte[] family) { + builder.setFamily(family); + return this; + } + + @Override + public CellBuilder setFamily(byte[] family, int fOffset, int fLength) { + builder.setFamily(family, fOffset, fLength); + return this; + } + + @Override + public CellBuilder setQualifier(byte[] qualifier) { + builder.setQualifier(qualifier); + return this; + } + + @Override + public CellBuilder setQualifier(byte[] qualifier, int qOffset, int qLength) { + builder.setQualifier(qualifier, qOffset, qLength); + return this; + } + + @Override + public CellBuilder setTimestamp(long timestamp) { + builder.setTimestamp(timestamp); + return this; + } + + @Override + public CellBuilder setValue(byte[] value) { + builder.setValue(value); + return this; + } + + @Override + public CellBuilder setValue(byte[] value, int vOffset, int vLength) { + builder.setValue(value, vOffset, vLength); + return this; + } + + @Override + public Cell build() { + return builder.build(); + } + + @Override + public CellBuilder clear() { + builder.clear(); + // reset the row and type + builder.setRow(row); + builder.setType(cellType); + return this; + } + }; + } + private static final class CellWrapper implements ExtendedCell { private static final long FIXED_OVERHEAD = ClassSize.align( ClassSize.OBJECT // object header diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Put.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Put.java index 21f22d18473..719251ff1f0 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Put.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Put.java @@ -26,6 +26,8 @@ import java.util.Map; import java.util.NavigableMap; import java.util.UUID; import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellBuilder; +import org.apache.hadoop.hbase.CellBuilderType; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.io.HeapSize; @@ -278,4 +280,10 @@ public class Put extends Mutation implements HeapSize { public Put setPriority(int priority) { return (Put) super.setPriority(priority); } + + @Override + public CellBuilder getCellBuilder(CellBuilderType type) { + return getCellBuilder(type, Cell.Type.Put); + } + } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestMutationGetCellBuilder.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestMutationGetCellBuilder.java new file mode 100644 index 00000000000..dd6485473cf --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestMutationGetCellBuilder.java @@ -0,0 +1,110 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.client; + +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellBuilder; +import org.apache.hadoop.hbase.CellUtil; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.testclassification.ClientTests; +import org.apache.hadoop.hbase.testclassification.MediumTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TestName; + +@Category({MediumTests.class, ClientTests.class}) +public class TestMutationGetCellBuilder { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestMutationGetCellBuilder.class); + + private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); + + @Rule + public TestName name = new TestName(); + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + TEST_UTIL.startMiniCluster(); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + TEST_UTIL.shutdownMiniCluster(); + } + + @Test + public void testMutationGetCellBuilder() throws Exception { + final TableName tableName = TableName.valueOf(name.getMethodName()); + final byte[] rowKey = Bytes.toBytes("12345678"); + final byte[] uselessRowKey = Bytes.toBytes("123"); + final byte[] family = Bytes.toBytes("cf"); + final byte[] qualifier = Bytes.toBytes("foo"); + final long now = System.currentTimeMillis(); + try (Table table = TEST_UTIL.createTable(tableName, family)) { + TEST_UTIL.waitTableAvailable(tableName.getName(), 5000); + // put one row + Put put = new Put(rowKey); + CellBuilder cellBuilder = put.getCellBuilder().setQualifier(qualifier) + .setFamily(family).setValue(Bytes.toBytes("bar")).setTimestamp(now); + //setRow is useless + cellBuilder.setRow(uselessRowKey); + put.add(cellBuilder.build()); + byte[] cloneRow = CellUtil.cloneRow(cellBuilder.build()); + assertTrue("setRow must be useless", !Arrays.equals(cloneRow, uselessRowKey)); + table.put(put); + + // get the row back and assert the values + Get get = new Get(rowKey); + get.setTimestamp(now); + Result result = table.get(get); + assertTrue("row key must be same", Arrays.equals(result.getRow(), rowKey)); + assertTrue("Column foo value should be bar", + Bytes.toString(result.getValue(family, qualifier)).equals("bar")); + + //Delete that row + Delete delete = new Delete(rowKey); + cellBuilder = delete.getCellBuilder().setQualifier(qualifier) + .setFamily(family); + //if this row has been deleted,then can check setType is useless. + cellBuilder.setType(Cell.Type.Put); + delete.add(cellBuilder.build()); + table.delete(delete); + + //check this row whether exist + get = new Get(rowKey); + get.setTimestamp(now); + result = table.get(get); + assertTrue("Column foo should not exist", + result.getValue(family, qualifier) == null); + } + } +} + + diff --git a/src/main/asciidoc/_chapters/datamodel.adoc b/src/main/asciidoc/_chapters/datamodel.adoc index d1e8303bbc7..dd54b1cc04c 100644 --- a/src/main/asciidoc/_chapters/datamodel.adoc +++ b/src/main/asciidoc/_chapters/datamodel.adoc @@ -471,6 +471,26 @@ Caution: the version timestamp is used internally by HBase for things like time- It's usually best to avoid setting this timestamp yourself. Prefer using a separate timestamp attribute of the row, or have the timestamp as a part of the row key, or both. +===== Cell Version Example + +The following Put uses a method getCellBuilder() to get a CellBuilder instance +that already has relevant Type and Row set. + +[source,java] +---- + +public static final byte[] CF = "cf".getBytes(); +public static final byte[] ATTR = "attr".getBytes(); +... + +Put put = new Put(Bytes.toBytes(row)); +put.add(put.getCellBuilder().setQualifier(ATTR) + .setFamily(CF) + .setValue(Bytes.toBytes(data)) + .build()); +table.put(put); +---- + [[version.delete]] ==== Delete