Merge pull request #144 from Nisus-Liu/bugfix/143-sqlcamel

fix: 驼峰列名转命名风格错误问题 #143
This commit is contained in:
Moshow郑锴 2023-08-31 01:04:10 +08:00 committed by GitHub
commit 068472d8cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 265 additions and 28 deletions

View File

@ -17,7 +17,6 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties> </properties>
<dependencies> <dependencies>

View File

@ -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;
}
}

View File

@ -40,7 +40,7 @@ public class StringUtils {
boolean flag = false; boolean flag = false;
for (int i = 0; i < underscoreName.length(); i++) { for (int i = 0; i < underscoreName.length(); i++) {
char ch = underscoreName.charAt(i); char ch = underscoreName.charAt(i);
if ("_".charAt(0) == ch) { if ('_' == ch) {
flag = true; flag = true;
} else { } else {
if (flag) { if (flag) {
@ -54,6 +54,42 @@ public class StringUtils {
} }
return result.toString(); 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){ public static boolean isNotNull(String str){
return org.apache.commons.lang3.StringUtils.isNotEmpty(str); return org.apache.commons.lang3.StringUtils.isNotEmpty(str);
} }

View File

@ -1,18 +1,16 @@
package com.softdev.system.generator.util; package com.softdev.system.generator.util;
import com.softdev.system.generator.util.mysqlJavaTypeUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.softdev.system.generator.entity.ClassInfo; import com.softdev.system.generator.entity.ClassInfo;
import com.softdev.system.generator.entity.FieldInfo; import com.softdev.system.generator.entity.FieldInfo;
import com.softdev.system.generator.entity.NonCaseString;
import com.softdev.system.generator.entity.ParamInfo; import com.softdev.system.generator.entity.ParamInfo;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -34,7 +32,7 @@ public class TableParseUtil {
public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo) public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo)
throws IOException { throws IOException {
//process the param //process the param
String tableSql = paramInfo.getTableSql(); NonCaseString tableSql = NonCaseString.of(paramInfo.getTableSql());
String nameCaseType = MapUtil.getString(paramInfo.getOptions(),"nameCaseType"); String nameCaseType = MapUtil.getString(paramInfo.getOptions(),"nameCaseType");
Boolean isPackageType = MapUtil.getBoolean(paramInfo.getOptions(),"isPackageType"); Boolean isPackageType = MapUtil.getBoolean(paramInfo.getOptions(),"isPackageType");
@ -42,15 +40,20 @@ public class TableParseUtil {
throw new CodeGenerateException("Table structure can not be empty. 表结构不能为空。"); throw new CodeGenerateException("Table structure can not be empty. 表结构不能为空。");
} }
//deal with special character //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" //deal with java string copy \n"
tableSql = tableSql.trim().replaceAll("\\\\n`", "").replaceAll("\\+", "").replaceAll("``", "`").replaceAll("\\\\", ""); tableSql = tableSql.trim().replaceAll("\\\\n`", "").replaceAll("\\+", "").replaceAll("``", "`").replaceAll("\\\\", "");
// table Name // table Name
String tableName = null; String tableName = null;
if (tableSql.contains("TABLE") && tableSql.contains("(")) { int tableKwIx = tableSql.indexOf("TABLE"); // 包含判断和位置一次搞定
tableName = tableSql.substring(tableSql.indexOf("TABLE") + 5, tableSql.indexOf("(")); if (tableKwIx > -1 && tableSql.contains("(")) {
} else if (tableSql.contains("table") && tableSql.contains("(")) { tableName = tableSql.substring(tableKwIx + 5, tableSql.indexOf("(")).get();
tableName = tableSql.substring(tableSql.indexOf("table") + 5, tableSql.indexOf("("));
} else { } else {
throw new CodeGenerateException("Table structure incorrect.表结构不正确。"); throw new CodeGenerateException("Table structure incorrect.表结构不正确。");
} }
@ -88,9 +91,11 @@ public class TableParseUtil {
String classComment = null; String classComment = null;
//mysql是comment=,pgsql/oracle是comment on table, //mysql是comment=,pgsql/oracle是comment on table,
//2020-05-25 优化表备注的获取逻辑 //2020-05-25 优化表备注的获取逻辑
if (tableSql.contains("comment=") || tableSql.contains("comment on table")) { if (tableSql.containsAny("comment=", "comment on table")) {
String classCommentTmp = (tableSql.contains("comment=")) ? int ix = tableSql.lastIndexOf("comment=");
tableSql.substring(tableSql.lastIndexOf("comment=") + 8).trim() : tableSql.substring(tableSql.lastIndexOf("comment on table") + 17).trim(); String classCommentTmp = (ix > -1) ?
tableSql.substring(ix + 8).trim().get() :
tableSql.substring(tableSql.lastIndexOf("comment on table") + 17).trim().get();
if (classCommentTmp.contains("`")) { if (classCommentTmp.contains("`")) {
classCommentTmp = classCommentTmp.substring(classCommentTmp.indexOf("`") + 1); classCommentTmp = classCommentTmp.substring(classCommentTmp.indexOf("`") + 1);
classCommentTmp = classCommentTmp.substring(0, classCommentTmp.indexOf("`")); classCommentTmp = classCommentTmp.substring(0, classCommentTmp.indexOf("`"));
@ -109,7 +114,7 @@ public class TableParseUtil {
List<FieldInfo> fieldList = new ArrayList<FieldInfo>(); 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替换备注里的小逗号, 防止不小心被当成切割符号切割 // 匹配 comment替换备注里的小逗号, 防止不小心被当成切割符号切割
String commentPattenStr1 = "comment `(.*?)\\`"; String commentPattenStr1 = "comment `(.*?)\\`";
@ -149,7 +154,8 @@ public class TableParseUtil {
if (fieldLineList.length > 0) { if (fieldLineList.length > 0) {
int i = 0; int i = 0;
//i为了解决primary key关键字出现的地方出现在前3行一般和id有关 //i为了解决primary key关键字出现的地方出现在前3行一般和id有关
for (String columnLine : fieldLineList) { for (String columnLine0 : fieldLineList) {
NonCaseString columnLine = NonCaseString.of(columnLine0);
i++; i++;
columnLine = columnLine.replaceAll("\n", "").replaceAll("\t", "").trim(); columnLine = columnLine.replaceAll("\n", "").replaceAll("\t", "").trim();
// `userid` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID', // `userid` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
@ -158,13 +164,10 @@ public class TableParseUtil {
// 2019-2-22 zhengkai 要在条件中使用复杂的表达式 // 2019-2-22 zhengkai 要在条件中使用复杂的表达式
// 2019-4-29 zhengkai 优化对普通和特殊storage关键字的判断感谢@AhHeadFloating的反馈 // 2019-4-29 zhengkai 优化对普通和特殊storage关键字的判断感谢@AhHeadFloating的反馈
// 2020-10-20 zhengkai 优化对fulltext/index关键字的处理感谢@WEGFan的反馈 // 2020-10-20 zhengkai 优化对fulltext/index关键字的处理感谢@WEGFan的反馈
boolean specialFlag = (!columnLine.contains("key ") && !columnLine.contains("constraint") && !columnLine.contains("using") && !columnLine.contains("unique ") // 2023-8-27 zhangfei 改用工具方法判断, 且修改变量名(非特殊标识), 方法抽取
&& !(columnLine.contains("primary ") && columnLine.indexOf("storage") + 3 > columnLine.indexOf("(")) boolean notSpecialFlag = isNotSpecialColumnLine(columnLine, i);
&& !columnLine.contains("fulltext ") && !columnLine.contains("index ")
&& !columnLine.contains("pctincrease") if (notSpecialFlag) {
&& !columnLine.contains("buffer_pool") && !columnLine.contains("tablespace")
&& !(columnLine.contains("primary ") && i > 3));
if (specialFlag) {
//如果是oracle的number(x,x)可能出现最后分割残留的,x)这里做排除处理 //如果是oracle的number(x,x)可能出现最后分割残留的,x)这里做排除处理
if (columnLine.length() < 5) { if (columnLine.length() < 5) {
continue; continue;
@ -174,7 +177,7 @@ public class TableParseUtil {
columnLine = columnLine.replaceAll("`", " ").replaceAll("\"", " ").replaceAll("'", "").replaceAll(" ", " ").trim(); columnLine = columnLine.replaceAll("`", " ").replaceAll("\"", " ").replaceAll("'", "").replaceAll(" ", " ").trim();
//如果遇到username varchar(65) default '' not null,这种情况判断第一个空格是否比第一个引号前 //如果遇到username varchar(65) default '' not null,这种情况判断第一个空格是否比第一个引号前
try { try {
columnName = columnLine.substring(0, columnLine.indexOf(" ")); columnName = columnLine.substring(0, columnLine.indexOf(" ")).get();
} catch (StringIndexOutOfBoundsException e) { } catch (StringIndexOutOfBoundsException e) {
System.out.println("err happened: " + columnLine); System.out.println("err happened: " + columnLine);
throw e; throw e;
@ -182,6 +185,7 @@ public class TableParseUtil {
// field Name // field Name
// 2019-09-08 yj 添加是否下划线转换为驼峰的判断 // 2019-09-08 yj 添加是否下划线转换为驼峰的判断
// 2023-8-27 zhangfei 支持原始列名任意命名风格, 不依赖用户是否输入下划线
String fieldName = null; String fieldName = null;
if (ParamInfo.NAME_CASE_TYPE.CAMEL_CASE.equals(nameCaseType)) { if (ParamInfo.NAME_CASE_TYPE.CAMEL_CASE.equals(nameCaseType)) {
fieldName = StringUtils.lowerCaseFirst(StringUtils.underlineToCamelCase(columnName)); fieldName = StringUtils.lowerCaseFirst(StringUtils.underlineToCamelCase(columnName));
@ -189,9 +193,9 @@ public class TableParseUtil {
fieldName = fieldName.replaceAll("_", ""); fieldName = fieldName.replaceAll("_", "");
} }
} else if (ParamInfo.NAME_CASE_TYPE.UNDER_SCORE_CASE.equals(nameCaseType)) { } 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)) { } else if (ParamInfo.NAME_CASE_TYPE.UPPER_UNDER_SCORE_CASE.equals(nameCaseType)) {
fieldName = StringUtils.lowerCaseFirst(columnName.toUpperCase()); fieldName = StringUtils.toUnderline(columnName.toUpperCase(), true);
} else { } else {
fieldName = columnName; fieldName = columnName;
} }
@ -228,12 +232,12 @@ public class TableParseUtil {
while (columnCommentMatcher.find()) { while (columnCommentMatcher.find()) {
String columnCommentTmp = columnCommentMatcher.group(); String columnCommentTmp = columnCommentMatcher.group();
//System.out.println(columnCommentTmp); //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(); fieldComment = fieldComment.substring(0, fieldComment.indexOf("`")).trim();
} }
} else if (columnLine.contains(" comment")) { } else if (columnLine.contains(" comment")) {
//20200518 zhengkai 修复包含comment关键字的问题 //20200518 zhengkai 修复包含comment关键字的问题
String commentTmp = columnLine.substring(columnLine.lastIndexOf("comment") + 7).trim(); String commentTmp = columnLine.substring(columnLine.lastIndexOf("comment") + 7).trim().get();
// '用户ID', // '用户ID',
if (commentTmp.contains("`") || commentTmp.indexOf("`") != commentTmp.lastIndexOf("`")) { if (commentTmp.contains("`") || commentTmp.indexOf("`") != commentTmp.lastIndexOf("`")) {
commentTmp = commentTmp.substring(commentTmp.indexOf("`") + 1, commentTmp.lastIndexOf("`")); commentTmp = commentTmp.substring(commentTmp.indexOf("`") + 1, commentTmp.lastIndexOf("`"));
@ -275,6 +279,24 @@ public class TableParseUtil {
return codeJavaInfo; 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生成类信息 * 解析JSON生成类信息
* *

View File

@ -36,6 +36,9 @@ const vm = new Vue({
" 'user_name' varchar(255) NOT NULL COMMENT '用户名',\n" + " 'user_name' varchar(255) NOT NULL COMMENT '用户名',\n" +
" 'status' tinyint(1) NOT NULL COMMENT '状态',\n" + " 'status' tinyint(1) NOT NULL COMMENT '状态',\n" +
" 'create_time' datetime 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" + " PRIMARY KEY ('user_id')\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息'", ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息'",
options: { options: {

View File

@ -0,0 +1,25 @@
import com.softdev.system.generator.util.StringUtils;
public class FooTest {
public static void main(String[] args) {
// String updateTime = StringUtils.underlineToCamelCase("updateTime");
// System.out.println(updateTime);
// System.out.println(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "userName"));
// System.out.println(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "userNAme-UUU"));
System.out.println(StringUtils.toUnderline("userName",false));
System.out.println(StringUtils.toUnderline("UserName",false));
System.out.println(StringUtils.toUnderline("user_NameGgg_x-UUU",false));
System.out.println(StringUtils.toUnderline("username",false));
System.out.println(StringUtils.toUnderline("userName",true));
System.out.println(StringUtils.toUnderline("UserName",true));
System.out.println(StringUtils.toUnderline("user_NameGgg_x-UUU",true));
System.out.println(StringUtils.toUnderline("username",true));
}
}