Skip to content

Commit

Permalink
Merge pull request #144 from Nisus-Liu/bugfix/143-sqlcamel
Browse files Browse the repository at this point in the history
fix: 驼峰列名转命名风格错误问题 #143
  • Loading branch information
moshowgame authored Aug 30, 2023
2 parents 17d668a + e008e4d commit 068472d
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 28 deletions.
1 change: 0 additions & 1 deletion generator-web/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package com.softdev.system.generator.entity;

import lombok.AllArgsConstructor;

/**
* String 包装类
* <p>
* 忽略大小写
**考虑增加这个类是, 如果在 StringUtils 中加工具方法, 使用起来代码非常冗长且不方便
* @author Nisus
* @see String
*/
@AllArgsConstructor
public class NonCaseString implements CharSequence {
private String value;

public static NonCaseString of(String str) {
assert str != null;
return new NonCaseString(str);
}


/**
* {@link String#indexOf(String)} 增强, 忽略大小写
*/
public int indexOf(String m) {
String text = this.value;
if (text == null || m == null || text.length() < m.length()) {
return -1;
}
return text.toLowerCase().indexOf(m.toLowerCase());
}

/**
* {@link String#lastIndexOf(String)} 增强, 忽略大小写
*/
public int lastIndexOf(String m) {
String text = this.value;
if (text == null || m == null || text.length() < m.length()) {
return -1;
}
return text.toLowerCase().lastIndexOf(m.toLowerCase());
}

/**
* 是否包含, 大小写不敏感
* <pre
* "abcxyz" 包含 "abc" => true
* "abcxyz" 包含 "ABC" => true
* "abcxyz" 包含 "aBC" => true
* </pre>
*
* @param m 被包含字符串
*/
public boolean contains(String m) {
String text = this.value;
if (text.length() < m.length()) {
return false;
}
return text.toLowerCase().contains(m.toLowerCase());
}

/**
* 任意一个包含返回true
* <pre>
* containsAny("abcdef", "a", "b)
* 等价
* "abcdef".contains("a") || "abcdef".contains("b")
* </pre>
*
* @param matchers 多个要判断的被包含项
*/
public boolean containsAny(String... matchers) {
for (String matcher : matchers) {
if (contains(matcher)) {
return true;
}
}
return false;
}

/**
* 所有都包含才返回true
*
* @param matchers 多个要判断的被包含项
*/
public boolean containsAllIgnoreCase(String... matchers) {
for (String matcher : matchers) {
if (contains(matcher) == false) {
return false;
}
}
return true;
}

public NonCaseString trim() {
return NonCaseString.of(this.value.trim());
}

public NonCaseString replace(char oldChar, char newChar) {
return NonCaseString.of(this.value.replace(oldChar, newChar));
}

public NonCaseString replaceAll(String regex, String replacement) {
return NonCaseString.of(this.value.replaceAll(regex, replacement));
}

public NonCaseString substring(int beginIndex) {
return NonCaseString.of(this.value.substring(beginIndex));
}

public NonCaseString substring(int beginIndex, int endIndex) {
return NonCaseString.of(this.value.substring(beginIndex, endIndex));
}

public boolean isNotEmpty() {
return !this.value.isEmpty();
}

@Override
public int length() {
return this.value.length();
}

@Override
public char charAt(int index) {
return this.value.charAt(index);
}


@Override
public CharSequence subSequence(int start, int end) {
return this.value.subSequence(start, end);
}

public String[] split(String regex) {
return this.value.split(regex);
}


/**
* @return 原始字符串
*/
public String get() {
return this.value;
}

@Override
public String toString() {
return this.value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static String underlineToCamelCase(String underscoreName) {
boolean flag = false;
for (int i = 0; i < underscoreName.length(); i++) {
char ch = underscoreName.charAt(i);
if ("_".charAt(0) == ch) {
if ('_' == ch) {
flag = true;
} else {
if (flag) {
Expand All @@ -54,6 +54,42 @@ public static String underlineToCamelCase(String underscoreName) {
}
return result.toString();
}

/**
* 转 user_name 风格
*
* 不管原始是什么风格
*/
public static String toUnderline(String str, boolean upperCase) {
if (str == null || str.trim().isEmpty()) {
return str;
}

StringBuilder result = new StringBuilder();
boolean preIsUnderscore = false;
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (ch == '_') {
preIsUnderscore = true;
} else if (ch == '-') {
ch = '_';
preIsUnderscore = true; // -A -> _a
} else if (ch >= 'A' && ch <= 'Z') {
// A -> _a
if (!preIsUnderscore && i > 0) { // _A -> _a
result.append("_");
}
preIsUnderscore = false;
} else {
preIsUnderscore = false;
}
result.append(upperCase ? Character.toUpperCase(ch) : Character.toLowerCase(ch));
}

return result.toString();
}


public static boolean isNotNull(String str){
return org.apache.commons.lang3.StringUtils.isNotEmpty(str);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package com.softdev.system.generator.util;

import com.softdev.system.generator.util.mysqlJavaTypeUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.softdev.system.generator.entity.ClassInfo;
import com.softdev.system.generator.entity.FieldInfo;
import com.softdev.system.generator.entity.NonCaseString;
import com.softdev.system.generator.entity.ParamInfo;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
Expand All @@ -34,23 +32,28 @@ public class TableParseUtil {
public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo)
throws IOException {
//process the param
String tableSql = paramInfo.getTableSql();
NonCaseString tableSql = NonCaseString.of(paramInfo.getTableSql());
String nameCaseType = MapUtil.getString(paramInfo.getOptions(),"nameCaseType");
Boolean isPackageType = MapUtil.getBoolean(paramInfo.getOptions(),"isPackageType");

if (tableSql == null || tableSql.trim().length() == 0) {
throw new CodeGenerateException("Table structure can not be empty. 表结构不能为空。");
}
//deal with special character
tableSql = tableSql.trim().replaceAll("'", "`").replaceAll("\"", "`").replaceAll(",", ",").toLowerCase();
tableSql = tableSql.trim()
.replaceAll("'", "`")
.replaceAll("\"", "`")
.replaceAll(",", ",")
// 这里全部转小写, 会让驼峰风格的字段名丢失驼峰信息(真有驼峰sql字段名的呢(* ̄︶ ̄)); 下文使用工具方法处理包含等
// .toLowerCase()
;
//deal with java string copy \n"
tableSql = tableSql.trim().replaceAll("\\\\n`", "").replaceAll("\\+", "").replaceAll("``", "`").replaceAll("\\\\", "");
// table Name
String tableName = null;
if (tableSql.contains("TABLE") && tableSql.contains("(")) {
tableName = tableSql.substring(tableSql.indexOf("TABLE") + 5, tableSql.indexOf("("));
} else if (tableSql.contains("table") && tableSql.contains("(")) {
tableName = tableSql.substring(tableSql.indexOf("table") + 5, tableSql.indexOf("("));
int tableKwIx = tableSql.indexOf("TABLE"); // 包含判断和位置一次搞定
if (tableKwIx > -1 && tableSql.contains("(")) {
tableName = tableSql.substring(tableKwIx + 5, tableSql.indexOf("(")).get();
} else {
throw new CodeGenerateException("Table structure incorrect.表结构不正确。");
}
Expand Down Expand Up @@ -88,9 +91,11 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo)
String classComment = null;
//mysql是comment=,pgsql/oracle是comment on table,
//2020-05-25 优化表备注的获取逻辑
if (tableSql.contains("comment=") || tableSql.contains("comment on table")) {
String classCommentTmp = (tableSql.contains("comment=")) ?
tableSql.substring(tableSql.lastIndexOf("comment=") + 8).trim() : tableSql.substring(tableSql.lastIndexOf("comment on table") + 17).trim();
if (tableSql.containsAny("comment=", "comment on table")) {
int ix = tableSql.lastIndexOf("comment=");
String classCommentTmp = (ix > -1) ?
tableSql.substring(ix + 8).trim().get() :
tableSql.substring(tableSql.lastIndexOf("comment on table") + 17).trim().get();
if (classCommentTmp.contains("`")) {
classCommentTmp = classCommentTmp.substring(classCommentTmp.indexOf("`") + 1);
classCommentTmp = classCommentTmp.substring(0, classCommentTmp.indexOf("`"));
Expand All @@ -109,7 +114,7 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo)
List<FieldInfo> fieldList = new ArrayList<FieldInfo>();

// 正常( ) 内的一定是字段相关的定义。
String fieldListTmp = tableSql.substring(tableSql.indexOf("(") + 1, tableSql.lastIndexOf(")"));
String fieldListTmp = tableSql.substring(tableSql.indexOf("(") + 1, tableSql.lastIndexOf(")")).get();

// 匹配 comment,替换备注里的小逗号, 防止不小心被当成切割符号切割
String commentPattenStr1 = "comment `(.*?)\\`";
Expand Down Expand Up @@ -149,7 +154,8 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo)
if (fieldLineList.length > 0) {
int i = 0;
//i为了解决primary key关键字出现的地方,出现在前3行,一般和id有关
for (String columnLine : fieldLineList) {
for (String columnLine0 : fieldLineList) {
NonCaseString columnLine = NonCaseString.of(columnLine0);
i++;
columnLine = columnLine.replaceAll("\n", "").replaceAll("\t", "").trim();
// `userid` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
Expand All @@ -158,13 +164,10 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo)
// 2019-2-22 zhengkai 要在条件中使用复杂的表达式
// 2019-4-29 zhengkai 优化对普通和特殊storage关键字的判断(感谢@AhHeadFloating的反馈 )
// 2020-10-20 zhengkai 优化对fulltext/index关键字的处理(感谢@WEGFan的反馈)
boolean specialFlag = (!columnLine.contains("key ") && !columnLine.contains("constraint") && !columnLine.contains("using") && !columnLine.contains("unique ")
&& !(columnLine.contains("primary ") && columnLine.indexOf("storage") + 3 > columnLine.indexOf("("))
&& !columnLine.contains("fulltext ") && !columnLine.contains("index ")
&& !columnLine.contains("pctincrease")
&& !columnLine.contains("buffer_pool") && !columnLine.contains("tablespace")
&& !(columnLine.contains("primary ") && i > 3));
if (specialFlag) {
// 2023-8-27 zhangfei 改用工具方法判断, 且修改变量名(非特殊标识), 方法抽取
boolean notSpecialFlag = isNotSpecialColumnLine(columnLine, i);

if (notSpecialFlag) {
//如果是oracle的number(x,x),可能出现最后分割残留的,x),这里做排除处理
if (columnLine.length() < 5) {
continue;
Expand All @@ -174,24 +177,25 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo)
columnLine = columnLine.replaceAll("`", " ").replaceAll("\"", " ").replaceAll("'", "").replaceAll(" ", " ").trim();
//如果遇到username varchar(65) default '' not null,这种情况,判断第一个空格是否比第一个引号前
try {
columnName = columnLine.substring(0, columnLine.indexOf(" "));
columnName = columnLine.substring(0, columnLine.indexOf(" ")).get();
} catch (StringIndexOutOfBoundsException e) {
System.out.println("err happened: " + columnLine);
throw e;
}

// field Name
// 2019-09-08 yj 添加是否下划线转换为驼峰的判断
// 2023-8-27 zhangfei 支持原始列名任意命名风格, 不依赖用户是否输入下划线
String fieldName = null;
if (ParamInfo.NAME_CASE_TYPE.CAMEL_CASE.equals(nameCaseType)) {
fieldName = StringUtils.lowerCaseFirst(StringUtils.underlineToCamelCase(columnName));
if (fieldName.contains("_")) {
fieldName = fieldName.replaceAll("_", "");
}
} else if (ParamInfo.NAME_CASE_TYPE.UNDER_SCORE_CASE.equals(nameCaseType)) {
fieldName = StringUtils.lowerCaseFirst(columnName);
fieldName = StringUtils.toUnderline(columnName, false);
} else if (ParamInfo.NAME_CASE_TYPE.UPPER_UNDER_SCORE_CASE.equals(nameCaseType)) {
fieldName = StringUtils.lowerCaseFirst(columnName.toUpperCase());
fieldName = StringUtils.toUnderline(columnName.toUpperCase(), true);
} else {
fieldName = columnName;
}
Expand Down Expand Up @@ -228,12 +232,12 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo)
while (columnCommentMatcher.find()) {
String columnCommentTmp = columnCommentMatcher.group();
//System.out.println(columnCommentTmp);
fieldComment = tableSql.substring(tableSql.indexOf(columnCommentTmp) + columnCommentTmp.length()).trim();
fieldComment = tableSql.substring(tableSql.indexOf(columnCommentTmp) + columnCommentTmp.length()).trim().get();
fieldComment = fieldComment.substring(0, fieldComment.indexOf("`")).trim();
}
} else if (columnLine.contains(" comment")) {
//20200518 zhengkai 修复包含comment关键字的问题
String commentTmp = columnLine.substring(columnLine.lastIndexOf("comment") + 7).trim();
String commentTmp = columnLine.substring(columnLine.lastIndexOf("comment") + 7).trim().get();
// '用户ID',
if (commentTmp.contains("`") || commentTmp.indexOf("`") != commentTmp.lastIndexOf("`")) {
commentTmp = commentTmp.substring(commentTmp.indexOf("`") + 1, commentTmp.lastIndexOf("`"));
Expand Down Expand Up @@ -275,6 +279,24 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo)
return codeJavaInfo;
}

private static boolean isNotSpecialColumnLine(NonCaseString columnLine, int lineSeq) {
return (
!columnLine.containsAny(
"key ",
"constraint",
"using",
"unique ",
"fulltext ",
"index ",
"pctincrease",
"buffer_pool",
"tablespace"
)
&& !(columnLine.contains("primary ") && columnLine.indexOf("storage") + 3 > columnLine.indexOf("("))
&& !(columnLine.contains("primary ") && lineSeq > 3)
);
}

/**
* 解析JSON生成类信息
*
Expand Down
3 changes: 3 additions & 0 deletions generator-web/src/main/resources/statics/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ const vm = new Vue({
" 'user_name' varchar(255) NOT NULL COMMENT '用户名',\n" +
" 'status' tinyint(1) NOT NULL COMMENT '状态',\n" +
" 'create_time' datetime NOT NULL COMMENT '创建时间',\n" +
//下面可以留着方便开发调试时打开
// " `updateTime` datetime NOT NULL COMMENT '更新时间',\n" +
// " ABc_under_Line-Hypen-CamelCase varchar comment '乱七八糟的命名风格',\n" +
" PRIMARY KEY ('user_id')\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息'",
options: {
Expand Down
Loading

0 comments on commit 068472d

Please sign in to comment.