Spring Boot

基于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
}
上一篇下一篇

猜你喜欢

热点阅读