diff --git a/src/main/java/net/helenus/core/AbstractSessionOperations.java b/src/main/java/net/helenus/core/AbstractSessionOperations.java index 85a3cd1..46d15e1 100644 --- a/src/main/java/net/helenus/core/AbstractSessionOperations.java +++ b/src/main/java/net/helenus/core/AbstractSessionOperations.java @@ -22,8 +22,8 @@ import com.datastax.driver.core.querybuilder.BuiltStatement; import com.google.common.util.concurrent.ListenableFuture; import java.io.PrintStream; import java.util.concurrent.Executor; -import net.helenus.core.operation.AbstractCache; -import net.helenus.core.operation.CacheManager; + +import net.helenus.core.operation.SessionCache; import net.helenus.mapping.value.ColumnValuePreparer; import net.helenus.mapping.value.ColumnValueProvider; import net.helenus.support.HelenusException; @@ -113,10 +113,6 @@ public abstract class AbstractSessionOperations { return null; } - public AbstractCache cacheFor(CacheManager.Type type) { - return null; - } - RuntimeException translateException(RuntimeException e) { if (e instanceof HelenusException) { return e; @@ -127,4 +123,7 @@ public abstract class AbstractSessionOperations { void printCql(String cql) { getPrintStream().println(cql); } + + public SessionCache getSessionCache() { return null; } + } diff --git a/src/main/java/net/helenus/core/HelenusSession.java b/src/main/java/net/helenus/core/HelenusSession.java index f3ab2f6..a086562 100644 --- a/src/main/java/net/helenus/core/HelenusSession.java +++ b/src/main/java/net/helenus/core/HelenusSession.java @@ -18,22 +18,10 @@ package net.helenus.core; import brave.Tracer; import com.codahale.metrics.MetricRegistry; import com.datastax.driver.core.*; -import com.diffplug.common.base.Errors; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import java.io.Closeable; -import java.io.PrintStream; -import java.lang.reflect.Method; -import java.util.*; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; import net.helenus.core.operation.*; import net.helenus.core.reflect.Drafted; -import net.helenus.core.reflect.DslExportable; import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.mapping.HelenusEntity; -import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.MappingUtil; import net.helenus.mapping.value.*; import net.helenus.support.DslPropertyException; @@ -41,11 +29,13 @@ import net.helenus.support.Fun; import net.helenus.support.Fun.Tuple1; import net.helenus.support.Fun.Tuple2; import net.helenus.support.Fun.Tuple6; +import net.helenus.support.HelenusMappingException; import java.io.Closeable; import java.io.PrintStream; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.Executor; import java.util.function.Function; @@ -72,7 +62,7 @@ public final class HelenusSession extends AbstractSessionOperations implements C private final RowColumnValueProvider valueProvider; private final StatementColumnValuePreparer valuePreparer; private final Metadata metadata; - private final CacheManager cacheManager; + private final SessionCache sessionCache; HelenusSession( Session session, @@ -103,7 +93,7 @@ public final class HelenusSession extends AbstractSessionOperations implements C this.valueProvider = new RowColumnValueProvider(this.sessionRepository); this.valuePreparer = new StatementColumnValuePreparer(this.sessionRepository); this.metadata = session.getCluster().getMetadata(); - this.cacheManager = new CacheManager(this); + this.sessionCache = new SessionCache(); } @Override @@ -190,20 +180,33 @@ public final class HelenusSession extends AbstractSessionOperations implements C return child.begin(); } - public SelectOperation select(Class entityClass) { + public SelectOperation select(E pojo) { + Objects.requireNonNull(pojo, "supplied object must be a dsl for a registered entity but cannot be null"); + ColumnValueProvider valueProvider = getValueProvider(); + HelenusEntity entity = Helenus.resolve(pojo); + Class entityClass = entity.getMappingInterface(); + return new SelectOperation( + this, + entity, + (r) -> { + Map map = new ValueProviderMap(r, valueProvider, entity); + return (E) Helenus.map(entityClass, map); + }); + } + + public SelectOperation select(Class entityClass) { Objects.requireNonNull(entityClass, "entityClass is empty"); ColumnValueProvider valueProvider = getValueProvider(); HelenusEntity entity = Helenus.entity(entityClass); - //TODO cache entity return new SelectOperation( - this, - entity, - (r) -> { - Map map = new ValueProviderMap(r, valueProvider, entity); - return (E) Helenus.map(entityClass, map); - }); + this, + entity, + (r) -> { + Map map = new ValueProviderMap(r, valueProvider, entity); + return (E) Helenus.map(entityClass, map); + }); } public SelectOperation select() { @@ -215,6 +218,12 @@ public final class HelenusSession extends AbstractSessionOperations implements C return new SelectOperation(this, Helenus.entity(entityClass)); } + public SelectOperation selectAll(E pojo) { + Objects.requireNonNull(pojo, "supplied object must be a dsl for a registered entity but cannot be null"); + HelenusEntity entity = Helenus.resolve(pojo); + return new SelectOperation(this, entity); + } + public SelectOperation selectAll(Class entityClass, Function rowMapper) { Objects.requireNonNull(entityClass, "entityClass is empty"); Objects.requireNonNull(rowMapper, "rowMapper is empty"); @@ -376,12 +385,12 @@ public final class HelenusSession extends AbstractSessionOperations implements C return new CountOperation(this, Helenus.resolve(dsl)); } - public UpdateOperation update() { - return new UpdateOperation(this); + public UpdateOperation update() { + return new UpdateOperation(this); } - public UpdateOperation update(Drafted draft) { - UpdateOperation update = new UpdateOperation(this); + public UpdateOperation update(Drafted draft) { + UpdateOperation update = new UpdateOperation(this, draft.build()); Map map = draft.toMap(); HelenusEntity entity = draft.getEntity(); Set mutatedProperties = draft.mutated(); @@ -427,28 +436,39 @@ public final class HelenusSession extends AbstractSessionOperations implements C return update; } - public UpdateOperation update(Getter getter, V v) { + public UpdateOperation update(Getter getter, V v) { Objects.requireNonNull(getter, "field is empty"); Objects.requireNonNull(v, "value is empty"); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter); - return new UpdateOperation(this, p, v); + return new UpdateOperation(this, p, v); } - public InsertOperation insert() { - return new InsertOperation(this, true); + public InsertOperation insert() { + return new InsertOperation(this, true); } - public InsertOperation insert(Object pojo) { - return this.insert(pojo, null); + public InsertOperation insert(Class resultType) { + return new InsertOperation(this, resultType, true); + } + + public InsertOperation insert(T pojo) { + Objects.requireNonNull(pojo, "supplied object must be either an instance of the entity class or a dsl for it, but cannot be null"); + HelenusEntity entity = null; + try { entity = Helenus.resolve(pojo); } catch (HelenusMappingException e) {} + if (entity != null) { + return new InsertOperation(this, entity.getMappingInterface(), true); + } else { + return this.insert(pojo, null); + } } public InsertOperation insert(Drafted draft) { - return this.insert(draft.build(), draft.mutated()); + return this.insert((T) draft.build(), draft.mutated()); } - public InsertOperation insert(Object pojo, Set mutations) { + public InsertOperation insert(T pojo, Set mutations) { Objects.requireNonNull(pojo, "pojo is empty"); Class iface = MappingUtil.getMappingInterface(pojo); @@ -457,19 +477,30 @@ public final class HelenusSession extends AbstractSessionOperations implements C return new InsertOperation(this, entity, pojo, mutations, true); } - public InsertOperation upsert() { - return new InsertOperation(this, false); + public InsertOperation upsert() { + return new InsertOperation(this, false); + } + + public InsertOperation upsert(Class resultType) { + return new InsertOperation(this, resultType, false); } public InsertOperation upsert(Drafted draft) { - return this.upsert(draft.build(), draft.mutated()); + return this.upsert((T) draft.build(), draft.mutated()); } - public InsertOperation upsert(Object pojo) { - return this.upsert(pojo, null); + public InsertOperation upsert(T pojo) { + Objects.requireNonNull(pojo, "supplied object must be either an instance of the entity class or a dsl for it, but cannot be null"); + HelenusEntity entity = null; + try { entity = Helenus.resolve(pojo); } catch (HelenusMappingException e) {} + if (entity != null) { + return new InsertOperation(this, entity.getMappingInterface(), false); + } else { + return this.upsert(pojo, null); + } } - public InsertOperation upsert(Object pojo, Set mutations) { + public InsertOperation upsert(T pojo, Set mutations) { Objects.requireNonNull(pojo, "pojo is empty"); Class iface = MappingUtil.getMappingInterface(pojo); @@ -487,11 +518,6 @@ public final class HelenusSession extends AbstractSessionOperations implements C return new DeleteOperation(this, Helenus.resolve(dsl)); } - @Override - public AbstractCache cacheFor(CacheManager.Type type) { - return cacheManager.of(type); - } - public Session getSession() { return session; } @@ -539,4 +565,7 @@ public final class HelenusSession extends AbstractSessionOperations implements C break; } } + + public SessionCache getSessionCache() { return sessionCache; } + } diff --git a/src/main/java/net/helenus/core/SessionInitializer.java b/src/main/java/net/helenus/core/SessionInitializer.java index 884fbc9..3267471 100644 --- a/src/main/java/net/helenus/core/SessionInitializer.java +++ b/src/main/java/net/helenus/core/SessionInitializer.java @@ -328,19 +328,6 @@ public final class SessionInitializer extends AbstractSessionOperations { e -> { action.accept(e); }); - /* - Set processedSet = new HashSet(); - Set stack = new HashSet(); - - sessionRepository.entities().stream() - .filter(e -> e.getType() == HelenusEntityType.UDT) - .collect(Collectors.toCollection(ArrayDeque::new)) - .descendingIterator() - .forEachRemaining(e -> { - stack.clear(); - eachUserTypeInRecursion(e, processedSet, stack, userTypeOps, action); - }); - */ } private void eachUserTypeInRecursion( diff --git a/src/main/java/net/helenus/core/UnitOfWork.java b/src/main/java/net/helenus/core/UnitOfWork.java index 7062a1d..0acc5f8 100644 --- a/src/main/java/net/helenus/core/UnitOfWork.java +++ b/src/main/java/net/helenus/core/UnitOfWork.java @@ -2,14 +2,16 @@ package net.helenus.core; import com.diffplug.common.base.Errors; import com.google.common.collect.TreeTraverser; +import net.helenus.core.operation.AbstractCache; +import net.helenus.core.operation.UnitOfWorkCache; +import net.helenus.support.HelenusException; -import java.io.Closeable; import java.io.IOException; import java.util.*; /** Encapsulates the concept of a "transaction" as a unit-of-work. */ -public final class UnitOfWork implements Closeable { +public final class UnitOfWork implements AutoCloseable { private final List nested = new ArrayList<>(); private final HelenusSession session; private final UnitOfWork parent; @@ -19,6 +21,7 @@ public final class UnitOfWork implements Closeable { protected UnitOfWork(HelenusSession session, UnitOfWork parent) { Objects.requireNonNull(session, "containing session cannot be null"); + this.session = session; this.parent = parent; } @@ -41,6 +44,10 @@ public final class UnitOfWork implements Closeable { return this; } + public UnitOfWorkCache getCacheEnclosing(AbstractCache cache) { + return new UnitOfWorkCache(this, cache); + } + private void applyPostCommitFunctions() { if (!postCommit.isEmpty()) { for (CommitThunk f : postCommit) { @@ -111,7 +118,7 @@ public final class UnitOfWork implements Closeable { } @Override - public void close() throws IOException { + public void close() throws HelenusException { // Closing a UnitOfWork will abort iff we've not already aborted or committed this unit of work. if (aborted == false && committed == false) { abort(); diff --git a/src/main/java/net/helenus/core/operation/AbstractCache.java b/src/main/java/net/helenus/core/operation/AbstractCache.java index 07375c7..5ad2f5f 100644 --- a/src/main/java/net/helenus/core/operation/AbstractCache.java +++ b/src/main/java/net/helenus/core/operation/AbstractCache.java @@ -4,37 +4,42 @@ import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSetFuture; import com.datastax.driver.core.Statement; import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.RemovalListener; +import com.google.common.cache.RemovalNotification; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; -public abstract class AbstractCache { - protected CacheManager.Type type; - protected Cache cache; +public abstract class AbstractCache { + final Logger logger = LoggerFactory.getLogger(getClass()); + protected Cache cache; - public AbstractCache(CacheManager.Type type, Cache cache) { - this.type = type; - this.cache = cache; + public AbstractCache() { + RemovalListener listener = + new RemovalListener() { + @Override + public void onRemoval(RemovalNotification n) { + if (n.wasEvicted()) { + String cause = n.getCause().name(); + logger.info(cause); + } + } + }; + + cache = CacheBuilder.newBuilder() + .maximumSize(10_000) + .expireAfterAccess(20, TimeUnit.MINUTES) + .weakKeys() + .softValues() + .removalListener(listener) + .build(); } - protected abstract ResultSet fetch( + protected abstract ResultSet apply( Statement statement, OperationsDelegate delegate, ResultSetFuture resultSetFuture) throws InterruptedException, ExecutionException; - protected abstract ResultSet mutate( - Statement statement, OperationsDelegate delegate, ResultSetFuture resultSetFuture) - throws InterruptedException, ExecutionException; - - public ResultSet apply( - Statement statement, OperationsDelegate delegate, ResultSetFuture futureResultSet) - throws InterruptedException, ExecutionException { - ResultSet resultSet = null; - switch (type) { - case FETCH: - resultSet = fetch(statement, delegate, futureResultSet); - break; - case MUTATE: - resultSet = mutate(statement, delegate, futureResultSet); - break; - } - return resultSet; - } } diff --git a/src/main/java/net/helenus/core/operation/AbstractOptionalOperation.java b/src/main/java/net/helenus/core/operation/AbstractOptionalOperation.java index 80c4268..5e19cd7 100644 --- a/src/main/java/net/helenus/core/operation/AbstractOptionalOperation.java +++ b/src/main/java/net/helenus/core/operation/AbstractOptionalOperation.java @@ -34,9 +34,7 @@ public abstract class AbstractOptionalOperation transform(ResultSet resultSet); - protected AbstractCache getCache() { - return null; - } + protected AbstractCache getCache() { return null; } public CacheKey getCacheKey() { return null; diff --git a/src/main/java/net/helenus/core/operation/CachableOperation.java b/src/main/java/net/helenus/core/operation/CachableOperation.java new file mode 100644 index 0000000..34dd403 --- /dev/null +++ b/src/main/java/net/helenus/core/operation/CachableOperation.java @@ -0,0 +1,8 @@ +package net.helenus.core.operation; + +import java.io.Serializable; + +public interface CachableOperation { + public T getCacheKey(); + public T valueToCache(); +} diff --git a/src/main/java/net/helenus/core/operation/CacheKey.java b/src/main/java/net/helenus/core/operation/CacheKey.java index 99460be..43545d1 100644 --- a/src/main/java/net/helenus/core/operation/CacheKey.java +++ b/src/main/java/net/helenus/core/operation/CacheKey.java @@ -2,7 +2,9 @@ package net.helenus.core.operation; import com.datastax.driver.core.Statement; -public class CacheKey { +import java.io.Serializable; + +public class CacheKey implements Serializable { private String key; @@ -38,4 +40,5 @@ public class CacheKey { public int hashCode() { return key.hashCode(); } + } diff --git a/src/main/java/net/helenus/core/operation/CacheManager.java b/src/main/java/net/helenus/core/operation/CacheManager.java deleted file mode 100644 index afddfc3..0000000 --- a/src/main/java/net/helenus/core/operation/CacheManager.java +++ /dev/null @@ -1,53 +0,0 @@ -package net.helenus.core.operation; - -import com.datastax.driver.core.ResultSet; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.RemovalListener; -import com.google.common.cache.RemovalNotification; -import java.util.concurrent.TimeUnit; -import net.helenus.core.HelenusSession; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class CacheManager { - public enum Type { - FETCH, - MUTATE - } - - final Logger logger = LoggerFactory.getLogger(getClass()); - final HelenusSession session; - - private AbstractCache sessionFetch; - - public CacheManager(HelenusSession session) { - this.session = session; - - RemovalListener listener = - new RemovalListener() { - @Override - public void onRemoval(RemovalNotification n) { - if (n.wasEvicted()) { - String cause = n.getCause().name(); - logger.info(cause); - } - } - }; - - Cache cache = - CacheBuilder.newBuilder() - .maximumSize(10_000) - .expireAfterAccess(20, TimeUnit.MINUTES) - .weakKeys() - .softValues() - .removalListener(listener) - .build(); - - sessionFetch = new SessionCache(Type.FETCH, this, cache); - } - - public AbstractCache of(CacheManager.Type type) { - return sessionFetch; - } -} diff --git a/src/main/java/net/helenus/core/operation/Executioner.java b/src/main/java/net/helenus/core/operation/Executioner.java index 0056a79..7a2e717 100644 --- a/src/main/java/net/helenus/core/operation/Executioner.java +++ b/src/main/java/net/helenus/core/operation/Executioner.java @@ -59,6 +59,10 @@ public enum Executioner { span = tracer.newChild(traceContext); } + if (uow != null) { + cache = uow.getCacheEnclosing(cache); + } + try { if (span != null) { span.name("cassandra"); diff --git a/src/main/java/net/helenus/core/operation/InsertOperation.java b/src/main/java/net/helenus/core/operation/InsertOperation.java index 46652b4..044d74f 100644 --- a/src/main/java/net/helenus/core/operation/InsertOperation.java +++ b/src/main/java/net/helenus/core/operation/InsertOperation.java @@ -38,10 +38,10 @@ public final class InsertOperation extends AbstractOperation> values = - new ArrayList>(); + private final List> values = new ArrayList>(); + private final T pojo; + private final Class resultType; private boolean ifNotExists; - private Object pojo; private int[] ttl; private long[] timestamp; @@ -50,12 +50,22 @@ public final class InsertOperation extends AbstractOperation resultType, boolean ifNotExists) { + super(sessionOperations); + + this.ifNotExists = ifNotExists; + this.pojo = null; + this.resultType = resultType; } public InsertOperation( AbstractSessionOperations sessionOperations, HelenusEntity entity, - Object pojo, + T pojo, Set mutations, boolean ifNotExists) { super(sessionOperations); @@ -63,6 +73,8 @@ public final class InsertOperation extends AbstractOperation properties = entity.getOrderedProperties(); Set keys = (mutations == null) ? null : mutations; @@ -141,51 +153,11 @@ public final class InsertOperation extends AbstractOperation 0) { - Collection properties = entity.getOrderedProperties(); - Map backingMap = new HashMap(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> 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. - Class iface = entity.getMappingInterface(); - pojo = Helenus.map(iface, backingMap); - } + Class iface = entity.getMappingInterface(); + if (resultType.isAssignableFrom(iface)) { + return TransformGeneric.INSTANCE.transform(sessionOps, pojo, iface, values, entity.getOrderedProperties()); } - return (T) pojo; + return pojo; } public InsertOperation usingTtl(int ttl) { diff --git a/src/main/java/net/helenus/core/operation/SelectOperation.java b/src/main/java/net/helenus/core/operation/SelectOperation.java index 2a467ba..32b3fc6 100644 --- a/src/main/java/net/helenus/core/operation/SelectOperation.java +++ b/src/main/java/net/helenus/core/operation/SelectOperation.java @@ -45,6 +45,7 @@ public final class SelectOperation extends AbstractFilterStreamOperation ordering = null; protected Integer limit = null; protected boolean allowFiltering = false; + protected boolean ignoreSessionCache = false; public SelectOperation(AbstractSessionOperations sessionOperations) { super(sessionOperations); @@ -165,6 +166,18 @@ public final class SelectOperation extends AbstractFilterStreamOperation ignoreCache() { + ignoreSessionCache = true; + return this; + } + public SelectOperation limit(Integer limit) { this.limit = limit; return this; diff --git a/src/main/java/net/helenus/core/operation/SessionCache.java b/src/main/java/net/helenus/core/operation/SessionCache.java index 7929663..9c3734d 100644 --- a/src/main/java/net/helenus/core/operation/SessionCache.java +++ b/src/main/java/net/helenus/core/operation/SessionCache.java @@ -6,16 +6,9 @@ import com.datastax.driver.core.Statement; import com.google.common.cache.Cache; import java.util.concurrent.ExecutionException; -public class SessionCache extends AbstractCache { +public class SessionCache extends AbstractCache { - private final CacheManager manager; - - SessionCache(CacheManager.Type type, CacheManager manager, Cache cache) { - super(type, cache); - this.manager = manager; - } - - protected ResultSet fetch( + protected ResultSet apply( Statement statement, OperationsDelegate delegate, ResultSetFuture resultSetFuture) throws InterruptedException, ExecutionException { final CacheKey key = delegate.getCacheKey(); @@ -28,7 +21,6 @@ public class SessionCache extends AbstractCache { if (resultSet == null) { resultSet = resultSetFuture.get(); if (resultSet != null) { - planEvictionFor(statement); cache.put(cacheKey, resultSet); } } @@ -36,25 +28,6 @@ public class SessionCache extends AbstractCache { return resultSet; } - protected ResultSet mutate( - Statement statement, OperationsDelegate delegate, ResultSetFuture resultSetFuture) - throws InterruptedException, ExecutionException { - CacheKey key = delegate.getCacheKey(); - final String cacheKey = key == null ? statement.toString() : key.toString(); - ResultSet resultSet = resultSetFuture.get(); - if (cacheKey != null && resultSet != null) { - planEvictionFor(statement); - //manager.evictIfNecessary(statement, delegate); - cache.put(cacheKey, resultSet); - } - return resultSet; - } - - private void planEvictionFor(Statement statement) { - //((Select)statement).table + statement.where.clauses.length == 0 - //TTL for rows read - } - public ResultSet get(Statement statement, OperationsDelegate delegate) { final CacheKey key = delegate.getCacheKey(); final String cacheKey = (key == null) ? CacheKey.of(statement) : key.toString(); diff --git a/src/main/java/net/helenus/core/operation/TransformGeneric.java b/src/main/java/net/helenus/core/operation/TransformGeneric.java new file mode 100644 index 0000000..ff41dbc --- /dev/null +++ b/src/main/java/net/helenus/core/operation/TransformGeneric.java @@ -0,0 +1,59 @@ +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 transform(AbstractSessionOperations sessionOps, T pojo, Class mappingInterface, List> assignments, Collection properties) { + if (assignments.size() > 0) { + Map backingMap = new HashMap(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> 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; + } +} diff --git a/src/main/java/net/helenus/core/operation/UnitOfWorkCache.java b/src/main/java/net/helenus/core/operation/UnitOfWorkCache.java new file mode 100644 index 0000000..d17798e --- /dev/null +++ b/src/main/java/net/helenus/core/operation/UnitOfWorkCache.java @@ -0,0 +1,51 @@ +package net.helenus.core.operation; + +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.ResultSetFuture; +import com.datastax.driver.core.Statement; +import net.helenus.core.UnitOfWork; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +public class UnitOfWorkCache extends AbstractCache { + + private final UnitOfWork uow; + private final Map cache = new HashMap(); + private AbstractCache sessionCache; + + public UnitOfWorkCache(UnitOfWork uow, AbstractCache sessionCache) { + super(); + this.sessionCache = sessionCache; + this.uow = uow; + } + + @Override + protected ResultSet apply(Statement statement, OperationsDelegate delegate, ResultSetFuture resultSetFuture) + throws InterruptedException, ExecutionException { + + final CacheKey key = delegate.getCacheKey(); + final String cacheKey = (key == null) ? CacheKey.of(statement) : key.toString(); + ResultSet resultSet = null; + if (cacheKey == null) { + if (sessionCache != null) { + ResultSet rs = sessionCache.apply(statement, delegate, resultSetFuture); + if (rs != null) { + return rs; + } + } + } else { + resultSet = cache.get(cacheKey); + if (resultSet != null) { + return resultSet; + } + } + resultSet = resultSetFuture.get(); + if (resultSet != null) { + cache.put(cacheKey, resultSet); + } + return resultSet; + } + +} diff --git a/src/main/java/net/helenus/core/operation/UpdateOperation.java b/src/main/java/net/helenus/core/operation/UpdateOperation.java index 47a45ca..30e4d33 100644 --- a/src/main/java/net/helenus/core/operation/UpdateOperation.java +++ b/src/main/java/net/helenus/core/operation/UpdateOperation.java @@ -32,22 +32,29 @@ import net.helenus.mapping.MappingUtil; import net.helenus.support.HelenusMappingException; import net.helenus.support.Immutables; -public final class UpdateOperation extends AbstractFilterOperation { +public final class UpdateOperation extends AbstractFilterOperation> { private HelenusEntity entity = null; private final List assignments = new ArrayList(); + private final T pojo; private int[] ttl; private long[] timestamp; - public UpdateOperation(AbstractSessionOperations sessionOperations) { + public UpdateOperation(AbstractSessionOperations sessionOperations){ super(sessionOperations); + this.pojo = null; } - public UpdateOperation( - AbstractSessionOperations sessionOperations, HelenusPropertyNode p, Object v) { + public UpdateOperation(AbstractSessionOperations sessionOperations, T pojo) { super(sessionOperations); + this.pojo = pojo; + } + + public UpdateOperation(AbstractSessionOperations sessionOperations, HelenusPropertyNode p, Object v) { + super(sessionOperations); + this.pojo = null; Object value = sessionOps.getValuePreparer().prepareColumnValue(v, p.getProperty()); assignments.add(QueryBuilder.set(p.getColumnName(), value)); @@ -55,7 +62,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation set(Getter getter, V v) { + public UpdateOperation set(Getter getter, V v) { Objects.requireNonNull(getter, "getter is empty"); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter); @@ -76,11 +83,11 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation increment(Getter counterGetter) { + public UpdateOperation increment(Getter counterGetter) { return increment(counterGetter, 1L); } - public UpdateOperation increment(Getter counterGetter, long delta) { + public UpdateOperation increment(Getter counterGetter, long delta) { Objects.requireNonNull(counterGetter, "counterGetter is empty"); @@ -92,11 +99,11 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation decrement(Getter counterGetter) { + public UpdateOperation decrement(Getter counterGetter) { return decrement(counterGetter, 1L); } - public UpdateOperation decrement(Getter counterGetter, long delta) { + public UpdateOperation decrement(Getter counterGetter, long delta) { Objects.requireNonNull(counterGetter, "counterGetter is empty"); @@ -115,7 +122,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation prepend(Getter> listGetter, V value) { + public UpdateOperation prepend(Getter> listGetter, V value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -129,7 +136,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation prependAll(Getter> listGetter, List value) { + public UpdateOperation prependAll(Getter> listGetter, List value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -143,7 +150,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation setIdx(Getter> listGetter, int idx, V value) { + public UpdateOperation setIdx(Getter> listGetter, int idx, V value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -157,7 +164,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation append(Getter> listGetter, V value) { + public UpdateOperation append(Getter> listGetter, V value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -171,7 +178,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation appendAll(Getter> listGetter, List value) { + public UpdateOperation appendAll(Getter> listGetter, List value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -185,7 +192,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation discard(Getter> listGetter, V value) { + public UpdateOperation discard(Getter> listGetter, V value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -199,7 +206,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation discardAll(Getter> listGetter, List value) { + public UpdateOperation discardAll(Getter> listGetter, List value) { Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -251,7 +258,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation add(Getter> setGetter, V value) { + public UpdateOperation add(Getter> setGetter, V value) { Objects.requireNonNull(setGetter, "setGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -265,7 +272,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation addAll(Getter> setGetter, Set value) { + public UpdateOperation addAll(Getter> setGetter, Set value) { Objects.requireNonNull(setGetter, "setGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -279,7 +286,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation remove(Getter> setGetter, V value) { + public UpdateOperation remove(Getter> setGetter, V value) { Objects.requireNonNull(setGetter, "setGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -293,7 +300,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation removeAll(Getter> setGetter, Set value) { + public UpdateOperation removeAll(Getter> setGetter, Set value) { Objects.requireNonNull(setGetter, "setGetter is empty"); Objects.requireNonNull(value, "value is empty"); @@ -344,7 +351,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation put(Getter> mapGetter, K key, V value) { + public UpdateOperation put(Getter> mapGetter, K key, V value) { Objects.requireNonNull(mapGetter, "mapGetter is empty"); Objects.requireNonNull(key, "key is empty"); @@ -368,7 +375,7 @@ public final class UpdateOperation extends AbstractFilterOperation UpdateOperation putAll(Getter> mapGetter, Map map) { + public UpdateOperation putAll(Getter> mapGetter, Map map) { Objects.requireNonNull(mapGetter, "mapGetter is empty"); Objects.requireNonNull(map, "map is empty"); @@ -419,6 +426,7 @@ public final class UpdateOperation extends AbstractFilterOperation usingTtl(int ttl) { this.ttl = new int[1]; this.ttl[0] = ttl; return this; } - public UpdateOperation usingTimestamp(long timestamp) { + public UpdateOperation usingTimestamp(long timestamp) { this.timestamp = new long[1]; this.timestamp[0] = timestamp; return this; diff --git a/src/test/java/net/helenus/test/integration/core/collection/CollectionTest.java b/src/test/java/net/helenus/test/integration/core/collection/CollectionTest.java index c2c070b..5b437eb 100644 --- a/src/test/java/net/helenus/test/integration/core/collection/CollectionTest.java +++ b/src/test/java/net/helenus/test/integration/core/collection/CollectionTest.java @@ -67,8 +67,11 @@ public class CollectionTest extends AbstractEmbeddedCassandraTest { // read full object - Customer actual = - session.select(Customer.class).where(customer::id, eq(id)).sync().findFirst().get(); + Customer actual = session.select(customer) + .where(customer::id, eq(id)) + .single() + .sync() + .orElse(null); Assert.assertEquals(id, actual.id()); Assert.assertEquals(aliases, actual.aliases()); Assert.assertNull(actual.names()); @@ -87,7 +90,12 @@ public class CollectionTest extends AbstractEmbeddedCassandraTest { session.update().set(customer::aliases, expected).where(customer::id, eq(id)).sync(); - actual = session.select(Customer.class).where(customer::id, eq(id)).sync().findFirst().get(); + actual = session.select(customer) + .where(customer::id, eq(id)) + .single() + .sync() + .orElse(null); + Assert.assertEquals(id, actual.id()); Assert.assertEquals(expected, actual.aliases()); @@ -162,8 +170,12 @@ public class CollectionTest extends AbstractEmbeddedCassandraTest { // read full object - Customer actual = - session.select(Customer.class).where(customer::id, eq(id)).sync().findFirst().get(); + Customer actual = session.select(customer) + .where(customer::id, eq(id)) + .single() + .sync() + .orElse(null); + Assert.assertEquals(id, actual.id()); Assert.assertEquals(names, actual.names()); Assert.assertNull(actual.aliases()); @@ -188,7 +200,12 @@ public class CollectionTest extends AbstractEmbeddedCassandraTest { session.update().set(customer::names, expected).where(customer::id, eq(id)).sync(); - actual = session.select(Customer.class).where(customer::id, eq(id)).sync().findFirst().get(); + actual = session.select(customer) + .where(customer::id, eq(id)) + .single() + .sync() + .orElse(null); + Assert.assertEquals(id, actual.id()); Assert.assertEquals(expected, actual.names()); @@ -289,8 +306,11 @@ public class CollectionTest extends AbstractEmbeddedCassandraTest { // read full object - Customer actual = - session.select(Customer.class).where(customer::id, eq(id)).sync().findFirst().get(); + Customer actual = session.select(customer) + .where(customer::id, eq(id)).single() + .sync() + .orElse(null); + Assert.assertEquals(id, actual.id()); Assert.assertEquals(props, actual.properties()); Assert.assertNull(actual.aliases()); @@ -323,7 +343,11 @@ public class CollectionTest extends AbstractEmbeddedCassandraTest { session.update().set(customer::properties, expected).where(customer::id, eq(id)).sync(); - actual = session.select(Customer.class).where(customer::id, eq(id)).sync().findFirst().get(); + actual = session.select(customer) + .where(customer::id, eq(id)) + .single() + .sync() + .orElse(null); Assert.assertEquals(id, actual.id()); Assert.assertEquals(expected, actual.properties()); diff --git a/src/test/java/net/helenus/test/integration/core/hierarchy/HierarchyTest.java b/src/test/java/net/helenus/test/integration/core/hierarchy/HierarchyTest.java index 4a0dd55..0fcb039 100644 --- a/src/test/java/net/helenus/test/integration/core/hierarchy/HierarchyTest.java +++ b/src/test/java/net/helenus/test/integration/core/hierarchy/HierarchyTest.java @@ -51,7 +51,7 @@ public class HierarchyTest extends AbstractEmbeddedCassandraTest { .sync(); Optional animal = - session.select(Cat.class).where(cat::nickname, eq("garfield")).sync().findFirst(); + session.select(Cat.class).where(cat::nickname, eq("garfield")).sync().findFirst(); Assert.assertTrue(animal.isPresent()); Assert.assertFalse(animal.get().eatable()); } @@ -64,8 +64,7 @@ public class HierarchyTest extends AbstractEmbeddedCassandraTest { .value(cat::nickname, "garfield") .value(cat::eatable, false) .sync(); - Optional animal = - session.select(Cat.class).where(cat::nickname, eq("garfield")).sync().findFirst(); + Optional animal = session.select(Cat.class).where(cat::nickname, eq("garfield")).single().sync(); Assert.assertTrue(animal.isPresent()); Cat cat = animal.get(); diff --git a/src/test/java/net/helenus/test/integration/core/prepared/PreparedStatementTest.java b/src/test/java/net/helenus/test/integration/core/prepared/PreparedStatementTest.java index fc7e170..4ea8271 100644 --- a/src/test/java/net/helenus/test/integration/core/prepared/PreparedStatementTest.java +++ b/src/test/java/net/helenus/test/integration/core/prepared/PreparedStatementTest.java @@ -70,7 +70,7 @@ public class PreparedStatementTest extends AbstractEmbeddedCassandraTest { selectOp = session - .select(Car.class) + .select(car) .where(car::make, Query.eq(Query.marker())) .and(car::model, Query.eq(Query.marker())) .prepare(); diff --git a/src/test/java/net/helenus/test/integration/core/simple/SimpleUserTest.java b/src/test/java/net/helenus/test/integration/core/simple/SimpleUserTest.java index f471e81..282231e 100644 --- a/src/test/java/net/helenus/test/integration/core/simple/SimpleUserTest.java +++ b/src/test/java/net/helenus/test/integration/core/simple/SimpleUserTest.java @@ -28,10 +28,7 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; public class SimpleUserTest extends AbstractEmbeddedCassandraTest { @@ -103,7 +100,11 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest { // select as object - actual = session.select(User.class).where(user::id, eq(100L)).sync().findFirst().get(); + actual = session.select(user) + .where(user::id, eq(100L)) + .single() + .sync() + .orElse(null); assertUsers(newUser, actual); // select by columns @@ -217,21 +218,34 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest { Assert.assertEquals("_albert", name); - User u = session.select(User.class).where(user::id, eq(100L)).sync().findFirst().get(); + User u = session.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()); - // INSERT + // User greg = - session - .insert() - .value(user::name, "greg") - .value(user::age, 44) - .value(user::type, UserType.USER) - .value(user::id, 1234L) - .sync(); + session + .insert(user) + .value(user::name, "greg") + .value(user::age, 44) + .value(user::type, UserType.USER) + .value(user::id, 1234L) + .sync(); + + Optional maybeGreg = + session + .select(user) + .where(user::id, eq(1234L)) + .single() + .sync(); + + // INSERT session .update() diff --git a/src/test/java/net/helenus/test/integration/core/simple/StaticColumnTest.java b/src/test/java/net/helenus/test/integration/core/simple/StaticColumnTest.java index 206c571..b863ace 100644 --- a/src/test/java/net/helenus/test/integration/core/simple/StaticColumnTest.java +++ b/src/test/java/net/helenus/test/integration/core/simple/StaticColumnTest.java @@ -104,7 +104,7 @@ public class StaticColumnTest extends AbstractEmbeddedCassandraTest { List actual = session - .select(Message.class) + .select(message) .where(message::id, Query.eq(123)) .sync() .collect(Collectors.toList()); diff --git a/src/test/java/net/helenus/test/integration/core/tuplecollection/TupleKeyMapTest.java b/src/test/java/net/helenus/test/integration/core/tuplecollection/TupleKeyMapTest.java index 09bcaf1..db84f09 100644 --- a/src/test/java/net/helenus/test/integration/core/tuplecollection/TupleKeyMapTest.java +++ b/src/test/java/net/helenus/test/integration/core/tuplecollection/TupleKeyMapTest.java @@ -40,7 +40,7 @@ public class TupleKeyMapTest extends TupleCollectionTest { // read full object - Book actual = session.select(Book.class).where(book::id, Query.eq(id)).sync().findFirst().get(); + Book actual = session.select(book).where(book::id, Query.eq(id)).sync().findFirst().get(); Assert.assertEquals(id, actual.id()); assertEqualMaps(notes, actual.notes()); Assert.assertNull(actual.reviewers()); @@ -71,7 +71,7 @@ public class TupleKeyMapTest extends TupleCollectionTest { session.update().set(book::notes, expected).where(book::id, Query.eq(id)).sync(); - actual = session.select(Book.class).where(book::id, Query.eq(id)).sync().findFirst().get(); + actual = session.select(book).where(book::id, Query.eq(id)).sync().findFirst().get(); Assert.assertEquals(id, actual.id()); assertEqualMaps(expected, actual.notes()); diff --git a/src/test/java/net/helenus/test/integration/core/tuplecollection/TupleMapTest.java b/src/test/java/net/helenus/test/integration/core/tuplecollection/TupleMapTest.java index 668f6ea..143e4f0 100644 --- a/src/test/java/net/helenus/test/integration/core/tuplecollection/TupleMapTest.java +++ b/src/test/java/net/helenus/test/integration/core/tuplecollection/TupleMapTest.java @@ -42,7 +42,11 @@ public class TupleMapTest extends TupleCollectionTest { // read full object - Book actual = session.select(Book.class).where(book::id, Query.eq(id)).sync().findFirst().get(); + Book actual = session.select(book) + .where(book::id, Query.eq(id)) + .single() + .sync() + .orElse(null); Assert.assertEquals(id, actual.id()); assertEqualMaps(writers, actual.writers()); Assert.assertNull(actual.reviewers()); @@ -73,7 +77,12 @@ public class TupleMapTest extends TupleCollectionTest { session.update().set(book::writers, expected).where(book::id, Query.eq(id)).sync(); - actual = session.select(Book.class).where(book::id, Query.eq(id)).sync().findFirst().get(); + actual = session.select(book) + .where(book::id, Query.eq(id)) + .single() + .sync() + .orElse(null); + Assert.assertEquals(id, actual.id()); assertEqualMaps(expected, actual.writers()); @@ -87,8 +96,7 @@ public class TupleMapTest extends TupleCollectionTest { expected.put(third, unk); session.update().put(book::writers, third, unk).where(book::id, Query.eq(id)).sync(); - actualMap = - session.select(book::writers).where(book::id, Query.eq(id)).sync().findFirst().get()._1; + actualMap = session.select(book::writers).where(book::id, Query.eq(id)).sync().findFirst().get()._1; assertEqualMaps(expected, actualMap); // putAll operation diff --git a/src/test/java/net/helenus/test/integration/core/udtcollection/UDTKeyMapTest.java b/src/test/java/net/helenus/test/integration/core/udtcollection/UDTKeyMapTest.java index f31061c..a6bc35c 100644 --- a/src/test/java/net/helenus/test/integration/core/udtcollection/UDTKeyMapTest.java +++ b/src/test/java/net/helenus/test/integration/core/udtcollection/UDTKeyMapTest.java @@ -42,7 +42,7 @@ public class UDTKeyMapTest extends UDTCollectionTest { // read full object - Book actual = session.select(Book.class).where(book::id, eq(id)).sync().findFirst().get(); + Book actual = session.select(book).where(book::id, eq(id)).sync().findFirst().get(); Assert.assertEquals(id, actual.id()); assertEqualMaps(notes, actual.notes()); Assert.assertNull(actual.reviewers()); @@ -70,7 +70,7 @@ public class UDTKeyMapTest extends UDTCollectionTest { session.update().set(book::notes, expected).where(book::id, eq(id)).sync(); - actual = session.select(Book.class).where(book::id, eq(id)).sync().findFirst().get(); + actual = session.select(book).where(book::id, eq(id)).sync().findFirst().get(); Assert.assertEquals(id, actual.id()); assertEqualMaps(expected, actual.notes()); diff --git a/src/test/java/net/helenus/test/integration/core/udtcollection/UDTListTest.java b/src/test/java/net/helenus/test/integration/core/udtcollection/UDTListTest.java index e9c33cb..bb42087 100644 --- a/src/test/java/net/helenus/test/integration/core/udtcollection/UDTListTest.java +++ b/src/test/java/net/helenus/test/integration/core/udtcollection/UDTListTest.java @@ -40,7 +40,7 @@ public class UDTListTest extends UDTCollectionTest { // read full object - Book actual = session.select(Book.class).where(book::id, Query.eq(id)).sync().findFirst().get(); + Book actual = session.select(book).where(book::id, Query.eq(id)).sync().findFirst().get(); Assert.assertEquals(id, actual.id()); assertEqualLists(authors, actual.authors()); Assert.assertNull(actual.reviewers()); @@ -65,7 +65,7 @@ public class UDTListTest extends UDTCollectionTest { session.update().set(book::authors, expected).where(book::id, Query.eq(id)).sync(); - actual = session.select(Book.class).where(book::id, Query.eq(id)).sync().findFirst().get(); + actual = session.select(book).where(book::id, Query.eq(id)).sync().findFirst().get(); Assert.assertEquals(id, actual.id()); assertEqualLists(expected, actual.authors()); diff --git a/src/test/java/net/helenus/test/integration/core/udtcollection/UDTMapTest.java b/src/test/java/net/helenus/test/integration/core/udtcollection/UDTMapTest.java index 36f6d9b..604972c 100644 --- a/src/test/java/net/helenus/test/integration/core/udtcollection/UDTMapTest.java +++ b/src/test/java/net/helenus/test/integration/core/udtcollection/UDTMapTest.java @@ -40,7 +40,7 @@ public class UDTMapTest extends UDTCollectionTest { // read full object - Book actual = session.select(Book.class).where(book::id, Query.eq(id)).sync().findFirst().get(); + Book actual = session.select(book).where(book::id, Query.eq(id)).sync().findFirst().get(); Assert.assertEquals(id, actual.id()); assertEqualMaps(writers, actual.writers()); Assert.assertNull(actual.reviewers()); @@ -71,7 +71,7 @@ public class UDTMapTest extends UDTCollectionTest { session.update().set(book::writers, expected).where(book::id, Query.eq(id)).sync(); - actual = session.select(Book.class).where(book::id, Query.eq(id)).sync().findFirst().get(); + actual = session.select(book).where(book::id, Query.eq(id)).sync().findFirst().get(); Assert.assertEquals(id, actual.id()); assertEqualMaps(expected, actual.writers()); diff --git a/src/test/java/net/helenus/test/integration/core/unitofwork/AndThenOrderTest.java b/src/test/java/net/helenus/test/integration/core/unitofwork/AndThenOrderTest.java index 9d1c065..37ba96a 100644 --- a/src/test/java/net/helenus/test/integration/core/unitofwork/AndThenOrderTest.java +++ b/src/test/java/net/helenus/test/integration/core/unitofwork/AndThenOrderTest.java @@ -15,14 +15,9 @@ */ package net.helenus.test.integration.core.unitofwork; -import com.datastax.driver.core.utils.UUIDs; -import net.bytebuddy.utility.RandomString; import net.helenus.core.Helenus; import net.helenus.core.HelenusSession; import net.helenus.core.UnitOfWork; -import net.helenus.mapping.annotation.Column; -import net.helenus.mapping.annotation.PartitionKey; -import net.helenus.mapping.annotation.Table; import net.helenus.test.integration.build.AbstractEmbeddedCassandraTest; import org.junit.Assert; import org.junit.BeforeClass; @@ -31,30 +26,18 @@ import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.UUID; - - -@Table -interface Widget { - @PartitionKey UUID id(); - @Column String name(); -} public class AndThenOrderTest extends AbstractEmbeddedCassandraTest { - static Widget widgets; - static HelenusSession session; @BeforeClass public static void beforeTest() { session = Helenus.init(getSession()) .showCql() - .add(Widget.class) .autoCreateDrop() .get(); - widgets = session.dsl(Widget.class); } @Test diff --git a/src/test/java/net/helenus/test/integration/core/unitofwork/UnitOfWorkTest.java b/src/test/java/net/helenus/test/integration/core/unitofwork/UnitOfWorkTest.java index 4c34d44..7f86a54 100644 --- a/src/test/java/net/helenus/test/integration/core/unitofwork/UnitOfWorkTest.java +++ b/src/test/java/net/helenus/test/integration/core/unitofwork/UnitOfWorkTest.java @@ -15,38 +15,96 @@ */ package net.helenus.test.integration.core.unitofwork; +import com.datastax.driver.core.utils.UUIDs; +import net.bytebuddy.utility.RandomString; import net.helenus.core.Helenus; import net.helenus.core.HelenusSession; +import net.helenus.core.UnitOfWork; +import net.helenus.core.annotation.Cacheable; +import net.helenus.mapping.annotation.Column; +import net.helenus.mapping.annotation.PartitionKey; +import net.helenus.mapping.annotation.Table; import net.helenus.test.integration.build.AbstractEmbeddedCassandraTest; -import net.helenus.test.integration.core.unitofwork.FilesystemNode; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import java.util.UUID; + +import static net.helenus.core.Query.eq; + + +@Table +@Cacheable +interface Widget { + @PartitionKey + UUID id(); + @Column + String name(); +} + public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest { - static FilesystemNode node; - + static Widget widget; static HelenusSession session; + @BeforeClass public static void beforeTest() { session = Helenus.init(getSession()) .showCql() - .add(FilesystemNode.class) + .add(Widget.class) .autoCreateDrop() .get(); - node = session.dsl(FilesystemNode.class); + widget = session.dsl(Widget.class); } - -/* @Test - public void testCruid() throws Exception { - session.insert() - .value(widgets::id, UUIDs.timeBased()) - .value(widgets::name, RandomString.make(20)) - .sync(uow5); + public void testSelectAfterInsertProperlyCachesEntity() throws Exception { + Widget w1, w2, w3, w4; + UUID key = UUIDs.timeBased(); + + try (UnitOfWork uow = session.begin()) { + + // This should cache the inserted Widget. + w1 = session.upsert(widget) + .value(widget::id, key) + .value(widget::name, RandomString.make(20)) + .sync(uow); + + // This should read from the cache and get the same instance of a Widget. + w2 = session.select(widget) + .where(widget::id, eq(key)) + .single() + .sync(uow) + .orElse(null); + + uow.commit() + .andThen(() -> { + Assert.assertEquals(w1, w2); + }); + } + + // This should read the widget from the session cache and maintain object identity. + w3 = session.select(widget) + .where(widget::id, eq(key)) + .single() + .sync() + .orElse(null); + + Assert.assertEquals(w1, w3); + + // This should read the widget from the database, no object identity but values should match. + w4 = session.select(widget) + .where(widget::id, eq(key)) + .ignoreCache() + .single() + .sync() + .orElse(null); + + Assert.assertNotEquals(w1, w4); + Assert.assertTrue(w1.equals(w4)); } -*/ + }