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,41 +1,38 @@
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> {
public AbstractAuditedEntityDraft(MapExportable entity) { public AbstractAuditedEntityDraft(MapExportable entity) {
super(entity); super(entity);
Date in = new Date(); Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault()); LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
Date now = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); Date now = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
String who = getCurrentAuditor(); String who = getCurrentAuditor();
if (entity == null) { if (entity == null) {
if (who != null) { if (who != null) {
set("createdBy", who); set("createdBy", who);
} }
set("createdAt", now); set("createdAt", now);
}
if (who != null) {
set("modifiedBy", who);
}
set("modifiedAt", now);
} }
if (who != null) {
protected String getCurrentAuditor() { set("modifiedBy", who);
return null;
} }
set("modifiedAt", now);
}
public Date createdAt() { protected String getCurrentAuditor() {
return (Date) get("createdAt", Date.class); return null;
} }
public Date createdAt() {
return (Date) get("createdAt", Date.class);
}
} }

View file

@ -1,151 +1,159 @@
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) {
this.entity = entity;
this.entityMap = entity != null ? entity.toMap() : new HashMap<String, Object>();
}
public AbstractEntityDraft(MapExportable entity) { public abstract Class<E> getEntityClass();
this.entity = entity;
this.entityMap = entity != null ? entity.toMap() : new HashMap<String, Object>(); 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) {
T value = (T) backingMap.get(key);
if (value == null) {
value = (T) entityMap.get(key);
if (value == null) {
if (Primitives.allPrimitiveTypes().contains(returnType)) {
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
if (type == null) {
throw new RuntimeException("unknown primitive type " + returnType);
}
return (T) type.getDefaultValue();
}
}
} }
public abstract Class<E> getEntityClass(); return value;
}
public E build() { return Helenus.map(getEntityClass(), toMap()); } protected <T> Object set(Getter<T> getter, Object value) {
return set(this.<T>methodNameFor(getter), value);
}
protected <T> T get(String key, Class<?> returnType) { protected Object set(String key, Object value) {
T value = (T) entityMap.get(key); if (key == null || value == null) {
return null;
if (value == null) {
if (Primitives.allPrimitiveTypes().contains(returnType)) {
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
if (type == null) {
throw new RuntimeException("unknown primitive type " + returnType);
}
return (T) type.getDefaultValue();
}
}
return value;
} }
protected Object set(String key, Object value) { backingMap.put(key, value);
return value;
}
if (key == null || value == null) { @SuppressWarnings("unchecked")
return null; protected <T> T mutate(Getter<T> getter, T value) {
} return (T) mutate(this.<T>methodNameFor(getter), value);
}
protected Object mutate(String key, Object value) {
Objects.requireNonNull(key);
if (value == null) {
return null;
}
if (entity != null) {
Map<String, Object> map = entity.toMap();
if (map.containsKey(key) && !value.equals(map.get(key))) {
backingMap.put(key, value); backingMap.put(key, value);
mutatedSet.add(key);
return value; return value;
}
return map.get(key);
} else {
backingMap.put(key, value);
return null;
} }
}
protected Object mutate(String key, Object value) { private <T> String methodNameFor(Getter<T> getter) {
Objects.requireNonNull(key); return MappingUtil.resolveMappingProperty(getter).getProperty().getPropertyName();
}
if (value == null) { public <T> Object unset(Getter<T> getter) {
return null; return unset(methodNameFor(getter));
} }
if (entity != null) { public Object unset(String key) {
Map<String, Object> map = entity.toMap(); if (key != null) {
Object value = backingMap.get(key);
if (map.containsKey(key) && !value.equals(map.get(key))) { backingMap.put(key, null);
backingMap.put(key, value); return value;
mutatedSet.add(key);
return value;
}
return map.get(key);
} else {
backingMap.put(key, value);
mutatedSet.add(key);
return null;
}
} }
return null;
}
private String methodNameFor(Getter<?> getter) { public <T> boolean reset(Getter<T> getter, T desiredValue) {
return MappingUtil.resolveMappingProperty(getter) return this.<T>reset(this.<T>methodNameFor(getter), desiredValue);
.getProperty() }
.getPropertyName();
public <T> boolean reset(String key, T desiredValue) {
if (key != null && desiredValue != null) {
@SuppressWarnings("unchecked")
T currentValue = (T) backingMap.get(key);
if (currentValue == null || !currentValue.equals(desiredValue)) {
set(key, desiredValue);
return true;
}
} }
return false;
}
public Object unset(Getter<?> getter) { @Override
return unset(methodNameFor(getter)); public Map<String, Object> toMap() {
return toMap(entityMap);
}
public Map<String, Object> toMap(Map<String, Object> entityMap) {
Map<String, Object> combined;
if (entityMap != null && entityMap.size() > 0) {
combined = new HashMap<String, Object>(entityMap.size());
for (String key : entityMap.keySet()) {
combined.put(key, entityMap.get(key));
}
} else {
combined = new HashMap<String, Object>(backingMap.size());
} }
for (String key : mutated()) {
public Object unset(String key) { combined.put(key, backingMap.get(key));
if (key != null) {
Object value = backingMap.get(key);
backingMap.put(key, null);
mutatedSet.add(key);
return value;
}
return null;
} }
return combined;
}
public <T> boolean reset(Getter<?> getter, T desiredValue) { @Override
return this.<T>reset(methodNameFor(getter), desiredValue); public Set<String> mutated() {
} return backingMap.keySet();
}
public <T> boolean reset(String key, T desiredValue) {
if (key != null && desiredValue != null) {
@SuppressWarnings("unchecked")
T currentValue = (T) backingMap.get(key);
if (currentValue != null && !currentValue.equals(desiredValue)) {
return set(key, desiredValue) != null;
}
}
return false;
}
@Override
public Map<String, Object> toMap() {
return toMap(entityMap);
}
public Map<String, Object> toMap(Map<String, Object>entityMap) {
Map<String, Object> combined;
if (entityMap != null && entityMap.size() > 0) {
combined = new HashMap<String, Object>(entityMap.size());
for (String key : entityMap.keySet()) {
combined.put(key, entityMap.get(key));
}
} else {
combined = new HashMap<String, Object>(backingMap.size());
}
for (String key : mutatedSet) {
combined.put(key, backingMap.get(key));
}
return combined;
}
@Override
public Set<String> mutated() {
return mutatedSet;
}
@Override
public String toString() {
return backingMap.toString();
}
@Override
public String 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,22 +68,25 @@ 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();
} }
/** /**
* Checks to see if the work performed between calling begin and now can be committed or not. * Checks to see if the work performed between calling begin and now can be committed or not.
* *
* @return a function from which to chain work that only happens when commit is successful * @return a function from which to chain work that only happens when commit is successful
* @throws E when the work overlaps with other concurrent writers. * @throws E when the work overlaps with other concurrent writers.
*/ */
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,9 +124,12 @@ 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
uow.applyPostCommitFunctions(); .postOrderTraversal(this)
}); .forEach(
uow -> {
uow.applyPostCommitFunctions();
});
return new PostCommitFunction(this, null); return new PostCommitFunction(this, null);
} }
} }
@ -137,11 +142,15 @@ 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);
uow.committed = false; traverser
uow.aborted = true; .postOrderTraversal(this)
}); .forEach(
uow -> {
uow.committed = false;
uow.aborted = true;
});
// log.record(txn::abort) // log.record(txn::abort)
// cache.invalidateSince(txn::start time) // cache.invalidateSince(txn::start time)
} }
@ -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,8 +1,7 @@
package net.helenus.core; package net.helenus.core;
import java.util.function.Function;
@FunctionalInterface @FunctionalInterface
public interface CommitThunk { public interface CommitThunk {
void apply(); void apply();
} }

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,31 +67,32 @@ 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,
CodecRegistry registry, CodecRegistry registry,
boolean showCql, boolean showCql,
PrintStream printStream, PrintStream printStream,
SessionRepositoryBuilder sessionRepositoryBuilder, SessionRepositoryBuilder sessionRepositoryBuilder,
Executor executor, Executor executor,
boolean dropSchemaOnClose, boolean dropSchemaOnClose,
ConsistencyLevel consistencyLevel, ConsistencyLevel consistencyLevel,
Class<? extends UnitOfWork> unitOfWorkClass, boolean defaultQueryIdempotency,
MetricRegistry metricRegistry, Class<? extends UnitOfWork> unitOfWorkClass,
Tracer tracer) { MetricRegistry metricRegistry,
Tracer tracer) {
this.session = session; this.session = session;
this.registry = registry == null ? CodecRegistry.DEFAULT_INSTANCE : registry; this.registry = registry == null ? CodecRegistry.DEFAULT_INSTANCE : registry;
this.usingKeyspace = this.usingKeyspace =
Objects.requireNonNull( Objects.requireNonNull(
usingKeyspace, "keyspace needs to be selected before creating session"); usingKeyspace, "keyspace needs to be selected before creating session");
this.showCql = showCql; this.showCql = showCql;
this.printStream = printStream; this.printStream = printStream;
this.sessionRepository = sessionRepositoryBuilder.build(); this.sessionRepository = sessionRepositoryBuilder.build();
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,46 +168,59 @@ 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;
} }
public synchronized UnitOfWork begin() { public synchronized UnitOfWork begin() {
return begin(null); return begin(null);
} }
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();
return new SelectOperation<E>( return new SelectOperation<E>(
this, this,
entity, entity,
(r) -> { (r) -> {
Map<String, Object> map = new ValueProviderMap(r, valueProvider, entity); Map<String, Object> map = new ValueProviderMap(r, valueProvider, entity);
return (E) Helenus.map(entityClass, map); return (E) Helenus.map(entityClass, map);
}); });
} }
public <E> SelectOperation<E> select(Class<E> entityClass) { public <E> SelectOperation<E> select(Class<E> entityClass) {
@ -216,12 +229,12 @@ public final class HelenusSession extends AbstractSessionOperations implements C
HelenusEntity entity = Helenus.entity(entityClass); HelenusEntity entity = Helenus.entity(entityClass);
return new SelectOperation<E>( return new SelectOperation<E>(
this, this,
entity, entity,
(r) -> { (r) -> {
Map<String, Object> map = new ValueProviderMap(r, valueProvider, entity); Map<String, Object> map = new ValueProviderMap(r, valueProvider, entity);
return (E) Helenus.map(entityClass, map); return (E) Helenus.map(entityClass, map);
}); });
} }
public SelectOperation<Fun.ArrayTuple> select() { public SelectOperation<Fun.ArrayTuple> select() {
@ -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,51 +420,62 @@ 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);
Map<String, Object> map = draft.toMap(); Map<String, Object> map = draft.toMap();
Set<String> mutatedProperties = draft.mutated(); Set<String> mutatedProperties = draft.mutated();
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
switch (property.getColumnType()) { .getOrderedProperties()
case PARTITION_KEY: .forEach(
case CLUSTERING_COLUMN: property -> {
break; switch (property.getColumnType()) {
default: case PARTITION_KEY:
String propertyName = property.getPropertyName(); case CLUSTERING_COLUMN:
if (mutatedProperties.contains(propertyName)) { break;
Object value = map.get(propertyName); default:
Getter<Object> getter = new Getter<Object>() { String propertyName = property.getPropertyName();
@Override if (mutatedProperties.contains(propertyName)) {
public Object get() { Object value = map.get(propertyName);
throw new DslPropertyException(new HelenusPropertyNode(property, Optional.empty())); Getter<Object> getter =
new Getter<Object>() {
@Override
public Object get() {
throw new DslPropertyException(
new HelenusPropertyNode(property, Optional.empty()));
}
};
update.set(getter, value);
}
} }
}; });
update.set(getter, value);
}
}
});
// 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
switch (property.getColumnType()) { .getOrderedProperties()
case PARTITION_KEY: .forEach(
case CLUSTERING_COLUMN: property -> {
String propertyName = property.getPropertyName(); switch (property.getColumnType()) {
Object value = map.get(propertyName); case PARTITION_KEY:
Getter<Object> getter = new Getter<Object>() { case CLUSTERING_COLUMN:
@Override String propertyName = property.getPropertyName();
public Object get() { Object value = map.get(propertyName);
throw new DslPropertyException(new HelenusPropertyNode(property, Optional.empty())); Getter<Object> getter =
} new Getter<Object>() {
}; @Override
update.where(getter, eq(value)); public Object get() {
} throw new DslPropertyException(
}); new HelenusPropertyNode(property, Optional.empty()));
}
};
update.where(getter, eq(value));
}
});
return update; return update;
} }
@ -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,30 +1,29 @@
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> {
private final UnitOfWork uow; private final UnitOfWork uow;
private final List<CommitThunk> postCommit; private final List<CommitThunk> postCommit;
PostCommitFunction(UnitOfWork uow, List<CommitThunk> postCommit) { PostCommitFunction(UnitOfWork uow, List<CommitThunk> postCommit) {
this.uow = uow; this.uow = uow;
this.postCommit = postCommit; this.postCommit = postCommit;
} }
public void andThen(CommitThunk after) { public void andThen(CommitThunk after) {
Objects.requireNonNull(after); Objects.requireNonNull(after);
if (postCommit == null) { if (postCommit == null) {
after.apply(); after.apply();
} else { } else {
postCommit.add(after); postCommit.add(after);
}
} }
}
@Override @Override
public R apply(T t) { public R apply(T t) {
return null; return null;
} }
} }

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,44 +15,42 @@
*/ */
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;
public interface UnitOfWork<E extends Exception> extends AutoCloseable { public interface UnitOfWork<E extends Exception> extends AutoCloseable {
/** /**
* Marks the beginning of a transactional section of work. Will write a record to the shared * Marks the beginning of a transactional section of work. Will write a record to the shared
* write-ahead log. * write-ahead log.
* *
* @return the handle used to commit or abort the work. * @return the handle used to commit or abort the work.
*/ */
UnitOfWork begin(); UnitOfWork begin();
UnitOfWork addNestedUnitOfWork(UnitOfWork uow); UnitOfWork addNestedUnitOfWork(UnitOfWork uow);
/** /**
* Checks to see if the work performed between calling begin and now can be committed or not. * Checks to see if the work performed between calling begin and now can be committed or not.
* *
* @return a function from which to chain work that only happens when commit is successful * @return a function from which to chain work that only happens when commit is successful
* @throws E when the work overlaps with other concurrent writers. * @throws E when the work overlaps with other concurrent writers.
*/ */
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(); //Either<Object, Set<Object>> cacheLookup(String key);
Set<Object> cacheLookup(String key);
//Either<Object, Set<Object>> cacheLookup(String key); Map<String, Set<Object>> getCache();
Set<Object> cacheLookup(String key);
Map<String, Set<Object>> getCache();
} }

View file

@ -19,9 +19,8 @@ import net.helenus.support.HelenusException;
class UnitOfWorkImpl extends AbstractUnitOfWork<HelenusException> { class UnitOfWorkImpl extends AbstractUnitOfWork<HelenusException> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
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;
@ -172,7 +173,7 @@ public final class InsertOperation<T> extends AbstractOperation<T, InsertOperati
// Some values man need to be converted (e.g. from String to Enum). This is done // Some values man need to be converted (e.g. from String to Enum). This is done
// within the BeanColumnValueProvider below. // within the BeanColumnValueProvider below.
Optional<Function<Object, Object>> converter = Optional<Function<Object, Object>> converter =
prop.getReadConverter(sessionOps.getSessionRepository()); prop.getReadConverter(sessionOps.getSessionRepository());
if (converter.isPresent()) { if (converter.isPresent()) {
backingMap.put(key, converter.get().apply(backingMap.get(key))); backingMap.put(key, converter.get().apply(backingMap.get(key)));
} }
@ -232,22 +233,25 @@ public final class InsertOperation<T> extends AbstractOperation<T, InsertOperati
public String getStatementCacheKey() { public String getStatementCacheKey() {
List<String> keys = new ArrayList<>(values.size()); List<String> keys = new ArrayList<>(values.size());
values.forEach( values.forEach(
t -> { t -> {
HelenusPropertyNode prop = t._1; HelenusPropertyNode prop = t._1;
switch (prop.getProperty().getColumnType()) { switch (prop.getProperty().getColumnType()) {
case PARTITION_KEY: case PARTITION_KEY:
case CLUSTERING_COLUMN: case CLUSTERING_COLUMN:
keys.add(prop.getColumnName() + "==" + t._2.toString()); keys.add(prop.getColumnName() + "==" + t._2.toString());
break; break;
default: default:
break; break;
} }
}); });
return entity.getName() + ": " + Joiner.on(",").join(keys); return entity.getName() + ": " + Joiner.on(",").join(keys);
} }
@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,73 +1,79 @@
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;
protected final Meter uowCacheHits; protected final Meter uowCacheHits;
protected final Meter uowCacheMiss; protected final Meter uowCacheMiss;
protected final Timer requestLatency; protected final Timer requestLatency;
Operation(AbstractSessionOperations sessionOperations) { Operation(AbstractSessionOperations sessionOperations) {
this.sessionOps = sessionOperations; this.sessionOps = sessionOperations;
MetricRegistry metrics = sessionOperations.getMetricRegistry(); MetricRegistry metrics = sessionOperations.getMetricRegistry();
this.uowCacheHits = metrics.meter("net.helenus.UOW-cache-hits"); this.uowCacheHits = metrics.meter("net.helenus.UOW-cache-hits");
this.uowCacheMiss = metrics.meter("net.helenus.UOW-cache-miss"); this.uowCacheMiss = metrics.meter("net.helenus.UOW-cache-miss");
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) {
// Start recording in a Zipkin sub-span our execution time to perform this operation.
Tracer tracer = session.getZipkinTracer();
Span span = null;
if (tracer != null && traceContext != null) {
span = tracer.newChild(traceContext);
} }
public ResultSet execute(AbstractSessionOperations session, UnitOfWork uow, TraceContext traceContext, boolean showValues, boolean cached) { try {
// Start recording in a Zipkin sub-span our execution time to perform this operation. if (span != null) {
Tracer tracer = session.getZipkinTracer(); span.name("cassandra");
Span span = null; span.start();
if (tracer != null && traceContext != null) { }
span = tracer.newChild(traceContext);
}
try { Statement statement = options(buildStatement(cached));
ResultSetFuture futureResultSet = session.executeAsync(statement, showValues);
return futureResultSet.get();
if (span != null) { } catch (InterruptedException | ExecutionException e) {
span.name("cassandra");
span.start();
}
Statement statement = options(buildStatement(cached)); throw new RuntimeException(e);
ResultSetFuture futureResultSet = session.executeAsync(statement, showValues);
return futureResultSet.get();
} catch (InterruptedException | ExecutionException e) { } finally {
throw new RuntimeException(e); if (span != null) {
span.finish();
} finally { }
if (span != null) {
span.finish();
}
}
} }
}
public Statement options(Statement statement) { return statement; } public Statement options(Statement statement) {
return statement;
}
public Statement buildStatement(boolean cached) { return null; } public Statement buildStatement(boolean cached) {
return null;
public String getStatementCacheKey() { 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());
@ -225,14 +237,14 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
entity = prop.getEntity(); entity = prop.getEntity();
} else if (entity != prop.getEntity()) { } else if (entity != prop.getEntity()) {
throw new HelenusMappingException( throw new HelenusMappingException(
"you can select columns only from a single entity " "you can select columns only from a single entity "
+ entity.getMappingInterface() + entity.getMappingInterface()
+ " or " + " or "
+ prop.getEntity().getMappingInterface()); + prop.getEntity().getMappingInterface());
} }
if (cached) { if (cached) {
switch(prop.getProperty().getColumnType()) { switch (prop.getProperty().getColumnType()) {
case PARTITION_KEY: case PARTITION_KEY:
case CLUSTERING_COLUMN: case CLUSTERING_COLUMN:
break; break;
@ -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;
@ -43,19 +41,21 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
private int[] ttl; private int[] ttl;
private long[] timestamp; private long[] timestamp;
public UpdateOperation(AbstractSessionOperations sessionOperations){ public UpdateOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
this.draft = null; this.draft = null;
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;
@ -152,7 +152,7 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
if (draft != null) { if (draft != null) {
String key = p.getProperty().getPropertyName(); String key = p.getProperty().getPropertyName();
List<V> list = (List<V>)draftMap.get(key); List<V> list = (List<V>) draftMap.get(key);
list.add(0, value); list.add(0, value);
} }
@ -194,7 +194,7 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
if (draft != null) { if (draft != null) {
String key = p.getProperty().getPropertyName(); String key = p.getProperty().getPropertyName();
List<V> list = (List<V>)draftMap.get(key); List<V> list = (List<V>) draftMap.get(key);
if (idx < 0) { if (idx < 0) {
list.add(0, value); list.add(0, value);
} else if (idx > list.size()) { } else if (idx > list.size()) {
@ -222,7 +222,7 @@ public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateO
if (draft != null) { if (draft != null) {
String key = p.getProperty().getPropertyName(); String key = p.getProperty().getPropertyName();
List<V> list = (List<V>)draftMap.get(key); List<V> list = (List<V>) draftMap.get(key);
list.add(value); list.add(value);
} }
@ -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,18 +53,46 @@ 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;
}
if (this.entity != null) { public void setCassandraMetadataForHelenusSesion(Metadata metadata) {
for (HelenusProperty prop : entity.getOrderedProperties()) { if (metadata != null) {
this.metadata = metadata;
entity = init(metadata);
}
}
map.put(prop.getGetterMethod(), prop); private HelenusEntity init(Metadata metadata) {
HelenusEntity entity = new HelenusMappingEntity(iface, metadata);
AbstractDataType type = prop.getDataType(); for (HelenusProperty prop : entity.getOrderedProperties()) {
Class<?> javaType = prop.getJavaType();
if (type instanceof UDTDataType && !UDTValue.class.isAssignableFrom(javaType)) { map.put(prop.getGetterMethod(), prop);
AbstractDataType type = prop.getDataType();
Class<?> javaType = prop.getJavaType();
if (type instanceof UDTDataType && !UDTValue.class.isAssignableFrom(javaType)) {
Object childDsl =
Helenus.dsl(
javaType,
classLoader,
Optional.of(new HelenusPropertyNode(prop, parent)),
metadata);
udtMap.put(prop.getGetterMethod(), childDsl);
}
if (type instanceof DTDataType) {
DTDataType dataType = (DTDataType) type;
if (dataType.getDataType() instanceof TupleType
&& !TupleValue.class.isAssignableFrom(javaType)) {
Object childDsl = Object childDsl =
Helenus.dsl( Helenus.dsl(
@ -68,32 +101,18 @@ public class DslInvocationHandler<E> implements InvocationHandler {
Optional.of(new HelenusPropertyNode(prop, parent)), Optional.of(new HelenusPropertyNode(prop, parent)),
metadata); metadata);
udtMap.put(prop.getGetterMethod(), childDsl); tupleMap.put(prop.getGetterMethod(), childDsl);
}
if (type instanceof DTDataType) {
DTDataType dataType = (DTDataType) type;
if (dataType.getDataType() instanceof TupleType
&& !TupleValue.class.isAssignableFrom(javaType)) {
Object childDsl =
Helenus.dsl(
javaType,
classLoader,
Optional.of(new HelenusPropertyNode(prop, parent)),
metadata);
tupleMap.put(prop.getGetterMethod(), childDsl);
}
} }
} }
} }
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

@ -4,10 +4,11 @@ import net.helenus.mapping.ColumnType;
public abstract class AbstractCollectionDataType extends AbstractDataType { public abstract class AbstractCollectionDataType extends AbstractDataType {
public AbstractCollectionDataType(ColumnType columnType) { public AbstractCollectionDataType(ColumnType columnType) {
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

@ -33,7 +33,7 @@ public enum BeanColumnValueProvider implements ColumnValueProvider {
try { try {
value = getter.invoke(bean, new Object[] {}); value = getter.invoke(bean, new Object[] {});
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
if (e.getCause() != null ) { if (e.getCause() != null) {
throw new HelenusException("getter threw an exception", e.getCause()); throw new HelenusException("getter threw an exception", e.getCause());
} }
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {

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,76 +18,72 @@ 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 HelenusSession session;
static Supply supply; @BeforeClass
static HelenusSession session; public static void beforeTest() {
session = Helenus.init(getSession()).showCql().add(Supply.class).autoCreateDrop().get();
supply = session.dsl(Supply.class);
}
@Test
public void testFoo() throws Exception {
Supply.Draft draft = null;
@BeforeClass draft =
public static void beforeTest() { Supply.draft("APAC")
session = Helenus.init(getSession()) .code("WIDGET-002")
.showCql() .description("Our second Widget!")
.add(Supply.class) .demand(
.autoCreateDrop() new HashMap<String, Long>() {
.get(); {
supply = session.dsl(Supply.class);
}
@Test
public void testFoo() throws Exception {
Supply.Draft draft = null;
draft = Supply.draft("APAC")
.code("WIDGET-002")
.description("Our second Widget!")
.demand(new HashMap<String, Long>() {{
put("APAC", 100L); put("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 =
.prepend(supply::suppliers, "Pignose Supply, LLC.") session
.sync(); .<Supply>update(s1.update())
Assert.assertEquals(s2.suppliers().get(0), "Pignose Supply, LLC."); .prepend(supply::suppliers, "Pignose Supply, LLC.")
.sync();
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) Assert.assertTrue(s3.shipments().contains(shipment));
.sync();
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) Assert.assertEquals((long) s4.demand().get("NORAM"), 10L);
.sync(); }
Assert.assertEquals((long)s4.demand().get("NORAM"), 10L);
}
} }

View file

@ -1,75 +1,93 @@
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();
class Draft extends AbstractAuditedEntityDraft<Inventory> { @Column("apac")
@Types.Counter
long APAC();
// Entity/Draft pattern-enabling methods: @Transient
Draft(UUID id) { static Draft draft(UUID id) {
super(null); return new Draft(id);
}
// Primary Key: @Transient
set("id", id); default Draft update() {
} return new Draft(this);
}
Draft(Inventory inventory) { class Draft extends AbstractAuditedEntityDraft<Inventory> {
super((MapExportable) inventory);
}
public Class<Inventory> getEntityClass() { return Inventory.class; } // Entity/Draft pattern-enabling methods:
Draft(UUID id) {
protected String getCurrentAuditor() { return "unknown"; } super(null);
// Immutable properties:
public UUID id() {
return this.<UUID>get("id", UUID.class);
}
public long EMEA() {
return this.<Long>get("EMEA", long.class);
}
public Draft EMEA(long count) {
mutate("EMEA", count);
return this;
}
public long APAC() {
return this.<Long>get("APAC", long.class);
}
public Draft APAC(long count) {
mutate("APAC", count);
return this;
}
public long NORAM() {
return this.<Long>get("NORAM", long.class);
}
public Draft NORAM(long count) {
mutate("NORAM", count);
return this;
}
// Primary Key:
set(inventory::id, id);
} }
Draft(Inventory inventory) {
super((MapExportable) inventory);
}
public Class<Inventory> getEntityClass() {
return Inventory.class;
}
protected String getCurrentAuditor() {
return "unknown";
}
// Immutable properties:
public UUID id() {
return this.<UUID>get(inventory::id, UUID.class);
}
public long EMEA() {
return this.<Long>get(inventory::EMEA, long.class);
}
public Draft EMEA(long count) {
mutate(inventory::EMEA, count);
return this;
}
public long APAC() {
return this.<Long>get(inventory::APAC, long.class);
}
public Draft APAC(long count) {
mutate(inventory::APAC, count);
return this;
}
public long NORAM() {
return this.<Long>get(inventory::NORAM, long.class);
}
public Draft NORAM(long count) {
mutate(inventory::NORAM, count);
return this;
}
}
} }

View file

@ -1,125 +1,145 @@
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
class Draft extends AbstractEntityDraft<Supply> { @Index
Map<String, Long> demand();
// Entity/Draft pattern-enabling methods: @Index
Draft(String region) { List<String> suppliers();
super(null);
// Primary Key: @Index
set("id", UUIDs.timeBased()); Set<String> shipments();
set("region", region);
}
Draft(Supply supply) { @Transient
super((MapExportable) supply); static Draft draft(String region) {
} return new Draft(region);
}
public Class<Supply> getEntityClass() { return Supply.class; } @Transient
default Draft update() {
return new Draft(this);
}
// Immutable properties: class Draft extends AbstractEntityDraft<Supply> {
public UUID id() {
return this.<UUID>get("id", UUID.class);
}
public String region() { // Entity/Draft pattern-enabling methods:
return this.<String>get("region", String.class); Draft(String region) {
} super(null);
// Mutable properties:
public String code() {
return this.<String>get("code", String.class);
}
public Draft code(String code) {
mutate("code", code);
return this;
}
public Draft setCode(String code) {
return code(code);
}
public String description() {
return this.<String>get("description", String.class);
}
public Draft description(String description) {
mutate("description", description);
return this;
}
public Draft setDescription(String description) {
return description(description);
}
public Map<String, Long> demand() {
return this.<Map<String, Long>>get("demand", Map.class);
}
public Draft demand(Map<String, Long> demand) {
mutate("demand", demand);
return this;
}
public Draft setDemand(Map<String, Long> demand) {
return demand(demand);
}
public List<String> suppliers() {
return this.<List<String>>get("suppliers", List.class);
}
public Draft suppliers(List<String> suppliers) {
mutate("suppliers", suppliers);
return this;
}
public Draft setSuppliers(List<String> suppliers) {
return suppliers(suppliers);
}
public Set<String> shipments() {
return this.<Set<String>>get("shipments", Set.class);
}
public Draft shipments(Set<String> shipments) {
mutate("shipments", shipments);
return this;
}
public Draft setshipments(Set<String> shipments) {
return shipments(shipments);
}
// Primary Key:
set(supply::id, UUIDs.timeBased());
set(supply::region, region);
} }
Draft(Supply supply) {
super((MapExportable) supply);
}
public Class<Supply> getEntityClass() {
return Supply.class;
}
// Immutable properties:
public UUID id() {
return this.<UUID>get(supply::id, UUID.class);
}
public String region() {
return this.<String>get(supply::region, String.class);
}
// Mutable properties:
public String code() {
return this.<String>get(supply::code, String.class);
}
public Draft code(String code) {
mutate(supply::code, code);
return this;
}
public Draft setCode(String code) {
return code(code);
}
public String description() {
return this.<String>get(supply::description, String.class);
}
public Draft description(String description) {
mutate(supply::description, description);
return this;
}
public Draft setDescription(String description) {
return description(description);
}
public Map<String, Long> demand() {
return this.<Map<String, Long>>get(supply::demand, Map.class);
}
public Draft demand(Map<String, Long> demand) {
mutate(supply::demand, demand);
return this;
}
public Draft setDemand(Map<String, Long> demand) {
return demand(demand);
}
public List<String> suppliers() {
return this.<List<String>>get(supply::suppliers, List.class);
}
public Draft suppliers(List<String> suppliers) {
mutate(supply::suppliers, suppliers);
return this;
}
public Draft setSuppliers(List<String> suppliers) {
return suppliers(suppliers);
}
public Set<String> shipments() {
return this.<Set<String>>get(supply::shipments, Set.class);
}
public Draft shipments(Set<String> shipments) {
mutate(supply::shipments, shipments);
return this;
}
public Draft setshipments(Set<String> 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());
@ -204,31 +192,27 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest {
// //
User greg = User greg =
session session
.<User>insert(user) .<User>insert(user)
.value(user::name, "greg") .value(user::name, "greg")
.value(user::age, 44) .value(user::age, 44)
.value(user::type, UserType.USER) .value(user::type, UserType.USER)
.value(user::id, 1234L) .value(user::id, 1234L)
.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
session session
.update() .update()
.set(user::name, null) .set(user::name, null)
.set(user::age, null) .set(user::age, null)
.set(user::type, null) .set(user::type, null)
.where(user::id, eq(100L)) .where(user::id, eq(100L))
.zipkinContext(null) .zipkinContext(null)
.sync(); .sync();
Fun.Tuple3<String, Integer, UserType> tuple = Fun.Tuple3<String, Integer, UserType> tuple =
session session
@ -252,21 +236,16 @@ public class SimpleUserTest extends AbstractEmbeddedCassandraTest {
public void testZipkin() throws Exception { public void testZipkin() throws Exception {
session session
.update() .update()
.set(user::name, null) .set(user::name, null)
.set(user::age, null) .set(user::age, null)
.set(user::type, null) .set(user::type, null)
.where(user::id, eq(100L)) .where(user::id, eq(100L))
.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,102 +26,125 @@ 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
public void testAndThenOrdering() throws Exception { public void testAndThenOrdering() throws Exception {
List<String> q = new ArrayList<String>(5); List<String> q = new ArrayList<String>(5);
UnitOfWork uow1, uow2, uow3, uow4, uow5; UnitOfWork uow1, uow2, uow3, uow4, uow5;
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()
uow2 = session.begin(uow3); .andThen(
uow2.commit().andThen(() -> { q.add("2"); }); () -> {
uow3.commit().andThen(() -> { q.add("3"); }); q.add("1");
uow4 = session.begin(uow5); });
uow4.commit().andThen(() -> { q.add("4"); }); uow2 = session.begin(uow3);
uow5.commit().andThen(() -> { q.add("5"); }); uow2.commit()
.andThen(
() -> {
q.add("2");
});
uow3.commit()
.andThen(
() -> {
q.add("3");
});
uow4 = session.begin(uow5);
uow4.commit()
.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
public void testExceptionWithinAndThen() throws Exception {
List<String> q = new ArrayList<String>(5);
UnitOfWork uow1, uow2, uow3, uow4, uow5;
@Test uow5 = session.begin();
public void testExceptionWithinAndThen() throws Exception { uow4 = session.begin(uow5);
List<String> q = new ArrayList<String>(5); try {
UnitOfWork uow1, uow2, uow3, uow4, uow5; uow3 = session.begin(uow4);
uow1 = session.begin(uow3);
uow5 = session.begin(); uow1.commit()
uow4 = session.begin(uow5); .andThen(
try { () -> {
uow3 = session.begin(uow4);
uow1 = session.begin(uow3);
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();
} catch (Exception e) {
uow4.abort();
}
uow5.commit()
.andThen(
() -> {
q.add("5");
}); });
throw new Exception();
} catch(Exception e) {
uow4.abort();
}
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
public void testClosableWillAbortWhenNotCommitted() throws Exception {
UnitOfWork unitOfWork;
try (UnitOfWork uow = session.begin()) {
unitOfWork = uow;
Assert.assertFalse(uow.hasAborted());
} }
Assert.assertTrue(unitOfWork.hasAborted());
}
@Test @Test
public void testClosableWillAbortWhenNotCommitted() throws Exception { public void testClosable() throws Exception {
UnitOfWork unitOfWork; UnitOfWork unitOfWork;
try(UnitOfWork uow = session.begin()) { try (UnitOfWork uow = session.begin()) {
unitOfWork = uow; unitOfWork = uow;
Assert.assertFalse(uow.hasAborted()); Assert.assertFalse(uow.hasAborted());
} uow.commit()
Assert.assertTrue(unitOfWork.hasAborted()); .andThen(
() -> {
}
@Test
public void testClosable() throws Exception {
UnitOfWork unitOfWork;
try(UnitOfWork uow = session.begin()) {
unitOfWork = uow;
Assert.assertFalse(uow.hasAborted());
uow.commit().andThen(() -> {
Assert.assertFalse(uow.hasAborted()); Assert.assertFalse(uow.hasAborted());
Assert.assertTrue(uow.hasCommitted()); Assert.assertTrue(uow.hasCommitted());
}); });
}
Assert.assertFalse(unitOfWork.hasAborted());
Assert.assertTrue(unitOfWork.hasCommitted());
} }
Assert.assertFalse(unitOfWork.hasAborted());
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

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

View file

@ -15,20 +15,18 @@
*/ */
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 {
@PartitionKey @PartitionKey
UUID inode(); UUID inode();
@ClusteringColumn @ClusteringColumn
String name(); String name();
@Column
FileAttributes attr();
@Column
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,167 +32,161 @@ 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
String name();
}
@Column
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
public static void beforeTest() {
session = Helenus.init(getSession()).showCql().add(Widget.class).autoCreateDrop().get();
widget = session.dsl(Widget.class);
}
@BeforeClass @Test
public static void beforeTest() { public void testSelectAfterSelect() throws Exception {
session = Helenus.init(getSession()) Widget w1, w2;
.showCql() UUID key = UUIDs.timeBased();
.add(Widget.class)
.autoCreateDrop()
.get();
widget = session.dsl(Widget.class);
}
@Test // This should inserted Widget, but not cache it.
public void testSelectAfterSelect() throws Exception { session
Widget w1, w2; .<Widget>insert(widget)
UUID key = UUIDs.timeBased(); .value(widget::id, key)
.value(widget::name, RandomString.make(20))
.sync();
// This should inserted Widget, but not cache it. try (UnitOfWork uow = session.begin()) {
session.<Widget>insert(widget)
.value(widget::id, key)
.value(widget::name, RandomString.make(20))
.sync();
try (UnitOfWork uow = session.begin()) { // This should read from the database and return a Widget.
w1 =
session.<Widget>select(widget).where(widget::id, eq(key)).single().sync(uow).orElse(null);
// This should read from the database and return a Widget. // This should read from the cache and get the same instance of a Widget.
w1 = 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);
// This should read from the cache and get the same instance of a Widget.
w2 = session.<Widget>select(widget)
.where(widget::id, eq(key))
.single()
.sync(uow)
.orElse(null);
uow.commit()
.andThen(() -> {
Assert.assertEquals(w1, w2);
});
}
}
@Test
public void testSelectAfterNestedSelect() throws Exception {
Widget w1, w2, w3, w4;
UUID key1 = UUIDs.timeBased();
UUID key2 = UUIDs.timeBased();
// This should inserted Widget, and not cache it in uow1.
try (UnitOfWork uow1 = session.begin()) {
w1 = session.<Widget>insert(widget)
.value(widget::id, key1)
.value(widget::name, RandomString.make(20))
.sync(uow1);
try (UnitOfWork uow2 = session.begin(uow1)) {
// This should read from uow1's cache and return the same Widget.
w2 = session.<Widget>select(widget)
.where(widget::id, eq(key1))
.single()
.sync(uow2)
.orElse(null);
uow.commit()
.andThen(
() -> {
Assert.assertEquals(w1, w2); Assert.assertEquals(w1, w2);
});
w3 = session.<Widget>insert(widget)
.value(widget::id, key2)
.value(widget::name, RandomString.make(20))
.sync(uow2);
uow2.commit()
.andThen(() -> {
Assert.assertEquals(w1, w2);
});
}
// This should read from the cache and get the same instance of a Widget.
w4 = session.<Widget>select(widget)
.where(widget::id, eq(key2))
.single()
.sync(uow1)
.orElse(null);
uow1.commit()
.andThen(() -> {
Assert.assertEquals(w3, w4);
});
}
} }
/* }
@Test
public void testSelectAfterInsertProperlyCachesEntity() throws Exception {
Widget w1, w2, w3, w4;
UUID key = UUIDs.timeBased();
try (UnitOfWork uow = session.begin()) { @Test
public void testSelectAfterNestedSelect() throws Exception {
Widget w1, w2, w3, w4;
UUID key1 = UUIDs.timeBased();
UUID key2 = UUIDs.timeBased();
// This should cache the inserted Widget. // This should inserted Widget, and not cache it in uow1.
w1 = session.<Widget>insert(widget) try (UnitOfWork uow1 = session.begin()) {
.value(widget::id, key) w1 =
.value(widget::name, RandomString.make(20)) session
.sync(uow); .<Widget>insert(widget)
.value(widget::id, key1)
.value(widget::name, RandomString.make(20))
.sync(uow1);
// This should read from the cache and get the same instance of a Widget. try (UnitOfWork uow2 = session.begin(uow1)) {
w2 = session.<Widget>select(widget)
.where(widget::id, eq(key))
.single()
.sync(uow)
.orElse(null);
uow.commit() // This should read from uow1's cache and return the same Widget.
.andThen(() -> { w2 =
Assert.assertEquals(w1, w2); session
}); .<Widget>select(widget)
} .where(widget::id, eq(key1))
// This should read the widget from the session cache and maintain object identity.
w3 = session.<Widget>select(widget)
.where(widget::id, eq(key))
.single() .single()
.sync() .sync(uow2)
.orElse(null); .orElse(null);
Assert.assertEquals(w1, w3); Assert.assertEquals(w1, w2);
// This should read the widget from the database, no object identity but values should match. w3 =
w4 = session.<Widget>select(widget) session
.where(widget::id, eq(key)) .<Widget>insert(widget)
.ignoreCache() .value(widget::id, key2)
.single() .value(widget::name, RandomString.make(20))
.sync() .sync(uow2);
.orElse(null);
Assert.assertNotEquals(w1, w4); uow2.commit()
Assert.assertTrue(w1.equals(w4)); .andThen(
() -> {
Assert.assertEquals(w1, w2);
});
}
// This should read from the cache and get the same instance of a Widget.
w4 =
session
.<Widget>select(widget)
.where(widget::id, eq(key2))
.single()
.sync(uow1)
.orElse(null);
uow1.commit()
.andThen(
() -> {
Assert.assertEquals(w3, w4);
});
} }
*/ }
/*
@Test
public void testSelectAfterInsertProperlyCachesEntity() throws Exception {
Widget w1, w2, w3, w4;
UUID key = UUIDs.timeBased();
try (UnitOfWork uow = session.begin()) {
// This should cache the inserted Widget.
w1 = session.<Widget>insert(widget)
.value(widget::id, key)
.value(widget::name, RandomString.make(20))
.sync(uow);
// This should read from the cache and get the same instance of a Widget.
w2 = session.<Widget>select(widget)
.where(widget::id, eq(key))
.single()
.sync(uow)
.orElse(null);
uow.commit()
.andThen(() -> {
Assert.assertEquals(w1, w2);
});
}
// This should read the widget from the session cache and maintain object identity.
w3 = session.<Widget>select(widget)
.where(widget::id, eq(key))
.single()
.sync()
.orElse(null);
Assert.assertEquals(w1, w3);
// This should read the widget from the database, no object identity but values should match.
w4 = session.<Widget>select(widget)
.where(widget::id, eq(key))
.ignoreCache()
.single()
.sync()
.orElse(null);
Assert.assertNotEquals(w1, w4);
Assert.assertTrue(w1.equals(w4));
}
*/
} }

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