基于Velocity的golang xorm生成器
2020-08-14 本文已影响0人
EasyNetCN
本示例可以生产xorm的struct和repository,也可以生成用于返回json的struct。
CodeGenerator接口
import java.util.List;
import javax.sql.DataSource;
public interface CodeGenerator {
boolean generate(DataSource datasource, String db, List<String> tables, String outputPath, String tpl);
}
数据列封装
import org.apache.commons.lang3.StringUtils;
public class DataColumn {
private String name;
private long ordinalPosition;
private String defaultValue;
private boolean nullable;
private Long characterMaxLength;
private Long characterOctetLength;
private Long numericPrecision;
private Long numericScale;
private String dataType;
private String columnType;
private String comment;
private boolean identity;
private boolean primary;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getOrdinalPosition() {
return ordinalPosition;
}
public void setOrdinalPosition(long ordinalPosition) {
this.ordinalPosition = ordinalPosition;
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
public boolean getNullable() {
return nullable;
}
public void setNullable(boolean nullable) {
this.nullable = nullable;
}
public Long getCharacterMaxLength() {
return characterMaxLength;
}
public void setCharacterMaxLength(Long characterMaxLength) {
this.characterMaxLength = characterMaxLength;
}
public Long getCharacterOctetLength() {
return characterOctetLength;
}
public void setCharacterOctetLength(Long characterOctetLength) {
this.characterOctetLength = characterOctetLength;
}
public Long getNumericPrecision() {
return numericPrecision;
}
public void setNumericPrecision(Long numericPrecision) {
this.numericPrecision = numericPrecision;
}
public Long getNumericScale() {
return numericScale;
}
public void setNumericScale(Long numericScale) {
this.numericScale = numericScale;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public String getColumnType() {
return columnType;
}
public void setColumnType(String columnType) {
this.columnType = columnType;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public boolean getIdentity() {
return identity;
}
public void setIdentity(boolean identity) {
this.identity = identity;
}
public boolean getPrimary() {
return primary;
}
public void setPrimary(boolean primary) {
this.primary = primary;
}
public String getPropertyName() {
StringBuilder sb = new StringBuilder();
String[] strs = name.split("_");
for (String str : strs) {
sb.append(StringUtils.capitalize(str));
}
return sb.toString();
}
public String getVariableName() {
StringBuilder sb = new StringBuilder();
String[] strs = name.split("_");
for (int i = 0; i < strs.length; i++) {
String str = strs[i];
sb.append(i == 0 ? str : StringUtils.capitalize(str));
}
return sb.toString();
}
public String getGolangType() {
if (dataType.equalsIgnoreCase("int")) {
return "int";
} else if (dataType.equalsIgnoreCase("varchar") || dataType.equalsIgnoreCase("text")
|| dataType.equalsIgnoreCase("longtext")) {
return nullable ? "*string" : "string";
} else if (dataType.equalsIgnoreCase("long") || dataType.equalsIgnoreCase("bigint")) {
return nullable ? "*int64" : "int64";
} else if (dataType.equalsIgnoreCase("decimal")) {
return nullable ? "*float64" : "float64";
} else if (dataType.equalsIgnoreCase("datetime")) {
return nullable ? "*time.Time" : "time.Time";
} else {
return dataType;
}
}
public Boolean isDateType() {
return dataType.equalsIgnoreCase("datetime");
}
public Boolean isDecimalType() {
return dataType.equalsIgnoreCase("decimal");
}
public String getXormTag() {
var sb = new StringBuilder("`xorm:\"");
sb.append(dataType);
sb.append(" '");
sb.append(name);
sb.append("'");
if (identity) {
sb.append(" autoincr");
}
if (primary) {
sb.append(" pk");
}
if (nullable) {
sb.append(" null");
} else {
sb.append(" notnull");
}
sb.append(" default(");
if (dataType.equalsIgnoreCase("varchar") || dataType.equalsIgnoreCase("text")
|| dataType.equalsIgnoreCase("longtext")) {
sb.append("'");
}
sb.append(defaultValue);
if (dataType.equalsIgnoreCase("varchar") || dataType.equalsIgnoreCase("text")
|| dataType.equalsIgnoreCase("longtext")) {
sb.append("'");
}
sb.append(")");
sb.append(" comment('");
sb.append(comment);
sb.append("')");
sb.append("\"`");
return sb.toString();
}
}
数据表信息封装
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
public class DataEntityDescriptor {
private String packageName;
private String tableName;
private List<DataColumn> columns;
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public List<DataColumn> getColumns() {
return columns;
}
public void setColumns(List<DataColumn> columns) {
this.columns = columns;
}
public String getEntityClassName() {
StringBuilder sb = new StringBuilder();
String[] strs = getTableName().split("_");
for (String str : strs) {
sb.append(StringUtils.capitalize(str));
}
return sb.toString();
}
public Boolean hasDateType() {
return columns.stream().anyMatch(DataColumn::isDateType);
}
public Boolean hasDecimalType() {
return columns.stream().anyMatch(DataColumn::isDecimalType);
}
public String getRepositoryClassName() {
StringBuilder sb = new StringBuilder();
String[] strs = tableName.split("_");
for (String str : strs) {
sb.append(StringUtils.capitalize(str));
}
sb.append("Repository");
return sb.toString();
}
public String getPrivateRepositoryClassName() {
StringBuilder sb = new StringBuilder();
String[] strs = tableName.split("_");
for (int i = 0; i < strs.length; i++) {
String str = strs[i];
sb.append(i == 0 ? str : StringUtils.capitalize(str));
}
sb.append("Repository");
return sb.toString();
}
public String getPrimaryColumnDataType() {
return columns.stream().filter(DataColumn::getPrimary).findFirst().get().getGolangType();
}
public List<DataColumn> getCommonColumns() {
return columns.stream().filter(DataColumn::getPrimary).collect(Collectors.toList());
}
}
golang代码生成实现
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import generator.model.DataColumn;
import generator.model.DataEntityDescriptor;
@Service
public class GolangCodeGenerator implements CodeGenerator {
private String getAllColumnInfoSql = "SELECT * FROM information_schema.columns WHERE table_schema = :db ORDER BY table_schema ASC,ordinal_position ASC";
private String getColumnInfoSql = "SELECT * FROM information_schema.columns WHERE table_schema = :db AND table_name IN (:tables) ORDER BY table_schema ASC,ordinal_position ASC";
@Autowired
VelocityEngine velocityEngine;
@Override
public boolean generate(DataSource datasource, String db, List<String> tables, String outputPath, String tpl) {
try {
Files.walkFileTree(Paths.get(outputPath), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.deleteIfExists(file);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
var namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(datasource);
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("db", db);
if (null != tables && !tables.isEmpty()) {
paramMap.put("tables", tables);
}
Map<String, DataEntityDescriptor> dataEntityDescriptorMap = new LinkedHashMap<>();
namedParameterJdbcTemplate.query(null == tables || tables.isEmpty() ? getAllColumnInfoSql : getColumnInfoSql,
paramMap, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
var table = rs.getString(3);
var columnKey = rs.getString(17);
var extra = rs.getString(18);
if (!dataEntityDescriptorMap.containsKey(table)) {
var dataEntityDescriptor = new DataEntityDescriptor();
dataEntityDescriptor.setTableName(table);
dataEntityDescriptor.setColumns(new ArrayList<>());
dataEntityDescriptorMap.put(table, dataEntityDescriptor);
}
var dataEntityDescriptor = dataEntityDescriptorMap.get(table);
var dataColumn = new DataColumn();
dataColumn.setName(rs.getString(4));
dataColumn.setOrdinalPosition(rs.getLong(5));
dataColumn.setDefaultValue(rs.getString(6));
dataColumn.setNullable(rs.getString(7).equalsIgnoreCase("YES"));
dataColumn.setCharacterMaxLength(rs.getLong(9));
dataColumn.setCharacterOctetLength(rs.getLong(10));
dataColumn.setNumericPrecision(rs.getLong(11));
dataColumn.setNumericScale(rs.getLong(12));
dataColumn.setDataType(rs.getString(8));
dataColumn.setColumnType(rs.getString(16));
dataColumn.setComment(rs.getString(20));
if (StringUtils.isNotBlank(columnKey) && columnKey.equalsIgnoreCase("PRI")) {
dataColumn.setPrimary(true);
}
if (StringUtils.isNotBlank(extra) && extra.equalsIgnoreCase("auto_increment")) {
dataColumn.setIdentity(true);
}
dataEntityDescriptor.getColumns().add(dataColumn);
}
});
dataEntityDescriptorMap.values().forEach(dataEntityDescriptor -> {
var entityClassName = dataEntityDescriptor.getEntityClassName();
var context = new VelocityContext();
context.put("dataEntityDescriptor", dataEntityDescriptor);
context.put("entityClassName", entityClassName);
context.put("repositoryClassName", dataEntityDescriptor.getRepositoryClassName());
context.put("privateRepositoryClassName", dataEntityDescriptor.getPrivateRepositoryClassName());
var sb = new StringBuilder(dataEntityDescriptor.getTableName());
if (tpl.equalsIgnoreCase("repository")) {
sb.append("_").append(tpl.toLowerCase());
}
sb.append(".go");
try (var fileWriter = new FileWriter(Paths.get(outputPath, sb.toString()).toAbsolutePath().toString())) {
velocityEngine.mergeTemplate("vm/" + tpl + ".vm", "UTF-8", context, fileWriter);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
return true;
}
}
golang repository模板文件
package repository
import (
"time"
"xorm.io/xorm"
)
type $entityClassName struct {
#foreach($column in $dataEntityDescriptor.columns)
$column.propertyName $column.golangType $column.xormTag
#end
}
var (
${dataEntityDescriptor.tableName.toUpperCase()}_COLUMNS=[]string{
#foreach($column in $dataEntityDescriptor.columns)
"$column.name",
#end
}
${dataEntityDescriptor.tableName.toUpperCase()}_COLUMNS_MAP=map[string]string{
#foreach($column in $dataEntityDescriptor.columns)
"$column.name":"$column.name",
#end
}
)
type $repositoryClassName interface {
}
type $privateRepositoryClassName struct {
engine *xorm.Engine
}
func New$repositoryClassName(engine *xorm.Engine) $repositoryClassName {
return &$privateRepositoryClassName{
engine: engine,
}
}
golang json struct模板文件
package model
type $entityClassName struct {
#foreach($column in $dataEntityDescriptor.columns)
$column.propertyName #if($column.golangType == '*time.Time') *string #elseif($column.golangType == 'time.Time') string #else $column.golangType #end `json:"${column.variableName}"`
#end
}