
com.spikeify.commands.MultiKeyUpdater Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core Show documentation
Show all versions of core Show documentation
Simple ORM for Aerospike
package com.spikeify.commands;
import com.aerospike.client.Bin;
import com.aerospike.client.IAerospikeClient;
import com.aerospike.client.Key;
import com.aerospike.client.async.IAsyncClient;
import com.aerospike.client.policy.RecordExistsAction;
import com.aerospike.client.policy.WritePolicy;
import com.spikeify.*;
import com.spikeify.annotations.Namespace;
import com.spikeify.annotations.SetName;
import java.util.*;
/**
* A command chain for creating or updating multiple objects in database.
* This class is not intended to be instantiated by user.
*/
@SuppressWarnings({"unchecked", "WeakerAccess"})
public class MultiKeyUpdater {
private boolean forceReplace = false;
/*
* Used internally to create a command chain. Not intended to be used by the user directly.
* Instead use {@link Spikeify#createAll(Key[], Object[])} (Object...)} method.
*/
@SuppressWarnings("SameParameterValue")
public MultiKeyUpdater(boolean isTx, IAsyncClient asynClient,
RecordsCache recordsCache, boolean create, String namespace,
Key[] keys, Object[] objects) {
this.isTx = isTx;
this.asynClient = asynClient;
this.recordsCache = recordsCache;
this.create = create;
this.namespace = namespace;
if (keys.length != objects.length) {
throw new SpikeifyError("Error: keys and objects arrays must be of the same size");
}
this.keys = Arrays.asList(keys);
this.objects = objects;
// must be set in order for later queries to return record keys
this.asynClient.getWritePolicyDefault().sendKey = true;
}
/**
* Used internally to create a command chain. Not intended to be used by the user directly.
* Instead use {@link Spikeify#createAll(Long[], Object[])} (Object...)} method.
*/
@SuppressWarnings("SameParameterValue")
public MultiKeyUpdater(boolean isTx, IAsyncClient asynClient,
RecordsCache recordsCache, boolean create, String namespace,
Long[] keys, Object[] objects) {
this.isTx = isTx;
this.asynClient = asynClient;
this.recordsCache = recordsCache;
this.create = create;
this.namespace = namespace;
if (keys.length != objects.length) {
throw new SpikeifyError("Error: keys and objects arrays must be of the same size");
}
this.longKeys = Arrays.asList(keys);
this.objects = objects;
// must be set in order for later queries to return record keys
this.asynClient.getWritePolicyDefault().sendKey = true;
}
/**
* Used internally to create a command chain. Not intended to be used by the user directly.
* Instead use {@link Spikeify#createAll(String[], Object[])} (Object...)} or method.
*/
@SuppressWarnings("SameParameterValue")
public MultiKeyUpdater(boolean isTx, IAsyncClient asynClient,
RecordsCache recordsCache, boolean create, String namespace,
String[] keys, Object[] objects) {
this.isTx = isTx;
this.asynClient = asynClient;
this.recordsCache = recordsCache;
this.create = create;
this.namespace = namespace;
if (keys.length != objects.length) {
throw new SpikeifyError("Error: keys and objects arrays must be of the same size");
}
this.stringKeys = Arrays.asList(keys);
this.objects = objects;
// must be set in order for later queries to return record keys
this.asynClient.getWritePolicyDefault().sendKey = true;
}
private final Object[] objects;
protected String namespace;
protected String setName;
protected List stringKeys = new ArrayList<>();
protected List longKeys = new ArrayList<>();
protected List keys = new ArrayList<>(10);
private final boolean isTx;
protected final IAsyncClient asynClient;
protected final RecordsCache recordsCache;
protected final boolean create;
protected WritePolicy overridePolicy;
/**
* Sets the Namespace. Overrides the default namespace and the namespace defined on the Class via {@link Namespace} annotation.
*
* @param namespace The namespace.
* @return updater
*/
public MultiKeyUpdater namespace(String namespace) {
this.namespace = namespace;
return this;
}
/**
* Sets the SetName. Overrides any SetName defined on the Class via {@link SetName} annotation.
*
* @param setName The name of the set.
* @return updater
*/
public MultiKeyUpdater setName(String setName) {
this.setName = setName;
return this;
}
/**
* Sets the keys of the records to be loaded.
*
* @param keys Array of keys
* @return updater
*/
public MultiKeyUpdater key(String... keys) {
if (keys.length != objects.length) {
throw new SpikeifyError("Number of keys does not match number of objects.");
}
this.stringKeys = Arrays.asList(keys);
this.longKeys.clear();
this.keys.clear();
return this;
}
/**
* Sets the keys of the records to be loaded.
*
* @param keys Array of keys
* @return updater
*/
public MultiKeyUpdater key(Long... keys) {
if (keys.length != objects.length) {
throw new SpikeifyError("Number of keys does not match number of objects.");
}
this.longKeys = Arrays.asList(keys);
this.stringKeys.clear();
this.keys.clear();
return this;
}
/**
* Sets the keys of the records to be loaded.
*
* @param keys Array of keys
* @return updater
*/
public MultiKeyUpdater key(Key... keys) {
if (keys.length != objects.length) {
throw new SpikeifyError("Number of keys does not match number of objects.");
}
this.keys = Arrays.asList(keys);
this.stringKeys.clear();
this.longKeys.clear();
return this;
}
/**
* Sets the {@link WritePolicy} to be used when creating or updating the record in the database.
* Internally the 'sendKey' property of the policy will always be set to true.
* If this method is called within .transact() method then the 'generationPolicy' property will be set to GenerationPolicy.EXPECT_GEN_EQUAL
* The 'recordExistsAction' property is set accordingly depending if this is a create or update operation
*
* @param policy The policy.
* @return updater
*/
public MultiKeyUpdater policy(WritePolicy policy) {
this.overridePolicy = policy;
return this;
}
/**
* Sets updater to skip cache check for object changes. This causes that all
* object properties will be written to database. It also deletes previous saved
* properties in database and now not mapped to object.
*
* @return updater
*/
public MultiKeyUpdater forceReplace() {
this.forceReplace = true;
return this;
}
protected void collectKeys() {
if (namespace == null) {
throw new SpikeifyError("Namespace not set.");
}
// check if any Long or String keys were provided
if (!stringKeys.isEmpty()) {
for (String stringKey : stringKeys) {
keys.add(new Key(namespace, setName, stringKey));
}
} else if (!longKeys.isEmpty()) {
for (long longKey : longKeys) {
keys.add(new Key(namespace, setName, longKey));
}
}
if (keys.isEmpty()) {
throw new SpikeifyError("Error: missing parameter 'key'");
}
}
private WritePolicy getPolicy() {
WritePolicy writePolicy = overridePolicy != null ? overridePolicy : new WritePolicy(asynClient.getWritePolicyDefault());
writePolicy.recordExistsAction = create ? RecordExistsAction.CREATE_ONLY : forceReplace ? RecordExistsAction.REPLACE : RecordExistsAction.UPDATE;
// must be set so that user key can be retrieved in queries
writePolicy.sendKey = true;
return writePolicy;
}
/**
* Synchronously executes multiple create or update commands and returns the keys of the records.
*
* @return The Map of Key, object pairs.
*/
public Map now() {
collectKeys();
if (objects.length != keys.size()) {
throw new SpikeifyError("Error: with multi-put you need to provide equal number of objects and keys");
}
WritePolicy usePolicy = getPolicy();
boolean isReplace = usePolicy.recordExistsAction == RecordExistsAction.REPLACE;
Map result = new HashMap<>(objects.length);
for (int i = 0; i < objects.length; i++) {
Object object = objects[i];
Key key = keys.get(i);
if (key == null || object == null) {
throw new SpikeifyError("Error: with multi-put all objects and keys must NOT be null");
}
result.put(key, object);
ClassMapper mapper = MapperService.getMapper(object.getClass());
Map props = mapper.getProperties(object);
Set changedProps = recordsCache.update(key, props, forceReplace);
List bins = new ArrayList<>();
boolean nonNullField = false;
for (String propName : changedProps) {
Object value = props.get(propName);
if (value == null) {
if (!isReplace) {
bins.add(Bin.asNull(propName));
}
} else if (value instanceof List>) {
bins.add(new Bin(propName, (List) value));
nonNullField = true;
} else if (value instanceof Map, ?>) {
bins.add(new Bin(propName, (Map) value));
nonNullField = true;
} else {
bins.add(new Bin(propName, value));
nonNullField = true;
}
}
if (!nonNullField && props.size() == changedProps.size()) {
throw new SpikeifyError("Error: cannot create object with no writable properties. " +
"At least one object property other then UserKey must be different from NULL.");
}
Integer recordExpiration = mapper.getRecordExpiration(object);
if (recordExpiration != null) {
usePolicy.expiration = recordExpiration;
}
// if we are updating an existing record and no bins are to be updated,
// then just touch the entity to update expiry timestamp
if (!create && bins.isEmpty()) {
if(recordExpiration != null){
asynClient.touch(usePolicy, key);
}
} else {
asynClient.put(usePolicy, key, bins.toArray(new Bin[bins.size()]));
}
// set LDT fields
mapper.setBigDatatypeFields(object, asynClient, key);
}
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy