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

MNT-24641
* 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
This commit is contained in:
Eva Vasques
2024-10-09 17:07:10 +01:00
committed by GitHub
parent 34fb5e9dd9
commit f4103c242f
3 changed files with 2009 additions and 1932 deletions

View File

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