Adding more support for the Draft/Builder pattern for entity objects. Fixed a number of issues and added more tests.

This commit is contained in:
Greg Burd 2017-08-31 14:06:44 -04:00
parent 3cd889ea61
commit 5089be2c22
15 changed files with 725 additions and 141 deletions

View file

@ -0,0 +1,33 @@
package net.helenus.core;
import net.helenus.core.reflect.MapExportable;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
public abstract class AbstractAuditedEntityDraft<E> extends AbstractEntityDraft<E> {
public AbstractAuditedEntityDraft(MapExportable entity, AuditProvider auditProvider) {
super(entity);
Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
Date now = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
String who = auditProvider == null ? "unknown" : auditProvider.operatorName();
if (entity == null) {
set("createdBy", who);
set("createdAt", now);
}
set("modifiedBy", who);
set("modifiedAt", now);
}
Date createdAt() {
return (Date) get("createdAt");
}
}

View file

@ -0,0 +1,174 @@
package net.helenus.core;
import java.util.*;
import com.google.common.primitives.Primitives;
import net.helenus.core.reflect.DefaultPrimitiveTypes;
import net.helenus.core.reflect.Drafted;
import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.MappingUtil;
public abstract class AbstractEntityDraft<E> implements Drafted<E> {
private final Map<String, Object> backingMap = new HashMap<String, Object>();
private final Set<String> mutatedSet = new HashSet<String>();
private final MapExportable entity;
private final Map<String, Object> entityMap;
public AbstractEntityDraft(MapExportable entity) {
this.entity = entity;
this.entityMap = entity != null ? entity.toMap() : new HashMap<String, Object>();
}
public abstract Class<E> getEntityClass();
public E build() { return Helenus.map(getEntityClass(), toMap()); }
@SuppressWarnings("unchecked")
protected <T> T get(String key) {
T value = (T) entityMap.get(key);
if (value == null) {
T obj = (T) new Object() {
};
Class<?> primitiveType = Primitives.unwrap(obj.getClass());
if (Primitives.allPrimitiveTypes().contains(primitiveType)) {
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(primitiveType);
if (type == null) {
throw new RuntimeException("unknown primitive type " + primitiveType.getTypeName());
}
return (T) type.getDefaultValue();
}
}
return value;
}
@SuppressWarnings("unchecked")
protected <T> T get(String key, Class<?> returnType) {
T value = (T) entityMap.get(key);
if (value == null) {
if (Primitives.allPrimitiveTypes().contains(returnType)) {
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
if (type == null) {
throw new RuntimeException("unknown primitive type " + returnType);
}
return (T) type.getDefaultValue();
}
}
return value;
}
protected Object set(String key, Object value) {
if (key == null || value == null) {
return null;
}
backingMap.put(key, value);
mutatedSet.add(key);
return value;
}
protected Object mutate(String key, Object value) {
Objects.requireNonNull(key);
if (value == null) {
return null;
}
if (entity != null) {
Map<String, Object> map = entity.toMap();
if (map.containsKey(key) && !value.equals(map.get(key))) {
backingMap.put(key, value);
mutatedSet.add(key);
return value;
}
return map.get(key);
} else {
backingMap.put(key, value);
mutatedSet.add(key);
return null;
}
}
private String methodNameFor(Getter<?> getter) {
return MappingUtil.resolveMappingProperty(getter)
.getProperty()
.getPropertyName();
}
public Object unset(Getter<?> getter) {
return unset(methodNameFor(getter));
}
public Object unset(String key) {
if (key != null) {
Object value = backingMap.get(key);
backingMap.put(key, null);
mutatedSet.add(key);
return value;
}
return null;
}
public <T> boolean reset(Getter<?> getter, T desiredValue) {
return this.<T>reset(methodNameFor(getter), desiredValue);
}
public <T> boolean reset(String key, T desiredValue) {
if (key != null && desiredValue != null) {
@SuppressWarnings("unchecked")
T currentValue = (T) backingMap.get(key);
if (currentValue != null && !currentValue.equals(desiredValue)) {
return set(key, desiredValue) != null;
}
}
return false;
}
@Override
public Map<String, Object> toMap() {
return toMap(entityMap);
}
public Map<String, Object> toMap(Map<String, Object>entityMap) {
Map<String, Object> combined;
if (entityMap != null && entityMap.size() > 0) {
combined = new HashMap<String, Object>(entityMap.size());
for (String key : entityMap.keySet()) {
combined.put(key, entityMap.get(key));
}
} else {
combined = new HashMap<String, Object>(backingMap.size());
}
for (String key : mutatedSet) {
combined.put(key, backingMap.get(key));
}
return combined;
}
@Override
public Set<String> mutated() {
return mutatedSet;
}
@Override
public String toString() {
return backingMap.toString();
}
}

View file

@ -0,0 +1,10 @@
package net.helenus.core;
public interface AuditProvider {
/**
* What to record in the database row as the name of the agent causing the mutation.
* @return a string name that indicates the identity of the operator mutating the data at this time.
*/
public String operatorName();
}

View file

@ -389,11 +389,15 @@ public final class HelenusSession extends AbstractSessionOperations implements C
return new UpdateOperation<ResultSet>(this);
}
public <T> UpdateOperation<T> update(Drafted<T> draft) {
UpdateOperation update = new UpdateOperation<T>(this, draft.build());
public <E> UpdateOperation<E> update(Drafted<E> drafted) {
if (drafted instanceof AbstractEntityDraft == false) {
throw new HelenusMappingException("update of draft objects that don't inherit from AbstractEntityDraft is not yet supported");
}
AbstractEntityDraft<E> draft = (AbstractEntityDraft<E>)drafted;
UpdateOperation update = new UpdateOperation<E>(this, draft);
Map<String, Object> map = draft.toMap();
HelenusEntity entity = draft.getEntity();
Set<String> mutatedProperties = draft.mutated();
HelenusEntity entity = Helenus.entity(draft.getEntityClass());
// Add all the mutated values contained in the draft.
entity.getOrderedProperties().forEach(property -> {
@ -464,11 +468,9 @@ public final class HelenusSession extends AbstractSessionOperations implements C
}
}
public <T> InsertOperation<T> insert(Drafted draft) {
return this.<T>insert((T) draft.build(), draft.mutated());
}
public <T> InsertOperation<T> insert(Drafted draft) { return insert(draft.build(), draft.mutated()); }
public <T> InsertOperation<T> insert(T pojo, Set<String> mutations) {
private <T> InsertOperation<T> insert(T pojo, Set<String> mutations) {
Objects.requireNonNull(pojo, "pojo is empty");
Class<?> iface = MappingUtil.getMappingInterface(pojo);
@ -500,7 +502,7 @@ public final class HelenusSession extends AbstractSessionOperations implements C
}
}
public <T> InsertOperation<T> upsert(T pojo, Set<String> mutations) {
private <T> InsertOperation<T> upsert(T pojo, Set<String> mutations) {
Objects.requireNonNull(pojo, "pojo is empty");
Class<?> iface = MappingUtil.getMappingInterface(pojo);

View file

@ -73,7 +73,7 @@ public final class InsertOperation<T> extends AbstractOperation<T, InsertOperati
this.entity = entity;
this.pojo = pojo;
this.ifNotExists = ifNotExists;
this.resultType = pojo.getClass() == null ? ResultSet.class : pojo.getClass();
this.resultType = entity.getMappingInterface();
Collection<HelenusProperty> properties = entity.getOrderedProperties();
Set<String> keys = (mutations == null) ? null : mutations;
@ -154,10 +154,51 @@ public final class InsertOperation<T> extends AbstractOperation<T, InsertOperati
@Override
public T transform(ResultSet resultSet) {
Class<?> iface = entity.getMappingInterface();
if (resultType.isAssignableFrom(iface)) {
return TransformGeneric.INSTANCE.<T>transform(sessionOps, pojo, iface, values, entity.getOrderedProperties());
if (resultType == iface) {
if (values.size() > 0) {
Collection<HelenusProperty> properties = entity.getOrderedProperties();
Map<String, Object> backingMap = new HashMap<String, Object>(properties.size());
// First, add all the inserted values into our new map.
values.forEach(t -> backingMap.put(t._1.getProperty().getPropertyName(), t._2));
// Then, fill in all the rest of the properties.
for (HelenusProperty prop : properties) {
String key = prop.getPropertyName();
if (backingMap.containsKey(key)) {
// Some values man need to be converted (e.g. from String to Enum). This is done
// within the BeanColumnValueProvider below.
Optional<Function<Object, Object>> converter =
prop.getReadConverter(sessionOps.getSessionRepository());
if (converter.isPresent()) {
backingMap.put(key, converter.get().apply(backingMap.get(key)));
}
} else {
// If we started this operation with an instance of this type, use values from that.
if (pojo != null) {
backingMap.put(key, BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
} else {
// Otherwise we'll use default values for the property type if available.
Class<?> propType = prop.getJavaType();
if (propType.isPrimitive()) {
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(propType);
if (type == null) {
throw new HelenusException("unknown primitive type " + propType);
}
backingMap.put(key, type.getDefaultValue());
}
}
}
}
// Lastly, create a new proxy object for the entity and return the new instance.
return (T) Helenus.map(iface, backingMap);
}
// Oddly, this insert didn't change any value so simply return the pojo.
// TODO(gburd): this pojo is the result of a Draft.build() call which will not preserve object identity (o1 == o2), ... fix me.
return (T) pojo;
}
return pojo;
return (T) resultSet;
}
public InsertOperation<T> usingTtl(int ttl) {

View file

@ -194,6 +194,10 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
HelenusEntity entity = null;
Selection selection = QueryBuilder.select();
// iff in txn or cacheable add ttl and timestamp to result set for each col selected
// construct set of primary keys (partition and col)
// construct cache key
for (HelenusPropertyNode prop : props) {
selection = selection.column(prop.getColumnName());

View file

@ -1,59 +0,0 @@
package net.helenus.core.operation;
import java.util.*;
import java.util.function.Function;
import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.Helenus;
import net.helenus.core.reflect.DefaultPrimitiveTypes;
import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.HelenusProperty;
import net.helenus.mapping.value.BeanColumnValueProvider;
import net.helenus.support.Fun;
import net.helenus.support.HelenusException;
public enum TransformGeneric {
INSTANCE;
public <T> T transform(AbstractSessionOperations sessionOps, T pojo, Class<?> mappingInterface, List<Fun.Tuple2<HelenusPropertyNode, Object>> assignments, Collection<HelenusProperty> properties) {
if (assignments.size() > 0) {
Map<String, Object> backingMap = new HashMap<String, Object>(properties.size());
// First, add all the inserted values into our new map.
assignments.forEach(t -> backingMap.put(t._1.getProperty().getPropertyName(), t._2));
// Then, fill in all the rest of the properties.
for (HelenusProperty prop : properties) {
String key = prop.getPropertyName();
if (backingMap.containsKey(key)) {
// Some values man need to be converted (e.g. from String to Enum). This is done
// within the BeanColumnValueProvider below.
Optional<Function<Object, Object>> converter =
prop.getReadConverter(sessionOps.getSessionRepository());
if (converter.isPresent()) {
backingMap.put(key, converter.get().apply(backingMap.get(key)));
}
} else {
// If we started this operation with an instance of this type, use values from that.
if (pojo != null) {
backingMap.put(key, BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
} else {
// Otherwise we'll use default values for the property type if available.
Class<?> propType = prop.getJavaType();
if (propType.isPrimitive()) {
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(propType);
if (type == null) {
throw new HelenusException("unknown primitive type " + propType);
}
backingMap.put(key, type.getDefaultValue());
}
}
}
}
// Lastly, create a new proxy object for the entity and return the new instance.
return (T) Helenus.map(mappingInterface, backingMap);
}
return (T) pojo;
}
}

View file

@ -15,16 +15,16 @@
*/
package net.helenus.core.operation;
import java.util.*;
import java.util.function.Function;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.querybuilder.Assignment;
import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Update;
import java.util.*;
import java.util.function.Function;
import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.Filter;
import net.helenus.core.Getter;
import net.helenus.core.*;
import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusProperty;
@ -32,29 +32,33 @@ import net.helenus.mapping.MappingUtil;
import net.helenus.support.HelenusMappingException;
import net.helenus.support.Immutables;
public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateOperation<T>> {
public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateOperation<E>> {
private HelenusEntity entity = null;
private final List<Assignment> assignments = new ArrayList<Assignment>();
private final T pojo;
private final AbstractEntityDraft<E> draft;
private final Map<String, Object> draftMap;
private int[] ttl;
private long[] timestamp;
public UpdateOperation(AbstractSessionOperations sessionOperations){
super(sessionOperations);
this.pojo = null;
this.draft = null;
this.draftMap = null;
}
public UpdateOperation(AbstractSessionOperations sessionOperations, T pojo) {
public UpdateOperation(AbstractSessionOperations sessionOperations, AbstractEntityDraft<E> draft) {
super(sessionOperations);
this.pojo = pojo;
this.draft = draft;
this.draftMap = draft.toMap();
}
public UpdateOperation(AbstractSessionOperations sessionOperations, HelenusPropertyNode p, Object v) {
super(sessionOperations);
this.pojo = null;
this.draft = null;
this.draftMap = null;
Object value = sessionOps.getValuePreparer().prepareColumnValue(v, p.getProperty());
assignments.add(QueryBuilder.set(p.getColumnName(), value));
@ -62,7 +66,7 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
addPropertyNode(p);
}
public <V> UpdateOperation<T> set(Getter<V> getter, V v) {
public <V> UpdateOperation<E> set(Getter<V> getter, V v) {
Objects.requireNonNull(getter, "getter is empty");
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter);
@ -83,11 +87,11 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
*
*/
public <V> UpdateOperation<T> increment(Getter<V> counterGetter) {
public <V> UpdateOperation<E> increment(Getter<V> counterGetter) {
return increment(counterGetter, 1L);
}
public <V> UpdateOperation<T> increment(Getter<V> counterGetter, long delta) {
public <V> UpdateOperation<E> increment(Getter<V> counterGetter, long delta) {
Objects.requireNonNull(counterGetter, "counterGetter is empty");
@ -96,14 +100,20 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.incr(p.getColumnName(), delta));
addPropertyNode(p);
if (draft != null) {
String key = p.getProperty().getPropertyName();
draftMap.put(key, (Long) draftMap.get(key) + delta);
}
return this;
}
public <V> UpdateOperation<T> decrement(Getter<V> counterGetter) {
public <V> UpdateOperation<E> decrement(Getter<V> counterGetter) {
return decrement(counterGetter, 1L);
}
public <V> UpdateOperation<T> decrement(Getter<V> counterGetter, long delta) {
public <V> UpdateOperation<E> decrement(Getter<V> counterGetter, long delta) {
Objects.requireNonNull(counterGetter, "counterGetter is empty");
@ -112,6 +122,12 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.decr(p.getColumnName(), delta));
addPropertyNode(p);
if (draft != null) {
String key = p.getProperty().getPropertyName();
draftMap.put(key, (Long) draftMap.get(key) - delta);
}
return this;
}
@ -122,7 +138,7 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
*
*/
public <V> UpdateOperation<T> prepend(Getter<List<V>> listGetter, V value) {
public <V> UpdateOperation<E> prepend(Getter<List<V>> listGetter, V value) {
Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty");
@ -133,10 +149,17 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.prepend(p.getColumnName(), valueObj));
addPropertyNode(p);
if (draft != null) {
String key = p.getProperty().getPropertyName();
List<V> list = (List<V>)draftMap.get(key);
list.add(0, value);
}
return this;
}
public <V> UpdateOperation<T> prependAll(Getter<List<V>> listGetter, List<V> value) {
public <V> UpdateOperation<E> prependAll(Getter<List<V>> listGetter, List<V> value) {
Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty");
@ -147,10 +170,17 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.prependAll(p.getColumnName(), valueObj));
addPropertyNode(p);
if (draft != null && value.size() > 0) {
String key = p.getProperty().getPropertyName();
List<V> list = (List<V>) draftMap.get(key);
list.addAll(0, value);
}
return this;
}
public <V> UpdateOperation<T> setIdx(Getter<List<V>> listGetter, int idx, V value) {
public <V> UpdateOperation<E> setIdx(Getter<List<V>> listGetter, int idx, V value) {
Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty");
@ -161,10 +191,24 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.setIdx(p.getColumnName(), idx, valueObj));
addPropertyNode(p);
if (draft != null) {
String key = p.getProperty().getPropertyName();
List<V> list = (List<V>)draftMap.get(key);
if (idx < 0) {
list.add(0, value);
} else if (idx > list.size()) {
list.add(list.size(), value);
} else {
list.add(idx, value);
}
list.add(0, value);
}
return this;
}
public <V> UpdateOperation<T> append(Getter<List<V>> listGetter, V value) {
public <V> UpdateOperation<E> append(Getter<List<V>> listGetter, V value) {
Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty");
@ -175,10 +219,17 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.append(p.getColumnName(), valueObj));
addPropertyNode(p);
if (draft != null) {
String key = p.getProperty().getPropertyName();
List<V> list = (List<V>)draftMap.get(key);
list.add(value);
}
return this;
}
public <V> UpdateOperation<T> appendAll(Getter<List<V>> listGetter, List<V> value) {
public <V> UpdateOperation<E> appendAll(Getter<List<V>> listGetter, List<V> value) {
Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty");
@ -189,10 +240,17 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.appendAll(p.getColumnName(), valueObj));
addPropertyNode(p);
if (draft != null && value.size() > 0) {
String key = p.getProperty().getPropertyName();
List<V> list = (List<V>) draftMap.get(key);
list.addAll(value);
}
return this;
}
public <V> UpdateOperation<T> discard(Getter<List<V>> listGetter, V value) {
public <V> UpdateOperation<E> discard(Getter<List<V>> listGetter, V value) {
Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty");
@ -203,10 +261,17 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.discard(p.getColumnName(), valueObj));
addPropertyNode(p);
if (draft != null) {
String key = p.getProperty().getPropertyName();
List<V> list = (List<V>) draftMap.get(key);
list.remove(value);
}
return this;
}
public <V> UpdateOperation<T> discardAll(Getter<List<V>> listGetter, List<V> value) {
public <V> UpdateOperation<E> discardAll(Getter<List<V>> listGetter, List<V> value) {
Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty");
@ -217,6 +282,13 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.discardAll(p.getColumnName(), valueObj));
addPropertyNode(p);
if (draft != null) {
String key = p.getProperty().getPropertyName();
List<V> list = (List<V>) draftMap.get(key);
list.removeAll(value);
}
return this;
}
@ -258,7 +330,7 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
*
*/
public <V> UpdateOperation<T> add(Getter<Set<V>> setGetter, V value) {
public <V> UpdateOperation<E> add(Getter<Set<V>> setGetter, V value) {
Objects.requireNonNull(setGetter, "setGetter is empty");
Objects.requireNonNull(value, "value is empty");
@ -269,10 +341,17 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.add(p.getColumnName(), valueObj));
addPropertyNode(p);
if (draft != null) {
String key = p.getProperty().getPropertyName();
Set<V> set = (Set<V>) draftMap.get(key);
set.add(value);
}
return this;
}
public <V> UpdateOperation<T> addAll(Getter<Set<V>> setGetter, Set<V> value) {
public <V> UpdateOperation<E> addAll(Getter<Set<V>> setGetter, Set<V> value) {
Objects.requireNonNull(setGetter, "setGetter is empty");
Objects.requireNonNull(value, "value is empty");
@ -283,10 +362,17 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.addAll(p.getColumnName(), valueObj));
addPropertyNode(p);
if (draft != null) {
String key = p.getProperty().getPropertyName();
Set<V> set = (Set<V>) draftMap.get(key);
set.addAll(value);
}
return this;
}
public <V> UpdateOperation<T> remove(Getter<Set<V>> setGetter, V value) {
public <V> UpdateOperation<E> remove(Getter<Set<V>> setGetter, V value) {
Objects.requireNonNull(setGetter, "setGetter is empty");
Objects.requireNonNull(value, "value is empty");
@ -297,10 +383,17 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.remove(p.getColumnName(), valueObj));
addPropertyNode(p);
if (draft != null) {
String key = p.getProperty().getPropertyName();
Set<V> set = (Set<V>) draftMap.get(key);
set.remove(value);
}
return this;
}
public <V> UpdateOperation<T> removeAll(Getter<Set<V>> setGetter, Set<V> value) {
public <V> UpdateOperation<E> removeAll(Getter<Set<V>> setGetter, Set<V> value) {
Objects.requireNonNull(setGetter, "setGetter is empty");
Objects.requireNonNull(value, "value is empty");
@ -311,6 +404,13 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
assignments.add(QueryBuilder.removeAll(p.getColumnName(), valueObj));
addPropertyNode(p);
if (draft != null) {
String key = p.getProperty().getPropertyName();
Set<V> set = (Set<V>) draftMap.get(key);
set.removeAll(value);
}
return this;
}
@ -351,7 +451,7 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
*
*/
public <K, V> UpdateOperation<T> put(Getter<Map<K, V>> mapGetter, K key, V value) {
public <K, V> UpdateOperation<E> put(Getter<Map<K, V>> mapGetter, K key, V value) {
Objects.requireNonNull(mapGetter, "mapGetter is empty");
Objects.requireNonNull(key, "key is empty");
@ -372,10 +472,15 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
}
addPropertyNode(p);
if (draft != null) {
((Map<K, V>) draftMap.get(prop.getPropertyName())).put(key, value);
}
return this;
}
public <K, V> UpdateOperation<T> putAll(Getter<Map<K, V>> mapGetter, Map<K, V> map) {
public <K, V> UpdateOperation<E> putAll(Getter<Map<K, V>> mapGetter, Map<K, V> map) {
Objects.requireNonNull(mapGetter, "mapGetter is empty");
Objects.requireNonNull(map, "map is empty");
@ -393,6 +498,11 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
}
addPropertyNode(p);
if (draft != null) {
((Map<K, V>) draftMap.get(prop.getPropertyName())).putAll(map);
}
return this;
}
@ -435,17 +545,21 @@ public final class UpdateOperation<T> extends AbstractFilterOperation<T, UpdateO
}
@Override
public T transform(ResultSet resultSet) {
return (T) resultSet;
public E transform(ResultSet resultSet) {
if (draft != null) {
return Helenus.map(draft.getEntityClass(), draft.toMap(draftMap));
} else {
return (E) resultSet;
}
}
public UpdateOperation<T> usingTtl(int ttl) {
public UpdateOperation<E> usingTtl(int ttl) {
this.ttl = new int[1];
this.ttl[0] = ttl;
return this;
}
public UpdateOperation<T> usingTimestamp(long timestamp) {
public UpdateOperation<E> usingTimestamp(long timestamp) {
this.timestamp = new long[1];
this.timestamp[0] = timestamp;
return this;

View file

@ -6,8 +6,6 @@ import java.util.Set;
public interface Drafted<T> extends MapExportable {
HelenusEntity getEntity();
Set<String> mutated();
T build();

View file

@ -15,8 +15,10 @@
*/
package net.helenus.mapping.value;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import net.helenus.mapping.HelenusProperty;
import net.helenus.support.HelenusException;
import net.helenus.support.HelenusMappingException;
public enum BeanColumnValueProvider implements ColumnValueProvider {
@ -30,6 +32,10 @@ public enum BeanColumnValueProvider implements ColumnValueProvider {
Object value = null;
try {
value = getter.invoke(bean, new Object[] {});
} catch (InvocationTargetException e) {
if (e.getCause() != null ) {
throw new HelenusException("getter threw an exception", e.getCause());
}
} catch (ReflectiveOperationException e) {
throw new HelenusMappingException("fail to call getter " + getter, e);
} catch (IllegalArgumentException e) {

View file

@ -0,0 +1,93 @@
/*
* Copyright (C) 2015 The Helenus Authors
*
* Licensed 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 net.helenus.test.integration.core.draft;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import net.helenus.core.Helenus;
import net.helenus.core.HelenusSession;
import net.helenus.test.integration.build.AbstractEmbeddedCassandraTest;
import static net.helenus.core.Query.eq;
public class EntityDraftBuilderTest extends AbstractEmbeddedCassandraTest {
static Supply supply;
static HelenusSession session;
@BeforeClass
public static void beforeTest() {
session = Helenus.init(getSession())
.showCql()
.add(Supply.class)
.autoCreateDrop()
.get();
supply = session.dsl(Supply.class);
}
@Test
public void testFoo() throws Exception {
Supply.Draft draft = null;
draft = Supply.draft("APAC")
.code("WIDGET-002")
.description("Our second Widget!")
.demand(new HashMap<String, Long>() {{
put("APAC", 100L);
put("EMEA", 10000L);
put("NORAM", 2000000L);
}})
.shipments(new HashSet<String>() {{
add("HMS Puddle in transit to APAC, 100 units.");
add("Frigate Jimmy in transit to EMEA, 10000 units.");
}})
.suppliers(new ArrayList<String>() {{
add("Puddle, Inc.");
add("Jimmy Town, LTD.");
}});
Supply s1 = session.<Supply>insert(draft)
.sync();
// List
Supply s2 = session.<Supply>update(s1.update())
.prepend(supply::suppliers, "Pignose Supply, LLC.")
.sync();
Assert.assertEquals(s2.suppliers().get(0), "Pignose Supply, LLC.");
// Set
String shipment = "Pignose, on the way! (1M units)";
Supply s3 = session.<Supply>update(s2.update())
.add(supply::shipments, shipment)
.sync();
Assert.assertTrue(s3.shipments().contains(shipment));
// Map
Supply s4 = session.<Supply>update(s3.update())
.put(supply::demand, "NORAM", 10L)
.sync();
Assert.assertEquals((long)s4.demand().get("NORAM"), 10L);
}
}

View file

@ -0,0 +1,73 @@
package net.helenus.test.integration.core.draft;
import java.util.UUID;
import net.helenus.core.AbstractAuditedEntityDraft;
import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.annotation.*;
@Table
public interface Inventory {
@PartitionKey UUID id();
@Column("emea") @Types.Counter long EMEA();
@Column("noram") @Types.Counter long NORAM();
@Column("apac") @Types.Counter long APAC();
@Transient static Draft draft(UUID id) { return new Draft(id); }
@Transient default Draft update() { return new Draft(this); }
class Draft extends AbstractAuditedEntityDraft<Inventory> {
// Entity/Draft pattern-enabling methods:
Draft(UUID id) {
super(null, null);
// Primary Key:
set("id", id);
}
Draft(Inventory inventory) {
super((MapExportable) inventory, null);
}
public Class<Inventory> getEntityClass() { return Inventory.class; }
// Immutable properties:
public UUID id() {
return this.<UUID>get("id");
}
public long EMEA() {
return this.<Long>get("EMEA", long.class);
}
public Draft EMEA(long count) {
mutate("EMEA", count);
return this;
}
public long APAC() {
return this.<Long>get("APAC", long.class);
}
public Draft APAC(long count) {
mutate("APAC", count);
return this;
}
public long NORAM() {
return this.<Long>get("NORAM", long.class);
}
public Draft NORAM(long count) {
mutate("NORAM", count);
return this;
}
}
}

View file

@ -0,0 +1,125 @@
package net.helenus.test.integration.core.draft;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.datastax.driver.core.utils.UUIDs;
import net.helenus.core.AbstractEntityDraft;
import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.annotation.*;
@Table
public interface Supply {
@PartitionKey UUID id();
@ClusteringColumn(ordinal=0) default String region() { return "NORAM"; }
@Index(caseSensitive = false) String code();
@Index String description(); // @IndexText == lucene index
@Index Map<String, Long> demand();
@Index List<String> suppliers();
@Index Set<String> shipments();
@Transient static Draft draft(String region) { return new Draft(region); }
@Transient default Draft update() { return new Draft(this); }
class Draft extends AbstractEntityDraft<Supply> {
// Entity/Draft pattern-enabling methods:
Draft(String region) {
super(null);
// Primary Key:
set("id", UUIDs.timeBased());
set("region", region);
}
Draft(Supply supply) {
super((MapExportable) supply);
}
public Class<Supply> getEntityClass() { return Supply.class; }
// Immutable properties:
public UUID id() {
return this.<UUID>get("id");
}
public String region() {
return this.<String>get("region");
}
// Mutable properties:
public String code() {
return this.<String>get("code");
}
public Draft code(String code) {
mutate("code", code);
return this;
}
public Draft setCode(String code) {
return code(code);
}
public String description() {
return this.<String>get("description");
}
public Draft description(String description) {
mutate("description", description);
return this;
}
public Draft setDescription(String description) {
return description(description);
}
public Map<String, Long> demand() {
return this.<Map<String, Long>>get("demand");
}
public Draft demand(Map<String, Long> demand) {
mutate("demand", demand);
return this;
}
public Draft setDemand(Map<String, Long> demand) {
return demand(demand);
}
public List<String> suppliers() {
return this.<List<String>>get("suppliers");
}
public Draft suppliers(List<String> suppliers) {
mutate("suppliers", suppliers);
return this;
}
public Draft setSuppliers(List<String> suppliers) {
return suppliers(suppliers);
}
public Set<String> shipments() {
return this.<Set<String>>get("shipments");
}
public Draft shipments(Set<String> shipments) {
mutate("shipments", shipments);
return this;
}
public Draft setshipments(Set<String> shipments) {
return shipments(shipments);
}
}
}

View file

@ -170,34 +170,6 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest {
// UPDATE
session.update(new Drafted<User>() {
@Override
public HelenusEntity getEntity() { return Helenus.entity(User.class); }
@Override
public Map<String, Object> toMap() {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("name", "joeseph");
map.put("age", Integer.valueOf(45));
map.put("id", 100L);
return map;
}
@Override
public Set<String> mutated() {
Set<String> set = new HashSet<String>();
set.add("name");
set.add("age");
return set;
}
@Override
public User build() {
return null;
}
}).sync();
session
.update(user::name, "albert")
.set(user::age, 35)
@ -218,15 +190,15 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest {
Assert.assertEquals("_albert", name);
User u = session.<User>select(user)
User u2 = session.<User>select(user)
.where(user::id, eq(100L))
.single()
.sync()
.orElse(null);
Assert.assertEquals(Long.valueOf(100L), u.id());
Assert.assertEquals("albert", u.name());
Assert.assertEquals(Integer.valueOf(35), u.age());
Assert.assertEquals(Long.valueOf(100L), u2.id());
Assert.assertEquals("albert", u2.name());
Assert.assertEquals(Integer.valueOf(35), u2.age());
//
User greg =
@ -253,6 +225,7 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest {
.set(user::age, null)
.set(user::type, null)
.where(user::id, eq(100L))
.zipkinContext(null)
.sync();
Fun.Tuple3<String, Integer, UserType> tuple =

View file

@ -42,9 +42,6 @@ public interface Account {
class Draft implements Drafted { //TODO
@Override
public HelenusEntity getEntity() { return null; }
@Override
public Set<String> mutated() {
return null;