package com.youran.generate.service; import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.statement.*; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique; import com.alibaba.druid.sql.parser.ParserException; import com.youran.common.constant.ErrorCode; import com.youran.common.exception.BusinessException; import com.youran.common.util.SafeUtil; import com.youran.generate.constant.JFieldType; import com.youran.generate.constant.PrimaryKeyStrategy; import com.youran.generate.pojo.dto.MetaEntityAddDTO; import com.youran.generate.pojo.dto.MetaFieldAddDTO; import com.youran.generate.pojo.dto.MetaIndexAddDTO; import com.youran.generate.pojo.dto.ReverseEngineeringDTO; import com.youran.generate.pojo.po.MetaEntityPO; import com.youran.generate.pojo.po.MetaFieldPO; import com.youran.generate.pojo.po.MetaIndexPO; import com.youran.generate.pojo.po.MetaProjectPO; import com.youran.generate.util.GuessUtil; import com.youran.generate.util.SwitchCaseUtil; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.stream.Collectors; /** * 反向工程Service * * @author: cbb * @date: 2018/5/30 */ @Service public class ReverseEngineeringService { private final static Logger LOGGER = LoggerFactory.getLogger(ReverseEngineeringService.class); @Autowired private MetaProjectService metaProjectService; @Autowired private MetaEntityService metaEntityService; @Autowired private MetaFieldService metaFieldService; @Autowired private MetaIndexService metaIndexService; /** * 清理反引号 * * @param value * @return */ private static String cleanQuote(String value) { if (StringUtils.isBlank(value)) { return value; } boolean surroundedByBackQuotes = value.startsWith("`") && value.endsWith("`"); boolean surroundedBySingleQuotes = value.startsWith("'") && value.endsWith("'"); if (surroundedByBackQuotes || surroundedBySingleQuotes) { return value.substring(1, value.length() - 1); } return value; } /** * 解析DDL * * @param dto * @return */ public List<SQLStatement> parse(ReverseEngineeringDTO dto) { List<SQLStatement> sqlStatements; try { sqlStatements = SQLUtils.parseStatements(dto.getDdl(), dto.getDbType()); } catch (ParserException e) { LOGGER.warn("反向工程校验失败:{}", e); throw new BusinessException(ErrorCode.BAD_PARAMETER, "DDL解析失败:" + e.getMessage()); } if (CollectionUtils.isEmpty(sqlStatements)) { throw new BusinessException(ErrorCode.BAD_PARAMETER, "未找到有效DDL语句"); } for (SQLStatement sqlStatement : sqlStatements) { if (!(sqlStatement instanceof SQLCreateTableStatement)) { throw new BusinessException(ErrorCode.BAD_PARAMETER, "只支持create table语句,请删除多余的sql"); } } return sqlStatements; } /** * 执行反向工程 * * @param dto */ @Transactional(rollbackFor = RuntimeException.class) public void execute(ReverseEngineeringDTO dto) { MetaProjectPO project = metaProjectService.getAndCheckProject(dto.getProjectId()); List<SQLStatement> list = this.parse(dto); for (SQLStatement sqlStatement : list) { this.saveEntityFromSqlStatement(project, (SQLCreateTableStatement) sqlStatement); } } /** * 从单个建表语句中创建实体 * * @param project * @param sqlStatement */ private void saveEntityFromSqlStatement(MetaProjectPO project, SQLCreateTableStatement sqlStatement) { SQLCreateTableStatement createTableStatement = sqlStatement; // 解析表名 String tableName = this.parseTableName(createTableStatement); if (StringUtils.isBlank(tableName)) { return; } String comment = cleanQuote(SafeUtil.getString(createTableStatement.getComment())); MetaEntityPO entity = createEntity(project, tableName, comment); // 获取单独声明的主键 String pkAlone = this.getPkAlone(createTableStatement); List<SQLTableElement> tableElementList = createTableStatement.getTableElementList(); int orderNo = 0; // 缓存遍历到的字段 Map<String, MetaFieldPO> fieldMap = new HashMap<>(32); for (SQLTableElement element : tableElementList) { // 创建字段 if (element instanceof SQLColumnDefinition) { orderNo += 10; SQLColumnDefinition sqlColumnDefinition = (SQLColumnDefinition) element; String fieldName = cleanQuote(sqlColumnDefinition.getNameAsString()); boolean pk = this.isPkField(sqlColumnDefinition, pkAlone); String fieldType = StringUtils.lowerCase(sqlColumnDefinition.getDataType().getName()); int fieldLength = 0; int fieldScale = 0; List<SQLExpr> arguments = sqlColumnDefinition.getDataType().getArguments(); if (CollectionUtils.isNotEmpty(arguments)) { fieldLength = SafeUtil.getInteger(arguments.get(0)); if (arguments.size() >= 2) { fieldScale = SafeUtil.getInteger(arguments.get(1)); } } boolean autoIncrement = sqlColumnDefinition.isAutoIncrement(); boolean notNull = sqlColumnDefinition.containsNotNullConstaint(); if (pk) { notNull = true; } String defaultValue = sqlColumnDefinition.getDefaultExpr() == null ? "NULL" : sqlColumnDefinition.getDefaultExpr().toString(); String desc = sqlColumnDefinition.getComment() == null ? "" : cleanQuote(sqlColumnDefinition.getComment().toString()); MetaFieldPO field = this.createField(entity, fieldName, fieldType, fieldLength, fieldScale, pk, autoIncrement, notNull, orderNo, defaultValue, desc); fieldMap.put(fieldName, field); continue; } if (element instanceof MySqlPrimaryKey) { continue; } // 创建索引 if (element instanceof MySqlKey) { boolean unique = (element instanceof MySqlUnique); MySqlKey sqlKey = (MySqlKey) element; String indexName = cleanQuote(sqlKey.getName().toString()); List<MetaFieldPO> fields = new ArrayList<>(); for (SQLSelectOrderByItem item : sqlKey.getColumns()) { String columnName = cleanQuote(item.getExpr().toString()); fields.add(fieldMap.get(columnName)); } this.createIndex(entity, indexName, unique, fields); } } } /** * 解析表名 * * @param createTableStatement * @return */ private String parseTableName(SQLCreateTableStatement createTableStatement) { return cleanQuote(createTableStatement.getTableSource().getName().getSimpleName()); } /** * 获取单独声明的主键名称 * * @param createTableStatement * @return */ private String getPkAlone(SQLCreateTableStatement createTableStatement) { SQLPrimaryKey primaryKey = createTableStatement.findPrimaryKey(); if (primaryKey != null) { if (primaryKey.getColumns().size() > 1) { throw new BusinessException(ErrorCode.BAD_PARAMETER, "表【" + this.parseTableName(createTableStatement) + "】存在联合主键,反向工程暂不支持联合主键"); } return cleanQuote(primaryKey.getColumns().get(0).getExpr().toString()); } return null; } /** * 判断是否主键字段 * * @param sqlColumnDefinition * @param pkAlone * @return */ private boolean isPkField(SQLColumnDefinition sqlColumnDefinition, String pkAlone) { if (sqlColumnDefinition.isPrimaryKey()) { return true; } if (StringUtils.isNotBlank(pkAlone)) { String fieldName = cleanQuote(sqlColumnDefinition.getNameAsString()); if (pkAlone.equals(fieldName)) { return true; } } return false; } /** * 创建实体 * * @param project * @param tableName * @param comment * @return */ private MetaEntityPO createEntity(MetaProjectPO project, String tableName, String comment) { MetaEntityAddDTO metaEntityDTO = new MetaEntityAddDTO(); metaEntityDTO.setProjectId(project.getProjectId()); metaEntityDTO.setSchemaName(""); metaEntityDTO.setClassName(SwitchCaseUtil.underlineToCamelCase(tableName, true)); metaEntityDTO.setTableName(tableName); metaEntityDTO.setTitle(StringUtils.abbreviate(comment, 25)); metaEntityDTO.setDesc(StringUtils.abbreviate(comment, 250)); metaEntityDTO.setPageSign(true); return metaEntityService.save(metaEntityDTO); } /** * 创建字段 * * @param entity * @param fieldName * @param fieldType * @param fieldLength * @param fieldScale * @param pk * @param autoIncrement * @param notNull * @param orderNo * @param defaultValue * @param desc * @return */ private MetaFieldPO createField(MetaEntityPO entity, String fieldName, String fieldType, int fieldLength, int fieldScale, boolean pk, boolean autoIncrement, boolean notNull, int orderNo, String defaultValue, String desc) { JFieldType jFieldType = GuessUtil.guessJFieldType(fieldName, fieldType, fieldLength); String specialField = GuessUtil.guessSpecialField(fieldName, jFieldType); PrimaryKeyStrategy primaryKeyStrategy = GuessUtil.guessPkStrategy(fieldType, fieldLength, autoIncrement); // 如果不存在字段描述,则使用字段名替代 if (StringUtils.isBlank(desc)) { desc = fieldName; } MetaFieldAddDTO metaFieldDTO = new MetaFieldAddDTO(); metaFieldDTO.setEntityId(entity.getEntityId()); metaFieldDTO.setPkStrategy(primaryKeyStrategy.getValue()); metaFieldDTO.setDefaultValue(defaultValue); metaFieldDTO.setDicType(null); metaFieldDTO.setEditType(GuessUtil.guessEditType(jFieldType, fieldType)); metaFieldDTO.setFieldComment(StringUtils.abbreviate(desc, 200)); metaFieldDTO.setFieldDesc(StringUtils.abbreviate(desc, 40)); metaFieldDTO.setFieldExample(GuessUtil.guessFieldExample(fieldName, jFieldType, fieldLength)); metaFieldDTO.setFieldLength(fieldLength); metaFieldDTO.setFieldName(fieldName); metaFieldDTO.setFieldScale(fieldScale); metaFieldDTO.setFieldType(fieldType); metaFieldDTO.setInsert(!pk); metaFieldDTO.setJfieldName(SwitchCaseUtil.underlineToCamelCase(fieldName, false)); metaFieldDTO.setJfieldType(jFieldType.getJavaType()); metaFieldDTO.setList(true); metaFieldDTO.setListSort(false); metaFieldDTO.setOrderNo(orderNo); metaFieldDTO.setPrimaryKey(pk); metaFieldDTO.setForeignKey(false); metaFieldDTO.setNotNull(notNull); metaFieldDTO.setForeignEntityId(null); metaFieldDTO.setForeignFieldId(null); metaFieldDTO.setQuery(!pk); metaFieldDTO.setQueryType(GuessUtil.guessQueryType(jFieldType, fieldLength)); metaFieldDTO.setShow(true); metaFieldDTO.setUpdate(!pk); metaFieldDTO.setSpecialField(specialField); return metaFieldService.save(metaFieldDTO); } /** * 创建索引 * * @param entity * @param indexName * @param unique * @param fields * @return */ public MetaIndexPO createIndex(MetaEntityPO entity, String indexName, boolean unique, List<MetaFieldPO> fields) { MetaIndexAddDTO metaIndexAddDTO = new MetaIndexAddDTO(); metaIndexAddDTO.setIndexName(indexName); metaIndexAddDTO.setEntityId(entity.getEntityId()); metaIndexAddDTO.setUnique(unique); metaIndexAddDTO.setUniqueCheck(unique); String fieldIds = fields.stream() .map(field -> field.getFieldId().toString()) .collect(Collectors.joining(",")); metaIndexAddDTO.setFieldIds(fieldIds); return metaIndexService.save(metaIndexAddDTO); } }