MNT-24641 Avoid duplicate key error on content upload (#2984) (#2986)

MNT-24641 Avoid duplicate key error on content upload (#2984)

* On createOrGetByValue in EntityLookupCache, also cache by value
* Created getCachedEntityByValue that attempt to retrieve the value only from cache
* On attempt to create content URL, first check cache before attempting to create in the database avoiding a duplicate key

(cherry picked from commit f4103c242f)

* Pre-commit formatting
* Fix EntityLookupCacheTest
This commit is contained in:
Eva Vasques
2024-10-10 19:08:46 +01:00
committed by GitHub
parent 3443ea9eb7
commit c1ee9467c8
3 changed files with 2007 additions and 1931 deletions

View File

@@ -21,391 +21,430 @@
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.cache.lookup; package org.alfresco.repo.cache.lookup;
import java.sql.Savepoint; import static org.junit.Assert.*;
import java.util.Map;
import java.util.TreeMap; import java.sql.Savepoint;
import java.util.Map;
import junit.framework.AssertionFailedError; import java.util.TreeMap;
import junit.framework.TestCase;
import org.junit.Before;
import org.alfresco.repo.cache.MemoryCache; import org.junit.Test;
import org.alfresco.repo.cache.SimpleCache; import org.mockito.Mockito;
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAO; import org.springframework.dao.DuplicateKeyException;
import org.alfresco.repo.domain.control.ControlDAO;
import org.alfresco.util.EqualsHelper; import org.alfresco.repo.cache.MemoryCache;
import org.alfresco.util.Pair; import org.alfresco.repo.cache.SimpleCache;
import org.mockito.Mockito; import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAO;
import org.springframework.dao.DuplicateKeyException; import org.alfresco.repo.domain.control.ControlDAO;
import org.alfresco.util.EqualsHelper;
/** import org.alfresco.util.Pair;
* A cache for two-way lookups of database entities. These are characterized by having a unique
* key (perhaps a database ID) and a separate unique key that identifies the object. /**
* <p> * A cache for two-way lookups of database entities. These are characterized by having a unique key (perhaps a database ID) and a separate unique key that identifies the object.
* The keys must have good <code>equals</code> and </code>hashCode</code> implementations and * <p>
* must respect the case-sensitivity of the use-case. * The keys must have good <code>equals</code> and </code>hashCode</code> implementations and must respect the case-sensitivity of the use-case.
* *
* @author Derek Hulley * @author Derek Hulley
* @since 3.2 * @since 3.2
*/ */
public class EntityLookupCacheTest extends TestCase implements EntityLookupCallbackDAO<Long, Object, String> public class EntityLookupCacheTest implements EntityLookupCallbackDAO<Long, Object, String>
{ {
SimpleCache<Long, Object> cache; SimpleCache<Long, Object> cache;
private EntityLookupCache<Long, Object, String> entityLookupCacheA; private EntityLookupCache<Long, Object, String> entityLookupCacheA;
private EntityLookupCache<Long, Object, String> entityLookupCacheB; private EntityLookupCache<Long, Object, String> entityLookupCacheB;
private TreeMap<Long, String> database; private TreeMap<Long, String> database;
private ControlDAO controlDAO; private ControlDAO controlDAO;
@Override @Before
protected void setUp() throws Exception public void setUp() throws Exception
{ {
cache = new MemoryCache<Long, Object>(); cache = new MemoryCache<Long, Object>();
entityLookupCacheA = new EntityLookupCache<Long, Object, String>(cache, "A", this); entityLookupCacheA = new EntityLookupCache<Long, Object, String>(cache, "A", this);
entityLookupCacheB = new EntityLookupCache<Long, Object, String>(cache, "B", this); entityLookupCacheB = new EntityLookupCache<Long, Object, String>(cache, "B", this);
database = new TreeMap<Long, String>(); database = new TreeMap<Long, String>();
controlDAO = Mockito.mock(ControlDAO.class); controlDAO = Mockito.mock(ControlDAO.class);
Mockito.when(controlDAO.createSavepoint(Mockito.anyString())).thenReturn(Mockito.mock(Savepoint.class)); Mockito.when(controlDAO.createSavepoint(Mockito.anyString())).thenReturn(Mockito.mock(Savepoint.class));
} }
public void testLookupsUsingIncorrectValue() throws Exception @Test
{ public void testLookupsUsingIncorrectValue() throws Exception
try {
{ try
// Keep the "database" empty {
entityLookupCacheA.getByValue(this); // Keep the "database" empty
} entityLookupCacheA.getByValue(this);
catch (AssertionFailedError e) }
{ catch (AssertionError e)
// Expected {
} // Expected
} }
}
public void testLookupAgainstEmpty() throws Exception
{ @Test
TestValue value = new TestValue("AAA"); public void testLookupAgainstEmpty() throws Exception
Pair<Long, Object> entityPair = entityLookupCacheA.getByValue(value); {
assertNull(entityPair); TestValue value = new TestValue("AAA");
assertTrue(database.isEmpty()); Pair<Long, Object> entityPair = entityLookupCacheA.getByValue(value);
assertNull(entityPair);
// Now do lookup or create assertTrue(database.isEmpty());
entityPair = entityLookupCacheA.getOrCreateByValue(value);
assertNotNull("Expected a value to be found", entityPair); // Now do lookup or create
Long entityId = entityPair.getFirst(); entityPair = entityLookupCacheA.getOrCreateByValue(value);
assertTrue("Database ID should have been created", database.containsKey(entityId)); assertNotNull("Expected a value to be found", entityPair);
assertEquals("Database value incorrect", value.val, database.get(entityId)); Long entityId = entityPair.getFirst();
assertTrue("Database ID should have been created", database.containsKey(entityId));
// Do lookup or create again assertEquals("Database value incorrect", value.val, database.get(entityId));
entityPair = entityLookupCacheA.getOrCreateByValue(value);
assertNotNull("Expected a value to be found", entityPair); // Do lookup or create again
assertEquals("Expected same entity ID", entityId, entityPair.getFirst()); entityPair = entityLookupCacheA.getOrCreateByValue(value);
assertNotNull("Expected a value to be found", entityPair);
// Look it up using the value assertEquals("Expected same entity ID", entityId, entityPair.getFirst());
entityPair = entityLookupCacheA.getByValue(value);
assertNotNull("Lookup after create should work", entityPair); // Look it up using the value
entityPair = entityLookupCacheA.getByValue(value);
// Look it up using the ID assertNotNull("Lookup after create should work", entityPair);
entityPair = entityLookupCacheA.getByKey(entityId);
assertNotNull("Lookup by key should work after create", entityPair); // Look it up using the ID
assertTrue("Looked-up type incorrect", entityPair.getSecond() instanceof TestValue); entityPair = entityLookupCacheA.getByKey(entityId);
assertEquals("Looked-up type value incorrect", value, entityPair.getSecond()); assertNotNull("Lookup by key should work after create", entityPair);
} assertTrue("Looked-up type incorrect", entityPair.getSecond() instanceof TestValue);
assertEquals("Looked-up type value incorrect", value, entityPair.getSecond());
public void testLookupAgainstExisting() throws Exception }
{
// Put some values in the "database" @Test
createValue(new TestValue("AAA")); public void testLookupAgainstExisting() throws Exception
createValue(new TestValue("BBB")); {
createValue(new TestValue("CCC")); // Put some values in the "database"
createValue(new TestValue("AAA"));
// Look up by value createValue(new TestValue("BBB"));
Pair<Long, Object> entityPair = entityLookupCacheA.getByValue(new TestValue("AAA")); createValue(new TestValue("CCC"));
assertNotNull("Expected value to be found", entityPair);
assertEquals("ID is incorrect", new Long(1), entityPair.getFirst()); // Look up by value
Pair<Long, Object> entityPair = entityLookupCacheA.getByValue(new TestValue("AAA"));
// Look up by ID assertNotNull("Expected value to be found", entityPair);
entityPair = entityLookupCacheA.getByKey(new Long(2)); assertEquals("ID is incorrect", new Long(1), entityPair.getFirst());
assertNotNull("Expected value to be found", entityPair);
// Look up by ID
// Do lookup or create entityPair = entityLookupCacheA.getByKey(new Long(2));
entityPair = entityLookupCacheA.getByValue(new TestValue("CCC")); assertNotNull("Expected value to be found", entityPair);
assertNotNull("Expected value to be found", entityPair);
assertEquals("ID is incorrect", new Long(3), entityPair.getFirst()); // Do lookup or create
} entityPair = entityLookupCacheA.getByValue(new TestValue("CCC"));
assertNotNull("Expected value to be found", entityPair);
public void testRegions() throws Exception assertEquals("ID is incorrect", new Long(3), entityPair.getFirst());
{ }
TestValue valueAAA = new TestValue("AAA");
Pair<Long, Object> entityPairAAA = entityLookupCacheA.getOrCreateByValue(valueAAA); @Test
assertNotNull(entityPairAAA); public void testRegions() throws Exception
assertEquals("AAA", database.get(entityPairAAA.getFirst())); {
assertEquals(2, cache.getKeys().size()); TestValue valueAAA = new TestValue("AAA");
Pair<Long, Object> entityPairAAA = entityLookupCacheA.getOrCreateByValue(valueAAA);
TestValue valueBBB = new TestValue("BBB"); assertNotNull(entityPairAAA);
Pair<Long, Object> entityPairBBB = entityLookupCacheB.getOrCreateByValue(valueBBB); assertEquals("AAA", database.get(entityPairAAA.getFirst()));
assertNotNull(entityPairBBB); assertEquals(2, cache.getKeys().size());
assertEquals("BBB", database.get(entityPairBBB.getFirst()));
assertEquals(4, cache.getKeys().size()); TestValue valueBBB = new TestValue("BBB");
Pair<Long, Object> entityPairBBB = entityLookupCacheB.getOrCreateByValue(valueBBB);
// Now cross-check against the caches and make sure that the cache assertNotNull(entityPairBBB);
entityPairBBB = entityLookupCacheA.getByValue(valueBBB); assertEquals("BBB", database.get(entityPairBBB.getFirst()));
assertEquals(6, cache.getKeys().size()); assertEquals(4, cache.getKeys().size());
entityPairBBB = entityLookupCacheB.getByValue(valueAAA);
assertEquals(8, cache.getKeys().size()); // Now cross-check against the caches and make sure that the cache
} entityPairBBB = entityLookupCacheA.getByValue(valueBBB);
assertEquals(6, cache.getKeys().size());
public void testNullLookups() throws Exception entityPairBBB = entityLookupCacheB.getByValue(valueAAA);
{ assertEquals(8, cache.getKeys().size());
TestValue valueNull = null; }
Pair<Long, Object> entityPairNull = entityLookupCacheA.getOrCreateByValue(valueNull);
assertNotNull(entityPairNull); @Test
assertTrue(database.containsKey(entityPairNull.getFirst())); public void testNullLookups() throws Exception
assertNull(database.get(entityPairNull.getFirst())); {
assertEquals(2, cache.getKeys().size()); TestValue valueNull = null;
Pair<Long, Object> entityPairNull = entityLookupCacheA.getOrCreateByValue(valueNull);
// Look it up again assertNotNull(entityPairNull);
Pair<Long, Object> entityPairCheck = entityLookupCacheA.getOrCreateByValue(valueNull); assertTrue(database.containsKey(entityPairNull.getFirst()));
assertNotNull(entityPairNull); assertNull(database.get(entityPairNull.getFirst()));
assertTrue(database.containsKey(entityPairNull.getFirst())); assertEquals(2, cache.getKeys().size());
assertNull(database.get(entityPairNull.getFirst()));
assertEquals(entityPairNull, entityPairCheck); // Look it up again
} Pair<Long, Object> entityPairCheck = entityLookupCacheA.getOrCreateByValue(valueNull);
assertNotNull(entityPairNull);
public void testGetOrCreate() throws Exception assertTrue(database.containsKey(entityPairNull.getFirst()));
{ assertNull(database.get(entityPairNull.getFirst()));
TestValue valueOne = new TestValue(getName() + "-ONE"); assertEquals(entityPairNull, entityPairCheck);
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne); }
assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst(); @Test
assertEquals(valueOne.val, database.get(id)); public void testGetOrCreate() throws Exception
assertEquals(2, cache.getKeys().size()); {
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
Pair<Long, Object> entityPairOneCheck = entityLookupCacheA.getOrCreateByValue(valueOne); Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOneCheck); assertNotNull(entityPairOne);
assertEquals(id, entityPairOneCheck.getFirst()); Long id = entityPairOne.getFirst();
} assertEquals(valueOne.val, database.get(id));
assertEquals(2, cache.getKeys().size());
public void testCreateOrGet() throws Exception
{ Pair<Long, Object> entityPairOneCheck = entityLookupCacheA.getOrCreateByValue(valueOne);
TestValue valueOne = new TestValue(getName() + "-ONE"); assertNotNull(entityPairOneCheck);
Pair<Long, Object> entityPairOne = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO); assertEquals(id, entityPairOneCheck.getFirst());
assertNotNull(entityPairOne); }
Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id)); @Test
assertEquals(1, cache.getKeys().size()); public void testCreateOrGet() throws Exception
{
Pair<Long, Object> entityPairOneCheck = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO); TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
assertNotNull(entityPairOneCheck); Pair<Long, Object> entityPairOne = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO);
assertEquals(id, entityPairOneCheck.getFirst()); assertNotNull(entityPairOne);
} Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
public void testUpdate() throws Exception // We cache both by value and by key, so we should have 2 entries
{ assertEquals(2, cache.getKeys().size());
TestValue valueOne = new TestValue(getName() + "-ONE");
TestValue valueTwo = new TestValue(getName() + "-TWO"); Pair<Long, Object> entityPairOneCheck = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO);
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne); assertNotNull(entityPairOneCheck);
assertNotNull(entityPairOne); assertEquals(id, entityPairOneCheck.getFirst());
Long id = entityPairOne.getFirst(); }
assertEquals(valueOne.val, database.get(id));
assertEquals(2, cache.getKeys().size()); @Test
public void testUpdate() throws Exception
// Update {
int updateCount = entityLookupCacheA.updateValue(id, valueTwo); TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
assertEquals("Update count was incorrect.", 1, updateCount); TestValue valueTwo = new TestValue(getClass().getName() + "-TWO");
assertEquals(valueTwo.val, database.get(id)); Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertEquals(2, cache.getKeys().size()); assertNotNull(entityPairOne);
} Long id = entityPairOne.getFirst();
assertEquals(valueOne.val, database.get(id));
public void testDeleteByKey() throws Exception assertEquals(2, cache.getKeys().size());
{
TestValue valueOne = new TestValue(getName() + "-ONE"); // Update
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne); int updateCount = entityLookupCacheA.updateValue(id, valueTwo);
assertNotNull(entityPairOne); assertEquals("Update count was incorrect.", 1, updateCount);
Long id = entityPairOne.getFirst(); assertEquals(valueTwo.val, database.get(id));
assertEquals(valueOne.val, database.get(id)); assertEquals(2, cache.getKeys().size());
assertEquals(2, cache.getKeys().size()); }
// Delete @Test
int deleteCount = entityLookupCacheA.deleteByKey(id); public void testDeleteByKey() throws Exception
assertEquals("Delete count was incorrect.", 1, deleteCount); {
assertNull(database.get(id)); TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
assertEquals(0, cache.getKeys().size()); Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
} assertNotNull(entityPairOne);
Long id = entityPairOne.getFirst();
public void testDeleteByValue() throws Exception assertEquals(valueOne.val, database.get(id));
{ assertEquals(2, cache.getKeys().size());
TestValue valueOne = new TestValue(getName() + "-ONE");
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne); // Delete
assertNotNull(entityPairOne); int deleteCount = entityLookupCacheA.deleteByKey(id);
Long id = entityPairOne.getFirst(); assertEquals("Delete count was incorrect.", 1, deleteCount);
assertEquals(valueOne.val, database.get(id)); assertNull(database.get(id));
assertEquals(2, cache.getKeys().size()); assertEquals(0, cache.getKeys().size());
}
// Delete
int deleteCount = entityLookupCacheA.deleteByValue(valueOne); @Test
assertEquals("Delete count was incorrect.", 1, deleteCount); public void testDeleteByValue() throws Exception
assertNull(database.get(id)); {
assertEquals(0, cache.getKeys().size()); TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
} Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOne);
public void testClear() throws Exception Long id = entityPairOne.getFirst();
{ assertEquals(valueOne.val, database.get(id));
TestValue valueOne = new TestValue(getName() + "-ONE"); assertEquals(2, cache.getKeys().size());
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
assertNotNull(entityPairOne); // Delete
Long id = entityPairOne.getFirst(); int deleteCount = entityLookupCacheA.deleteByValue(valueOne);
assertEquals(valueOne.val, database.get(id)); assertEquals("Delete count was incorrect.", 1, deleteCount);
assertEquals(2, cache.getKeys().size()); assertNull(database.get(id));
assertEquals(0, cache.getKeys().size());
// Clear it }
entityLookupCacheA.clear();
assertEquals(valueOne.val, database.get(id)); // Must still be in database @Test
assertEquals(0, cache.getKeys().size()); // ... but cache must be empty public void testClear() throws Exception
} {
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
/** Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
* Helper class to represent business object assertNotNull(entityPairOne);
*/ Long id = entityPairOne.getFirst();
private static class TestValue assertEquals(valueOne.val, database.get(id));
{ assertEquals(2, cache.getKeys().size());
private final String val;
private TestValue(String val) // Clear it
{ entityLookupCacheA.clear();
this.val = val; assertEquals(valueOne.val, database.get(id)); // Must still be in database
} assertEquals(0, cache.getKeys().size()); // ... but cache must be empty
@Override }
public boolean equals(Object obj)
{ @Test
if (obj == null || !(obj instanceof TestValue)) public void testGetCachedValue() throws Exception
{ {
return false; // Create a new value
} TestValue valueCached = new TestValue(getClass().getName() + "-CACHED");
return val.equals( ((TestValue)obj).val ); Pair<Long, Object> entityPairOne = entityLookupCacheA.createOrGetByValue(valueCached, controlDAO);
} assertNotNull(entityPairOne);
@Override Long id = entityPairOne.getFirst();
public int hashCode() // We cache both by value and by key, so we should have 2 entries
{ assertEquals(2, cache.getKeys().size());
return val.hashCode();
} // Check the cache for the previously created value
Pair<Long, Object> entityPairCacheCheck = entityLookupCacheA.getCachedEntityByValue(valueCached);
} assertNotNull(entityPairCacheCheck);
assertEquals(id, entityPairCacheCheck.getFirst());
public String getValueKey(Object value)
{ // Clear the cache and attempt to retrieve it again
assertNotNull(value); entityLookupCacheA.clear();
assertTrue(value instanceof TestValue); entityPairCacheCheck = entityLookupCacheA.getCachedEntityByValue(valueCached);
String dbValue = ((TestValue)value).val;
return dbValue; // Since we are only retrieving from cache, the value should not be found
} assertNull(entityPairCacheCheck);
}
public Pair<Long, Object> findByKey(Long key)
{ /**
assertNotNull(key); * Helper class to represent business object
*/
String dbValue = database.get(key); private static class TestValue
if (dbValue == null) {
{ private final String val;
return null;
} private TestValue(String val)
// Make a value object {
TestValue value = new TestValue(dbValue); this.val = val;
return new Pair<Long, Object>(key, value); }
}
@Override
public Pair<Long, Object> findByValue(Object value) public boolean equals(Object obj)
{ {
assertTrue(value == null || value instanceof TestValue); if (obj == null || !(obj instanceof TestValue))
String dbValue = (value == null) ? null : ((TestValue)value).val; {
return false;
for (Map.Entry<Long, String> entry : database.entrySet()) }
{ return val.equals(((TestValue) obj).val);
if (EqualsHelper.nullSafeEquals(entry.getValue(), dbValue)) }
{
return new Pair<Long, Object>(entry.getKey(), entry.getValue()); @Override
} public int hashCode()
} {
return null; return val.hashCode();
} }
/** }
* Simulate creation of a new database entry
*/ public String getValueKey(Object value)
public Pair<Long, Object> createValue(Object value) {
{ assertNotNull(value);
assertTrue(value == null || value instanceof TestValue); assertTrue(value instanceof TestValue);
String dbValue = (value == null) ? null : ((TestValue)value).val; String dbValue = ((TestValue) value).val;
return dbValue;
// Kick out any duplicate values }
if (database.containsValue(dbValue))
{ public Pair<Long, Object> findByKey(Long key)
throw new DuplicateKeyException("Value is duplicated: " + value); {
} assertNotNull(key);
// Get the last key String dbValue = database.get(key);
Long lastKey = database.isEmpty() ? null : database.lastKey(); if (dbValue == null)
Long newKey = null; {
if (lastKey == null) return null;
{ }
newKey = new Long(1); // Make a value object
} TestValue value = new TestValue(dbValue);
else return new Pair<Long, Object>(key, value);
{ }
newKey = new Long(lastKey.longValue() + 1);
} public Pair<Long, Object> findByValue(Object value)
database.put(newKey, dbValue); {
return new Pair<Long, Object>(newKey, value); assertTrue(value == null || value instanceof TestValue);
} String dbValue = (value == null) ? null : ((TestValue) value).val;
public int updateValue(Long key, Object value) for (Map.Entry<Long, String> entry : database.entrySet())
{ {
assertNotNull(key); if (EqualsHelper.nullSafeEquals(entry.getValue(), dbValue))
assertTrue(value == null || value instanceof TestValue); {
return new Pair<Long, Object>(entry.getKey(), entry.getValue());
// Find it }
Pair<Long, Object> entityPair = findByKey(key); }
if (entityPair == null) return null;
{ }
return 0;
} /**
else * Simulate creation of a new database entry
{ */
database.put(key, ((TestValue)value).val); public Pair<Long, Object> createValue(Object value)
return 1; {
} assertTrue(value == null || value instanceof TestValue);
} String dbValue = (value == null) ? null : ((TestValue) value).val;
public int deleteByKey(Long key) // Kick out any duplicate values
{ if (database.containsValue(dbValue))
assertNotNull(key); {
throw new DuplicateKeyException("Value is duplicated: " + value);
if (database.containsKey(key)) }
{
database.remove(key); // Get the last key
return 1; Long lastKey = database.isEmpty() ? null : database.lastKey();
} Long newKey = null;
else if (lastKey == null)
{ {
return 0; newKey = new Long(1);
} }
} else
{
public int deleteByValue(Object value) newKey = new Long(lastKey.longValue() + 1);
{ }
assertTrue(value == null || value instanceof TestValue); database.put(newKey, dbValue);
return new Pair<Long, Object>(newKey, value);
// Find it }
Pair<Long, Object> entityPair = findByValue(value);
if (entityPair == null) public int updateValue(Long key, Object value)
{ {
return 0; assertNotNull(key);
} assertTrue(value == null || value instanceof TestValue);
else
{ // Find it
database.remove(entityPair.getFirst()); Pair<Long, Object> entityPair = findByKey(key);
return 1; if (entityPair == null)
} {
} return 0;
} }
else
{
database.put(key, ((TestValue) value).val);
return 1;
}
}
public int deleteByKey(Long key)
{
assertNotNull(key);
if (database.containsKey(key))
{
database.remove(key);
return 1;
}
else
{
return 0;
}
}
public int deleteByValue(Object value)
{
assertTrue(value == null || value instanceof TestValue);
// Find it
Pair<Long, Object> entityPair = findByValue(value);
if (entityPair == null)
{
return 0;
}
else
{
database.remove(entityPair.getFirst());
return 1;
}
}
}