More caching fixes.
This commit is contained in:
parent
b04e033bf4
commit
7b14eda9b3
15 changed files with 555 additions and 284 deletions
|
@ -1,9 +1,7 @@
|
|||
package net.helenus.core;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
import com.google.common.primitives.Primitives;
|
||||
|
||||
|
@ -11,6 +9,7 @@ import net.helenus.core.reflect.DefaultPrimitiveTypes;
|
|||
import net.helenus.core.reflect.Drafted;
|
||||
import net.helenus.core.reflect.MapExportable;
|
||||
import net.helenus.mapping.MappingUtil;
|
||||
import org.apache.commons.lang3.SerializationUtils;
|
||||
|
||||
public abstract class AbstractEntityDraft<E> implements Drafted<E> {
|
||||
|
||||
|
@ -30,38 +29,49 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T get(Getter<T> getter, Class<?> returnType) {
|
||||
public <T> T get(Getter<T> getter, Class<?> returnType) {
|
||||
return (T) get(this.<T>methodNameFor(getter), returnType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T get(String key, Class<?> returnType) {
|
||||
public <T> T get(String key, Class<?> returnType) {
|
||||
T value = (T) backingMap.get(key);
|
||||
|
||||
if (value == null) {
|
||||
value = (T) entityMap.get(key);
|
||||
if (value == null) {
|
||||
value = (T) entityMap.get(key);
|
||||
if (value == null) {
|
||||
|
||||
if (Primitives.allPrimitiveTypes().contains(returnType)) {
|
||||
if (Primitives.allPrimitiveTypes().contains(returnType)) {
|
||||
|
||||
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
|
||||
if (type == null) {
|
||||
throw new RuntimeException("unknown primitive type " + returnType);
|
||||
}
|
||||
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
|
||||
if (type == null) {
|
||||
throw new RuntimeException("unknown primitive type " + returnType);
|
||||
}
|
||||
|
||||
return (T) type.getDefaultValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return (T) type.getDefaultValue();
|
||||
}
|
||||
} else {
|
||||
// Collections fetched from the entityMap
|
||||
if (value instanceof Collection) {
|
||||
try {
|
||||
value = MappingUtil.<T>clone(value);
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
//TODO(gburd): deep?shallow? copy of List, Map, Set to a mutable collection.
|
||||
value = (T)SerializationUtils.<Serializable>clone((Serializable)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
protected <T> Object set(Getter<T> getter, Object value) {
|
||||
public <T> Object set(Getter<T> getter, Object value) {
|
||||
return set(this.<T>methodNameFor(getter), value);
|
||||
}
|
||||
|
||||
protected Object set(String key, Object value) {
|
||||
public Object set(String key, Object value) {
|
||||
if (key == null || value == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -71,11 +81,11 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T mutate(Getter<T> getter, T value) {
|
||||
public <T> T mutate(Getter<T> getter, T value) {
|
||||
return (T) mutate(this.<T>methodNameFor(getter), value);
|
||||
}
|
||||
|
||||
protected Object mutate(String key, Object value) {
|
||||
public Object mutate(String key, Object value) {
|
||||
Objects.requireNonNull(key);
|
||||
|
||||
if (value == null) {
|
||||
|
|
|
@ -240,49 +240,48 @@ public final class HelenusSession extends AbstractSessionOperations implements C
|
|||
|
||||
@Override
|
||||
public void mergeCache(Table<String, String, Either<Object, List<Facet>>> uowCache) {
|
||||
List<Either<Object, List<Facet>>> items = uowCache.values().stream().distinct().collect(Collectors.toList());
|
||||
for (Either<Object, List<Facet>> item : items) {
|
||||
if (item.isRight()) {
|
||||
List<Facet> facets = item.getRight();
|
||||
String tableName = CacheUtil.schemaName(facets);
|
||||
List<String[]> combinations = CacheUtil.flattenFacets(facets);
|
||||
for (String[] combination : combinations) {
|
||||
String cacheKey = tableName + "." + Arrays.toString(combination);
|
||||
sessionCache.invalidate(cacheKey);
|
||||
}
|
||||
} else {
|
||||
Object pojo = item.getLeft();
|
||||
HelenusEntity entity = Helenus.resolve(MappingUtil.getMappingInterface(pojo));
|
||||
Map<String, Object> valueMap = pojo instanceof MapExportable ? ((MapExportable) pojo).toMap() : null;
|
||||
if (entity.isCacheable()) {
|
||||
List<Facet> boundFacets = new ArrayList<>();
|
||||
for (Facet facet : entity.getFacets()) {
|
||||
if (facet instanceof UnboundFacet) {
|
||||
UnboundFacet unboundFacet = (UnboundFacet) facet;
|
||||
UnboundFacet.Binder binder = unboundFacet.binder();
|
||||
unboundFacet.getProperties().forEach(prop -> {
|
||||
if (valueMap == null) {
|
||||
Object value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop,
|
||||
false);
|
||||
binder.setValueForProperty(prop, value.toString());
|
||||
} else {
|
||||
binder.setValueForProperty(prop, valueMap.get(prop.getPropertyName()).toString());
|
||||
}
|
||||
});
|
||||
if (binder.isBound()) {
|
||||
boundFacets.add(binder.bind());
|
||||
}
|
||||
} else {
|
||||
boundFacets.add(facet);
|
||||
}
|
||||
}
|
||||
// NOTE: should equal `String tableName = CacheUtil.schemaName(facets);`
|
||||
List<String[]> facetCombinations = CacheUtil.flattenFacets(boundFacets);
|
||||
String tableName = CacheUtil.schemaName(boundFacets);
|
||||
mergeAndUpdateCacheValues(pojo, tableName, facetCombinations);
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Object> items = uowCache.values().stream().filter(Either::isLeft).map(Either::getLeft).distinct().collect(Collectors.toList());
|
||||
for (Object pojo : items) {
|
||||
HelenusEntity entity = Helenus.resolve(MappingUtil.getMappingInterface(pojo));
|
||||
Map<String, Object> valueMap = pojo instanceof MapExportable ? ((MapExportable) pojo).toMap() : null;
|
||||
if (entity.isCacheable()) {
|
||||
List<Facet> boundFacets = new ArrayList<>();
|
||||
for (Facet facet : entity.getFacets()) {
|
||||
if (facet instanceof UnboundFacet) {
|
||||
UnboundFacet unboundFacet = (UnboundFacet) facet;
|
||||
UnboundFacet.Binder binder = unboundFacet.binder();
|
||||
unboundFacet.getProperties().forEach(prop -> {
|
||||
if (valueMap == null) {
|
||||
Object value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop);
|
||||
binder.setValueForProperty(prop, value.toString());
|
||||
} else {
|
||||
binder.setValueForProperty(prop, valueMap.get(prop.getPropertyName()).toString());
|
||||
}
|
||||
});
|
||||
if (binder.isBound()) {
|
||||
boundFacets.add(binder.bind());
|
||||
}
|
||||
} else {
|
||||
boundFacets.add(facet);
|
||||
}
|
||||
}
|
||||
// NOTE: should equal `String tableName = CacheUtil.schemaName(facets);`
|
||||
List<String[]> facetCombinations = CacheUtil.flattenFacets(boundFacets);
|
||||
String tableName = CacheUtil.schemaName(boundFacets);
|
||||
mergeAndUpdateCacheValues(pojo, tableName, facetCombinations);
|
||||
}
|
||||
}
|
||||
|
||||
List<List<Facet>> deletedFacetSets = uowCache.values().stream().filter(Either::isRight).map(Either::getRight).collect(
|
||||
Collectors.toList());
|
||||
for (List<Facet> facets : deletedFacetSets) {
|
||||
String tableName = CacheUtil.schemaName(facets);
|
||||
List<String[]> combinations = CacheUtil.flattenFacets(facets);
|
||||
for (String[] combination : combinations) {
|
||||
String cacheKey = tableName + "." + Arrays.toString(combination);
|
||||
sessionCache.invalidate(cacheKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeAndUpdateCacheValues(Object pojo, String tableName, List<String[]> facetCombinations) {
|
||||
|
@ -503,13 +502,21 @@ public final class HelenusSession extends AbstractSessionOperations implements C
|
|||
return new UpdateOperation<ResultSet>(this);
|
||||
}
|
||||
|
||||
public <E> UpdateOperation<E> update(Object pojo) {
|
||||
if (pojo instanceof MapExportable == false) {
|
||||
throw new HelenusMappingException(
|
||||
"update of objects that don't implement MapExportable is not yet supported");
|
||||
}
|
||||
return new UpdateOperation<E>(this, pojo);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
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);
|
||||
UpdateOperation update = new UpdateOperation<E>(this, draft);
|
||||
Map<String, Object> map = draft.toMap();
|
||||
Set<String> mutatedProperties = draft.mutated();
|
||||
HelenusEntity entity = Helenus.entity(draft.getEntityClass());
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package net.helenus.core.cache;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -23,7 +24,13 @@ import net.helenus.mapping.HelenusProperty;
|
|||
public class BoundFacet extends Facet<String> {
|
||||
private final Map<HelenusProperty, Object> properties;
|
||||
|
||||
BoundFacet(String name, Map<HelenusProperty, Object> properties) {
|
||||
public BoundFacet(HelenusProperty property, Object value) {
|
||||
super(property.getPropertyName(), value == null ? null : value.toString());
|
||||
this.properties = new HashMap<HelenusProperty, Object>(1);
|
||||
this.properties.put(property, value);
|
||||
}
|
||||
|
||||
public BoundFacet(String name, Map<HelenusProperty, Object> properties) {
|
||||
super(name,
|
||||
(properties.keySet().size() > 1)
|
||||
? "[" + String.join(", ",
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
package net.helenus.core.cache;
|
||||
|
||||
import net.helenus.core.Helenus;
|
||||
import net.helenus.core.reflect.MapExportable;
|
||||
import net.helenus.mapping.HelenusEntity;
|
||||
import net.helenus.mapping.MappingUtil;
|
||||
import net.helenus.mapping.value.BeanColumnValueProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CacheUtil {
|
||||
|
@ -38,7 +45,22 @@ public class CacheUtil {
|
|||
}
|
||||
|
||||
public static Object merge(Object to, Object from) {
|
||||
return to; // TODO(gburd): yeah...
|
||||
if (to == from) {
|
||||
return to;
|
||||
}
|
||||
|
||||
//TODO(gburd): take ttl and writeTime into account when merging.
|
||||
Map<String, Object> toValueMap = to instanceof MapExportable ? ((MapExportable) to).toMap() : null;
|
||||
Map<String, Object> fromValueMap = to instanceof MapExportable ? ((MapExportable) from).toMap() : null;
|
||||
|
||||
if (toValueMap != null && fromValueMap != null) {
|
||||
for (String key : fromValueMap.keySet()) {
|
||||
if (toValueMap.containsKey(key) && toValueMap.get(key) != fromValueMap.get(key)) {
|
||||
toValueMap.put(key, fromValueMap.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
public static String schemaName(List<Facet> facets) {
|
||||
|
|
|
@ -15,10 +15,13 @@
|
|||
*/
|
||||
package net.helenus.core.operation;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import net.helenus.core.*;
|
||||
import net.helenus.core.cache.Facet;
|
||||
import net.helenus.core.cache.UnboundFacet;
|
||||
import net.helenus.mapping.HelenusEntity;
|
||||
import net.helenus.mapping.HelenusProperty;
|
||||
|
||||
public abstract class AbstractFilterOperation<E, O extends AbstractFilterOperation<E, O>>
|
||||
extends
|
||||
|
@ -108,4 +111,38 @@ public abstract class AbstractFilterOperation<E, O extends AbstractFilterOperati
|
|||
ifFilters.add(filter);
|
||||
}
|
||||
|
||||
protected List<Facet> bindFacetValues(List<Facet> facets) {
|
||||
if (facets == null) {
|
||||
return new ArrayList<Facet>();
|
||||
}
|
||||
List<Facet> boundFacets = new ArrayList<>();
|
||||
Map<HelenusProperty, Filter> filterMap = new HashMap<>(filters.size());
|
||||
filters.forEach(f -> filterMap.put(f.getNode().getProperty(), f));
|
||||
|
||||
for (Facet facet : facets) {
|
||||
if (facet instanceof UnboundFacet) {
|
||||
UnboundFacet unboundFacet = (UnboundFacet) facet;
|
||||
UnboundFacet.Binder binder = unboundFacet.binder();
|
||||
if (filters != null) {
|
||||
for (HelenusProperty prop : unboundFacet.getProperties()) {
|
||||
|
||||
Filter filter = filterMap.get(prop);
|
||||
if (filter != null) {
|
||||
Object[] postulates = filter.postulateValues();
|
||||
for (Object p : postulates) {
|
||||
binder.setValueForProperty(prop, p.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (binder.isBound()) {
|
||||
boundFacets.add(binder.bind());
|
||||
}
|
||||
} else {
|
||||
boundFacets.add(facet);
|
||||
}
|
||||
}
|
||||
return boundFacets;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,9 +66,9 @@ public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOpe
|
|||
try {
|
||||
Optional<E> result = Optional.empty();
|
||||
E cacheResult = null;
|
||||
boolean updateCache = isSessionCacheable();
|
||||
boolean updateCache = isSessionCacheable() && checkCache;
|
||||
|
||||
if (enableCache && isSessionCacheable()) {
|
||||
if (checkCache && isSessionCacheable()) {
|
||||
List<Facet> facets = bindFacetValues();
|
||||
String tableName = CacheUtil.schemaName(facets);
|
||||
cacheResult = (E) sessionOps.checkCache(tableName, facets);
|
||||
|
@ -115,7 +115,7 @@ public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOpe
|
|||
E cachedResult = null;
|
||||
final boolean updateCache;
|
||||
|
||||
if (enableCache) {
|
||||
if (checkCache) {
|
||||
Stopwatch timer = Stopwatch.createStarted();
|
||||
try {
|
||||
List<Facet> facets = bindFacetValues();
|
||||
|
|
|
@ -45,7 +45,7 @@ import net.helenus.support.HelenusException;
|
|||
|
||||
public abstract class AbstractStatementOperation<E, O extends AbstractStatementOperation<E, O>> extends Operation<E> {
|
||||
|
||||
protected boolean enableCache = true;
|
||||
protected boolean checkCache = true;
|
||||
protected boolean showValues = true;
|
||||
protected TraceContext traceContext;
|
||||
long queryExecutionTimeout = 10;
|
||||
|
@ -66,13 +66,13 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
|
|||
|
||||
public abstract Statement buildStatement(boolean cached);
|
||||
|
||||
public O ignoreCache(boolean enabled) {
|
||||
enableCache = enabled;
|
||||
public O uncached(boolean enabled) {
|
||||
checkCache = enabled;
|
||||
return (O) this;
|
||||
}
|
||||
|
||||
public O ignoreCache() {
|
||||
enableCache = true;
|
||||
public O uncached() {
|
||||
checkCache = false;
|
||||
return (O) this;
|
||||
}
|
||||
|
||||
|
@ -333,10 +333,10 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
|
|||
List<Facet> facets = new ArrayList<>();
|
||||
Map<String, Object> valueMap = pojo instanceof MapExportable ? ((MapExportable) pojo).toMap() : null;
|
||||
|
||||
for (Facet facet : identifyingFacets) {
|
||||
if (facet instanceof UnboundFacet) {
|
||||
UnboundFacet unboundFacet = (UnboundFacet) facet;
|
||||
UnboundFacet.Binder binder = unboundFacet.binder();
|
||||
for (Facet facet : identifyingFacets) {
|
||||
if (facet instanceof UnboundFacet) {
|
||||
UnboundFacet unboundFacet = (UnboundFacet) facet;
|
||||
UnboundFacet.Binder binder = unboundFacet.binder();
|
||||
for (HelenusProperty prop : unboundFacet.getProperties()) {
|
||||
Object value;
|
||||
if (valueMap == null) {
|
||||
|
|
|
@ -69,7 +69,7 @@ public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperati
|
|||
E cacheResult = null;
|
||||
boolean updateCache = isSessionCacheable();
|
||||
|
||||
if (enableCache && isSessionCacheable()) {
|
||||
if (checkCache && isSessionCacheable()) {
|
||||
List<Facet> facets = bindFacetValues();
|
||||
String tableName = CacheUtil.schemaName(facets);
|
||||
cacheResult = (E) sessionOps.checkCache(tableName, facets);
|
||||
|
@ -121,7 +121,7 @@ public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperati
|
|||
E cachedResult = null;
|
||||
final boolean updateCache;
|
||||
|
||||
if (enableCache) {
|
||||
if (checkCache) {
|
||||
Stopwatch timer = Stopwatch.createStarted();
|
||||
try {
|
||||
List<Facet> facets = bindFacetValues();
|
||||
|
|
|
@ -133,40 +133,9 @@ public final class DeleteOperation extends AbstractFilterOperation<ResultSet, De
|
|||
}
|
||||
}
|
||||
|
||||
public List<Facet> bindFacetValues(List<Facet> facets) {
|
||||
if (facets == null) {
|
||||
return new ArrayList<Facet>();
|
||||
}
|
||||
List<Facet> boundFacets = new ArrayList<>();
|
||||
Map<HelenusProperty, Filter> filterMap = new HashMap<>(filters.size());
|
||||
filters.forEach(f -> filterMap.put(f.getNode().getProperty(), f));
|
||||
|
||||
for (Facet facet : facets) {
|
||||
if (facet instanceof UnboundFacet) {
|
||||
UnboundFacet unboundFacet = (UnboundFacet) facet;
|
||||
UnboundFacet.Binder binder = unboundFacet.binder();
|
||||
if (filters != null) {
|
||||
for (HelenusProperty prop : unboundFacet.getProperties()) {
|
||||
|
||||
Filter filter = filterMap.get(prop);
|
||||
if (filter != null) {
|
||||
Object[] postulates = filter.postulateValues();
|
||||
for (Object p : postulates) {
|
||||
binder.setValueForProperty(prop, p.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (binder.isBound()) {
|
||||
boundFacets.add(binder.bind());
|
||||
}
|
||||
} else {
|
||||
boundFacets.add(facet);
|
||||
}
|
||||
}
|
||||
return boundFacets;
|
||||
}
|
||||
public List<Facet> bindFacetValues() {
|
||||
return bindFacetValues(getFacets());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet sync() throws TimeoutException {
|
||||
|
@ -177,20 +146,19 @@ public final class DeleteOperation extends AbstractFilterOperation<ResultSet, De
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Facet> getFacets() {
|
||||
return entity.getFacets();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet sync(UnitOfWork uow) throws TimeoutException {
|
||||
if (uow == null) {
|
||||
return sync();
|
||||
}
|
||||
ResultSet result = super.sync(uow);
|
||||
List<Facet> facets = getFacets();
|
||||
uow.cacheEvict(bindFacetValues(facets));
|
||||
uow.cacheEvict(bindFacetValues());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Facet> getFacets() {
|
||||
return entity.getFacets();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public final class InsertOperation<T> extends AbstractOperation<T, InsertOperati
|
|||
|
||||
private final List<Fun.Tuple2<HelenusPropertyNode, Object>> values = new ArrayList<Fun.Tuple2<HelenusPropertyNode, Object>>();
|
||||
private final T pojo;
|
||||
private final Class<?> resultType;
|
||||
private final Class<?> resultType;
|
||||
private HelenusEntity entity;
|
||||
private boolean ifNotExists;
|
||||
|
||||
|
@ -56,7 +56,7 @@ public final class InsertOperation<T> extends AbstractOperation<T, InsertOperati
|
|||
|
||||
this.ifNotExists = ifNotExists;
|
||||
this.pojo = null;
|
||||
this.resultType = ResultSet.class;
|
||||
this.resultType = ResultSet.class;
|
||||
}
|
||||
|
||||
public InsertOperation(AbstractSessionOperations sessionOperations, Class<?> resultType, boolean ifNotExists) {
|
||||
|
@ -251,10 +251,14 @@ public final class InsertOperation<T> extends AbstractOperation<T, InsertOperati
|
|||
return sync();
|
||||
}
|
||||
T result = super.sync(uow);
|
||||
Class<?> iface = entity.getMappingInterface();
|
||||
if (resultType == iface) {
|
||||
Class<?> iface = entity.getMappingInterface();
|
||||
if (resultType == iface) {
|
||||
cacheUpdate(uow, result, entity.getFacets());
|
||||
}
|
||||
} else {
|
||||
if (entity.isCacheable()) {
|
||||
sessionOps.cacheEvict(bindFacetValues());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -207,7 +207,7 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
|
|||
if (facet instanceof UnboundFacet) {
|
||||
UnboundFacet unboundFacet = (UnboundFacet) facet;
|
||||
UnboundFacet.Binder binder = unboundFacet.binder();
|
||||
for (HelenusProperty prop : unboundFacet.getProperties()) {
|
||||
for (HelenusProperty prop : unboundFacet.getProperties()) {
|
||||
if (filters != null) {
|
||||
Filter filter = filters.get(prop);
|
||||
if (filter != null) {
|
||||
|
|
|
@ -18,6 +18,7 @@ package net.helenus.core.operation;
|
|||
import java.util.*;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.datastax.driver.core.ResultSet;
|
||||
import com.datastax.driver.core.querybuilder.Assignment;
|
||||
|
@ -26,20 +27,25 @@ import com.datastax.driver.core.querybuilder.QueryBuilder;
|
|||
import com.datastax.driver.core.querybuilder.Update;
|
||||
|
||||
import net.helenus.core.*;
|
||||
import net.helenus.core.cache.BoundFacet;
|
||||
import net.helenus.core.cache.Facet;
|
||||
import net.helenus.core.cache.UnboundFacet;
|
||||
import net.helenus.core.reflect.HelenusPropertyNode;
|
||||
import net.helenus.core.reflect.MapExportable;
|
||||
import net.helenus.mapping.HelenusEntity;
|
||||
import net.helenus.mapping.HelenusProperty;
|
||||
import net.helenus.mapping.MappingUtil;
|
||||
import net.helenus.mapping.value.BeanColumnValueProvider;
|
||||
import net.helenus.support.HelenusMappingException;
|
||||
import net.helenus.support.Immutables;
|
||||
|
||||
public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateOperation<E>> {
|
||||
|
||||
private final List<Assignment> assignments = new ArrayList<Assignment>();
|
||||
private final Map<Assignment, BoundFacet> assignments = new HashMap<>();
|
||||
private final AbstractEntityDraft<E> draft;
|
||||
private final Map<String, Object> draftMap;
|
||||
private HelenusEntity entity = null;
|
||||
private Object pojo;
|
||||
private int[] ttl;
|
||||
private long[] timestamp;
|
||||
|
||||
|
@ -55,24 +61,47 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
this.draftMap = draft.toMap();
|
||||
}
|
||||
|
||||
public UpdateOperation(AbstractSessionOperations sessionOperations, HelenusPropertyNode p, Object v) {
|
||||
super(sessionOperations);
|
||||
this.draft = null;
|
||||
this.draftMap = null;
|
||||
public UpdateOperation(AbstractSessionOperations sessionOperations, Object pojo) {
|
||||
super(sessionOperations);
|
||||
this.draft = null;
|
||||
this.draftMap = null;
|
||||
this.pojo = pojo;
|
||||
}
|
||||
|
||||
Object value = sessionOps.getValuePreparer().prepareColumnValue(v, p.getProperty());
|
||||
assignments.add(QueryBuilder.set(p.getColumnName(), value));
|
||||
public UpdateOperation(AbstractSessionOperations sessionOperations, HelenusPropertyNode p, Object v) {
|
||||
super(sessionOperations);
|
||||
this.draft = null;
|
||||
this.draftMap = null;
|
||||
|
||||
addPropertyNode(p);
|
||||
}
|
||||
Object value = sessionOps.getValuePreparer().prepareColumnValue(v, p.getProperty());
|
||||
assignments.put(QueryBuilder.set(p.getColumnName(), value), new BoundFacet(p.getProperty(), v));
|
||||
|
||||
public <V> UpdateOperation<E> set(Getter<V> getter, V v) {
|
||||
addPropertyNode(p);
|
||||
}
|
||||
|
||||
public <V> UpdateOperation<E> set(Getter<V> getter, V v) {
|
||||
Objects.requireNonNull(getter, "getter is empty");
|
||||
|
||||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter);
|
||||
HelenusProperty prop = p.getProperty();
|
||||
|
||||
Object value = sessionOps.getValuePreparer().prepareColumnValue(v, p.getProperty());
|
||||
assignments.add(QueryBuilder.set(p.getColumnName(), value));
|
||||
Object value = sessionOps.getValuePreparer().prepareColumnValue(v, prop);
|
||||
assignments.put(QueryBuilder.set(p.getColumnName(), value), new BoundFacet(prop, value));
|
||||
|
||||
if (draft != null) {
|
||||
String key = prop.getPropertyName();
|
||||
if (draft.get(key, value.getClass()) != value) {
|
||||
draft.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (pojo != null && pojo instanceof MapExportable) {
|
||||
String key = prop.getPropertyName();
|
||||
Map<String, Object> map = ((MapExportable)pojo).toMap();
|
||||
if (map.get(key) != value) {
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
|
@ -97,15 +126,20 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
|
||||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(counterGetter);
|
||||
|
||||
assignments.add(QueryBuilder.incr(p.getColumnName(), delta));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
HelenusProperty prop = p.getProperty();
|
||||
Long value = (Long)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop);
|
||||
facet = new BoundFacet(prop, value + delta);
|
||||
} else if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
draftMap.put(key, (Long) draftMap.get(key) + delta);
|
||||
}
|
||||
|
||||
assignments.put(QueryBuilder.incr(p.getColumnName(), delta), facet);
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
draftMap.put(key, (Long) draftMap.get(key) + delta);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -119,15 +153,20 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
|
||||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(counterGetter);
|
||||
|
||||
assignments.add(QueryBuilder.decr(p.getColumnName(), delta));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
HelenusProperty prop = p.getProperty();
|
||||
Long value = (Long) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop);
|
||||
facet = new BoundFacet(prop, value - delta);
|
||||
} else if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
draftMap.put(key, (Long) draftMap.get(key) - delta);
|
||||
}
|
||||
|
||||
assignments.put(QueryBuilder.decr(p.getColumnName(), delta), facet);
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
draftMap.put(key, (Long) draftMap.get(key) - delta);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -146,16 +185,22 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
|
||||
Object valueObj = prepareSingleListValue(p, value);
|
||||
|
||||
assignments.add(QueryBuilder.prepend(p.getColumnName(), valueObj));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
HelenusProperty prop = p.getProperty();
|
||||
List<V> list = new ArrayList<V>((List<V>)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
list.add(0, value);
|
||||
facet = new BoundFacet(prop, list);
|
||||
} else if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
List<V> list = (List<V>) draftMap.get(key);
|
||||
list.add(0, value);
|
||||
}
|
||||
|
||||
assignments.put(QueryBuilder.prepend(p.getColumnName(), valueObj), facet);
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
List<V> list = (List<V>) draftMap.get(key);
|
||||
list.add(0, value);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -167,16 +212,22 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
|
||||
List valueObj = prepareListValue(p, value);
|
||||
|
||||
assignments.add(QueryBuilder.prependAll(p.getColumnName(), valueObj));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
HelenusProperty prop = p.getProperty();
|
||||
List<V> list = new ArrayList<V>((List<V>)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
list.addAll(0, value);
|
||||
facet = new BoundFacet(prop, list);
|
||||
} else if (draft != null && value.size() > 0) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
List<V> list = (List<V>) draftMap.get(key);
|
||||
list.addAll(0, value);
|
||||
}
|
||||
|
||||
assignments.put(QueryBuilder.prependAll(p.getColumnName(), valueObj), facet);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -188,23 +239,31 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
|
||||
Object valueObj = prepareSingleListValue(p, value);
|
||||
|
||||
assignments.add(QueryBuilder.setIdx(p.getColumnName(), idx, valueObj));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null || draft != null) {
|
||||
List<V> list;
|
||||
HelenusProperty prop = p.getProperty();
|
||||
if (pojo != null) {
|
||||
list = new ArrayList<V>((List<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
} else {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
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);
|
||||
facet = new BoundFacet(prop, list);
|
||||
}
|
||||
|
||||
assignments.put(QueryBuilder.setIdx(p.getColumnName(), idx, valueObj), facet);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -216,16 +275,21 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
|
||||
Object valueObj = prepareSingleListValue(p, value);
|
||||
|
||||
assignments.add(QueryBuilder.append(p.getColumnName(), valueObj));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
HelenusProperty prop = p.getProperty();
|
||||
List<V> list = new ArrayList<V>((List<V>)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
list.add(value);
|
||||
facet = new BoundFacet(prop, list);
|
||||
} else if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
List<V> list = (List<V>) draftMap.get(key);
|
||||
list.add(value);
|
||||
}
|
||||
assignments.put(QueryBuilder.append(p.getColumnName(), valueObj), facet);
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
List<V> list = (List<V>) draftMap.get(key);
|
||||
list.add(value);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -237,16 +301,21 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
|
||||
List valueObj = prepareListValue(p, value);
|
||||
|
||||
assignments.add(QueryBuilder.appendAll(p.getColumnName(), valueObj));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
HelenusProperty prop = p.getProperty();
|
||||
List<V> list = new ArrayList<V>((List<V>)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
list.addAll(value);
|
||||
facet = new BoundFacet(prop, list);
|
||||
} else if (draft != null && value.size() > 0) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
List<V> list = (List<V>) draftMap.get(key);
|
||||
list.addAll(value);
|
||||
}
|
||||
assignments.put(QueryBuilder.appendAll(p.getColumnName(), valueObj), facet);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -258,16 +327,21 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
|
||||
Object valueObj = prepareSingleListValue(p, value);
|
||||
|
||||
assignments.add(QueryBuilder.discard(p.getColumnName(), valueObj));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
HelenusProperty prop = p.getProperty();
|
||||
List<V> list = new ArrayList<V>((List<V>)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
list.remove(value);
|
||||
facet = new BoundFacet(prop, list);
|
||||
} else if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
List<V> list = (List<V>) draftMap.get(key);
|
||||
list.remove(value);
|
||||
}
|
||||
assignments.put(QueryBuilder.discard(p.getColumnName(), valueObj), facet);
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
List<V> list = (List<V>) draftMap.get(key);
|
||||
list.remove(value);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -279,16 +353,21 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
|
||||
List valueObj = prepareListValue(p, value);
|
||||
|
||||
assignments.add(QueryBuilder.discardAll(p.getColumnName(), valueObj));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
HelenusProperty prop = p.getProperty();
|
||||
List<V> list = new ArrayList<V>((List<V>)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
list.removeAll(value);
|
||||
facet = new BoundFacet(prop, list);
|
||||
} else if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
List<V> list = (List<V>) draftMap.get(key);
|
||||
list.removeAll(value);
|
||||
}
|
||||
assignments.put(QueryBuilder.discardAll(p.getColumnName(), valueObj), facet);
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
List<V> list = (List<V>) draftMap.get(key);
|
||||
list.removeAll(value);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -336,16 +415,21 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(setGetter);
|
||||
Object valueObj = prepareSingleSetValue(p, value);
|
||||
|
||||
assignments.add(QueryBuilder.add(p.getColumnName(), valueObj));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
HelenusProperty prop = p.getProperty();
|
||||
Set<V> set = new HashSet<V>((Set<V>)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
set.add(value);
|
||||
facet = new BoundFacet(prop, set);
|
||||
} else if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
Set<V> set = (Set<V>) draftMap.get(key);
|
||||
set.add(value);
|
||||
}
|
||||
assignments.put(QueryBuilder.add(p.getColumnName(), valueObj), facet);
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
Set<V> set = (Set<V>) draftMap.get(key);
|
||||
set.add(value);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -357,16 +441,21 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(setGetter);
|
||||
Set valueObj = prepareSetValue(p, value);
|
||||
|
||||
assignments.add(QueryBuilder.addAll(p.getColumnName(), valueObj));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
HelenusProperty prop = p.getProperty();
|
||||
Set<V> set = new HashSet<V>((Set<V>)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
set.addAll(value);
|
||||
facet = new BoundFacet(prop, set);
|
||||
} else if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
Set<V> set = (Set<V>) draftMap.get(key);
|
||||
set.addAll(value);
|
||||
}
|
||||
assignments.put(QueryBuilder.addAll(p.getColumnName(), valueObj), facet);
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
Set<V> set = (Set<V>) draftMap.get(key);
|
||||
set.addAll(value);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -378,16 +467,21 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(setGetter);
|
||||
Object valueObj = prepareSingleSetValue(p, value);
|
||||
|
||||
assignments.add(QueryBuilder.remove(p.getColumnName(), valueObj));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
HelenusProperty prop = p.getProperty();
|
||||
Set<V> set = new HashSet<V>((Set<V>)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
set.remove(value);
|
||||
facet = new BoundFacet(prop, set);
|
||||
} else if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
Set<V> set = (Set<V>) draftMap.get(key);
|
||||
set.remove(value);
|
||||
}
|
||||
assignments.put(QueryBuilder.remove(p.getColumnName(), valueObj), facet);
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
Set<V> set = (Set<V>) draftMap.get(key);
|
||||
set.remove(value);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -399,16 +493,21 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(setGetter);
|
||||
Set valueObj = prepareSetValue(p, value);
|
||||
|
||||
assignments.add(QueryBuilder.removeAll(p.getColumnName(), valueObj));
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
HelenusProperty prop = p.getProperty();
|
||||
Set<V> set = new HashSet<V>((Set<V>)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
set.removeAll(value);
|
||||
facet = new BoundFacet(prop, set);
|
||||
} else if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
Set<V> set = (Set<V>) draftMap.get(key);
|
||||
set.removeAll(value);
|
||||
}
|
||||
assignments.put(QueryBuilder.removeAll(p.getColumnName(), valueObj), facet);
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
if (draft != null) {
|
||||
String key = p.getProperty().getPropertyName();
|
||||
Set<V> set = (Set<V>) draftMap.get(key);
|
||||
set.removeAll(value);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -455,23 +554,28 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(mapGetter);
|
||||
HelenusProperty prop = p.getProperty();
|
||||
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
Map<K, V> map = new HashMap<K, V>((Map<K, V>)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
map.put(key, value);
|
||||
facet = new BoundFacet(prop, map);
|
||||
} else if (draft != null) {
|
||||
((Map<K, V>) draftMap.get(prop.getPropertyName())).put(key, value);
|
||||
}
|
||||
|
||||
Optional<Function<Object, Object>> converter = prop.getWriteConverter(sessionOps.getSessionRepository());
|
||||
if (converter.isPresent()) {
|
||||
Map<Object, Object> convertedMap = (Map<Object, Object>) converter.get()
|
||||
.apply(Immutables.mapOf(key, value));
|
||||
for (Map.Entry<Object, Object> e : convertedMap.entrySet()) {
|
||||
assignments.add(QueryBuilder.put(p.getColumnName(), e.getKey(), e.getValue()));
|
||||
assignments.put(QueryBuilder.put(p.getColumnName(), e.getKey(), e.getValue()), facet);
|
||||
}
|
||||
} else {
|
||||
assignments.add(QueryBuilder.put(p.getColumnName(), key, value));
|
||||
assignments.put(QueryBuilder.put(p.getColumnName(), key, value), facet);
|
||||
}
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
if (draft != null) {
|
||||
((Map<K, V>) draftMap.get(prop.getPropertyName())).put(key, value);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -483,20 +587,25 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(mapGetter);
|
||||
HelenusProperty prop = p.getProperty();
|
||||
|
||||
BoundFacet facet = null;
|
||||
if (pojo != null) {
|
||||
Map<K, V> newMap = new HashMap<K, V>((Map<K, V>)BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop));
|
||||
newMap.putAll(map);
|
||||
facet = new BoundFacet(prop, newMap);
|
||||
} else if (draft != null) {
|
||||
((Map<K, V>) draftMap.get(prop.getPropertyName())).putAll(map);
|
||||
}
|
||||
|
||||
Optional<Function<Object, Object>> converter = prop.getWriteConverter(sessionOps.getSessionRepository());
|
||||
if (converter.isPresent()) {
|
||||
Map convertedMap = (Map) converter.get().apply(map);
|
||||
assignments.add(QueryBuilder.putAll(p.getColumnName(), convertedMap));
|
||||
assignments.put(QueryBuilder.putAll(p.getColumnName(), convertedMap), facet);
|
||||
} else {
|
||||
assignments.add(QueryBuilder.putAll(p.getColumnName(), map));
|
||||
assignments.put(QueryBuilder.putAll(p.getColumnName(), map), facet);
|
||||
}
|
||||
|
||||
addPropertyNode(p);
|
||||
|
||||
if (draft != null) {
|
||||
((Map<K, V>) draftMap.get(prop.getPropertyName())).putAll(map);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -509,7 +618,7 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
|
||||
Update update = QueryBuilder.update(entity.getName().toCql());
|
||||
|
||||
for (Assignment assignment : assignments) {
|
||||
for (Assignment assignment : assignments.keySet()) {
|
||||
update.with(assignment);
|
||||
}
|
||||
|
||||
|
@ -571,8 +680,14 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
@Override
|
||||
public E sync() throws TimeoutException {
|
||||
E result = super.sync();
|
||||
if (entity.isCacheable() && draft != null) {
|
||||
sessionOps.updateCache(result, getFacets());
|
||||
if (entity.isCacheable()) {
|
||||
if (draft != null) {
|
||||
sessionOps.updateCache(draft, bindFacetValues());
|
||||
} else if (pojo != null) {
|
||||
sessionOps.updateCache(pojo, bindFacetValues());
|
||||
} else {
|
||||
sessionOps.cacheEvict(bindFacetValues());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -584,12 +699,21 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
|
|||
}
|
||||
E result = super.sync(uow);
|
||||
if (draft != null) {
|
||||
cacheUpdate(uow, result, getFacets());
|
||||
}
|
||||
cacheUpdate(uow, result, bindFacetValues());
|
||||
} else if (pojo != null) {
|
||||
cacheUpdate(uow, (E)pojo, bindFacetValues());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public List<Facet> bindFacetValues() {
|
||||
List<Facet> facets = bindFacetValues(entity.getFacets());
|
||||
facets.addAll(assignments.values().stream().distinct().filter(o -> o != null).collect(Collectors.toList()));
|
||||
return facets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Facet> getFacets() {
|
||||
if (entity != null) {
|
||||
return entity.getFacets();
|
||||
|
|
|
@ -102,7 +102,8 @@ public class MapperInvocationHandler<E> implements InvocationHandler, Serializab
|
|||
}
|
||||
|
||||
if (MapExportable.TO_MAP_METHOD.equals(methodName)) {
|
||||
return Collections.unmodifiableMap(src);
|
||||
//return Collections.unmodifiableMap(src);
|
||||
return src;
|
||||
}
|
||||
|
||||
Object value = src.get(methodName);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package net.helenus.mapping;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -282,4 +283,43 @@ public final class MappingUtil {
|
|||
return e.getPropertyNode();
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/4882306/366692
|
||||
public static <T> T clone(T object)
|
||||
throws CloneNotSupportedException {
|
||||
Object clone = null;
|
||||
|
||||
// Use reflection, because there is no other way
|
||||
try {
|
||||
Method method = object.getClass().getMethod("clone");
|
||||
clone = method.invoke(object);
|
||||
} catch (InvocationTargetException e) {
|
||||
rethrow(e.getCause());
|
||||
} catch (Exception cause) {
|
||||
rethrow(cause);
|
||||
}
|
||||
if (object.getClass().isInstance(clone)) {
|
||||
@SuppressWarnings("unchecked") // clone class <= object class <= T
|
||||
T t = (T) clone;
|
||||
return t;
|
||||
} else {
|
||||
throw new ClassCastException(clone.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static void rethrow(Throwable cause)
|
||||
throws CloneNotSupportedException {
|
||||
if (cause instanceof RuntimeException) {
|
||||
throw (RuntimeException) cause;
|
||||
}
|
||||
if (cause instanceof Error) {
|
||||
throw (Error) cause;
|
||||
}
|
||||
if (cause instanceof CloneNotSupportedException) {
|
||||
throw (CloneNotSupportedException) cause;
|
||||
}
|
||||
CloneNotSupportedException e = new CloneNotSupportedException();
|
||||
e.initCause(cause);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,41 +140,92 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest {
|
|||
});
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void testSelectAfterDeleted() throws Exception {
|
||||
Widget w1, w2, w3, w4;
|
||||
UUID key = UUIDs.timeBased();
|
||||
|
||||
// This should inserted Widget, but not cache it.
|
||||
w1 = session.<Widget>insert(widget).value(widget::id, key).value(widget::name, RandomString.make(20)).sync();
|
||||
@Test
|
||||
public void testSelectAfterUpdated() throws Exception {
|
||||
Widget w1, w2, w3, w4, w5, w6;
|
||||
UUID key = UUIDs.timeBased();
|
||||
|
||||
try (UnitOfWork uow = session.begin()) {
|
||||
// This should inserted Widget, but not cache it.
|
||||
w1 = session.<Widget>insert(widget).value(widget::id, key).value(widget::name, RandomString.make(20)).sync();
|
||||
|
||||
// This should read from the database and return a Widget.
|
||||
w2 = session.<Widget>select(widget).where(widget::id, eq(key)).single()
|
||||
try (UnitOfWork uow = session.begin()) {
|
||||
|
||||
// This should read from the database and return a Widget.
|
||||
w2 = session.<Widget>select(widget).where(widget::id, eq(key)).single()
|
||||
.sync(uow).orElse(null);
|
||||
Assert.assertEquals(w1, w2);
|
||||
|
||||
// This should remove the object from the cache.
|
||||
session.delete(widget).where(widget::id, eq(key))
|
||||
// This should remove the object from the cache.
|
||||
//TODO(gburd): w3 = session.
|
||||
session.<Widget>update(w2)
|
||||
.set(widget::name, "Bill")
|
||||
.where(widget::id, eq(key))
|
||||
.sync(uow);
|
||||
|
||||
// This should fail to read from the cache.
|
||||
w3 = session.<Widget>select(widget).where(widget::id, eq(key)).single()
|
||||
// Fetch from session cache, should have old name.
|
||||
w4 = session.<Widget>select(widget).where(widget::id, eq(key)).single()
|
||||
.sync().orElse(null);
|
||||
Assert.assertEquals(w4, w2);
|
||||
Assert.assertEquals(w4.name(), w1.name());
|
||||
|
||||
// This should skip the cache.
|
||||
w5 = session.<Widget>select(widget).where(widget::id, eq(key)).single()
|
||||
.uncached()
|
||||
.sync().orElse(null);
|
||||
|
||||
Assert.assertNotEquals(w5, w2); // Not the same instance
|
||||
Assert.assertTrue(w2.equals(w5)); // But they have the same values
|
||||
Assert.assertFalse(w5.equals(w2)); // TODO(gburd): should also work
|
||||
Assert.assertEquals(w5.name(), "Bill");
|
||||
|
||||
uow.commit().andThen(() -> {
|
||||
Assert.assertEquals(w1, w2);
|
||||
});
|
||||
}
|
||||
|
||||
// The name changed, this should miss cache and not find anything in the database.
|
||||
w6 = session.<Widget>select(widget).where(widget::name, eq(w1.name())).single()
|
||||
.sync().orElse(null);
|
||||
Assert.assertTrue(w2.equals(w5));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSelectAfterDeleted() throws Exception {
|
||||
Widget w1, w2, w3, w4;
|
||||
UUID key = UUIDs.timeBased();
|
||||
|
||||
// This should inserted Widget, but not cache it.
|
||||
w1 = session.<Widget>insert(widget).value(widget::id, key).value(widget::name, RandomString.make(20)).sync();
|
||||
|
||||
try (UnitOfWork uow = session.begin()) {
|
||||
|
||||
// This should read from the database and return a Widget.
|
||||
w2 = session.<Widget>select(widget).where(widget::id, eq(key)).single()
|
||||
.sync(uow).orElse(null);
|
||||
|
||||
Assert.assertEquals(w3, null);
|
||||
// This should remove the object from the cache.
|
||||
session.delete(widget).where(widget::id, eq(key))
|
||||
.sync(uow);
|
||||
|
||||
uow.commit().andThen(() -> {
|
||||
Assert.assertEquals(w1, w2);
|
||||
Assert.assertEquals(w3, null);
|
||||
});
|
||||
}
|
||||
// This should fail to read from the cache.
|
||||
w3 = session.<Widget>select(widget).where(widget::id, eq(key)).single()
|
||||
.sync(uow).orElse(null);
|
||||
|
||||
w4 = session.<Widget>select(widget).where(widget::name, eq(w1.name())).single()
|
||||
Assert.assertEquals(w3, null);
|
||||
|
||||
uow.commit().andThen(() -> {
|
||||
Assert.assertEquals(w1, w2);
|
||||
Assert.assertEquals(w3, null);
|
||||
});
|
||||
}
|
||||
|
||||
w4 = session.<Widget>select(widget).where(widget::name, eq(w1.name())).single()
|
||||
.sync().orElse(null);
|
||||
|
||||
Assert.assertEquals(w4, null);
|
||||
}
|
||||
Assert.assertEquals(w4, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* @Test public void testSelectAfterInsertProperlyCachesEntity() throws
|
||||
|
@ -200,7 +251,7 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest {
|
|||
*
|
||||
* // This should read the widget from the database, no object identity but
|
||||
* values should match. w4 = session.<Widget>select(widget) .where(widget::id,
|
||||
* eq(key)) .ignoreCache() .single() .sync() .orElse(null);
|
||||
* eq(key)) .uncached() .single() .sync() .orElse(null);
|
||||
*
|
||||
* Assert.assertNotEquals(w1, w4); Assert.assertTrue(w1.equals(w4)); }
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue