Skip to content

Instantly share code, notes, and snippets.

@eintopf
Last active April 29, 2017 21:21
Show Gist options
  • Save eintopf/3ae360110846cb80a227 to your computer and use it in GitHub Desktop.
Save eintopf/3ae360110846cb80a227 to your computer and use it in GitHub Desktop.
Since the Datastax driver currently does not support the Auto-Creation of table schema I created this class. It creates the create queries (strings). The execution has to be done in another step. The original problem originates from here: http://stackoverflow.com/questions/32953050/datastax-cassandra-java-driver-object-mapper-auto-create-tables
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.datastax.driver.mapping.annotations.ClusteringColumn;
import com.datastax.driver.mapping.annotations.Column;
import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table;
import com.datastax.driver.mapping.annotations.UDT;
import com.google.common.base.CaseFormat;
import de.tudarmstadt.informatik.tk.assistanceplatform.data.sensor.SensorData;
public class CassandraSchemaGenerator {
public enum StructureType {
TABLE,
TYPE
}
private Map<Class<?>, String> classToUdtName = new HashMap<>();
public String createTableQuery(Class<?> c) throws Exception {
String requiredTypeQueries = createRequiredTypes(c);
return requiredTypeQueries + "\n" + createStructure(StructureType.TABLE, c);
}
private String createRequiredTypes(Class<?> c) throws Exception {
Field[] fields = c.getFields();
StringBuilder stringBuilder = new StringBuilder();
for(Field f : fields) {
Class<?> type = f.getType();
if(classToUdtName.containsKey(type)) {
continue;
}
if(isCustomType(type)) {
stringBuilder.append(createTypeQuery(type));
stringBuilder.append("\n");
classToUdtName.put(type, extractSchemaName(type));
}
}
return stringBuilder.toString();
}
public String createTypeQuery(Class<?> c) throws Exception {
return createStructure(StructureType.TYPE, c);
}
private String createStructure(StructureType structureType, Class<?> c) throws Exception {
String schemaName = extractSchemaName(c);
if(schemaName == null) {
throw new Exception("No table name defined for type / table: " + schemaName);
}
Field[] fields = c.getFields();
String structureName = structureType.name();
StringBuilder stringBuilder = new StringBuilder("CREATE " + structureName + " ");
stringBuilder.append(schemaName);
stringBuilder.append(" (\n");
int i = 0;
for(Field f : fields) {
// Prepare the field name
Column nameAnnotation = f.getAnnotation(Column.class);
String fieldName = getNameOfField(f);
// Append field name
stringBuilder.append("\t" + fieldName);
// Append Field Type
String fieldType = mapJavaToCassandraType(fieldName, f.getType());
stringBuilder.append(" " + fieldType);
// Append comma for next line if not last field
if(i != fields.length - 1 || structureType.equals(StructureType.TABLE)) {
stringBuilder.append(",\n");
}
i++;
}
// Append Primary Key description
if(structureType.equals(StructureType.TABLE)) {
stringBuilder.append("\t" + generatePrimaryKey(c));
}
// Close create statement
stringBuilder.append("\n);");
return stringBuilder.toString();
}
private static String getNameOfField(Field f) {
Column nameAnnotation = f.getAnnotation(Column.class);
String fieldName = null;
if(nameAnnotation == null) {
fieldName = f.getName();
} else {
fieldName = (nameAnnotation).name();
}
return fieldName;
}
private static String generatePrimaryKey(Class<?> c) throws Exception {
Field[] fields = c.getFields();
List<PartitionKey> partitionKeys = new LinkedList<>();
Map<PartitionKey, String> partitionKeyToFieldName = new HashMap<>();
List<String> clusterColumns = new LinkedList<>();
for(Field f : fields) {
PartitionKey partitionKey = f.getAnnotation(PartitionKey.class);
ClusteringColumn clusterColumn = f.getAnnotation(ClusteringColumn.class);
if(partitionKey != null) {
partitionKeys.add(partitionKey);
partitionKeyToFieldName.put(partitionKey, getNameOfField(f));
//partitonKeys.
}
if(clusterColumn != null) {
clusterColumns.add(getNameOfField(f));
}
}
if(partitionKeys.size() == 0) {
throw new Exception("Partition key annotations are required!");
}
String partitonKey = partitionKeys.stream()
.sorted((p1, p2) -> Integer.compare(p1.value(), p2.value()))
.map((p) -> partitionKeyToFieldName.get(p))
.reduce((p1, p2) -> p1 + ", " + p2).get();
boolean multiPartitionKey = partitionKeys.size() > 1;
String result = "PRIMARY KEY (";
if(multiPartitionKey) {
result += "(";
}
result += partitonKey;
if(multiPartitionKey) {
result += ")";
}
if(clusterColumns != null) {
result += ", ";
result += clusterColumns.stream().reduce((c1, c2) -> c1 + ", " + c2).get();
}
result += ")";
return result;
}
private static String extractSchemaName(Class<?> c) {
Table tableAnnotation = c.getAnnotation(Table.class);
UDT udtAnnotation = c.getAnnotation(UDT.class);
if(tableAnnotation != null) {
return tableAnnotation.name();
} else if(udtAnnotation != null) {
return udtAnnotation.name();
}
return null;
}
private String mapJavaToCassandraType(String fieldName, Class<?> type) {
String simpleTypeName = type.getSimpleName();
if(fieldName.equals("uuid")) {
return "uuid";
}
switch(simpleTypeName) {
case "String":
return "text";
case "long":
// TODO: Just supports signed longs here, could be quite confusing!
return "bigint";
case "String[]":
return "list<text>";
}
if(classToUdtName.containsKey(type)) {
return "frozen<" + classToUdtName.get(type) + ">";
}
return javaToSqlNotation(simpleTypeName);
}
private static boolean isCustomType(Class<?> type) {
String typeName = type.getName();
boolean isCustomType = typeName.indexOf(".") != -1 && typeName.indexOf("java.") == -1;
return isCustomType;
}
private static String javaToSqlNotation(String input) {
input = input.replace("UUID", "Uuid");
return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, input);
}
public static void main(String[] args) {
try {
CassandraSchemaGenerator schemaGenerator = new CassandraSchemaGenerator();
schemaGenerator.createTableQuery(AClass.class);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@eintopf
Copy link
Author

eintopf commented Oct 5, 2015

NOTE: The mapJavaToCassandraType method is uncomplete! If the respective TypeMapping class of the Datastax Object Mapper would be public one could use their method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment