Merge branch 'DATACASS-32' of https://github.com/shvid/spring-data-cassandra into DATACASS-32
This commit is contained in:
@@ -25,12 +25,12 @@ import com.datastax.driver.core.DataType;
|
||||
public class ColumnBuilder {
|
||||
|
||||
/**
|
||||
* Default ordering of primary key fields is {@link Ordering#ASCENDING}.
|
||||
* Default ordering of primary key fields; value is {@link Ordering#ASCENDING}.
|
||||
*/
|
||||
public static final Ordering DFAULT_ORDERING = ASCENDING;
|
||||
|
||||
private String name;
|
||||
private DataType type;
|
||||
private DataType type; // TODO: determining if we should be coupling this to Datastax Java Driver type?
|
||||
private KeyType keyType;
|
||||
private Ordering ordering;
|
||||
|
||||
|
||||
@@ -8,4 +8,4 @@ public class CqlBuilder {
|
||||
public static CreateTableBuilder createTable() {
|
||||
return new CreateTableBuilder();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import static org.springframework.data.cassandra.mapping.KeyType.PRIMARY;
|
||||
import static org.springframework.data.cassandra.mapping.Ordering.ASCENDING;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -29,7 +29,7 @@ public class CreateTableBuilder {
|
||||
private boolean ifNotExists = false;
|
||||
private String name;
|
||||
private List<ColumnBuilder> columns = new ArrayList<ColumnBuilder>();
|
||||
private Map<String, Object> options = new HashMap<String, Object>();
|
||||
private Map<String, Object> options = new LinkedHashMap<String, Object>();
|
||||
|
||||
/**
|
||||
* Causes the inclusion of an <code>IF NOT EXISTS</code> clause.
|
||||
@@ -65,40 +65,27 @@ public class CreateTableBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given single-string option with no value to this table's options. Convenient overload of
|
||||
* <code>with(string, null, false, false)</code>.
|
||||
* Convenience method that calls <code>with(option, null)</code>.
|
||||
*
|
||||
* @param singleStringOption
|
||||
* @return
|
||||
* @return this
|
||||
*/
|
||||
public CreateTableBuilder with(String string) {
|
||||
return with(string, null, false, false);
|
||||
public CreateTableBuilder with(TableOption option) {
|
||||
return with(option, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given single-quote-escaped then single-quoted option value by name to this table's options. Convenient
|
||||
* overload of <code>with(name, value, true, true)</code>
|
||||
* Sets the given table option. This is a convenience method that calls
|
||||
* {@link #with(String, Object, boolean, boolean)} appropriately from the given {@link TableOption} and value for that
|
||||
* option.
|
||||
*
|
||||
* @see #with(String, Object, boolean, boolean)
|
||||
* @param option The option to set.
|
||||
* @param value The value of the option. Must be type-compatible with the {@link TableOption}.
|
||||
* @return this
|
||||
*/
|
||||
public CreateTableBuilder withQuoted(String name, Object value) {
|
||||
return with(name, value, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given option value by name with no quoting or escaping to this table's options. Convenient overload of
|
||||
* <code>with(name, value, false, false)</code>
|
||||
*
|
||||
* @see #with(String, Object, boolean, boolean)
|
||||
* @return this
|
||||
*/
|
||||
public CreateTableBuilder withUnquoted(String name, Object value) {
|
||||
return with(name, value, false, false);
|
||||
}
|
||||
|
||||
public CreateTableBuilder with(String name, Map<String, Object> valueMap) {
|
||||
return with(name, valueMap, false, false);
|
||||
public CreateTableBuilder with(TableOption option, Object value) {
|
||||
option.checkValue(value);
|
||||
return with(option.getName(), value, option.escapesValue(), option.quotesValue());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,22 +139,12 @@ public class CreateTableBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient method that calls <code>with("COMPACT STORAGE", null)</code>.
|
||||
*
|
||||
* @see #with(String, Object)
|
||||
* @return this
|
||||
*/
|
||||
public CreateTableBuilder withCompactStorage() {
|
||||
return with("COMPACT STORAGE");
|
||||
}
|
||||
|
||||
protected List<ColumnBuilder> columns() {
|
||||
return columns == null ? columns = new ArrayList<ColumnBuilder>() : columns;
|
||||
}
|
||||
|
||||
protected Map<String, Object> options() {
|
||||
return options == null ? options = new HashMap<String, Object>() : options;
|
||||
return options == null ? options = new LinkedHashMap<String, Object>() : options;
|
||||
}
|
||||
|
||||
public String toCql() {
|
||||
@@ -269,10 +246,11 @@ public class CreateTableBuilder {
|
||||
// begin options
|
||||
// begin option clause
|
||||
if (clustering != null || !options.isEmpty()) {
|
||||
|
||||
// option preamble
|
||||
first = true;
|
||||
|
||||
cql.append(" WITH ");
|
||||
// end option preamble
|
||||
|
||||
if (clustering != null) {
|
||||
cql.append(clustering);
|
||||
@@ -297,24 +275,32 @@ public class CreateTableBuilder {
|
||||
|
||||
cql.append(" = ");
|
||||
|
||||
Map<String, Object> valueMap = null;
|
||||
if ((value instanceof Map) && !(valueMap = (Map<String, Object>) value).isEmpty()) {
|
||||
Map<Option, Object> valueMap = null;
|
||||
if ((value instanceof Map) && !(valueMap = (Map<Option, Object>) value).isEmpty()) {
|
||||
// then option value is a non-empty map
|
||||
|
||||
// append { 'name' : 'value', ... }
|
||||
cql.append("{ ");
|
||||
boolean mapFirst = true;
|
||||
for (Map.Entry<String, Object> entry : valueMap.entrySet()) {
|
||||
for (Map.Entry<Option, Object> entry : valueMap.entrySet()) {
|
||||
if (mapFirst) {
|
||||
mapFirst = false;
|
||||
} else {
|
||||
cql.append(", ");
|
||||
}
|
||||
|
||||
cql.append(singleQuote(entry.getKey())); // 'name'
|
||||
Option option = entry.getKey();
|
||||
cql.append(singleQuote(option.getName())); // entries in map keys are always quoted
|
||||
cql.append(" : ");
|
||||
Object entryValue = entry.getValue();
|
||||
cql.append(singleQuote(entryValue == null ? "" : entryValue.toString())); // 'value'
|
||||
entryValue = entryValue == null ? "" : entryValue.toString();
|
||||
if (option.escapesValue()) {
|
||||
entryValue = escapeSingle(value);
|
||||
}
|
||||
if (option.quotesValue()) {
|
||||
entryValue = singleQuote(value);
|
||||
}
|
||||
cql.append(entryValue);
|
||||
}
|
||||
cql.append(" }");
|
||||
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.escapeSingle;
|
||||
import static org.springframework.data.cassandra.cql.CqlStringUtils.singleQuote;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A default implementation of {@link Option}.
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
*/
|
||||
public class DefaultOption implements Option {
|
||||
|
||||
private String name;
|
||||
private Class<?> type;
|
||||
private boolean requiresValue;
|
||||
private boolean escapesValue;
|
||||
private boolean quotesValue;
|
||||
|
||||
public DefaultOption(String name, Class<?> type, boolean requiresValue, boolean escapesValue, boolean quotesValue) {
|
||||
setName(name);
|
||||
setType(type);
|
||||
this.requiresValue = requiresValue;
|
||||
this.escapesValue = escapesValue;
|
||||
this.quotesValue = quotesValue;
|
||||
|
||||
}
|
||||
|
||||
protected void setName(String name) {
|
||||
Assert.hasLength(name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
protected void setType(Class<?> type) {
|
||||
if (type != null) {
|
||||
if (type.isInterface() && !(Map.class.isAssignableFrom(type) || Collection.class.isAssignableFrom(type))) {
|
||||
throw new IllegalArgumentException("given type [" + type.getName() + "] must be a class, Map or Collection");
|
||||
}
|
||||
}
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public boolean isCoerceable(Object value) {
|
||||
if (value == null || type == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// check collections
|
||||
if (Map.class.isAssignableFrom(type)) {
|
||||
return Map.class.isAssignableFrom(value.getClass());
|
||||
}
|
||||
// check map
|
||||
if (Collection.class.isAssignableFrom(type)) {
|
||||
return Collection.class.isAssignableFrom(value.getClass());
|
||||
}
|
||||
// check enum
|
||||
if (type.isEnum()) {
|
||||
try {
|
||||
String name = value instanceof Enum ? name = ((Enum) value).name() : value.toString();
|
||||
Enum.valueOf((Class<? extends Enum>) type, name);
|
||||
return true;
|
||||
} catch (NullPointerException x) {
|
||||
return false;
|
||||
} catch (IllegalArgumentException x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check class via String constructor
|
||||
try {
|
||||
Constructor<?> ctor = type.getConstructor(String.class);
|
||||
if (!ctor.isAccessible()) {
|
||||
ctor.setAccessible(true);
|
||||
}
|
||||
ctor.newInstance(value.toString());
|
||||
return true;
|
||||
} catch (InstantiationException e) {
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (InvocationTargetException e) {
|
||||
} catch (NoSuchMethodException e) {
|
||||
} catch (SecurityException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean takesValue() {
|
||||
return type != null;
|
||||
}
|
||||
|
||||
public boolean requiresValue() {
|
||||
return this.requiresValue;
|
||||
}
|
||||
|
||||
public boolean escapesValue() {
|
||||
return this.escapesValue;
|
||||
}
|
||||
|
||||
public boolean quotesValue() {
|
||||
return this.quotesValue;
|
||||
}
|
||||
|
||||
public void checkValue(Object value) {
|
||||
if (takesValue()) {
|
||||
if (value == null) {
|
||||
if (requiresValue) {
|
||||
throw new IllegalArgumentException("Option [" + getName() + "] requires a value");
|
||||
}
|
||||
return; // doesn't require a value, so null is ok
|
||||
}
|
||||
// else value is not null
|
||||
if (isCoerceable(value)) {
|
||||
return;
|
||||
}
|
||||
// else value is not coerceable into the expected type
|
||||
throw new IllegalArgumentException("Option [" + getName() + "] takes value coerceable to type ["
|
||||
+ getType().getName() + "]");
|
||||
}
|
||||
// else this option doesn't take a value
|
||||
if (value != null) {
|
||||
throw new IllegalArgumentException("Option [" + getName() + "] takes no value");
|
||||
}
|
||||
}
|
||||
|
||||
public String toString(Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
checkValue(value);
|
||||
|
||||
String string = value.toString();
|
||||
string = escapesValue ? escapeSingle(string) : string;
|
||||
string = quotesValue ? singleQuote(string) : string;
|
||||
return string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[name=" + name + ", type=" + type.getName() + ", requiresValue=" + requiresValue + ", escapesValue="
|
||||
+ escapesValue + ", quotesValue=" + quotesValue + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Builder for maps, which also conveniently implements {@link Map} via delegation for convenience so you don't have to
|
||||
* actually {@link #build()} it if you don't want to (or forget).
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
* @param <K> The key type of the map.
|
||||
* @param <V> The value type of the map.
|
||||
*/
|
||||
public class MapBuilder<K, V> implements Map<K, V> {
|
||||
|
||||
/**
|
||||
* Factory method to construct a new <code>MapBuilder<Object,Object></code>. Convenient if imported statically.
|
||||
*/
|
||||
public static MapBuilder<Object, Object> map() {
|
||||
return map(Object.class, Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to construct a new builder with the given key & value types. Convenient if imported statically.
|
||||
*/
|
||||
public static <K, V> MapBuilder<K, V> map(Class<K> keyType, Class<V> valueType) {
|
||||
return new MapBuilder<K, V>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to construct a new builder with a shallow copy of the given map. Convenient if imported statically.
|
||||
*/
|
||||
public static <K, V> MapBuilder<K, V> map(Map<K, V> source) {
|
||||
return new MapBuilder<K, V>(source);
|
||||
}
|
||||
|
||||
private Map<K, V> map;
|
||||
|
||||
public MapBuilder() {
|
||||
this(new LinkedHashMap<K, V>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance with a copy of the given map.
|
||||
*/
|
||||
public MapBuilder(Map<K, V> source) {
|
||||
this.map = new LinkedHashMap<K, V>(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entry to this map, then returns <code>this</code>.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public MapBuilder<K, V> entry(K key, V value) {
|
||||
map.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new map based on the current state of this builder's map.
|
||||
*
|
||||
* @return A new Map<K, V> with this builder's map's current content.
|
||||
*/
|
||||
public Map<K, V> build() {
|
||||
return new LinkedHashMap<K, V>(map);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return map.containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Object value) {
|
||||
return map.containsValue(value);
|
||||
}
|
||||
|
||||
public V get(Object key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
public V put(K key, V value) {
|
||||
return map.put(key, value);
|
||||
}
|
||||
|
||||
public V remove(Object key) {
|
||||
return map.remove(key);
|
||||
}
|
||||
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
map.putAll(m);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
map.clear();
|
||||
}
|
||||
|
||||
public Set<K> keySet() {
|
||||
return map.keySet();
|
||||
}
|
||||
|
||||
public Collection<V> values() {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
public Set<java.util.Map.Entry<K, V>> entrySet() {
|
||||
return map.entrySet();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return map.equals(o);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return map.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
/**
|
||||
* Interface to represent option types.
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
*/
|
||||
public interface Option {
|
||||
|
||||
/**
|
||||
* The type that values must be able to be coerced into for this option.
|
||||
*/
|
||||
Class<?> getType();
|
||||
|
||||
/**
|
||||
* The (usually lower-cased, underscore-separated) name of this table option.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Whether this option takes a value.
|
||||
*/
|
||||
boolean takesValue();
|
||||
|
||||
/**
|
||||
* Whether this option should escape single quotes in its value.
|
||||
*/
|
||||
boolean escapesValue();
|
||||
|
||||
/**
|
||||
* Whether this option's value should be single-quoted.
|
||||
*/
|
||||
boolean quotesValue();
|
||||
|
||||
/**
|
||||
* Whether this option requires a value.
|
||||
*/
|
||||
boolean requiresValue();
|
||||
|
||||
/**
|
||||
* Checks that the given value can be coerced into the type given by {@link #getType()}.
|
||||
*/
|
||||
void checkValue(Object value);
|
||||
|
||||
/**
|
||||
* Tests whether the given value can be coerced into the type given by {@link #getType()}.
|
||||
*/
|
||||
boolean isCoerceable(Object value);
|
||||
|
||||
/**
|
||||
* First ensures that the given value is coerceable into the type expected by this option, then returns the result of
|
||||
* {@link Object#toString()} called on the given value. If this option is escaping quotes ({@link #escapesValue()} is
|
||||
* <code>true</code>), then single quotes will be escaped, and if this option is quoting values (
|
||||
* {@link #quotesValue()} is <code>true</code>), then the value will be surrounded by single quotes. Given
|
||||
* <code>null</code>, returns <code>null</code>.
|
||||
*
|
||||
* @see #escapesValue()
|
||||
* @see #quotesValue()
|
||||
*/
|
||||
String toString(Object value);
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Enumeration that represents all known table options. If a table option is not listed here, but is supported by
|
||||
* Cassandra, use the method {@link CreateTableBuilder#with(String, Object, boolean, boolean)} to write the raw value.
|
||||
*
|
||||
* Implements {@link Option} via delegation, since {@link Enum}s can't extend anything.
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
* @see CompactionOption
|
||||
* @see CompressionOption
|
||||
* @see CachingOption
|
||||
*/
|
||||
public enum TableOption implements Option {
|
||||
/**
|
||||
* <code>comment</code>
|
||||
*/
|
||||
COMMENT("comment", String.class, false, true, true),
|
||||
/**
|
||||
* <code>COMPACT STORAGE</code>
|
||||
*/
|
||||
COMPACT_STORAGE("COMPACT STORAGE", null, false, false, false),
|
||||
/**
|
||||
* <code>compaction</code>. Value is a <code>Map<CompactionOption,Object></code>.
|
||||
*
|
||||
* @see CompactionOption
|
||||
*/
|
||||
COMPACTION("compaction", Map.class, false, false, false),
|
||||
/**
|
||||
* <code>compression</code>. Value is a <code>Map<CompressionOption,Object></code>.
|
||||
*
|
||||
* @see {@link CompressionOption}
|
||||
*/
|
||||
COMPRESSION("compression", Map.class, false, false, false),
|
||||
/**
|
||||
* <code>replicate_on_write</code>
|
||||
*/
|
||||
REPLICATE_ON_WRITE("replicate_on_write", Boolean.class, false, false, false),
|
||||
/**
|
||||
* <code>caching</code>
|
||||
*
|
||||
* @see CachingOption
|
||||
*/
|
||||
CACHING("caching", CachingOption.class, false, false, false),
|
||||
/**
|
||||
* <code>bloom_filter_fp_chance</code>
|
||||
*/
|
||||
BLOOM_FILTER_FP_CHANCE("bloom_filter_fp_chance", Double.class, false, false, false),
|
||||
/**
|
||||
* <code>read_repair_chance</code>
|
||||
*/
|
||||
READ_REPAIR_CHANCE("read_repair_chance", Double.class, false, false, false),
|
||||
/**
|
||||
* <code>dclocal_read_repair_chance</code>
|
||||
*/
|
||||
DCLOCAL_READ_REPAIR_CHANCE("dclocal_read_repair_chance", Double.class, false, false, false),
|
||||
/**
|
||||
* <code>gc_grace_seconds</code>
|
||||
*/
|
||||
GC_GRACE_SECONDS("gc_grace_seconds", Long.class, false, false, false);
|
||||
|
||||
private Option delegate;
|
||||
|
||||
private TableOption(String name, Class<?> type, boolean requiresValue, boolean escapesValue, boolean quotesValue) {
|
||||
this.delegate = new DefaultOption(name, type, requiresValue, escapesValue, quotesValue);
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return delegate.getType();
|
||||
}
|
||||
|
||||
public boolean takesValue() {
|
||||
return delegate.takesValue();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return delegate.getName();
|
||||
}
|
||||
|
||||
public boolean escapesValue() {
|
||||
return delegate.escapesValue();
|
||||
}
|
||||
|
||||
public boolean quotesValue() {
|
||||
return delegate.quotesValue();
|
||||
}
|
||||
|
||||
public boolean requiresValue() {
|
||||
return delegate.requiresValue();
|
||||
}
|
||||
|
||||
public void checkValue(Object value) {
|
||||
delegate.checkValue(value);
|
||||
}
|
||||
|
||||
public boolean isCoerceable(Object value) {
|
||||
return delegate.isCoerceable(value);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
|
||||
public String toString(Object value) {
|
||||
return delegate.toString(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Known caching options.
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
*/
|
||||
public enum CachingOption {
|
||||
ALL("all"), KEYS_ONLY("keys_only"), ROWS_ONLY("rows_only"), NONE("none");
|
||||
|
||||
private String value;
|
||||
|
||||
private CachingOption(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Known compaction options.
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
*/
|
||||
public enum CompactionOption implements Option {
|
||||
/**
|
||||
* <code>tombstone_threshold</code>
|
||||
*/
|
||||
TOMBSTONE_THRESHOLD("tombstone_threshold", Double.class, false, false, false),
|
||||
/**
|
||||
* <code>tombstone_compaction_interval</code>
|
||||
*/
|
||||
TOMBSTONE_COMPACTION_INTERVAL("tombstone_compaction_interval", Double.class, false, false, false),
|
||||
/**
|
||||
* <code>min_sstable_size</code>
|
||||
*/
|
||||
MIN_SSTABLE_SIZE("min_sstable_size", Long.class, false, false, false),
|
||||
/**
|
||||
* <code>min_threshold</code>
|
||||
*/
|
||||
MIN_THRESHOLD("min_threshold", Long.class, false, false, false),
|
||||
/**
|
||||
* <code>max_threshold</code>
|
||||
*/
|
||||
MAX_THRESHOLD("max_threshold", Long.class, false, false, false),
|
||||
/**
|
||||
* <code>bucket_low</code>
|
||||
*/
|
||||
BUCKET_LOW("bucket_low", Double.class, false, false, false),
|
||||
/**
|
||||
* <code>bucket_high</code>
|
||||
*/
|
||||
BUCKET_HIGH("bucket_high", Double.class, false, false, false),
|
||||
/**
|
||||
* <code>sstable_size_in_mb</code>
|
||||
*/
|
||||
SSTABLE_SIZE_IN_MB("sstable_size_in_mb", Long.class, false, false, false);
|
||||
|
||||
private Option delegate;
|
||||
|
||||
private CompactionOption(String name, Class<?> type, boolean requiresValue, boolean escapesValue,
|
||||
boolean quotesValue) {
|
||||
this.delegate = new DefaultOption(name, type, requiresValue, escapesValue, quotesValue);
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return delegate.getType();
|
||||
}
|
||||
|
||||
public boolean takesValue() {
|
||||
return delegate.takesValue();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return delegate.getName();
|
||||
}
|
||||
|
||||
public boolean escapesValue() {
|
||||
return delegate.escapesValue();
|
||||
}
|
||||
|
||||
public boolean quotesValue() {
|
||||
return delegate.quotesValue();
|
||||
}
|
||||
|
||||
public boolean requiresValue() {
|
||||
return delegate.requiresValue();
|
||||
}
|
||||
|
||||
public void checkValue(Object value) {
|
||||
delegate.checkValue(value);
|
||||
}
|
||||
|
||||
public boolean isCoerceable(Object value) {
|
||||
return delegate.isCoerceable(value);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
|
||||
public String toString(Object value) {
|
||||
return delegate.toString(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Known compression options.
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
*/
|
||||
public enum CompressionOption implements Option {
|
||||
/**
|
||||
* <code>sstable_compression</code>
|
||||
*/
|
||||
STABLE_COMPRESSION("sstable_compression", String.class, false, false, false),
|
||||
/**
|
||||
* <code>chunk_length_kb</code>
|
||||
*/
|
||||
CHUNK_LENGTH_KB("chunk_length_kb", Long.class, false, false, false),
|
||||
/**
|
||||
* <code>crc_check_chance</code>
|
||||
*/
|
||||
CRC_CHECK_CHANCE("crc_check_chance", Double.class, false, false, false);
|
||||
|
||||
private Option delegate;
|
||||
|
||||
private CompressionOption(String name, Class<?> type, boolean requiresValue, boolean escapesValue,
|
||||
boolean quotesValue) {
|
||||
this.delegate = new DefaultOption(name, type, requiresValue, escapesValue, quotesValue);
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return delegate.getType();
|
||||
}
|
||||
|
||||
public boolean takesValue() {
|
||||
return delegate.takesValue();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return delegate.getName();
|
||||
}
|
||||
|
||||
public boolean escapesValue() {
|
||||
return delegate.escapesValue();
|
||||
}
|
||||
|
||||
public boolean quotesValue() {
|
||||
return delegate.quotesValue();
|
||||
}
|
||||
|
||||
public boolean requiresValue() {
|
||||
return delegate.requiresValue();
|
||||
}
|
||||
|
||||
public void checkValue(Object value) {
|
||||
delegate.checkValue(value);
|
||||
}
|
||||
|
||||
public boolean isCoerceable(Object value) {
|
||||
return delegate.isCoerceable(value);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
|
||||
public String toString(Object value) {
|
||||
return delegate.toString(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,12 @@ package org.springframework.data.cassandra.mapping;
|
||||
public enum KeyType {
|
||||
|
||||
/**
|
||||
* Used for a column that is a primary key that also is or is part of the partition key.
|
||||
* Used for a column that is a primary key and that also is or is part of the partition key.
|
||||
*/
|
||||
PARTITION,
|
||||
|
||||
/**
|
||||
* Use for a primary key column that is not part of the partition key and, therefore, may also be ordered.
|
||||
* Used for a primary key column that is not part of the partition key.
|
||||
*/
|
||||
PRIMARY
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package org.springframework.data.cassandra.cql;
|
||||
|
||||
import static org.springframework.data.cassandra.cql.builder.CqlBuilder.createTable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.cassandra.cql.builder.CreateTableBuilder;
|
||||
|
||||
import com.datastax.driver.core.DataType;
|
||||
|
||||
public class CreateTableBuilderTest {
|
||||
|
||||
@Test
|
||||
public void createTableTest() {
|
||||
String name = "mytable";
|
||||
DataType type0 = DataType.text();
|
||||
String partition0 = "partitionKey0";
|
||||
String partition1 = "partitionKey1";
|
||||
String primary0 = "primary0";
|
||||
DataType type1 = DataType.text();
|
||||
String column1 = "column1";
|
||||
DataType type2 = DataType.bigint();
|
||||
String column2 = "column2";
|
||||
Object value2 = "this is a comment";
|
||||
String option2 = "comment";
|
||||
Object value3 = "0.00075";
|
||||
String option3 = "bloom_filter_fp_chance";
|
||||
Map<String, Object> value4 = new HashMap<String, Object>();
|
||||
value4.put("class", "LeveledCompactionStrategy");
|
||||
String option4 = "compaction";
|
||||
|
||||
CreateTableBuilder builder = createTable().ifNotExists().name(name).partitionKeyColumn(partition0, type0)
|
||||
.partitionKeyColumn(partition1, type0).primaryKeyColumn(primary0, type0).column(column1, type1)
|
||||
.column(column2, type2).withQuoted(option2, value2).withUnquoted(option3, value3).with(option4, value4)
|
||||
.withCompactStorage();
|
||||
|
||||
String cql = builder.toCql();
|
||||
System.out.println(cql);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.springframework.data.cassandra.cql.builder.CqlBuilder.createTable;
|
||||
import static org.springframework.data.cassandra.cql.builder.MapBuilder.map;
|
||||
import static org.springframework.data.cassandra.cql.builder.TableOption.BLOOM_FILTER_FP_CHANCE;
|
||||
import static org.springframework.data.cassandra.cql.builder.TableOption.CACHING;
|
||||
import static org.springframework.data.cassandra.cql.builder.TableOption.COMMENT;
|
||||
import static org.springframework.data.cassandra.cql.builder.TableOption.COMPACTION;
|
||||
import static org.springframework.data.cassandra.cql.builder.TableOption.CompactionOption.TOMBSTONE_THRESHOLD;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.cassandra.cql.builder.TableOption.CachingOption;
|
||||
|
||||
import com.datastax.driver.core.DataType;
|
||||
|
||||
public class CreateTableBuilderTest {
|
||||
|
||||
@Test
|
||||
public void createTableTest() {
|
||||
String name = "mytable";
|
||||
DataType type0 = DataType.text();
|
||||
String partKey0 = "partitionKey0";
|
||||
String partition1 = "partitionKey1";
|
||||
String primary0 = "primary0";
|
||||
DataType type1 = DataType.text();
|
||||
String column1 = "column1";
|
||||
DataType type2 = DataType.bigint();
|
||||
String column2 = "column2";
|
||||
Object comment = "this is a comment";
|
||||
Object bloom = "0.00075";
|
||||
Object caching = CachingOption.KEYS_ONLY;
|
||||
|
||||
CreateTableBuilder builder = createTable().ifNotExists().name(name).partitionKeyColumn(partKey0, type0)
|
||||
.partitionKeyColumn(partition1, type0).primaryKeyColumn(primary0, type0).column(column1, type1)
|
||||
.column(column2, type2).with(COMMENT, comment).with(BLOOM_FILTER_FP_CHANCE, bloom)
|
||||
.with(COMPACTION, map().entry(TOMBSTONE_THRESHOLD, "0.15")).with(CACHING, caching);
|
||||
|
||||
String cql = builder.toCql();
|
||||
assertEquals(
|
||||
"CREATE TABLE IF NOT EXISTS mytable (partitionKey0 text, partitionKey1 text, primary0 text, column1 text, column2 bigint, PRIMARY KEY ((partitionKey0, partitionKey1), primary0) WITH CLUSTERING ORDER BY (primary0 ASC) AND comment = 'this is a comment' AND bloom_filter_fp_chance = 0.00075 AND compaction = { 'tombstone_threshold' : 0.15 } AND caching = keys_only;",
|
||||
cql);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package org.springframework.data.cassandra.cql.builder;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class OptionTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testOptionWithNullName() {
|
||||
new DefaultOption(null, Object.class, true, true, true);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testOptionWithEmptyName() {
|
||||
new DefaultOption("", Object.class, true, true, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionWithNullType() {
|
||||
new DefaultOption("opt", null, true, true, true);
|
||||
new DefaultOption("opt", null, false, true, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionWithNullTypeIsCoerceable() {
|
||||
Option op = new DefaultOption("opt", null, true, true, true);
|
||||
assertTrue(op.isCoerceable(""));
|
||||
assertTrue(op.isCoerceable(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionValueCoercion() {
|
||||
String name = "my_option";
|
||||
Class<?> type = String.class;
|
||||
boolean requires = true;
|
||||
boolean escapes = true;
|
||||
boolean quotes = true;
|
||||
|
||||
Option op = new DefaultOption(name, type, requires, escapes, quotes);
|
||||
|
||||
assertTrue(op.isCoerceable("opt"));
|
||||
assertEquals("'opt'", op.toString("opt"));
|
||||
assertEquals("'opt''n'", op.toString("opt'n"));
|
||||
|
||||
type = Long.class;
|
||||
escapes = false;
|
||||
quotes = false;
|
||||
op = new DefaultOption(name, type, requires, escapes, quotes);
|
||||
|
||||
String expected = "1";
|
||||
for (Object value : new Object[] { 1, "1" }) {
|
||||
assertTrue(op.isCoerceable(value));
|
||||
assertEquals(expected, op.toString(value));
|
||||
}
|
||||
assertFalse(op.isCoerceable("x"));
|
||||
assertTrue(op.isCoerceable(null));
|
||||
|
||||
type = Long.class;
|
||||
escapes = false;
|
||||
quotes = true;
|
||||
op = new DefaultOption(name, type, requires, escapes, quotes);
|
||||
|
||||
expected = "'1'";
|
||||
for (Object value : new Object[] { 1, "1" }) {
|
||||
assertTrue(op.isCoerceable(value));
|
||||
assertEquals(expected, op.toString(value));
|
||||
}
|
||||
assertFalse(op.isCoerceable("x"));
|
||||
assertTrue(op.isCoerceable(null));
|
||||
|
||||
type = Double.class;
|
||||
escapes = false;
|
||||
quotes = false;
|
||||
op = new DefaultOption(name, type, requires, escapes, quotes);
|
||||
|
||||
String[] expecteds = new String[] { "1", "1.0", "1.0", "1", "1.0", null };
|
||||
Object[] values = new Object[] { 1, 1.0F, 1.0D, "1", "1.0", null };
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
assertTrue(op.isCoerceable(values[i]));
|
||||
assertEquals(expecteds[i], op.toString(values[i]));
|
||||
}
|
||||
assertFalse(op.isCoerceable("x"));
|
||||
assertTrue(op.isCoerceable(null));
|
||||
|
||||
type = RetentionPolicy.class;
|
||||
escapes = false;
|
||||
quotes = false;
|
||||
op = new DefaultOption(name, type, requires, escapes, quotes);
|
||||
|
||||
assertTrue(op.isCoerceable(null));
|
||||
assertTrue(op.isCoerceable(RetentionPolicy.CLASS));
|
||||
assertTrue(op.isCoerceable("CLASS"));
|
||||
assertFalse(op.isCoerceable("x"));
|
||||
assertEquals("CLASS", op.toString("CLASS"));
|
||||
assertEquals("CLASS", op.toString(RetentionPolicy.CLASS));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user