Last active
April 29, 2017 21:21
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
NOTE: The mapJavaToCassandraType method is uncomplete! If the respective TypeMapping class of the Datastax Object Mapper would be public one could use their method.