package core.framework.test.db;
import core.framework.api.db.Column;
import core.framework.api.db.Database;
import core.framework.api.db.PrimaryKey;
import core.framework.api.db.Table;
import core.framework.api.util.Exceptions;
import core.framework.api.util.Lists;
import core.framework.api.util.StopWatch;
import core.framework.api.validate.Length;
import core.framework.api.validate.NotNull;
import core.framework.impl.reflect.Classes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.List;
/**
* @author neo
*/
public final class EntitySchemaGenerator {
private final Logger logger = LoggerFactory.getLogger(EntitySchemaGenerator.class);
private final Database database;
private final Class<?> entityClass;
public EntitySchemaGenerator(Database database, Class<?> entityClass) {
this.database = database;
this.entityClass = entityClass;
}
public void generate() {
StopWatch watch = new StopWatch();
String sql = schemeSQL();
try {
database.execute(sql);
} finally {
logger.info("create schema, entityClass={}, sql={}, elapsedTime={}", entityClass.getCanonicalName(), sql, watch.elapsedTime());
}
}
private String schemeSQL() {
StringBuilder builder = new StringBuilder("CREATE TABLE ");
Table table = entityClass.getDeclaredAnnotation(Table.class);
builder.append(table.name()).append(" (");
List<String> primaryKeys = Lists.newArrayList();
for (Field field : Classes.instanceFields(entityClass)) {
Column column = field.getDeclaredAnnotation(Column.class);
PrimaryKey primaryKey = field.getDeclaredAnnotation(PrimaryKey.class);
builder.append(column.name()).append(' ');
builder.append(columnType(field.getType(), field.getDeclaredAnnotation(Length.class)));
if (primaryKey != null) {
if (primaryKey.autoIncrement()) builder.append(" AUTO_INCREMENT");
primaryKeys.add(column.name());
}
if (field.isAnnotationPresent(NotNull.class)) {
builder.append(" NOT NULL");
}
builder.append(", ");
}
builder.append("PRIMARY KEY(");
int index = 0;
for (String primaryKey : primaryKeys) {
if (index > 0) builder.append(", ");
builder.append(primaryKey);
index++;
}
builder.append("))");
return builder.toString();
}
// http://dev.mysql.com/doc/connector-j/en/connector-j-reference-type-conversions.html
private String columnType(Class<?> fieldClass, Length lengthAnnotation) {
if (Integer.class.equals(fieldClass)) return "INT";
if (Long.class.equals(fieldClass)) return "BIGINT";
if (String.class.equals(fieldClass)) {
int length = 500;
if (lengthAnnotation != null && lengthAnnotation.max() > 0) length = lengthAnnotation.max();
return "VARCHAR(" + length + ")";
}
if (fieldClass.isEnum()) {
return "VARCHAR(100)";
}
if (Boolean.class.equals(fieldClass)) {
return "BIT(1)";
}
if (Double.class.equals(fieldClass)) {
return "DOUBLE";
}
if (BigDecimal.class.equals(fieldClass)) {
return "DECIMAL(10,2)";
}
if (LocalDateTime.class.equals(fieldClass) || ZonedDateTime.class.equals(fieldClass)) {
return "TIMESTAMP";
}
if (LocalDate.class.equals(fieldClass)) {
return "DATE";
}
throw Exceptions.error("unsupported field class, class={}", fieldClass.getCanonicalName());
}
}