Merge branch 'develop'

This commit is contained in:
Greg Burd 2017-10-10 15:30:27 -04:00
commit 107f67735c
75 changed files with 1780 additions and 1020 deletions

7
bin/format.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/bash
for f in $(find ./src -name \*.java); do
echo Formatting $f
java -jar ./lib/google-java-format-1.3-all-deps.jar --replace $f
done

Binary file not shown.

View file

@ -4,8 +4,8 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>net.helenus</groupId> <groupId>net.helenus</groupId>
<artifactId>helenus-core</artifactId> <artifactId>helenus-net-core</artifactId>
<version>2.0.40-SNAPSHOT</version> <version>2.1</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>helenus</name> <name>helenus</name>

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) 2015 The Helenus Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.datastax.driver.core.querybuilder;
import com.datastax.driver.core.CodecRegistry;
import java.util.List;
public class IsNotNullClause extends Clause {
final String name;
public IsNotNullClause(String name) {
this.name = name;
}
@Override
String name() {
return name;
}
@Override
Object firstValue() {
return null;
}
@Override
void appendTo(StringBuilder sb, List<Object> variables, CodecRegistry codecRegistry) {
Utils.appendName(name, sb).append(" IS NOT NULL");
}
@Override
boolean containsBindMarker() {
return false;
}
}

View file

@ -0,0 +1,53 @@
package com.datastax.driver.core.schemabuilder;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.querybuilder.Select;
public class CreateMaterializedView extends Create {
private String viewName;
private Select.Where selection;
private String primaryKey;
private String clustering;
public CreateMaterializedView(
String keyspaceName, String viewName, Select.Where selection, String primaryKey, String clustering) {
super(keyspaceName, viewName);
this.viewName = viewName;
this.selection = selection;
this.primaryKey = primaryKey;
this.clustering = clustering;
}
public String getQueryString(CodecRegistry codecRegistry) {
return buildInternal();
}
public String buildInternal() {
StringBuilder createStatement =
new StringBuilder(STATEMENT_START).append("CREATE MATERIALIZED VIEW");
if (ifNotExists) {
createStatement.append(" IF NOT EXISTS");
}
createStatement.append(" ");
if (keyspaceName.isPresent()) {
createStatement.append(keyspaceName.get()).append(".");
}
createStatement.append(viewName);
createStatement.append(" AS ");
createStatement.append(selection.getQueryString());
createStatement.setLength(createStatement.length() - 1);
createStatement.append(" ");
createStatement.append(primaryKey);
if (clustering != null) {
createStatement.append(" ").append(clustering);
}
createStatement.append(";");
return createStatement.toString();
}
public String toString() {
return buildInternal();
}
}

View file

@ -0,0 +1,53 @@
package com.datastax.driver.core.schemabuilder;
import com.google.common.base.Optional;
public class DropMaterializedView extends Drop {
enum DroppedItem {
TABLE,
TYPE,
INDEX,
MATERIALIZED_VIEW
}
private Optional<String> keyspaceName = Optional.absent();
private String itemName;
private boolean ifExists = true;
private final String itemType = "MATERIALIZED VIEW";
public DropMaterializedView(String keyspaceName, String viewName) {
this(keyspaceName, viewName, DroppedItem.MATERIALIZED_VIEW);
}
private DropMaterializedView(String keyspaceName, String viewName, DroppedItem itemType) {
super(keyspaceName, viewName, Drop.DroppedItem.TABLE);
validateNotEmpty(keyspaceName, "Keyspace name");
this.keyspaceName = Optional.fromNullable(keyspaceName);
this.itemName = viewName;
}
/**
* Add the 'IF EXISTS' condition to this DROP statement.
*
* @return this statement.
*/
public Drop ifExists() {
this.ifExists = true;
return this;
}
@Override
public String buildInternal() {
StringBuilder dropStatement = new StringBuilder("DROP " + itemType + " ");
if (ifExists) {
dropStatement.append("IF EXISTS ");
}
if (keyspaceName.isPresent()) {
dropStatement.append(keyspaceName.get()).append(".");
}
dropStatement.append(itemName);
return dropStatement.toString();
}
}

View file

@ -16,6 +16,7 @@
package net.helenus.config; package net.helenus.config;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.function.Function; import java.util.function.Function;
import net.helenus.mapping.annotation.Transient; import net.helenus.mapping.annotation.Transient;
@ -33,6 +34,10 @@ public enum GetterMethodDetector implements Function<Method, Boolean> {
return false; return false;
} }
if (Modifier.isStatic(method.getModifiers())) {
return false;
}
// Methods marked "Transient" are not mapped, skip them. // Methods marked "Transient" are not mapped, skip them.
if (method.getDeclaredAnnotation(Transient.class) != null) { if (method.getDeclaredAnnotation(Transient.class) != null) {
return false; return false;

View file

@ -1,11 +1,9 @@
package net.helenus.core; package net.helenus.core;
import net.helenus.core.reflect.MapExportable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.Date; import java.util.Date;
import net.helenus.core.reflect.MapExportable;
public abstract class AbstractAuditedEntityDraft<E> extends AbstractEntityDraft<E> { public abstract class AbstractAuditedEntityDraft<E> extends AbstractEntityDraft<E> {
@ -37,5 +35,4 @@ public abstract class AbstractAuditedEntityDraft<E> extends AbstractEntityDraft<
public Date createdAt() { public Date createdAt() {
return (Date) get("createdAt", Date.class); return (Date) get("createdAt", Date.class);
} }
} }

View file

@ -1,23 +1,18 @@
package net.helenus.core; package net.helenus.core;
import java.util.*;
import com.google.common.primitives.Primitives; import com.google.common.primitives.Primitives;
import java.util.*;
import net.helenus.core.reflect.DefaultPrimitiveTypes; import net.helenus.core.reflect.DefaultPrimitiveTypes;
import net.helenus.core.reflect.Drafted; import net.helenus.core.reflect.Drafted;
import net.helenus.core.reflect.MapExportable; import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.MappingUtil; import net.helenus.mapping.MappingUtil;
public abstract class AbstractEntityDraft<E> implements Drafted<E> { public abstract class AbstractEntityDraft<E> implements Drafted<E> {
private final Map<String, Object> backingMap = new HashMap<String, Object>(); private final Map<String, Object> backingMap = new HashMap<String, Object>();
private final Set<String> mutatedSet = new HashSet<String>();
private final MapExportable entity; private final MapExportable entity;
private final Map<String, Object> entityMap; private final Map<String, Object> entityMap;
public AbstractEntityDraft(MapExportable entity) { public AbstractEntityDraft(MapExportable entity) {
this.entity = entity; this.entity = entity;
this.entityMap = entity != null ? entity.toMap() : new HashMap<String, Object>(); this.entityMap = entity != null ? entity.toMap() : new HashMap<String, Object>();
@ -25,11 +20,21 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
public abstract Class<E> getEntityClass(); public abstract Class<E> getEntityClass();
public E build() { return Helenus.map(getEntityClass(), toMap()); } public E build() {
return Helenus.map(getEntityClass(), toMap());
}
@SuppressWarnings("unchecked")
protected <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) { protected <T> T get(String key, Class<?> returnType) {
T value = (T) entityMap.get(key); T value = (T) backingMap.get(key);
if (value == null) {
value = (T) entityMap.get(key);
if (value == null) { if (value == null) {
if (Primitives.allPrimitiveTypes().contains(returnType)) { if (Primitives.allPrimitiveTypes().contains(returnType)) {
@ -42,21 +47,29 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
return (T) type.getDefaultValue(); return (T) type.getDefaultValue();
} }
} }
}
return value; return value;
} }
protected Object set(String key, Object value) { protected <T> Object set(Getter<T> getter, Object value) {
return set(this.<T>methodNameFor(getter), value);
}
protected Object set(String key, Object value) {
if (key == null || value == null) { if (key == null || value == null) {
return null; return null;
} }
backingMap.put(key, value); backingMap.put(key, value);
mutatedSet.add(key);
return value; return value;
} }
@SuppressWarnings("unchecked")
protected <T> T mutate(Getter<T> getter, T value) {
return (T) mutate(this.<T>methodNameFor(getter), value);
}
protected Object mutate(String key, Object value) { protected Object mutate(String key, Object value) {
Objects.requireNonNull(key); Objects.requireNonNull(key);
@ -69,26 +82,22 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
if (map.containsKey(key) && !value.equals(map.get(key))) { if (map.containsKey(key) && !value.equals(map.get(key))) {
backingMap.put(key, value); backingMap.put(key, value);
mutatedSet.add(key);
return value; return value;
} }
return map.get(key); return map.get(key);
} else { } else {
backingMap.put(key, value); backingMap.put(key, value);
mutatedSet.add(key);
return null; return null;
} }
} }
private String methodNameFor(Getter<?> getter) { private <T> String methodNameFor(Getter<T> getter) {
return MappingUtil.resolveMappingProperty(getter) return MappingUtil.resolveMappingProperty(getter).getProperty().getPropertyName();
.getProperty()
.getPropertyName();
} }
public Object unset(Getter<?> getter) { public <T> Object unset(Getter<T> getter) {
return unset(methodNameFor(getter)); return unset(methodNameFor(getter));
} }
@ -96,22 +105,22 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
if (key != null) { if (key != null) {
Object value = backingMap.get(key); Object value = backingMap.get(key);
backingMap.put(key, null); backingMap.put(key, null);
mutatedSet.add(key);
return value; return value;
} }
return null; return null;
} }
public <T> boolean reset(Getter<?> getter, T desiredValue) { public <T> boolean reset(Getter<T> getter, T desiredValue) {
return this.<T>reset(methodNameFor(getter), desiredValue); return this.<T>reset(this.<T>methodNameFor(getter), desiredValue);
} }
public <T> boolean reset(String key, T desiredValue) { public <T> boolean reset(String key, T desiredValue) {
if (key != null && desiredValue != null) { if (key != null && desiredValue != null) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
T currentValue = (T) backingMap.get(key); T currentValue = (T) backingMap.get(key);
if (currentValue != null && !currentValue.equals(desiredValue)) { if (currentValue == null || !currentValue.equals(desiredValue)) {
return set(key, desiredValue) != null; set(key, desiredValue);
return true;
} }
} }
return false; return false;
@ -132,7 +141,7 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
} else { } else {
combined = new HashMap<String, Object>(backingMap.size()); combined = new HashMap<String, Object>(backingMap.size());
} }
for (String key : mutatedSet) { for (String key : mutated()) {
combined.put(key, backingMap.get(key)); combined.put(key, backingMap.get(key));
} }
return combined; return combined;
@ -140,12 +149,11 @@ public abstract class AbstractEntityDraft<E> implements Drafted<E> {
@Override @Override
public Set<String> mutated() { public Set<String> mutated() {
return mutatedSet; return backingMap.keySet();
} }
@Override @Override
public String toString() { public String toString() {
return backingMap.toString(); return backingMap.toString();
} }
} }

View file

@ -22,7 +22,6 @@ import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import net.helenus.mapping.value.ColumnValuePreparer; import net.helenus.mapping.value.ColumnValuePreparer;
import net.helenus.mapping.value.ColumnValueProvider; import net.helenus.mapping.value.ColumnValueProvider;
import net.helenus.support.HelenusException; import net.helenus.support.HelenusException;
@ -51,6 +50,8 @@ public abstract class AbstractSessionOperations {
public abstract ConsistencyLevel getDefaultConsistencyLevel(); public abstract ConsistencyLevel getDefaultConsistencyLevel();
public abstract boolean getDefaultQueryIdempotency();
public PreparedStatement prepare(RegularStatement statement) { public PreparedStatement prepare(RegularStatement statement) {
try { try {
log(statement, false); log(statement, false);
@ -122,5 +123,4 @@ public abstract class AbstractSessionOperations {
void printCql(String cql) { void printCql(String cql) {
getPrintStream().println(cql); getPrintStream().println(cql);
} }
} }

View file

@ -17,10 +17,8 @@ package net.helenus.core;
import com.diffplug.common.base.Errors; import com.diffplug.common.base.Errors;
import com.google.common.collect.TreeTraverser; import com.google.common.collect.TreeTraverser;
import java.util.*; import java.util.*;
/** Encapsulates the concept of a "transaction" as a unit-of-work. */ /** Encapsulates the concept of a "transaction" as a unit-of-work. */
public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfWork, AutoCloseable { public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfWork, AutoCloseable {
private final List<AbstractUnitOfWork<E>> nested = new ArrayList<>(); private final List<AbstractUnitOfWork<E>> nested = new ArrayList<>();
@ -70,7 +68,9 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
return null; return null;
} }
public Map<String, Set<Object>> getCache() { return cache; } public Map<String, Set<Object>> getCache() {
return cache;
}
private Iterator<AbstractUnitOfWork<E>> getChildNodes() { private Iterator<AbstractUnitOfWork<E>> getChildNodes() {
return nested.iterator(); return nested.iterator();
@ -85,7 +85,8 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
public PostCommitFunction<Void, Void> commit() throws E { public PostCommitFunction<Void, Void> commit() throws E {
// All nested UnitOfWork should be committed (not aborted) before calls to commit, check. // All nested UnitOfWork should be committed (not aborted) before calls to commit, check.
boolean canCommit = true; boolean canCommit = true;
TreeTraverser<AbstractUnitOfWork<E>> traverser = TreeTraverser.using(node -> node::getChildNodes); TreeTraverser<AbstractUnitOfWork<E>> traverser =
TreeTraverser.using(node -> node::getChildNodes);
for (AbstractUnitOfWork<E> uow : traverser.postOrderTraversal(this)) { for (AbstractUnitOfWork<E> uow : traverser.postOrderTraversal(this)) {
if (this != uow) { if (this != uow) {
canCommit &= (!uow.aborted && uow.committed); canCommit &= (!uow.aborted && uow.committed);
@ -112,7 +113,8 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
if (parentCache.containsKey(key)) { if (parentCache.containsKey(key)) {
// merge the sets // merge the sets
Set<Object> ps = parentCache.get(key); Set<Object> ps = parentCache.get(key);
ps.addAll(cache.get(key)); //TODO(gburd): review this, likely not correct in all cases as-is. ps.addAll(
cache.get(key)); //TODO(gburd): review this, likely not correct in all cases as-is.
} else { } else {
// add the missing set // add the missing set
parentCache.put(key, cache.get(key)); parentCache.put(key, cache.get(key));
@ -122,7 +124,10 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
// Apply all post-commit functions for // Apply all post-commit functions for
if (parent == null) { if (parent == null) {
traverser.postOrderTraversal(this).forEach(uow -> { traverser
.postOrderTraversal(this)
.forEach(
uow -> {
uow.applyPostCommitFunctions(); uow.applyPostCommitFunctions();
}); });
return new PostCommitFunction(this, null); return new PostCommitFunction(this, null);
@ -137,8 +142,12 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
/* Explicitly discard the work and mark it as as such in the log. */ /* Explicitly discard the work and mark it as as such in the log. */
public void abort() { public void abort() {
TreeTraverser<AbstractUnitOfWork<E>> traverser = TreeTraverser.using(node -> node::getChildNodes); TreeTraverser<AbstractUnitOfWork<E>> traverser =
traverser.postOrderTraversal(this).forEach(uow -> { TreeTraverser.using(node -> node::getChildNodes);
traverser
.postOrderTraversal(this)
.forEach(
uow -> {
uow.committed = false; uow.committed = false;
uow.aborted = true; uow.aborted = true;
}); });
@ -165,5 +174,4 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
public boolean hasCommitted() { public boolean hasCommitted() {
return committed; return committed;
} }
} }

View file

@ -1,6 +1,5 @@
package net.helenus.core; package net.helenus.core;
import java.util.function.Function;
@FunctionalInterface @FunctionalInterface
public interface CommitThunk { public interface CommitThunk {

View file

@ -140,7 +140,14 @@ public final class Helenus {
} }
public static HelenusEntity entity(Class<?> iface) { public static HelenusEntity entity(Class<?> iface) {
return entity(iface, metadataForEntity.get(iface)); Metadata metadata = metadataForEntity.get(iface);
if (metadata == null) {
HelenusSession session = session();
if (session != null) {
metadata = session.getMetadata();
}
}
return entity(iface, metadata);
} }
public static HelenusEntity entity(Class<?> iface, Metadata metadata) { public static HelenusEntity entity(Class<?> iface, Metadata metadata) {

View file

@ -15,10 +15,21 @@
*/ */
package net.helenus.core; package net.helenus.core;
import static net.helenus.core.Query.eq;
import brave.Tracer; import brave.Tracer;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.datastax.driver.core.*; import com.datastax.driver.core.*;
import java.io.Closeable;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
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;
import net.helenus.core.operation.*; import net.helenus.core.operation.*;
import net.helenus.core.reflect.Drafted; import net.helenus.core.reflect.Drafted;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
@ -33,19 +44,6 @@ import net.helenus.support.Fun.Tuple6;
import net.helenus.support.HelenusException; import net.helenus.support.HelenusException;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
import java.io.Closeable;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
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;
import static net.helenus.core.Query.eq;
public final class HelenusSession extends AbstractSessionOperations implements Closeable { public final class HelenusSession extends AbstractSessionOperations implements Closeable {
private final int MAX_CACHE_SIZE = 10000; private final int MAX_CACHE_SIZE = 10000;
@ -56,6 +54,7 @@ public final class HelenusSession extends AbstractSessionOperations implements C
private volatile String usingKeyspace; private volatile String usingKeyspace;
private volatile boolean showCql; private volatile boolean showCql;
private final ConsistencyLevel defaultConsistencyLevel; private final ConsistencyLevel defaultConsistencyLevel;
private final boolean defaultQueryIdempotency;
private final MetricRegistry metricRegistry; private final MetricRegistry metricRegistry;
private final Tracer zipkinTracer; private final Tracer zipkinTracer;
private final PrintStream printStream; private final PrintStream printStream;
@ -68,7 +67,6 @@ public final class HelenusSession extends AbstractSessionOperations implements C
private final StatementColumnValuePreparer valuePreparer; private final StatementColumnValuePreparer valuePreparer;
private final Metadata metadata; private final Metadata metadata;
HelenusSession( HelenusSession(
Session session, Session session,
String usingKeyspace, String usingKeyspace,
@ -79,6 +77,7 @@ public final class HelenusSession extends AbstractSessionOperations implements C
Executor executor, Executor executor,
boolean dropSchemaOnClose, boolean dropSchemaOnClose,
ConsistencyLevel consistencyLevel, ConsistencyLevel consistencyLevel,
boolean defaultQueryIdempotency,
Class<? extends UnitOfWork> unitOfWorkClass, Class<? extends UnitOfWork> unitOfWorkClass,
MetricRegistry metricRegistry, MetricRegistry metricRegistry,
Tracer tracer) { Tracer tracer) {
@ -93,6 +92,7 @@ public final class HelenusSession extends AbstractSessionOperations implements C
this.executor = executor; this.executor = executor;
this.dropSchemaOnClose = dropSchemaOnClose; this.dropSchemaOnClose = dropSchemaOnClose;
this.defaultConsistencyLevel = consistencyLevel; this.defaultConsistencyLevel = consistencyLevel;
this.defaultQueryIdempotency = defaultQueryIdempotency;
this.unitOfWorkClass = unitOfWorkClass; this.unitOfWorkClass = unitOfWorkClass;
this.metricRegistry = metricRegistry; this.metricRegistry = metricRegistry;
this.zipkinTracer = tracer; this.zipkinTracer = tracer;
@ -168,10 +168,16 @@ public final class HelenusSession extends AbstractSessionOperations implements C
return metricRegistry; return metricRegistry;
} }
@Override
public ConsistencyLevel getDefaultConsistencyLevel() { public ConsistencyLevel getDefaultConsistencyLevel() {
return defaultConsistencyLevel; return defaultConsistencyLevel;
} }
@Override
public boolean getDefaultQueryIdempotency() {
return defaultQueryIdempotency;
}
public Metadata getMetadata() { public Metadata getMetadata() {
return metadata; return metadata;
} }
@ -183,20 +189,27 @@ public final class HelenusSession extends AbstractSessionOperations implements C
public synchronized UnitOfWork begin(UnitOfWork parent) { public synchronized UnitOfWork begin(UnitOfWork parent) {
try { try {
Class<? extends UnitOfWork> clazz = unitOfWorkClass; Class<? extends UnitOfWork> clazz = unitOfWorkClass;
Constructor<? extends UnitOfWork> ctor = clazz.getConstructor(HelenusSession.class, UnitOfWork.class); Constructor<? extends UnitOfWork> ctor =
clazz.getConstructor(HelenusSession.class, UnitOfWork.class);
UnitOfWork uow = ctor.newInstance(this, parent); UnitOfWork uow = ctor.newInstance(this, parent);
if (parent != null) { if (parent != null) {
parent.addNestedUnitOfWork(uow); parent.addNestedUnitOfWork(uow);
} }
return uow.begin(); return uow.begin();
} } catch (NoSuchMethodException
catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { | InvocationTargetException
throw new HelenusException(String.format("Unable to instantiate {} as a UnitOfWork.", unitOfWorkClass.getSimpleName()), e); | InstantiationException
| IllegalAccessException e) {
throw new HelenusException(
String.format(
"Unable to instantiate {} as a UnitOfWork.", unitOfWorkClass.getSimpleName()),
e);
} }
} }
public <E> SelectOperation<E> select(E pojo) { public <E> SelectOperation<E> select(E pojo) {
Objects.requireNonNull(pojo, "supplied object must be a dsl for a registered entity but cannot be null"); Objects.requireNonNull(
pojo, "supplied object must be a dsl for a registered entity but cannot be null");
ColumnValueProvider valueProvider = getValueProvider(); ColumnValueProvider valueProvider = getValueProvider();
HelenusEntity entity = Helenus.resolve(pojo); HelenusEntity entity = Helenus.resolve(pojo);
Class<?> entityClass = entity.getMappingInterface(); Class<?> entityClass = entity.getMappingInterface();
@ -234,7 +247,8 @@ public final class HelenusSession extends AbstractSessionOperations implements C
} }
public <E> SelectOperation<Row> selectAll(E pojo) { public <E> SelectOperation<Row> selectAll(E pojo) {
Objects.requireNonNull(pojo, "supplied object must be a dsl for a registered entity but cannot be null"); Objects.requireNonNull(
pojo, "supplied object must be a dsl for a registered entity but cannot be null");
HelenusEntity entity = Helenus.resolve(pojo); HelenusEntity entity = Helenus.resolve(pojo);
return new SelectOperation<Row>(this, entity); return new SelectOperation<Row>(this, entity);
} }
@ -406,7 +420,8 @@ public final class HelenusSession extends AbstractSessionOperations implements C
public <E> UpdateOperation<E> update(Drafted<E> drafted) { public <E> UpdateOperation<E> update(Drafted<E> drafted) {
if (drafted instanceof AbstractEntityDraft == false) { if (drafted instanceof AbstractEntityDraft == false) {
throw new HelenusMappingException("update of draft objects that don't inherit from AbstractEntityDraft is not yet supported"); throw new HelenusMappingException(
"update of draft objects that don't inherit from AbstractEntityDraft is not yet supported");
} }
AbstractEntityDraft<E> draft = (AbstractEntityDraft<E>) drafted; AbstractEntityDraft<E> draft = (AbstractEntityDraft<E>) drafted;
UpdateOperation update = new UpdateOperation<E>(this, draft); UpdateOperation update = new UpdateOperation<E>(this, draft);
@ -415,7 +430,10 @@ public final class HelenusSession extends AbstractSessionOperations implements C
HelenusEntity entity = Helenus.entity(draft.getEntityClass()); HelenusEntity entity = Helenus.entity(draft.getEntityClass());
// Add all the mutated values contained in the draft. // Add all the mutated values contained in the draft.
entity.getOrderedProperties().forEach(property -> { entity
.getOrderedProperties()
.forEach(
property -> {
switch (property.getColumnType()) { switch (property.getColumnType()) {
case PARTITION_KEY: case PARTITION_KEY:
case CLUSTERING_COLUMN: case CLUSTERING_COLUMN:
@ -424,10 +442,12 @@ public final class HelenusSession extends AbstractSessionOperations implements C
String propertyName = property.getPropertyName(); String propertyName = property.getPropertyName();
if (mutatedProperties.contains(propertyName)) { if (mutatedProperties.contains(propertyName)) {
Object value = map.get(propertyName); Object value = map.get(propertyName);
Getter<Object> getter = new Getter<Object>() { Getter<Object> getter =
new Getter<Object>() {
@Override @Override
public Object get() { public Object get() {
throw new DslPropertyException(new HelenusPropertyNode(property, Optional.empty())); throw new DslPropertyException(
new HelenusPropertyNode(property, Optional.empty()));
} }
}; };
update.set(getter, value); update.set(getter, value);
@ -436,16 +456,21 @@ public final class HelenusSession extends AbstractSessionOperations implements C
}); });
// Add the partition and clustering keys if they were in the draft (normally the case). // Add the partition and clustering keys if they were in the draft (normally the case).
entity.getOrderedProperties().forEach(property -> { entity
.getOrderedProperties()
.forEach(
property -> {
switch (property.getColumnType()) { switch (property.getColumnType()) {
case PARTITION_KEY: case PARTITION_KEY:
case CLUSTERING_COLUMN: case CLUSTERING_COLUMN:
String propertyName = property.getPropertyName(); String propertyName = property.getPropertyName();
Object value = map.get(propertyName); Object value = map.get(propertyName);
Getter<Object> getter = new Getter<Object>() { Getter<Object> getter =
new Getter<Object>() {
@Override @Override
public Object get() { public Object get() {
throw new DslPropertyException(new HelenusPropertyNode(property, Optional.empty())); throw new DslPropertyException(
new HelenusPropertyNode(property, Optional.empty()));
} }
}; };
update.where(getter, eq(value)); update.where(getter, eq(value));
@ -473,9 +498,14 @@ public final class HelenusSession extends AbstractSessionOperations implements C
} }
public <T> InsertOperation<T> insert(T pojo) { public <T> InsertOperation<T> 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"); 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; HelenusEntity entity = null;
try { entity = Helenus.resolve(pojo); } catch (HelenusMappingException e) {} try {
entity = Helenus.resolve(pojo);
} catch (HelenusMappingException e) {
}
if (entity != null) { if (entity != null) {
return new InsertOperation<T>(this, entity.getMappingInterface(), true); return new InsertOperation<T>(this, entity.getMappingInterface(), true);
} else { } else {
@ -483,7 +513,9 @@ public final class HelenusSession extends AbstractSessionOperations implements C
} }
} }
public <T> InsertOperation<T> insert(Drafted draft) { return insert(draft.build(), draft.mutated()); } public <T> InsertOperation<T> insert(Drafted draft) {
return insert(draft.build(), draft.mutated());
}
private <T> InsertOperation<T> insert(T pojo, Set<String> mutations) { private <T> InsertOperation<T> insert(T pojo, Set<String> mutations) {
Objects.requireNonNull(pojo, "pojo is empty"); Objects.requireNonNull(pojo, "pojo is empty");
@ -507,9 +539,14 @@ public final class HelenusSession extends AbstractSessionOperations implements C
} }
public <T> InsertOperation<T> upsert(T pojo) { public <T> InsertOperation<T> 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"); 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; HelenusEntity entity = null;
try { entity = Helenus.resolve(pojo); } catch (HelenusMappingException e) {} try {
entity = Helenus.resolve(pojo);
} catch (HelenusMappingException e) {
}
if (entity != null) { if (entity != null) {
return new InsertOperation<T>(this, entity.getMappingInterface(), false); return new InsertOperation<T>(this, entity.getMappingInterface(), false);
} else { } else {
@ -582,5 +619,4 @@ public final class HelenusSession extends AbstractSessionOperations implements C
break; break;
} }
} }
} }

View file

@ -1,8 +1,7 @@
package net.helenus.core; package net.helenus.core;
import java.util.Objects;
import java.util.*; import java.util.*;
import java.util.Objects;
public class PostCommitFunction<T, R> implements java.util.function.Function<T, R> { public class PostCommitFunction<T, R> implements java.util.function.Function<T, R> {

View file

@ -17,16 +17,22 @@ package net.helenus.core;
import com.datastax.driver.core.*; import com.datastax.driver.core.*;
import com.datastax.driver.core.IndexMetadata; import com.datastax.driver.core.IndexMetadata;
import com.datastax.driver.core.querybuilder.IsNotNullClause;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.schemabuilder.*; import com.datastax.driver.core.schemabuilder.*;
import com.datastax.driver.core.schemabuilder.Create.Options; import com.datastax.driver.core.schemabuilder.Create.Options;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.*; import net.helenus.mapping.*;
import net.helenus.mapping.ColumnType; import net.helenus.mapping.ColumnType;
import net.helenus.mapping.annotation.ClusteringColumn;
import net.helenus.mapping.type.OptionalColumnMetadata; import net.helenus.mapping.type.OptionalColumnMetadata;
import net.helenus.support.CqlUtil; import net.helenus.support.CqlUtil;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
public final class SchemaUtil { public final class SchemaUtil {
private SchemaUtil() {} private SchemaUtil() {}
@ -143,6 +149,78 @@ public final class SchemaUtil {
return SchemaBuilder.dropType(type.getTypeName()).ifExists(); return SchemaBuilder.dropType(type.getTypeName()).ifExists();
} }
public static SchemaStatement createMaterializedView(
String keyspace, String viewName, HelenusEntity entity) {
if (entity.getType() != HelenusEntityType.VIEW) {
throw new HelenusMappingException("expected view entity " + entity);
}
if (entity == null) {
throw new HelenusMappingException("no entity or table to select data");
}
List<HelenusPropertyNode> props = new ArrayList<HelenusPropertyNode>();
entity
.getOrderedProperties()
.stream()
.map(p -> new HelenusPropertyNode(p, Optional.empty()))
.forEach(p -> props.add(p));
Select.Selection selection = QueryBuilder.select();
for (HelenusPropertyNode prop : props) {
String columnName = prop.getColumnName();
selection = selection.column(columnName);
}
Class<?> iface = entity.getMappingInterface();
String tableName = Helenus.entity(iface.getInterfaces()[0]).getName().toCql();
Select.Where where = selection.from(tableName).where();
List<String> p = new ArrayList<String>(props.size());
List<String> c = new ArrayList<String>(props.size());
List<String> o = new ArrayList<String>(props.size());
for (HelenusPropertyNode prop : props) {
String columnName = prop.getColumnName();
switch (prop.getProperty().getColumnType()) {
case PARTITION_KEY:
p.add(columnName);
where = where.and(new IsNotNullClause(columnName));
break;
case CLUSTERING_COLUMN:
c.add(columnName);
where = where.and(new IsNotNullClause(columnName));
ClusteringColumn clusteringColumn = prop.getProperty().getGetterMethod().getAnnotation(ClusteringColumn.class);
if (clusteringColumn != null && clusteringColumn.ordering() != null) {
o.add(columnName + " " + clusteringColumn.ordering().cql());
}
break;
default:
break;
}
}
String primaryKey =
"PRIMARY KEY ("
+ ((p.size() > 1) ? "(" + String.join(", ", p) + ")" : p.get(0))
+ ((c.size() > 0)
? ", " + ((c.size() > 1) ? "(" + String.join(", ", c) + ")" : c.get(0))
: "")
+ ")";
String clustering = "";
if (o.size() > 0) {
clustering = "WITH CLUSTERING ORDER BY (" + String.join(", ", o) + ")";
}
return new CreateMaterializedView(keyspace, viewName, where, primaryKey, clustering);
}
public static SchemaStatement dropMaterializedView(
String keyspace, String viewName, HelenusEntity entity) {
return new DropMaterializedView(keyspace, viewName);
}
public static SchemaStatement createTable(HelenusEntity entity) { public static SchemaStatement createTable(HelenusEntity entity) {
if (entity.getType() != HelenusEntityType.TABLE) { if (entity.getType() != HelenusEntityType.TABLE) {

View file

@ -25,10 +25,13 @@ import java.util.*;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.function.Consumer; import java.util.function.Consumer;
import net.helenus.core.reflect.DslExportable;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusEntityType; import net.helenus.mapping.HelenusEntityType;
import net.helenus.mapping.MappingUtil;
import net.helenus.mapping.value.ColumnValuePreparer; import net.helenus.mapping.value.ColumnValuePreparer;
import net.helenus.mapping.value.ColumnValueProvider; import net.helenus.mapping.value.ColumnValueProvider;
import net.helenus.support.Either;
import net.helenus.support.HelenusException; import net.helenus.support.HelenusException;
import net.helenus.support.PackageUtil; import net.helenus.support.PackageUtil;
@ -39,6 +42,7 @@ public final class SessionInitializer extends AbstractSessionOperations {
private String usingKeyspace; private String usingKeyspace;
private boolean showCql = false; private boolean showCql = false;
private ConsistencyLevel consistencyLevel; private ConsistencyLevel consistencyLevel;
private boolean idempotent = true;
private MetricRegistry metricRegistry = new MetricRegistry(); private MetricRegistry metricRegistry = new MetricRegistry();
private Tracer zipkinTracer; private Tracer zipkinTracer;
private PrintStream printStream = System.out; private PrintStream printStream = System.out;
@ -52,7 +56,7 @@ public final class SessionInitializer extends AbstractSessionOperations {
private KeyspaceMetadata keyspaceMetadata; private KeyspaceMetadata keyspaceMetadata;
private final List<Object> initList = new ArrayList<Object>(); private final List<Either<Object, Class<?>>> initList = new ArrayList<Either<Object, Class<?>>>();
private AutoDdl autoDdl = AutoDdl.UPDATE; private AutoDdl autoDdl = AutoDdl.UPDATE;
SessionInitializer(Session session) { SessionInitializer(Session session) {
@ -125,6 +129,15 @@ public final class SessionInitializer extends AbstractSessionOperations {
return consistencyLevel; return consistencyLevel;
} }
public SessionInitializer idempotentQueryExecution(boolean idempotent) {
this.idempotent = idempotent;
return this;
}
public boolean getDefaultQueryIdempotency() {
return idempotent;
}
@Override @Override
public PrintStream getPrintStream() { public PrintStream getPrintStream() {
return printStream; return printStream;
@ -171,7 +184,10 @@ public final class SessionInitializer extends AbstractSessionOperations {
PackageUtil.getClasses(packageName) PackageUtil.getClasses(packageName)
.stream() .stream()
.filter(c -> c.isInterface() && !c.isAnnotation()) .filter(c -> c.isInterface() && !c.isAnnotation())
.forEach(initList::add); .forEach(
clazz -> {
initList.add(Either.right(clazz));
});
} catch (IOException | ClassNotFoundException e) { } catch (IOException | ClassNotFoundException e) {
throw new HelenusException("fail to add package " + packageName, e); throw new HelenusException("fail to add package " + packageName, e);
} }
@ -183,7 +199,7 @@ public final class SessionInitializer extends AbstractSessionOperations {
int len = dsls.length; int len = dsls.length;
for (int i = 0; i != len; ++i) { for (int i = 0; i != len; ++i) {
Object obj = Objects.requireNonNull(dsls[i], "element " + i + " is empty"); Object obj = Objects.requireNonNull(dsls[i], "element " + i + " is empty");
initList.add(obj); initList.add(Either.left(obj));
} }
return this; return this;
} }
@ -241,6 +257,7 @@ public final class SessionInitializer extends AbstractSessionOperations {
executor, executor,
autoDdl == AutoDdl.CREATE_DROP, autoDdl == AutoDdl.CREATE_DROP,
consistencyLevel, consistencyLevel,
idempotent,
unitOfWorkClass, unitOfWorkClass,
metricRegistry, metricRegistry,
zipkinTracer); zipkinTracer);
@ -250,7 +267,19 @@ public final class SessionInitializer extends AbstractSessionOperations {
Objects.requireNonNull(usingKeyspace, "please define keyspace by 'use' operator"); Objects.requireNonNull(usingKeyspace, "please define keyspace by 'use' operator");
initList.forEach(dsl -> sessionRepository.add(dsl)); initList.forEach(
(either) -> {
Class<?> iface = null;
if (either.isLeft()) {
iface = MappingUtil.getMappingInterface(either.getLeft());
} else {
iface = either.getRight();
}
DslExportable dsl = (DslExportable) Helenus.dsl(iface);
dsl.setCassandraMetadataForHelenusSesion(session.getCluster().getMetadata());
sessionRepository.add(dsl);
});
TableOperations tableOps = new TableOperations(this, dropUnusedColumns, dropUnusedIndexes); TableOperations tableOps = new TableOperations(this, dropUnusedColumns, dropUnusedIndexes);
UserTypeOperations userTypeOps = new UserTypeOperations(this, dropUnusedColumns); UserTypeOperations userTypeOps = new UserTypeOperations(this, dropUnusedColumns);
@ -258,8 +287,16 @@ public final class SessionInitializer extends AbstractSessionOperations {
switch (autoDdl) { switch (autoDdl) {
case CREATE_DROP: case CREATE_DROP:
// Drop tables first, otherwise a `DROP TYPE ...` will fail as the type is still referenced // Drop view first, otherwise a `DROP TABLE ...` will fail as the type is still referenced
// by a table. // by a view.
sessionRepository
.entities()
.stream()
.filter(e -> e.getType() == HelenusEntityType.VIEW)
.forEach(e -> tableOps.dropView(e));
// Drop tables second, before DROP TYPE otherwise a `DROP TYPE ...` will fail as the type is
// still referenced by a table.
sessionRepository sessionRepository
.entities() .entities()
.stream() .stream()
@ -278,6 +315,12 @@ public final class SessionInitializer extends AbstractSessionOperations {
.filter(e -> e.getType() == HelenusEntityType.TABLE) .filter(e -> e.getType() == HelenusEntityType.TABLE)
.forEach(e -> tableOps.createTable(e)); .forEach(e -> tableOps.createTable(e));
sessionRepository
.entities()
.stream()
.filter(e -> e.getType() == HelenusEntityType.VIEW)
.forEach(e -> tableOps.createView(e));
break; break;
case VALIDATE: case VALIDATE:
@ -288,16 +331,29 @@ public final class SessionInitializer extends AbstractSessionOperations {
.stream() .stream()
.filter(e -> e.getType() == HelenusEntityType.TABLE) .filter(e -> e.getType() == HelenusEntityType.TABLE)
.forEach(e -> tableOps.validateTable(getTableMetadata(e), e)); .forEach(e -> tableOps.validateTable(getTableMetadata(e), e));
break; break;
case UPDATE: case UPDATE:
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.updateUserType(getUserType(e), e)); eachUserTypeInOrder(userTypeOps, e -> userTypeOps.updateUserType(getUserType(e), e));
sessionRepository
.entities()
.stream()
.filter(e -> e.getType() == HelenusEntityType.VIEW)
.forEach(e -> tableOps.dropView(e));
sessionRepository sessionRepository
.entities() .entities()
.stream() .stream()
.filter(e -> e.getType() == HelenusEntityType.TABLE) .filter(e -> e.getType() == HelenusEntityType.TABLE)
.forEach(e -> tableOps.updateTable(getTableMetadata(e), e)); .forEach(e -> tableOps.updateTable(getTableMetadata(e), e));
sessionRepository
.entities()
.stream()
.filter(e -> e.getType() == HelenusEntityType.VIEW)
.forEach(e -> tableOps.createView(e));
break; break;
} }

View file

@ -35,14 +35,11 @@ public final class TableOperations {
} }
public void createTable(HelenusEntity entity) { public void createTable(HelenusEntity entity) {
sessionOps.execute(SchemaUtil.createTable(entity), true); sessionOps.execute(SchemaUtil.createTable(entity), true);
executeBatch(SchemaUtil.createIndexes(entity)); executeBatch(SchemaUtil.createIndexes(entity));
} }
public void dropTable(HelenusEntity entity) { public void dropTable(HelenusEntity entity) {
sessionOps.execute(SchemaUtil.dropTable(entity), true); sessionOps.execute(SchemaUtil.dropTable(entity), true);
} }
@ -50,7 +47,10 @@ public final class TableOperations {
if (tmd == null) { if (tmd == null) {
throw new HelenusException( throw new HelenusException(
"table not exists " + entity.getName() + "for entity " + entity.getMappingInterface()); "table does not exists "
+ entity.getName()
+ "for entity "
+ entity.getMappingInterface());
} }
List<SchemaStatement> list = SchemaUtil.alterTable(tmd, entity, dropUnusedColumns); List<SchemaStatement> list = SchemaUtil.alterTable(tmd, entity, dropUnusedColumns);
@ -67,7 +67,31 @@ public final class TableOperations {
} }
public void updateTable(TableMetadata tmd, HelenusEntity entity) { public void updateTable(TableMetadata tmd, HelenusEntity entity) {
if (tmd == null) {
createTable(entity);
return;
}
executeBatch(SchemaUtil.alterTable(tmd, entity, dropUnusedColumns));
executeBatch(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes));
}
public void createView(HelenusEntity entity) {
sessionOps.execute(
SchemaUtil.createMaterializedView(
sessionOps.usingKeyspace(), entity.getName().toCql(), entity),
true);
// executeBatch(SchemaUtil.createIndexes(entity)); NOTE: Unfortunately C* 3.10 does not yet support 2i on materialized views.
}
public void dropView(HelenusEntity entity) {
sessionOps.execute(
SchemaUtil.dropMaterializedView(
sessionOps.usingKeyspace(), entity.getName().toCql(), entity),
true);
}
public void updateView(TableMetadata tmd, HelenusEntity entity) {
if (tmd == null) { if (tmd == null) {
createTable(entity); createTable(entity);
return; return;

View file

@ -15,7 +15,6 @@
*/ */
package net.helenus.core; package net.helenus.core;
import net.helenus.support.Either;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -41,12 +40,11 @@ public interface UnitOfWork<E extends Exception> extends AutoCloseable {
PostCommitFunction<Void, Void> commit() throws E; PostCommitFunction<Void, Void> commit() throws E;
/** /**
* Explicitly abort the work within this unit of work. Any nested aborted unit of work * Explicitly abort the work within this unit of work. Any nested aborted unit of work will
* will trigger the entire unit of work to commit. * trigger the entire unit of work to commit.
*/ */
void abort(); void abort();
boolean hasAborted(); boolean hasAborted();
boolean hasCommitted(); boolean hasCommitted();

View file

@ -23,5 +23,4 @@ class UnitOfWorkImpl extends AbstractUnitOfWork<HelenusException> {
public UnitOfWorkImpl(HelenusSession session, UnitOfWork parent) { public UnitOfWorkImpl(HelenusSession session, UnitOfWork parent) {
super(session, (AbstractUnitOfWork<HelenusException>) parent); super(session, (AbstractUnitOfWork<HelenusException>) parent);
} }
} }

View file

@ -19,11 +19,11 @@ import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import net.helenus.core.*; import net.helenus.core.*;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
public abstract class AbstractFilterOptionalOperation<E, O extends AbstractFilterOptionalOperation<E, O>> public abstract class AbstractFilterOptionalOperation<
E, O extends AbstractFilterOptionalOperation<E, O>>
extends AbstractOptionalOperation<E, O> { extends AbstractOptionalOperation<E, O> {
protected Map<HelenusProperty, Filter<?>> filters = null; protected Map<HelenusProperty, Filter<?>> filters = null;

View file

@ -19,11 +19,11 @@ import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import net.helenus.core.*; import net.helenus.core.*;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
public abstract class AbstractFilterStreamOperation<E, O extends AbstractFilterStreamOperation<E, O>> public abstract class AbstractFilterStreamOperation<
E, O extends AbstractFilterStreamOperation<E, O>>
extends AbstractStreamOperation<E, O> { extends AbstractStreamOperation<E, O> {
protected Map<HelenusProperty, Filter<?>> filters = null; protected Map<HelenusProperty, Filter<?>> filters = null;

View file

@ -17,8 +17,6 @@ package net.helenus.core.operation;
import com.codahale.metrics.Timer; import com.codahale.metrics.Timer;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
import java.util.Objects;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
@ -40,7 +38,6 @@ public abstract class AbstractOperation<E, O extends AbstractOperation<E, O>>
return new PreparedOperation<E>(prepareStatement(), this); return new PreparedOperation<E>(prepareStatement(), this);
} }
public E sync() { public E sync() {
final Timer.Context context = requestLatency.time(); final Timer.Context context = requestLatency.time();
try { try {
@ -72,5 +69,4 @@ public abstract class AbstractOperation<E, O extends AbstractOperation<E, O>>
if (uow == null) return async(); if (uow == null) return async();
return CompletableFuture.<E>supplyAsync(() -> sync(uow)); return CompletableFuture.<E>supplyAsync(() -> sync(uow));
} }
} }

View file

@ -21,12 +21,10 @@ import com.datastax.driver.core.ResultSet;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
@ -114,5 +112,4 @@ public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOpe
if (uow == null) return async(); if (uow == null) return async();
return CompletableFuture.<Optional<E>>supplyAsync(() -> sync(uow)); return CompletableFuture.<Optional<E>>supplyAsync(() -> sync(uow));
} }
} }

View file

@ -32,7 +32,8 @@ import net.helenus.support.HelenusException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public abstract class AbstractStatementOperation<E, O extends AbstractStatementOperation<E, O>> extends Operation<E> { public abstract class AbstractStatementOperation<E, O extends AbstractStatementOperation<E, O>>
extends Operation<E> {
final Logger logger = LoggerFactory.getLogger(getClass()); final Logger logger = LoggerFactory.getLogger(getClass());
@ -44,6 +45,7 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
private ConsistencyLevel consistencyLevel; private ConsistencyLevel consistencyLevel;
private ConsistencyLevel serialConsistencyLevel; private ConsistencyLevel serialConsistencyLevel;
private RetryPolicy retryPolicy; private RetryPolicy retryPolicy;
private boolean idempotent = false;
private boolean enableTracing = false; private boolean enableTracing = false;
private long[] defaultTimestamp = null; private long[] defaultTimestamp = null;
private int[] fetchSize = null; private int[] fetchSize = null;
@ -51,9 +53,9 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
public AbstractStatementOperation(AbstractSessionOperations sessionOperations) { public AbstractStatementOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
this.consistencyLevel = sessionOperations.getDefaultConsistencyLevel(); this.consistencyLevel = sessionOperations.getDefaultConsistencyLevel();
this.idempotent = sessionOperations.getDefaultQueryIdempotency();
} }
public O ignoreCache(boolean enabled) { public O ignoreCache(boolean enabled) {
enableCache = enabled; enableCache = enabled;
return (O) this; return (O) this;
@ -85,6 +87,16 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
return (O) this; return (O) this;
} }
public O idempotent() {
this.idempotent = true;
return (O) this;
}
public O isIdempotent(boolean idempotent) {
this.idempotent = idempotent;
return (O) this;
}
public O downgradingConsistencyRetryPolicy() { public O downgradingConsistencyRetryPolicy() {
this.retryPolicy = DowngradingConsistencyRetryPolicy.INSTANCE; this.retryPolicy = DowngradingConsistencyRetryPolicy.INSTANCE;
return (O) this; return (O) this;
@ -219,6 +231,10 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
statement.setFetchSize(fetchSize[0]); statement.setFetchSize(fetchSize[0]);
} }
if (idempotent) {
statement.setIdempotent(true);
}
return statement; return statement;
} }

View file

@ -21,20 +21,11 @@ import com.datastax.driver.core.ResultSet;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.Helenus;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.value.ColumnValueProvider;
import net.helenus.mapping.value.ValueProviderMap;
public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperation<E, O>> public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperation<E, O>>
extends AbstractStatementOperation<E, O> { extends AbstractStatementOperation<E, O> {
@ -113,5 +104,4 @@ public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperati
if (uow == null) return async(); if (uow == null) return async();
return CompletableFuture.<Stream<E>>supplyAsync(() -> sync(uow)); return CompletableFuture.<Stream<E>>supplyAsync(() -> sync(uow));
} }
} }

View file

@ -34,7 +34,9 @@ public final class BoundOptionalOperation<E>
} }
@Override @Override
public Optional<E> transform(ResultSet resultSet) { return delegate.transform(resultSet); } public Optional<E> transform(ResultSet resultSet) {
return delegate.transform(resultSet);
}
@Override @Override
public Statement buildStatement(boolean cached) { public Statement buildStatement(boolean cached) {

View file

@ -26,14 +26,17 @@ public final class BoundStreamOperation<E>
private final BoundStatement boundStatement; private final BoundStatement boundStatement;
private final AbstractStreamOperation<E, ?> delegate; private final AbstractStreamOperation<E, ?> delegate;
public BoundStreamOperation(BoundStatement boundStatement, AbstractStreamOperation<E, ?> operation) { public BoundStreamOperation(
BoundStatement boundStatement, AbstractStreamOperation<E, ?> operation) {
super(operation.sessionOps); super(operation.sessionOps);
this.boundStatement = boundStatement; this.boundStatement = boundStatement;
this.delegate = operation; this.delegate = operation;
} }
@Override @Override
public String getStatementCacheKey() { return delegate.getStatementCacheKey(); } public String getStatementCacheKey() {
return delegate.getStatementCacheKey();
}
@Override @Override
public Stream<E> transform(ResultSet resultSet) { public Stream<E> transform(ResultSet resultSet) {
@ -41,5 +44,7 @@ public final class BoundStreamOperation<E>
} }
@Override @Override
public Statement buildStatement(boolean cached) { return boundStatement; } public Statement buildStatement(boolean cached) {
return boundStatement;
}
} }

View file

@ -19,10 +19,9 @@ import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.querybuilder.BuiltStatement; import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.datastax.driver.core.querybuilder.Insert; import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.google.common.base.Joiner;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import com.google.common.base.Joiner;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.Getter; import net.helenus.core.Getter;
import net.helenus.core.Helenus; import net.helenus.core.Helenus;
@ -41,7 +40,8 @@ public final class InsertOperation<T> extends AbstractOperation<T, InsertOperati
private HelenusEntity entity; private HelenusEntity entity;
private final List<Fun.Tuple2<HelenusPropertyNode, Object>> values = new ArrayList<Fun.Tuple2<HelenusPropertyNode, Object>>(); private final List<Fun.Tuple2<HelenusPropertyNode, Object>> values =
new ArrayList<Fun.Tuple2<HelenusPropertyNode, Object>>();
private final T pojo; private final T pojo;
private final Class<?> resultType; private final Class<?> resultType;
private boolean ifNotExists; private boolean ifNotExists;
@ -57,7 +57,8 @@ public final class InsertOperation<T> extends AbstractOperation<T, InsertOperati
this.resultType = ResultSet.class; this.resultType = ResultSet.class;
} }
public InsertOperation(AbstractSessionOperations sessionOperations, Class<?> resultType, boolean ifNotExists) { public InsertOperation(
AbstractSessionOperations sessionOperations, Class<?> resultType, boolean ifNotExists) {
super(sessionOperations); super(sessionOperations);
this.ifNotExists = ifNotExists; this.ifNotExists = ifNotExists;
@ -248,6 +249,9 @@ public final class InsertOperation<T> extends AbstractOperation<T, InsertOperati
@Override @Override
public T sync(UnitOfWork uow) { public T sync(UnitOfWork uow) {
if (uow == null) {
return sync();
}
T result = super.sync(uow); T result = super.sync(uow);
Class<?> iface = entity.getMappingInterface(); Class<?> iface = entity.getMappingInterface();
if (resultType == iface) { if (resultType == iface) {
@ -260,5 +264,4 @@ public final class InsertOperation<T> extends AbstractOperation<T, InsertOperati
} }
return result; return result;
} }
} }

View file

@ -1,21 +1,18 @@
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.concurrent.ExecutionException; import brave.Span;
import brave.Tracer;
import brave.propagation.TraceContext;
import com.codahale.metrics.Meter; import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer; import com.codahale.metrics.Timer;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture; import com.datastax.driver.core.ResultSetFuture;
import com.datastax.driver.core.Statement; import com.datastax.driver.core.Statement;
import java.util.concurrent.ExecutionException;
import brave.Span;
import brave.Tracer;
import brave.propagation.TraceContext;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
public abstract class Operation<E> { public abstract class Operation<E> {
protected final AbstractSessionOperations sessionOps; protected final AbstractSessionOperations sessionOps;
@ -31,7 +28,12 @@ public abstract class Operation<E> {
this.requestLatency = metrics.timer("net.helenus.request-latency"); this.requestLatency = metrics.timer("net.helenus.request-latency");
} }
public ResultSet execute(AbstractSessionOperations session, UnitOfWork uow, TraceContext traceContext, boolean showValues, boolean cached) { public ResultSet execute(
AbstractSessionOperations session,
UnitOfWork uow,
TraceContext traceContext,
boolean showValues,
boolean cached) {
// Start recording in a Zipkin sub-span our execution time to perform this operation. // Start recording in a Zipkin sub-span our execution time to perform this operation.
Tracer tracer = session.getZipkinTracer(); Tracer tracer = session.getZipkinTracer();
@ -60,14 +62,18 @@ public abstract class Operation<E> {
if (span != null) { if (span != null) {
span.finish(); span.finish();
} }
} }
} }
public Statement options(Statement statement) { return statement; } public Statement options(Statement statement) {
return statement;
public Statement buildStatement(boolean cached) { return null; } }
public String getStatementCacheKey() { return null; } public Statement buildStatement(boolean cached) {
return null;
}
public String getStatementCacheKey() {
return null;
}
} }

View file

@ -17,7 +17,6 @@ package net.helenus.core.operation;
import com.datastax.driver.core.BoundStatement; import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.PreparedStatement;
import java.util.regex.Pattern;
public final class PreparedStreamOperation<E> { public final class PreparedStreamOperation<E> {

View file

@ -38,7 +38,9 @@ public final class SelectFirstOperation<E>
} }
@Override @Override
public String getStatementCacheKey() { return delegate.getStatementCacheKey(); } public String getStatementCacheKey() {
return delegate.getStatementCacheKey();
}
@Override @Override
public BuiltStatement buildStatement(boolean cached) { public BuiltStatement buildStatement(boolean cached) {

View file

@ -36,7 +36,9 @@ public final class SelectFirstTransformingOperation<R, E>
} }
@Override @Override
public String getStatementCacheKey() { return delegate.getStatementCacheKey(); } public String getStatementCacheKey() {
return delegate.getStatementCacheKey();
}
@Override @Override
public BuiltStatement buildStatement(boolean cached) { public BuiltStatement buildStatement(boolean cached) {

View file

@ -23,13 +23,12 @@ import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select; import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Select.Selection; import com.datastax.driver.core.querybuilder.Select.Selection;
import com.datastax.driver.core.querybuilder.Select.Where; import com.datastax.driver.core.querybuilder.Select.Where;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import net.helenus.core.*; import net.helenus.core.*;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
@ -48,6 +47,7 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
protected List<Ordering> ordering = null; protected List<Ordering> ordering = null;
protected Integer limit = null; protected Integer limit = null;
protected boolean allowFiltering = false; protected boolean allowFiltering = false;
protected String alternateTableName = null;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public SelectOperation(AbstractSessionOperations sessionOperations) { public SelectOperation(AbstractSessionOperations sessionOperations) {
@ -129,6 +129,19 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
return new CountOperation(sessionOps, entity); return new CountOperation(sessionOps, entity);
} }
public <V extends E> SelectOperation<E> from(Class<V> materializedViewClass) {
Objects.requireNonNull(materializedViewClass);
HelenusEntity entity = Helenus.entity(materializedViewClass);
this.alternateTableName = entity.getName().toCql();
this.allowFiltering = true;
return this;
}
public SelectOperation<E> from(String alternateTableName) {
this.alternateTableName = alternateTableName;
return this;
}
public SelectFirstOperation<E> single() { public SelectFirstOperation<E> single() {
limit(1); limit(1);
return new SelectFirstOperation<E>(this); return new SelectFirstOperation<E>(this);
@ -189,7 +202,6 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
switch (prop.getProperty().getColumnType()) { switch (prop.getProperty().getColumnType()) {
case PARTITION_KEY: case PARTITION_KEY:
case CLUSTERING_COLUMN: case CLUSTERING_COLUMN:
Filter filter = filters.get(prop.getProperty()); Filter filter = filters.get(prop.getProperty());
if (filter != null) { if (filter != null) {
keys.add(filter.toString()); keys.add(filter.toString());
@ -255,7 +267,8 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
throw new HelenusMappingException("no entity or table to select data"); throw new HelenusMappingException("no entity or table to select data");
} }
Select select = selection.from(entity.getName().toCql()); String tableName = alternateTableName == null ? entity.getName().toCql() : alternateTableName;
Select select = selection.from(tableName);
if (ordering != null && !ordering.isEmpty()) { if (ordering != null && !ordering.isEmpty()) {
select.orderBy(ordering.toArray(new Ordering[ordering.size()])); select.orderBy(ordering.toArray(new Ordering[ordering.size()]));
@ -290,10 +303,14 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
@Override @Override
public Stream<E> transform(ResultSet resultSet) { public Stream<E> transform(ResultSet resultSet) {
if (rowMapper != null) { if (rowMapper != null) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED), false).map(rowMapper); return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED), false)
.map(rowMapper);
} else { } else {
return (Stream<E>) return (Stream<E>)
StreamSupport.stream(Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED),false); StreamSupport.stream(
Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED),
false);
} }
} }

View file

@ -36,7 +36,9 @@ public final class SelectTransformingOperation<R, E>
} }
@Override @Override
public String getStatementCacheKey() { return delegate.getStatementCacheKey(); } public String getStatementCacheKey() {
return delegate.getStatementCacheKey();
}
@Override @Override
public BuiltStatement buildStatement(boolean cached) { public BuiltStatement buildStatement(boolean cached) {
@ -47,5 +49,4 @@ public final class SelectTransformingOperation<R, E>
public Stream<R> transform(ResultSet resultSet) { public Stream<R> transform(ResultSet resultSet) {
return delegate.transform(resultSet).map(fn); return delegate.transform(resultSet).map(fn);
} }
} }

View file

@ -15,15 +15,13 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.*;
import java.util.function.Function;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.querybuilder.Assignment; import com.datastax.driver.core.querybuilder.Assignment;
import com.datastax.driver.core.querybuilder.BuiltStatement; import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Update; import com.datastax.driver.core.querybuilder.Update;
import java.util.*;
import java.util.function.Function;
import net.helenus.core.*; import net.helenus.core.*;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
@ -49,13 +47,15 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
this.draftMap = null; this.draftMap = null;
} }
public UpdateOperation(AbstractSessionOperations sessionOperations, AbstractEntityDraft<E> draft) { public UpdateOperation(
AbstractSessionOperations sessionOperations, AbstractEntityDraft<E> draft) {
super(sessionOperations); super(sessionOperations);
this.draft = draft; this.draft = draft;
this.draftMap = draft.toMap(); this.draftMap = draft.toMap();
} }
public UpdateOperation(AbstractSessionOperations sessionOperations, HelenusPropertyNode p, Object v) { public UpdateOperation(
AbstractSessionOperations sessionOperations, HelenusPropertyNode p, Object v) {
super(sessionOperations); super(sessionOperations);
this.draft = null; this.draft = null;
this.draftMap = null; this.draftMap = null;
@ -579,6 +579,9 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
@Override @Override
public E sync(UnitOfWork uow) { public E sync(UnitOfWork uow) {
if (uow == null) {
return sync();
}
E result = super.sync(uow); E result = super.sync(uow);
if (draft != null) { if (draft != null) {
String key = getStatementCacheKey(); String key = getStatementCacheKey();
@ -590,5 +593,4 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
} }
return result; return result;
} }
} }

View file

@ -1,6 +1,5 @@
package net.helenus.core.reflect; package net.helenus.core.reflect;
import net.helenus.mapping.HelenusEntity;
import java.util.Set; import java.util.Set;

View file

@ -15,14 +15,18 @@
*/ */
package net.helenus.core.reflect; package net.helenus.core.reflect;
import com.datastax.driver.core.Metadata;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
public interface DslExportable { public interface DslExportable {
public static final String GET_ENTITY_METHOD = "getHelenusMappingEntity"; public static final String GET_ENTITY_METHOD = "getHelenusMappingEntity";
public static final String GET_PARENT_METHOD = "getParentDslHelenusPropertyNode"; public static final String GET_PARENT_METHOD = "getParentDslHelenusPropertyNode";
public static final String SET_METADATA_METHOD = "setCassandraMetadataForHelenusSesion";
HelenusEntity getHelenusMappingEntity(); HelenusEntity getHelenusMappingEntity();
HelenusPropertyNode getParentDslHelenusPropertyNode(); HelenusPropertyNode getParentDslHelenusPropertyNode();
void setCassandraMetadataForHelenusSesion(Metadata metadata);
} }

View file

@ -34,7 +34,12 @@ import net.helenus.support.HelenusException;
public class DslInvocationHandler<E> implements InvocationHandler { public class DslInvocationHandler<E> implements InvocationHandler {
private final HelenusEntity entity; private HelenusEntity entity = null;
private Metadata metadata = null;
private final Class<E> iface;
private final ClassLoader classLoader;
private final Optional<HelenusPropertyNode> parent; private final Optional<HelenusPropertyNode> parent;
private final Map<Method, HelenusProperty> map = new HashMap<Method, HelenusProperty>(); private final Map<Method, HelenusProperty> map = new HashMap<Method, HelenusProperty>();
@ -48,10 +53,22 @@ public class DslInvocationHandler<E> implements InvocationHandler {
Optional<HelenusPropertyNode> parent, Optional<HelenusPropertyNode> parent,
Metadata metadata) { Metadata metadata) {
this.entity = new HelenusMappingEntity(iface, metadata); this.metadata = metadata;
this.parent = parent; this.parent = parent;
this.iface = iface;
this.classLoader = classLoader;
}
public void setCassandraMetadataForHelenusSesion(Metadata metadata) {
if (metadata != null) {
this.metadata = metadata;
entity = init(metadata);
}
}
private HelenusEntity init(Metadata metadata) {
HelenusEntity entity = new HelenusMappingEntity(iface, metadata);
if (this.entity != null) {
for (HelenusProperty prop : entity.getOrderedProperties()) { for (HelenusProperty prop : entity.getOrderedProperties()) {
map.put(prop.getGetterMethod(), prop); map.put(prop.getGetterMethod(), prop);
@ -88,12 +105,14 @@ public class DslInvocationHandler<E> implements InvocationHandler {
} }
} }
} }
}
return entity;
} }
@Override @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HelenusEntity entity = this.entity;
String methodName = method.getName(); String methodName = method.getName();
if ("equals".equals(methodName) && method.getParameterCount() == 1) { if ("equals".equals(methodName) && method.getParameterCount() == 1) {
@ -107,6 +126,15 @@ public class DslInvocationHandler<E> implements InvocationHandler {
return false; return false;
} }
if (DslExportable.SET_METADATA_METHOD.equals(methodName)
&& args.length == 1
&& args[0] instanceof Metadata) {
if (metadata == null) {
this.setCassandraMetadataForHelenusSesion((Metadata) args[0]);
}
return null;
}
if (method.getParameterCount() != 0 || method.getReturnType() == void.class) { if (method.getParameterCount() != 0 || method.getReturnType() == void.class) {
throw new HelenusException("invalid getter method " + method); throw new HelenusException("invalid getter method " + method);
} }
@ -115,6 +143,14 @@ public class DslInvocationHandler<E> implements InvocationHandler {
return hashCode(); return hashCode();
} }
if (DslExportable.GET_PARENT_METHOD.equals(methodName)) {
return parent.get();
}
if (entity == null) {
entity = init(metadata);
}
if ("toString".equals(methodName)) { if ("toString".equals(methodName)) {
return entity.toString(); return entity.toString();
} }
@ -123,10 +159,6 @@ public class DslInvocationHandler<E> implements InvocationHandler {
return entity; return entity;
} }
if (DslExportable.GET_PARENT_METHOD.equals(methodName)) {
return parent.get();
}
HelenusProperty prop = map.get(method); HelenusProperty prop = map.get(method);
if (prop == null) { if (prop == null) {
prop = entity.getProperty(methodName); prop = entity.getProperty(methodName);

View file

@ -23,6 +23,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import net.helenus.core.Helenus;
import net.helenus.mapping.annotation.Transient; import net.helenus.mapping.annotation.Transient;
import net.helenus.support.HelenusException; import net.helenus.support.HelenusException;
@ -91,6 +92,10 @@ public class MapperInvocationHandler<E> implements InvocationHandler, Serializab
return iface.getSimpleName() + ": " + src.toString(); return iface.getSimpleName() + ": " + src.toString();
} }
if ("dsl".equals(methodName)) {
return Helenus.dsl(iface);
}
if (MapExportable.TO_MAP_METHOD.equals(methodName)) { if (MapExportable.TO_MAP_METHOD.equals(methodName)) {
return Collections.unmodifiableMap(src); return Collections.unmodifiableMap(src);
} }

View file

@ -17,6 +17,7 @@ package net.helenus.mapping;
public enum HelenusEntityType { public enum HelenusEntityType {
TABLE, TABLE,
VIEW,
TUPLE, TUPLE,
UDT; UDT;
} }

View file

@ -25,6 +25,7 @@ import net.helenus.core.Helenus;
import net.helenus.core.annotation.Cacheable; import net.helenus.core.annotation.Cacheable;
import net.helenus.mapping.annotation.*; import net.helenus.mapping.annotation.*;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
import org.apache.commons.lang3.ClassUtils;
public final class HelenusMappingEntity implements HelenusEntity { public final class HelenusMappingEntity implements HelenusEntity {
@ -52,18 +53,33 @@ public final class HelenusMappingEntity implements HelenusEntity {
HelenusSettings settings = Helenus.settings(); HelenusSettings settings = Helenus.settings();
List<Method> methods = new ArrayList<Method>(); Map<String, Method> methods = new HashMap<String, Method>();
for (Method m : iface.getDeclaredMethods()) {
methods.put(m.getName(), m);
}
methods.addAll(Arrays.asList(iface.getDeclaredMethods())); for (Class<?> c : ClassUtils.getAllInterfaces(iface)) {
for (Class<?> c : iface.getInterfaces()) { if (c.getDeclaredAnnotation(Table.class) != null
methods.addAll(Arrays.asList(c.getDeclaredMethods())); || c.getDeclaredAnnotation(InheritedTable.class) != null) {
for (Method m : c.getDeclaredMethods()) {
Method o = methods.get(m.getName());
if (o != null) {
// Prefer overridden method implementation.
if (o.getDeclaringClass().isAssignableFrom(m.getDeclaringClass())) {
methods.put(m.getName(), m);
}
} else {
methods.put(m.getName(), m);
}
}
}
} }
List<HelenusProperty> propsLocal = new ArrayList<HelenusProperty>(); List<HelenusProperty> propsLocal = new ArrayList<HelenusProperty>();
ImmutableMap.Builder<String, HelenusProperty> propsBuilder = ImmutableMap.builder(); ImmutableMap.Builder<String, HelenusProperty> propsBuilder = ImmutableMap.builder();
ImmutableMap.Builder<String, Method> methodsBuilder = ImmutableMap.builder(); ImmutableMap.Builder<String, Method> methodsBuilder = ImmutableMap.builder();
for (Method method : methods) { for (Method method : methods.values()) {
if (settings.getGetterMethodDetector().apply(method)) { if (settings.getGetterMethodDetector().apply(method)) {
@ -130,6 +146,9 @@ public final class HelenusMappingEntity implements HelenusEntity {
case TABLE: case TABLE:
return MappingUtil.getTableName(iface, true); return MappingUtil.getTableName(iface, true);
case VIEW:
return MappingUtil.getViewName(iface, true);
case TUPLE: case TUPLE:
return IdentityName.of(MappingUtil.getDefaultEntityName(iface), false); return IdentityName.of(MappingUtil.getDefaultEntityName(iface), false);
@ -146,6 +165,8 @@ public final class HelenusMappingEntity implements HelenusEntity {
if (null != iface.getDeclaredAnnotation(Table.class)) { if (null != iface.getDeclaredAnnotation(Table.class)) {
return HelenusEntityType.TABLE; return HelenusEntityType.TABLE;
} else if (null != iface.getDeclaredAnnotation(MaterializedView.class)) {
return HelenusEntityType.VIEW;
} else if (null != iface.getDeclaredAnnotation(Tuple.class)) { } else if (null != iface.getDeclaredAnnotation(Tuple.class)) {
return HelenusEntityType.TUPLE; return HelenusEntityType.TUPLE;
} else if (null != iface.getDeclaredAnnotation(UDT.class)) { } else if (null != iface.getDeclaredAnnotation(UDT.class)) {

View file

@ -25,10 +25,7 @@ import javax.validation.ConstraintValidator;
import net.helenus.core.Getter; import net.helenus.core.Getter;
import net.helenus.core.Helenus; import net.helenus.core.Helenus;
import net.helenus.core.reflect.*; import net.helenus.core.reflect.*;
import net.helenus.mapping.annotation.Index; import net.helenus.mapping.annotation.*;
import net.helenus.mapping.annotation.Table;
import net.helenus.mapping.annotation.Tuple;
import net.helenus.mapping.annotation.UDT;
import net.helenus.support.DslPropertyException; import net.helenus.support.DslPropertyException;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
@ -172,6 +169,28 @@ public final class MappingUtil {
return udt != null; return udt != null;
} }
public static IdentityName getViewName(Class<?> iface, boolean required) {
String viewName = null;
boolean forceQuote = false;
MaterializedView view = iface.getDeclaredAnnotation(MaterializedView.class);
if (view != null) {
viewName = view.value();
forceQuote = view.forceQuote();
} else if (required) {
throw new HelenusMappingException("entity must have annotation @Table " + iface);
}
if (viewName == null || viewName.isEmpty()) {
viewName = getDefaultEntityName(iface);
}
return new IdentityName(viewName, forceQuote);
}
public static IdentityName getTableName(Class<?> iface, boolean required) { public static IdentityName getTableName(Class<?> iface, boolean required) {
String tableName = null; String tableName = null;
@ -222,6 +241,7 @@ public final class MappingUtil {
} }
if (iface.getDeclaredAnnotation(Table.class) != null if (iface.getDeclaredAnnotation(Table.class) != null
|| iface.getDeclaredAnnotation(MaterializedView.class) != null
|| iface.getDeclaredAnnotation(UDT.class) != null || iface.getDeclaredAnnotation(UDT.class) != null
|| iface.getDeclaredAnnotation(Tuple.class) != null) { || iface.getDeclaredAnnotation(Tuple.class) != null) {

View file

@ -0,0 +1,50 @@
package net.helenus.mapping.annotation;
import java.lang.annotation.*;
/**
* CoveringIndex annotation is using under the specific column or method in entity interface
* with @Table annotation.
*
* <p>A corresponding materialized view will be created based on the underline @Table for the
* specific column.
*
* <p>This is useful when you need to perform IN or SORT/ORDER-BY queries and to do so you'll need
* different materialized table on disk in Cassandra.
*
* <p>For each @Table annotated interface Helenus will create/update/verify Cassandra Materialized
* Views and some indexes if needed on startup.
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface CoveringIndex {
/**
* Defined the name of the index. By default the entity name with column name as suffix.
*
* @return name of the covering index
*/
String name() default "";
/**
* Set of fields in this entity to replicate in the index.
*
* @return array of the string names of the fields.
*/
String[] covering() default "";
/**
* Set of fields to use as the partition keys for this projection.
*
* @return array of the string names of the fields.
*/
String[] partitionKeys() default "";
/**
* Set of fields to use as the clustering columns for this projection.
*
* @return array of the string names of the fields.
*/
String[] clusteringColumns() default "";
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (C) 2015 The Helenus Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.helenus.mapping.annotation;
import java.lang.annotation.*;
/**
* Materialized alternate view of another Entity annotation
*
* <p>MaterializedView annotation is used to define different mapping to some other Table interface
*
* <p>This is useful when you need to perform IN or SORT/ORDER-BY queries and to do so you'll need
* different materialized table on disk in Cassandra.
*
* <p>For each @Table annotated interface Helenus will create/update/verify Cassandra Materialized
* Views and some indexes if needed on startup.
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface MaterializedView {
/**
* Default value is the SimpleName of the interface normalized to underscore
*
* @return name of the type
*/
String value() default "";
/**
* For reserved words in Cassandra we need quotation in CQL queries. This property marks that the
* name of the type needs to be quoted.
*
* <p>Default value is false, we are quoting only selected names.
*
* @return true if name have to be quoted
*/
boolean forceQuote() default false;
}

View file

@ -2,6 +2,7 @@ package net.helenus.mapping.javatype;
public abstract class AbstractCollectionJavaType extends AbstractJavaType { public abstract class AbstractCollectionJavaType extends AbstractJavaType {
public static boolean isCollectionType() { return true; } public static boolean isCollectionType() {
return true;
}
} }

View file

@ -33,7 +33,9 @@ import net.helenus.support.HelenusMappingException;
public abstract class AbstractJavaType { public abstract class AbstractJavaType {
public static boolean isCollectionType() { return false; } public static boolean isCollectionType() {
return false;
}
public abstract Class<?> getJavaClass(); public abstract Class<?> getJavaClass();

View file

@ -18,7 +18,6 @@ package net.helenus.mapping.javatype;
import com.datastax.driver.core.*; import com.datastax.driver.core.*;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.AbstractCollection;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;

View file

@ -8,6 +8,7 @@ public abstract class AbstractCollectionDataType extends AbstractDataType {
super(columnType); super(columnType);
} }
public boolean isCollectionType() { return true; } public boolean isCollectionType() {
return true;
}
} }

View file

@ -55,6 +55,7 @@ public abstract class AbstractDataType {
"wrong column type " + columnType + " for UserDefinedType in columnName " + columnName); "wrong column type " + columnType + " for UserDefinedType in columnName " + columnName);
} }
public boolean isCollectionType() { return false; } public boolean isCollectionType() {
return false;
}
} }

View file

@ -18,8 +18,6 @@ package net.helenus.mapping.type;
import com.datastax.driver.core.DataType; import com.datastax.driver.core.DataType;
import com.datastax.driver.core.UserType; import com.datastax.driver.core.UserType;
import com.datastax.driver.core.schemabuilder.*; import com.datastax.driver.core.schemabuilder.*;
import java.util.AbstractCollection;
import java.util.List; import java.util.List;
import net.helenus.mapping.ColumnType; import net.helenus.mapping.ColumnType;
import net.helenus.mapping.IdentityName; import net.helenus.mapping.IdentityName;

View file

@ -67,11 +67,8 @@ public class CollectionTest extends AbstractEmbeddedCassandraTest {
// read full object // read full object
Customer actual = session.<Customer>select(customer) Customer actual =
.where(customer::id, eq(id)) session.<Customer>select(customer).where(customer::id, eq(id)).single().sync().orElse(null);
.single()
.sync()
.orElse(null);
Assert.assertEquals(id, actual.id()); Assert.assertEquals(id, actual.id());
Assert.assertEquals(aliases, actual.aliases()); Assert.assertEquals(aliases, actual.aliases());
Assert.assertNull(actual.names()); Assert.assertNull(actual.names());
@ -90,11 +87,8 @@ public class CollectionTest extends AbstractEmbeddedCassandraTest {
session.update().set(customer::aliases, expected).where(customer::id, eq(id)).sync(); session.update().set(customer::aliases, expected).where(customer::id, eq(id)).sync();
actual = session.<Customer>select(customer) actual =
.where(customer::id, eq(id)) session.<Customer>select(customer).where(customer::id, eq(id)).single().sync().orElse(null);
.single()
.sync()
.orElse(null);
Assert.assertEquals(id, actual.id()); Assert.assertEquals(id, actual.id());
Assert.assertEquals(expected, actual.aliases()); Assert.assertEquals(expected, actual.aliases());
@ -170,11 +164,8 @@ public class CollectionTest extends AbstractEmbeddedCassandraTest {
// read full object // read full object
Customer actual = session.<Customer>select(customer) Customer actual =
.where(customer::id, eq(id)) session.<Customer>select(customer).where(customer::id, eq(id)).single().sync().orElse(null);
.single()
.sync()
.orElse(null);
Assert.assertEquals(id, actual.id()); Assert.assertEquals(id, actual.id());
Assert.assertEquals(names, actual.names()); Assert.assertEquals(names, actual.names());
@ -200,11 +191,8 @@ public class CollectionTest extends AbstractEmbeddedCassandraTest {
session.update().set(customer::names, expected).where(customer::id, eq(id)).sync(); session.update().set(customer::names, expected).where(customer::id, eq(id)).sync();
actual = session.<Customer>select(customer) actual =
.where(customer::id, eq(id)) session.<Customer>select(customer).where(customer::id, eq(id)).single().sync().orElse(null);
.single()
.sync()
.orElse(null);
Assert.assertEquals(id, actual.id()); Assert.assertEquals(id, actual.id());
Assert.assertEquals(expected, actual.names()); Assert.assertEquals(expected, actual.names());
@ -306,10 +294,8 @@ public class CollectionTest extends AbstractEmbeddedCassandraTest {
// read full object // read full object
Customer actual = session.<Customer>select(customer) Customer actual =
.where(customer::id, eq(id)).single() session.<Customer>select(customer).where(customer::id, eq(id)).single().sync().orElse(null);
.sync()
.orElse(null);
Assert.assertEquals(id, actual.id()); Assert.assertEquals(id, actual.id());
Assert.assertEquals(props, actual.properties()); Assert.assertEquals(props, actual.properties());
@ -343,11 +329,8 @@ public class CollectionTest extends AbstractEmbeddedCassandraTest {
session.update().set(customer::properties, expected).where(customer::id, eq(id)).sync(); session.update().set(customer::properties, expected).where(customer::id, eq(id)).sync();
actual = session.<Customer>select(customer) actual =
.where(customer::id, eq(id)) session.<Customer>select(customer).where(customer::id, eq(id)).single().sync().orElse(null);
.single()
.sync()
.orElse(null);
Assert.assertEquals(id, actual.id()); Assert.assertEquals(id, actual.id());
Assert.assertEquals(expected, actual.properties()); Assert.assertEquals(expected, actual.properties());

View file

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

View file

@ -1,24 +1,40 @@
package net.helenus.test.integration.core.draft; package net.helenus.test.integration.core.draft;
import java.util.UUID; import java.util.UUID;
import net.helenus.core.AbstractAuditedEntityDraft; import net.helenus.core.AbstractAuditedEntityDraft;
import net.helenus.core.Helenus;
import net.helenus.core.reflect.MapExportable; import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.annotation.*; import net.helenus.mapping.annotation.*;
@Table @Table
public interface Inventory { public interface Inventory {
@PartitionKey UUID id(); static Inventory inventory = Helenus.dsl(Inventory.class);
@Column("emea") @Types.Counter long EMEA();
@Column("noram") @Types.Counter long NORAM();
@Column("apac") @Types.Counter long APAC();
@Transient static Draft draft(UUID id) { return new Draft(id); } @PartitionKey
UUID id();
@Transient default Draft update() { return new Draft(this); } @Column("emea")
@Types.Counter
long EMEA();
@Column("noram")
@Types.Counter
long NORAM();
@Column("apac")
@Types.Counter
long APAC();
@Transient
static Draft draft(UUID id) {
return new Draft(id);
}
@Transient
default Draft update() {
return new Draft(this);
}
class Draft extends AbstractAuditedEntityDraft<Inventory> { class Draft extends AbstractAuditedEntityDraft<Inventory> {
@ -27,49 +43,51 @@ public interface Inventory {
super(null); super(null);
// Primary Key: // Primary Key:
set("id", id); set(inventory::id, id);
} }
Draft(Inventory inventory) { Draft(Inventory inventory) {
super((MapExportable) inventory); super((MapExportable) inventory);
} }
public Class<Inventory> getEntityClass() { return Inventory.class; } public Class<Inventory> getEntityClass() {
return Inventory.class;
}
protected String getCurrentAuditor() { return "unknown"; } protected String getCurrentAuditor() {
return "unknown";
}
// Immutable properties: // Immutable properties:
public UUID id() { public UUID id() {
return this.<UUID>get("id", UUID.class); return this.<UUID>get(inventory::id, UUID.class);
} }
public long EMEA() { public long EMEA() {
return this.<Long>get("EMEA", long.class); return this.<Long>get(inventory::EMEA, long.class);
} }
public Draft EMEA(long count) { public Draft EMEA(long count) {
mutate("EMEA", count); mutate(inventory::EMEA, count);
return this; return this;
} }
public long APAC() { public long APAC() {
return this.<Long>get("APAC", long.class); return this.<Long>get(inventory::APAC, long.class);
} }
public Draft APAC(long count) { public Draft APAC(long count) {
mutate("APAC", count); mutate(inventory::APAC, count);
return this; return this;
} }
public long NORAM() { public long NORAM() {
return this.<Long>get("NORAM", long.class); return this.<Long>get(inventory::NORAM, long.class);
} }
public Draft NORAM(long count) { public Draft NORAM(long count) {
mutate("NORAM", count); mutate(inventory::NORAM, count);
return this; return this;
} }
} }
} }

View file

@ -1,33 +1,52 @@
package net.helenus.test.integration.core.draft; package net.helenus.test.integration.core.draft;
import com.datastax.driver.core.utils.UUIDs;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import com.datastax.driver.core.utils.UUIDs;
import net.helenus.core.AbstractEntityDraft; import net.helenus.core.AbstractEntityDraft;
import net.helenus.core.Helenus;
import net.helenus.core.reflect.MapExportable; import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.annotation.*; import net.helenus.mapping.annotation.*;
@Table @Table
public interface Supply { public interface Supply {
@PartitionKey UUID id(); static Supply supply = Helenus.dsl(Supply.class);
@ClusteringColumn(ordinal=0) default String region() { return "NORAM"; }
@Index(caseSensitive = false) String code(); @PartitionKey
@Index String description(); // @IndexText == lucene index UUID id();
@Index Map<String, Long> demand();
@Index List<String> suppliers();
@Index Set<String> shipments();
@Transient static Draft draft(String region) { return new Draft(region); } @ClusteringColumn(ordinal = 0)
default String region() {
return "NORAM";
}
@Transient default Draft update() { return new Draft(this); } @Index(caseSensitive = false)
String code();
@Index
String description(); // @IndexText == lucene index
@Index
Map<String, Long> demand();
@Index
List<String> suppliers();
@Index
Set<String> shipments();
@Transient
static Draft draft(String region) {
return new Draft(region);
}
@Transient
default Draft update() {
return new Draft(this);
}
class Draft extends AbstractEntityDraft<Supply> { class Draft extends AbstractEntityDraft<Supply> {
@ -36,32 +55,34 @@ public interface Supply {
super(null); super(null);
// Primary Key: // Primary Key:
set("id", UUIDs.timeBased()); set(supply::id, UUIDs.timeBased());
set("region", region); set(supply::region, region);
} }
Draft(Supply supply) { Draft(Supply supply) {
super((MapExportable) supply); super((MapExportable) supply);
} }
public Class<Supply> getEntityClass() { return Supply.class; } public Class<Supply> getEntityClass() {
return Supply.class;
}
// Immutable properties: // Immutable properties:
public UUID id() { public UUID id() {
return this.<UUID>get("id", UUID.class); return this.<UUID>get(supply::id, UUID.class);
} }
public String region() { public String region() {
return this.<String>get("region", String.class); return this.<String>get(supply::region, String.class);
} }
// Mutable properties: // Mutable properties:
public String code() { public String code() {
return this.<String>get("code", String.class); return this.<String>get(supply::code, String.class);
} }
public Draft code(String code) { public Draft code(String code) {
mutate("code", code); mutate(supply::code, code);
return this; return this;
} }
@ -70,11 +91,11 @@ public interface Supply {
} }
public String description() { public String description() {
return this.<String>get("description", String.class); return this.<String>get(supply::description, String.class);
} }
public Draft description(String description) { public Draft description(String description) {
mutate("description", description); mutate(supply::description, description);
return this; return this;
} }
@ -83,11 +104,11 @@ public interface Supply {
} }
public Map<String, Long> demand() { public Map<String, Long> demand() {
return this.<Map<String, Long>>get("demand", Map.class); return this.<Map<String, Long>>get(supply::demand, Map.class);
} }
public Draft demand(Map<String, Long> demand) { public Draft demand(Map<String, Long> demand) {
mutate("demand", demand); mutate(supply::demand, demand);
return this; return this;
} }
@ -96,11 +117,11 @@ public interface Supply {
} }
public List<String> suppliers() { public List<String> suppliers() {
return this.<List<String>>get("suppliers", List.class); return this.<List<String>>get(supply::suppliers, List.class);
} }
public Draft suppliers(List<String> suppliers) { public Draft suppliers(List<String> suppliers) {
mutate("suppliers", suppliers); mutate(supply::suppliers, suppliers);
return this; return this;
} }
@ -109,17 +130,16 @@ public interface Supply {
} }
public Set<String> shipments() { public Set<String> shipments() {
return this.<Set<String>>get("shipments", Set.class); return this.<Set<String>>get(supply::shipments, Set.class);
} }
public Draft shipments(Set<String> shipments) { public Draft shipments(Set<String> shipments) {
mutate("shipments", shipments); mutate(supply::shipments, shipments);
return this; return this;
} }
public Draft setshipments(Set<String> shipments) { public Draft setshipments(Set<String> shipments) {
return shipments(shipments); return shipments(shipments);
} }
} }
} }

View file

@ -29,6 +29,9 @@ public interface Animal {
@Column(ordinal = 1) @Column(ordinal = 1)
boolean eatable(); boolean eatable();
@Column
boolean warmBlodded();
@Transient @Transient
default Animal me() { default Animal me() {
return this; return this;

View file

@ -20,7 +20,7 @@ import net.helenus.mapping.annotation.Index;
import net.helenus.mapping.annotation.Table; import net.helenus.mapping.annotation.Table;
@Table("cats") @Table("cats")
public interface Cat extends Animal { public interface Cat extends Mammal {
@Column(ordinal = 0) @Column(ordinal = 0)
@Index(caseSensitive = false) @Index(caseSensitive = false)

View file

@ -53,6 +53,7 @@ public class HierarchyTest extends AbstractEmbeddedCassandraTest {
Optional<Cat> animal = Optional<Cat> animal =
session.<Cat>select(Cat.class).where(cat::nickname, eq("garfield")).sync().findFirst(); session.<Cat>select(Cat.class).where(cat::nickname, eq("garfield")).sync().findFirst();
Assert.assertTrue(animal.isPresent()); Assert.assertTrue(animal.isPresent());
Assert.assertTrue(animal.get().warmBlodded());
Assert.assertFalse(animal.get().eatable()); Assert.assertFalse(animal.get().eatable());
} }
@ -64,7 +65,8 @@ public class HierarchyTest extends AbstractEmbeddedCassandraTest {
.value(cat::nickname, "garfield") .value(cat::nickname, "garfield")
.value(cat::eatable, false) .value(cat::eatable, false)
.sync(); .sync();
Optional<Cat> animal = session.select(Cat.class).where(cat::nickname, eq("garfield")).single().sync(); Optional<Cat> animal =
session.select(Cat.class).where(cat::nickname, eq("garfield")).single().sync();
Assert.assertTrue(animal.isPresent()); Assert.assertTrue(animal.isPresent());
Cat cat = animal.get(); Cat cat = animal.get();

View file

@ -0,0 +1,26 @@
/*
* Copyright (C) 2015 The Helenus Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.helenus.test.integration.core.hierarchy;
import net.helenus.mapping.annotation.InheritedTable;
@InheritedTable
public interface Mammal extends Animal {
default boolean warmBlodded() {
return true;
}
}

View file

@ -19,7 +19,7 @@ import net.helenus.mapping.annotation.Column;
import net.helenus.mapping.annotation.Table; import net.helenus.mapping.annotation.Table;
@Table("pigs") @Table("pigs")
public interface Pig extends Animal { public interface Pig extends Mammal {
@Column(ordinal = 0) @Column(ordinal = 0)
String nickname(); String nickname();

View file

@ -18,21 +18,17 @@ package net.helenus.test.integration.core.simple;
import static net.helenus.core.Query.eq; import static net.helenus.core.Query.eq;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
import java.util.*;
import net.helenus.core.Helenus; import net.helenus.core.Helenus;
import net.helenus.core.HelenusSession; import net.helenus.core.HelenusSession;
import net.helenus.core.Operator; import net.helenus.core.Operator;
import net.helenus.core.operation.UpdateOperation; import net.helenus.core.operation.UpdateOperation;
import net.helenus.core.reflect.Drafted;
import net.helenus.mapping.HelenusEntity;
import net.helenus.support.Fun; import net.helenus.support.Fun;
import net.helenus.test.integration.build.AbstractEmbeddedCassandraTest; import net.helenus.test.integration.build.AbstractEmbeddedCassandraTest;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import java.util.*;
public class SimpleUserTest extends AbstractEmbeddedCassandraTest { public class SimpleUserTest extends AbstractEmbeddedCassandraTest {
static User user; static User user;
@ -102,11 +98,7 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest {
// select as object // select as object
actual = session.<User>select(user) actual = session.<User>select(user).where(user::id, eq(100L)).single().sync().orElse(null);
.where(user::id, eq(100L))
.single()
.sync()
.orElse(null);
assertUsers(newUser, actual); assertUsers(newUser, actual);
// select by columns // select by columns
@ -192,11 +184,7 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest {
Assert.assertEquals("_albert", name); Assert.assertEquals("_albert", name);
User u2 = session.<User>select(user) User u2 = session.<User>select(user).where(user::id, eq(100L)).single().sync().orElse(null);
.where(user::id, eq(100L))
.single()
.sync()
.orElse(null);
Assert.assertEquals(Long.valueOf(100L), u2.id()); Assert.assertEquals(Long.valueOf(100L), u2.id());
Assert.assertEquals("albert", u2.name()); Assert.assertEquals("albert", u2.name());
@ -213,11 +201,7 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest {
.sync(); .sync();
Optional<User> maybeGreg = Optional<User> maybeGreg =
session session.<User>select(user).where(user::id, eq(1234L)).single().sync();
.<User>select(user)
.where(user::id, eq(1234L))
.single()
.sync();
// INSERT // INSERT
@ -260,13 +244,8 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest {
.zipkinContext(null) .zipkinContext(null)
.sync(); .sync();
UpdateOperation<ResultSet> update = session.update(); UpdateOperation<ResultSet> update = session.update();
update update.set(user::name, null).zipkinContext(null).sync();
.set(user::name, null)
.zipkinContext(null)
.sync();
} }
private void assertUsers(User expected, User actual) { private void assertUsers(User expected, User actual) {

View file

@ -42,11 +42,8 @@ public class TupleMapTest extends TupleCollectionTest {
// read full object // read full object
Book actual = session.<Book>select(book) Book actual =
.where(book::id, Query.eq(id)) session.<Book>select(book).where(book::id, Query.eq(id)).single().sync().orElse(null);
.single()
.sync()
.orElse(null);
Assert.assertEquals(id, actual.id()); Assert.assertEquals(id, actual.id());
assertEqualMaps(writers, actual.writers()); assertEqualMaps(writers, actual.writers());
Assert.assertNull(actual.reviewers()); Assert.assertNull(actual.reviewers());
@ -77,11 +74,7 @@ public class TupleMapTest extends TupleCollectionTest {
session.update().set(book::writers, expected).where(book::id, Query.eq(id)).sync(); session.update().set(book::writers, expected).where(book::id, Query.eq(id)).sync();
actual = session.<Book>select(book) actual = session.<Book>select(book).where(book::id, Query.eq(id)).single().sync().orElse(null);
.where(book::id, Query.eq(id))
.single()
.sync()
.orElse(null);
Assert.assertEquals(id, actual.id()); Assert.assertEquals(id, actual.id());
assertEqualMaps(expected, actual.writers()); assertEqualMaps(expected, actual.writers());
@ -96,7 +89,8 @@ public class TupleMapTest extends TupleCollectionTest {
expected.put(third, unk); expected.put(third, unk);
session.update().put(book::writers, third, unk).where(book::id, Query.eq(id)).sync(); 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); assertEqualMaps(expected, actualMap);
// putAll operation // putAll operation

View file

@ -15,6 +15,9 @@
*/ */
package net.helenus.test.integration.core.unitofwork; package net.helenus.test.integration.core.unitofwork;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.helenus.core.Helenus; import net.helenus.core.Helenus;
import net.helenus.core.HelenusSession; import net.helenus.core.HelenusSession;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
@ -23,21 +26,13 @@ import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class AndThenOrderTest extends AbstractEmbeddedCassandraTest { public class AndThenOrderTest extends AbstractEmbeddedCassandraTest {
static HelenusSession session; static HelenusSession session;
@BeforeClass @BeforeClass
public static void beforeTest() { public static void beforeTest() {
session = Helenus.init(getSession()) session = Helenus.init(getSession()).showCql().autoCreateDrop().get();
.showCql()
.autoCreateDrop()
.get();
} }
@Test @Test
@ -48,17 +43,37 @@ public class AndThenOrderTest extends AbstractEmbeddedCassandraTest {
uow5 = session.begin(); uow5 = session.begin();
uow3 = session.begin(uow5); uow3 = session.begin(uow5);
uow1 = session.begin(uow3); uow1 = session.begin(uow3);
uow1.commit().andThen(() -> { q.add("1"); }); uow1.commit()
.andThen(
() -> {
q.add("1");
});
uow2 = session.begin(uow3); uow2 = session.begin(uow3);
uow2.commit().andThen(() -> { q.add("2"); }); uow2.commit()
uow3.commit().andThen(() -> { q.add("3"); }); .andThen(
() -> {
q.add("2");
});
uow3.commit()
.andThen(
() -> {
q.add("3");
});
uow4 = session.begin(uow5); uow4 = session.begin(uow5);
uow4.commit().andThen(() -> { q.add("4"); }); uow4.commit()
uow5.commit().andThen(() -> { q.add("5"); }); .andThen(
() -> {
q.add("4");
});
uow5.commit()
.andThen(
() -> {
q.add("5");
});
System.out.println(q); System.out.println(q);
Assert.assertTrue(Arrays.equals(q.toArray(new String[5]), new String[] {"1", "2", "3", "4", "5"})); Assert.assertTrue(
Arrays.equals(q.toArray(new String[5]), new String[] {"1", "2", "3", "4", "5"}));
} }
@Test @Test
@ -71,28 +86,39 @@ public class AndThenOrderTest extends AbstractEmbeddedCassandraTest {
try { try {
uow3 = session.begin(uow4); uow3 = session.begin(uow4);
uow1 = session.begin(uow3); uow1 = session.begin(uow3);
uow1.commit().andThen(() -> { uow1.commit()
.andThen(
() -> {
q.add("1"); q.add("1");
}); });
uow2 = session.begin(uow3); uow2 = session.begin(uow3);
uow2.commit().andThen(() -> { uow2.commit()
.andThen(
() -> {
q.add("2"); q.add("2");
}); });
uow3.commit().andThen(() -> { uow3.commit()
.andThen(
() -> {
q.add("3"); q.add("3");
}); });
uow4.commit().andThen(() -> { uow4.commit()
.andThen(
() -> {
q.add("4"); q.add("4");
}); });
throw new Exception(); throw new Exception();
} catch (Exception e) { } catch (Exception e) {
uow4.abort(); uow4.abort();
} }
uow5.commit().andThen(() -> { q.add("5"); }); uow5.commit()
.andThen(
() -> {
q.add("5");
});
System.out.println(q); System.out.println(q);
Assert.assertTrue(q.isEmpty() == true); Assert.assertTrue(q.isEmpty() == true);
} }
@Test @Test
@ -103,7 +129,6 @@ public class AndThenOrderTest extends AbstractEmbeddedCassandraTest {
Assert.assertFalse(uow.hasAborted()); Assert.assertFalse(uow.hasAborted());
} }
Assert.assertTrue(unitOfWork.hasAborted()); Assert.assertTrue(unitOfWork.hasAborted());
} }
@Test @Test
@ -112,7 +137,9 @@ public class AndThenOrderTest extends AbstractEmbeddedCassandraTest {
try (UnitOfWork uow = session.begin()) { try (UnitOfWork uow = session.begin()) {
unitOfWork = uow; unitOfWork = uow;
Assert.assertFalse(uow.hasAborted()); Assert.assertFalse(uow.hasAborted());
uow.commit().andThen(() -> { uow.commit()
.andThen(
() -> {
Assert.assertFalse(uow.hasAborted()); Assert.assertFalse(uow.hasAborted());
Assert.assertTrue(uow.hasCommitted()); Assert.assertTrue(uow.hasCommitted());
}); });
@ -120,5 +147,4 @@ public class AndThenOrderTest extends AbstractEmbeddedCassandraTest {
Assert.assertFalse(unitOfWork.hasAborted()); Assert.assertFalse(unitOfWork.hasAborted());
Assert.assertTrue(unitOfWork.hasCommitted()); Assert.assertTrue(unitOfWork.hasCommitted());
} }
} }

View file

@ -15,16 +15,13 @@
*/ */
package net.helenus.test.integration.core.unitofwork; package net.helenus.test.integration.core.unitofwork;
import net.helenus.mapping.annotation.*;
import com.datastax.driver.core.DataType.Name; import com.datastax.driver.core.DataType.Name;
import java.util.Set; import java.util.Set;
import net.helenus.mapping.annotation.*;
@UDT @UDT
public interface Directory extends FilesystemNode { public interface Directory extends FilesystemNode {
@Types.Set(Name.TIMEUUID) @Types.Set(Name.TIMEUUID)
Set<FilesystemNode> inodes(); Set<FilesystemNode> inodes();
} }

View file

@ -17,11 +17,9 @@ package net.helenus.test.integration.core.unitofwork;
import net.helenus.mapping.annotation.*; import net.helenus.mapping.annotation.*;
@UDT @UDT
public interface File extends FilesystemNode { public interface File extends FilesystemNode {
@Column @Column
byte[] data(); byte[] data();
} }

View file

@ -21,5 +21,4 @@ import net.helenus.mapping.annotation.UDT;
public interface FileAttributes { public interface FileAttributes {
String owner(); String owner();
} }

View file

@ -15,9 +15,8 @@
*/ */
package net.helenus.test.integration.core.unitofwork; package net.helenus.test.integration.core.unitofwork;
import net.helenus.mapping.annotation.*;
import java.util.UUID; import java.util.UUID;
import net.helenus.mapping.annotation.*;
@Table("fs") @Table("fs")
public interface FilesystemNode { public interface FilesystemNode {
@ -30,5 +29,4 @@ public interface FilesystemNode {
@Column @Column
FileAttributes attr(); FileAttributes attr();
} }

View file

@ -15,7 +15,10 @@
*/ */
package net.helenus.test.integration.core.unitofwork; package net.helenus.test.integration.core.unitofwork;
import static net.helenus.core.Query.eq;
import com.datastax.driver.core.utils.UUIDs; import com.datastax.driver.core.utils.UUIDs;
import java.util.UUID;
import net.bytebuddy.utility.RandomString; import net.bytebuddy.utility.RandomString;
import net.helenus.core.Helenus; import net.helenus.core.Helenus;
import net.helenus.core.HelenusSession; import net.helenus.core.HelenusSession;
@ -29,34 +32,24 @@ import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import java.util.UUID;
import static net.helenus.core.Query.eq;
@Table @Table
@Cacheable @Cacheable
interface Widget { interface Widget {
@PartitionKey @PartitionKey
UUID id(); UUID id();
@Column @Column
String name(); String name();
} }
public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest { public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest {
static Widget widget; static Widget widget;
static HelenusSession session; static HelenusSession session;
@BeforeClass @BeforeClass
public static void beforeTest() { public static void beforeTest() {
session = Helenus.init(getSession()) session = Helenus.init(getSession()).showCql().add(Widget.class).autoCreateDrop().get();
.showCql()
.add(Widget.class)
.autoCreateDrop()
.get();
widget = session.dsl(Widget.class); widget = session.dsl(Widget.class);
} }
@ -66,7 +59,8 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest {
UUID key = UUIDs.timeBased(); UUID key = UUIDs.timeBased();
// This should inserted Widget, but not cache it. // This should inserted Widget, but not cache it.
session.<Widget>insert(widget) session
.<Widget>insert(widget)
.value(widget::id, key) .value(widget::id, key)
.value(widget::name, RandomString.make(20)) .value(widget::name, RandomString.make(20))
.sync(); .sync();
@ -74,25 +68,19 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest {
try (UnitOfWork uow = session.begin()) { try (UnitOfWork uow = session.begin()) {
// This should read from the database and return a Widget. // This should read from the database and return a Widget.
w1 = session.<Widget>select(widget) w1 =
.where(widget::id, eq(key)) session.<Widget>select(widget).where(widget::id, eq(key)).single().sync(uow).orElse(null);
.single()
.sync(uow)
.orElse(null);
// This should read from the cache and get the same instance of a Widget. // This should read from the cache and get the same instance of a Widget.
w2 = session.<Widget>select(widget) w2 =
.where(widget::id, eq(key)) session.<Widget>select(widget).where(widget::id, eq(key)).single().sync(uow).orElse(null);
.single()
.sync(uow)
.orElse(null);
uow.commit() uow.commit()
.andThen(() -> { .andThen(
() -> {
Assert.assertEquals(w1, w2); Assert.assertEquals(w1, w2);
}); });
} }
} }
@Test @Test
@ -103,7 +91,9 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest {
// This should inserted Widget, and not cache it in uow1. // This should inserted Widget, and not cache it in uow1.
try (UnitOfWork uow1 = session.begin()) { try (UnitOfWork uow1 = session.begin()) {
w1 = session.<Widget>insert(widget) w1 =
session
.<Widget>insert(widget)
.value(widget::id, key1) .value(widget::id, key1)
.value(widget::name, RandomString.make(20)) .value(widget::name, RandomString.make(20))
.sync(uow1); .sync(uow1);
@ -111,7 +101,9 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest {
try (UnitOfWork uow2 = session.begin(uow1)) { try (UnitOfWork uow2 = session.begin(uow1)) {
// This should read from uow1's cache and return the same Widget. // This should read from uow1's cache and return the same Widget.
w2 = session.<Widget>select(widget) w2 =
session
.<Widget>select(widget)
.where(widget::id, eq(key1)) .where(widget::id, eq(key1))
.single() .single()
.sync(uow2) .sync(uow2)
@ -119,30 +111,35 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest {
Assert.assertEquals(w1, w2); Assert.assertEquals(w1, w2);
w3 = session.<Widget>insert(widget) w3 =
session
.<Widget>insert(widget)
.value(widget::id, key2) .value(widget::id, key2)
.value(widget::name, RandomString.make(20)) .value(widget::name, RandomString.make(20))
.sync(uow2); .sync(uow2);
uow2.commit() uow2.commit()
.andThen(() -> { .andThen(
() -> {
Assert.assertEquals(w1, w2); Assert.assertEquals(w1, w2);
}); });
} }
// This should read from the cache and get the same instance of a Widget. // This should read from the cache and get the same instance of a Widget.
w4 = session.<Widget>select(widget) w4 =
session
.<Widget>select(widget)
.where(widget::id, eq(key2)) .where(widget::id, eq(key2))
.single() .single()
.sync(uow1) .sync(uow1)
.orElse(null); .orElse(null);
uow1.commit() uow1.commit()
.andThen(() -> { .andThen(
() -> {
Assert.assertEquals(w3, w4); Assert.assertEquals(w3, w4);
}); });
} }
} }
/* /*
@Test @Test

View file

@ -0,0 +1,29 @@
package net.helenus.test.integration.core.views;
import java.util.Date;
import java.util.UUID;
import net.helenus.mapping.annotation.ClusteringColumn;
import net.helenus.mapping.annotation.CoveringIndex;
import net.helenus.mapping.annotation.PartitionKey;
import net.helenus.mapping.annotation.Table;
@Table
@CoveringIndex(
name = "cyclist_mv",
covering = {"age", "birthday", "country"},
partitionKeys = {"age", "cid"},
clusteringColumns = {}
)
public interface Cyclist {
@ClusteringColumn
UUID cid();
String name();
@PartitionKey
int age();
Date birthday();
String country();
}

View file

@ -0,0 +1,21 @@
package net.helenus.test.integration.core.views;
import java.util.Date;
import java.util.UUID;
import net.helenus.mapping.OrderingDirection;
import net.helenus.mapping.annotation.*;
@MaterializedView
public interface CyclistsByAge extends Cyclist {
@PartitionKey
UUID cid();
@ClusteringColumn(ordering = OrderingDirection.ASC)
int age();
Date birthday();
@Index
String country();
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2015 The Helenus Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.helenus.test.integration.core.views;
import static net.helenus.core.Query.eq;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import net.helenus.core.Helenus;
import net.helenus.core.HelenusSession;
import net.helenus.test.integration.build.AbstractEmbeddedCassandraTest;
import org.junit.BeforeClass;
import org.junit.Test;
// See: https://docs.datastax.com/en/cql/3.3/cql/cql_using/useCreateMV.html
// https://docs.datastax.com/en/cql/3.3/cql/cql_reference/cqlCreateMaterializedView.html
// https://www.datastax.com/dev/blog/materialized-view-performance-in-cassandra-3-x
public class MaterializedViewTest extends AbstractEmbeddedCassandraTest {
static Cyclist cyclist;
static HelenusSession session;
static Date dateFromString(String dateInString) {
SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy");
try {
return formatter.parse(dateInString);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
@BeforeClass
public static void beforeTest() {
session =
Helenus.init(getSession())
.showCql()
.add(Cyclist.class)
.add(CyclistsByAge.class)
.autoCreateDrop()
.get();
cyclist = session.dsl(Cyclist.class);
session
.insert(cyclist)
.value(cyclist::cid, UUID.randomUUID())
.value(cyclist::age, 18)
.value(cyclist::birthday, dateFromString("1997-02-08"))
.value(cyclist::country, "Netherlands")
.value(cyclist::name, "Pascal EENKHOORN")
.sync();
}
@Test
public void testMv() throws Exception {
session
.select(Cyclist.class)
.<CyclistsByAge>from(CyclistsByAge.class)
.where(cyclist::age, eq(18))
.sync();
}
}

View file

@ -19,7 +19,6 @@ import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import net.helenus.core.reflect.Drafted; import net.helenus.core.reflect.Drafted;
import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.annotation.*; import net.helenus.mapping.annotation.*;
@Table @Table