org.test4j.module.database.script.EntityScriptParser Maven / Gradle / Ivy
package org.test4j.module.database.script;
import cn.org.atool.fluent.mybatis.metadata.DbType;
import lombok.Setter;
import org.test4j.module.database.annotations.ColumnDef;
import org.test4j.module.database.annotations.ScriptTable;
import org.test4j.module.database.script.script.*;
import org.test4j.tools.commons.AnnotationHelper;
import org.test4j.tools.commons.StringHelper;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static org.test4j.tools.commons.StringHelper.notBlank;
/**
* 实体对应数据表脚本生成
*
* @author darui.wu
*/
@SuppressWarnings({"rawtypes"})
public abstract class EntityScriptParser {
protected static final String NEW_LINE_JOIN = ",\n\t";
protected final Class klass;
protected final DbTypeConvert typeConvert;
public EntityScriptParser(DbTypeConvert typeConvert, Class klass) {
this.typeConvert = typeConvert == null ? new NonDbTypeConvert() : typeConvert;
this.klass = klass;
}
/**
* 根据实体classes定义自动生成数据库脚本
*
* @param classes 数据库脚本定义类列表
* @return 建库脚本
*/
public static String script(DbType type, DbTypeConvert typeConvert, List classes) {
return classes.stream()
.map(klass -> EntityScriptParser.newScriptParser(type, typeConvert, klass))
.map(EntityScriptParser::script)
.collect(joining("\n\n"));
}
protected static EntityScriptParser newScriptParser(DbType type, DbTypeConvert typeConvert, Class klass) {
switch (type) {
case H2:
return new H2Script(typeConvert, klass);
case SQLITE:
return new SqliteScript(typeConvert, klass);
case HSQL:
return new HSqlScript(typeConvert, klass);
case DERBY:
return new DerbyScript(typeConvert, klass);
case MYSQL:
case MARIADB:
return new MysqlScript(typeConvert, klass);
case POSTGRE_SQL:
return new PostgreScript(typeConvert, klass);
default:
throw new RuntimeException("not support!");
}
}
/**
* 构造具体的脚步语句
*
* @return 建库脚本
*/
public String script() {
List columns = this.findColumns();
String table = this.dbType().wrap(this.getTableName());
StringBuilder buff = new StringBuilder()
.append(String.format("DROP TABLE IF EXISTS %s;\n", table))
.append(String.format("CREATE TABLE %s (\n\t", table))
.append(this.parseColumn(columns));
return buff.append(");\n").toString();
}
/**
* 数据库类型
*
* @return DbType
*/
public DbType dbType() {
return DbType.OTHER;
}
protected String findPrimaryFieldNames(List columns) {
return columns.stream()
.filter(column -> column.primary)
.map(column -> column.name)
.collect(joining(","));
}
protected List findColumns() {
Set annotations = AnnotationHelper.getFieldsAnnotatedWith(klass, ColumnDef.class);
if (annotations != null && !annotations.isEmpty()) {
return annotations.stream().map(ColumnDefine::new).collect(toList());
} else {
throw new RuntimeException("the entity[" + klass.getName() + "] field should be defined by @ColumnDef");
}
}
protected String parseColumn(List columns) {
return columns.stream()
.map(this::parseColumn)
.collect(joining(NEW_LINE_JOIN));
}
protected abstract String parseColumn(ColumnDefine column);
protected String convertColumnType(String type) {
String _type = typeConvert.convertType(type);
return _type == null ? type : _type;
}
protected String getTableName() {
ScriptTable annotation = AnnotationHelper.getClassLevelAnnotation(ScriptTable.class, klass);
if (annotation == null) {
throw new RuntimeException("the entity class[" + klass.getName() + "] should be defined by @ScriptTable");
} else {
return annotation.value();
}
}
protected String getDefaultValue(ColumnDefine column) {
return column.defaultValue;
}
@Setter
public static class ColumnDefine {
public String name;
/**
* 数据库字段类型
*/
public String type;
/**
* 是否主键
*/
public boolean primary;
/**
* 自增
*/
public boolean autoIncrease;
/**
* 允许字段为null
*/
public boolean notNull;
/**
* 默认值
*/
public String defaultValue;
public ColumnDefine(Field field) {
this.name = StringHelper.camel(field.getName());
ColumnDef def = field.getAnnotation(ColumnDef.class);
if (def != null) {
this.init(def);
} else {
throw new RuntimeException("the field[" + field.getName() + "] should be defined by @ColumnDef");
}
}
private void init(ColumnDef def) {
if (notBlank(def.value())) {
this.name = def.value();
}
this.type = def.type();
this.primary = def.primary();
this.autoIncrease = def.autoIncrease();
this.notNull = def.notNull();
this.defaultValue = def.defaultValue();
}
}
public interface DbTypeConvert {
/**
* 原生数据库字段类型转换为测试数据库(内存库)字段类型
*
* @param type 原生数据库字段类型
* @return 测试数据库(内存库)字段类型
*/
String convertType(String type);
}
public static class NonDbTypeConvert implements DbTypeConvert {
public static final DbTypeConvert INSTANCE = new NonDbTypeConvert();
protected final Map types = new HashMap<>();
protected NonDbTypeConvert() {
}
/**
* 默认, 如果没有特殊处理,返回原类型
*
* @param type 字段类型
* @return 转换后的类型
*/
@Override
public String convertType(String type) {
String uType = mapping(type);
if (uType != null) {
return uType;
}
String rType = regex(type.toUpperCase().replaceAll("\\s+", " ").trim());
if (rType != null) {
return rType;
}
return type;
}
protected String mapping(String type) {
String uType = type.toUpperCase();
return types.get(uType);
}
protected String regex(String type) {
return null;
}
protected boolean isEndsUnsigned(String type) {
return type.endsWith(" UNSIGNED");
}
protected String trimUnsigned(String type) {
return type.substring(0, type.length() - "UNSIGNED".length());
}
}
protected static DbTypeConvert typeConvert(DbTypeConvert outerTypeConvert, DbTypeConvert innerTypeConvert) {
return outerTypeConvert == EntityScriptParser.NonDbTypeConvert.INSTANCE ? innerTypeConvert : outerTypeConvert;
}
}