Create SQL by JSqlParser Engine升级

This commit is contained in:
Moshow郑锴
2025-09-14 00:08:22 +08:00
parent 2a70d7ecfc
commit 2e74d50296
9 changed files with 143 additions and 213 deletions

View File

@@ -37,8 +37,7 @@
- 感谢`卡卡`部署在[BEJSON](https://java.bejson.com/generator)上目前是BeJSON专供的`金牌工具`<br>
- 感谢`jully.top`部署的副本 [https://jully.top/generator/](https://jully.top/generator/)。<br>
- 感谢`bytecdntp`字节跳动CDN提供稳定、快速、免费的静态文件CDN加速服务(在线版本)
- Thanks `JetBrains` for providing us the `Licenses for Open Source Development` [Get free access to all JetBrains tools for developing your open source project!](https://www.jetbrains.com/community/opensource/#support) .<br>
- 感谢`staticfile`CDN提供稳定、快速、免费的静态文件CDN加速服务(在线版本)
| 访问地址 | http://localhost:1234/generator |
|:-----------------------|:--------------------------------------------------------------|
@@ -77,7 +76,7 @@
# Update Logs
| 更新日期 | 更新内容 |
|:-----------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 2025.09.13 | 修复CDN问题切换为staticfile.org |
| 2025.09.13 | Create SQL by JSqlParser Engine升级<br>更新SpringBoot等类库版本修复漏洞<br>修复CDN问题切换为staticfile.org |
| 2025.03.31 | 优化说明 |
| 2025.03.16 | NewUI V2前端优化<br>移除不必要内容优化Local和CDN静态文件引入。<br><br>修复由于SQL类型大写导致无法转换的问题。感谢@zzy-design的反馈<br><br>JPA模板优化感谢@PenroseYang的反馈<br>修复不开启Lombok情况下Set/Get方法生成问题;<br>修复importDdate判断为true后没有引入日期类的问题<br> |
| 2024.12.29 | 优化前端加载速度优化输出代码着色CDN改字节跳动静态资源公共库。<br> |

View File

@@ -32,16 +32,16 @@ public class WebMvcConfig implements WebMvcConfigurer {
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/statics/**").addResourceLocations("classpath:/statics/");
}
@Bean
public FilterRegistrationBean xssFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new XssFilter());
registration.addUrlPatterns("/*");
registration.setName("xssFilter");
registration.setOrder(Integer.MAX_VALUE);
return registration;
}
// @Bean
// public FilterRegistrationBean xssFilterRegistration() {
// FilterRegistrationBean registration = new FilterRegistrationBean();
// registration.setDispatcherTypes(DispatcherType.REQUEST);
// registration.setFilter(new XssFilter());
// registration.addUrlPatterns("/*");
// registration.setName("xssFilter");
// registration.setOrder(Integer.MAX_VALUE);
// return registration;
// }
// @Override
// public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
@@ -74,8 +74,20 @@ public class WebMvcConfig implements WebMvcConfigurer {
//自定义配置...
FastJsonConfig config = new FastJsonConfig();
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
config.setReaderFeatures(JSONReader.Feature.FieldBased, JSONReader.Feature.SupportArrayToBean);
config.setWriterFeatures(JSONWriter.Feature.WriteMapNullValue, JSONWriter.Feature.PrettyFormat);
// 添加更多解析特性以提高容错性
config.setReaderFeatures(
JSONReader.Feature.FieldBased,
JSONReader.Feature.SupportArrayToBean,
// JSONReader.Feature.IgnoreNoneFieldGetter,
JSONReader.Feature.InitStringFieldAsEmpty
);
config.setWriterFeatures(
JSONWriter.Feature.WriteMapNullValue,
JSONWriter.Feature.PrettyFormat
);
converter.setFastJsonConfig(config);
converter.setDefaultCharset(StandardCharsets.UTF_8);
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON));

View File

@@ -1,31 +0,0 @@
package com.softdev.system.generator.config;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* XSS过滤
*
* @author Mark sunlightcs@gmail.com
*/
public class XssFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest)request);
chain.doFilter(xssRequest, response);
}
@Override
public void destroy() {
}
}

View File

@@ -1,142 +0,0 @@
package com.softdev.system.generator.config;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* XSS过滤处理
*
* @author Mark sunlightcs@gmail.com
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* 没被包装过的HttpServletRequest特殊场景需要自己过滤
*/
HttpServletRequest orgRequest;
/**
* html过滤
*/
private final static HTMLFilter htmlFilter = new HTMLFilter();
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
orgRequest = request;
}
@Override
public ServletInputStream getInputStream() throws IOException {
//非json类型直接返回
if(!MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(super.getHeader(HttpHeaders.CONTENT_TYPE))){
return super.getInputStream();
}
//为空,直接返回
String json = IOUtils.toString(super.getInputStream(), "utf-8");
if (StringUtils.isBlank(json)) {
return super.getInputStream();
}
//xss过滤
json = xssEncode(json);
final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes("utf-8"));
return new ServletInputStream() {
@Override
public boolean isFinished() {
return true;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bis.read();
}
};
}
@Override
public String getParameter(String name) {
String value = super.getParameter(xssEncode(name));
if (StringUtils.isNotBlank(value)) {
value = xssEncode(value);
}
return value;
}
@Override
public String[] getParameterValues(String name) {
String[] parameters = super.getParameterValues(name);
if (parameters == null || parameters.length == 0) {
return null;
}
for (int i = 0; i < parameters.length; i++) {
parameters[i] = xssEncode(parameters[i]);
}
return parameters;
}
@Override
public Map<String,String[]> getParameterMap() {
Map<String,String[]> map = new LinkedHashMap<>();
Map<String,String[]> parameters = super.getParameterMap();
for (String key : parameters.keySet()) {
String[] values = parameters.get(key);
for (int i = 0; i < values.length; i++) {
values[i] = xssEncode(values[i]);
}
map.put(key, values);
}
return map;
}
@Override
public String getHeader(String name) {
String value = super.getHeader(xssEncode(name));
if (StringUtils.isNotBlank(value)) {
value = xssEncode(value);
}
return value;
}
private String xssEncode(String input) {
return htmlFilter.filter(input);
}
/**
* 获取最原始的request
*/
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
/**
* 获取最原始的request
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
if (request instanceof XssHttpServletRequestWrapper) {
return ((XssHttpServletRequestWrapper) request).getOrgRequest();
}
return request;
}
}

View File

@@ -87,6 +87,10 @@ public class GeneratorController {
//SelectSqlBySQLPraser模式:parse select sql by JSqlParser
classInfo = generatorService.generateSelectSqlBySQLPraser(paramInfo);
break;
case "create-sql":
//SelectSqlBySQLPraser模式:parse select sql by JSqlParser
classInfo = generatorService.generateCreateSqlBySQLPraser(paramInfo);
break;
default:
//默认模式parse DDL table structure from sql
classInfo = generatorService.processTableIntoClassInfo(paramInfo);

View File

@@ -25,6 +25,7 @@ public interface GeneratorService {
* @return
*/
ClassInfo generateSelectSqlBySQLPraser(ParamInfo paramInfo) throws Exception;
ClassInfo generateCreateSqlBySQLPraser(ParamInfo paramInfo) throws Exception;
/**
* 解析DDL-SQL生成类信息
* @auther: zhengkai.blog.csdn.net

View File

@@ -7,23 +7,29 @@ import com.softdev.system.generator.entity.*;
import com.softdev.system.generator.util.*;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.util.TablesNamesFinder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static net.sf.jsqlparser.parser.feature.Feature.createTable;
/**
* GeneratorService
*
@@ -81,12 +87,31 @@ public class GeneratorServiceImpl implements GeneratorService {
@Override
public ClassInfo generateSelectSqlBySQLPraser(ParamInfo paramInfo) throws Exception {
ClassInfo classInfo = new ClassInfo();
PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(paramInfo.getTableSql());
List<SelectItem<?>> columnNameList = select.getSelectItems();
log.info("tableName:{}", select.getFromItem().toString());
Statement statement = CCJSqlParserUtil.parse(paramInfo.getTableSql());
CCJSqlParserManager parserManager = new CCJSqlParserManager();
statement = parserManager.parse(new StringReader(paramInfo.getTableSql()));
TablesNamesFinder tablesNamesFinder = new TablesNamesFinder(); // 创建表名发现者对象
List<String> tableNameList = tablesNamesFinder.getTableList(statement); // 获取到表名列表
//一般这里应该只解析到一个表名,除非多个表名,取第一个
if (!CollectionUtils.isEmpty(tableNameList)) {
String tableName = tableNameList.get(0).trim();
classInfo.setTableName(tableName);
String className = StringUtilsPlus.upperCaseFirst(StringUtilsPlus.underlineToCamelCase(tableName));
if (className.contains("_")) {
className = className.replaceAll("_", "");
}
classInfo.setClassName(className);
classInfo.setClassComment(paramInfo.getTableSql());
}
//解析查询元素
Select select = null;
select = (Select) CCJSqlParserUtil.parse(paramInfo.getTableSql());
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
List<SelectItem<?>> selectItems = plainSelect.getSelectItems();
// field List
List<FieldInfo> fieldList = new ArrayList<FieldInfo>();
columnNameList.forEach(t->{
selectItems.forEach(t->{
FieldInfo fieldInfo = new FieldInfo();
String fieldName = ((Column)t.getExpression()).getColumnName();
String aliasName = t.getAlias() != null ? t.getAlias().getName() : ((Column)t.getExpression()).getColumnName();
@@ -119,18 +144,79 @@ public class GeneratorServiceImpl implements GeneratorService {
fieldList.add(fieldInfo);
});
classInfo.setFieldList(fieldList);
String tableName = select.getFromItem().toString();
classInfo.setTableName(tableName);
//如果表名有空格,取空格前的第一个单词作为类名
if(tableName.indexOf(" ")>0){
classInfo.setClassName(StringUtilsPlus.upperCaseFirst(StringUtilsPlus.underlineToCamelCase(tableName.substring(0,tableName.indexOf(" ")))));
}else{
classInfo.setClassName(StringUtilsPlus.upperCaseFirst(StringUtilsPlus.underlineToCamelCase(tableName)));
}
log.info("classInfo:{}", JSON.toJSONString(classInfo));
return classInfo;
}
/**
* 根据SQL解析器解析表结构
* @author zhengkai.blog.csdn.net
* @param paramInfo
* @return
* @throws Exception
*/
@Override
public ClassInfo generateCreateSqlBySQLPraser(ParamInfo paramInfo) throws Exception {
ClassInfo classInfo = new ClassInfo();
Statement statement = CCJSqlParserUtil.parse(paramInfo.getTableSql());
CCJSqlParserManager parserManager = new CCJSqlParserManager();
statement = parserManager.parse(new StringReader(paramInfo.getTableSql()));
TablesNamesFinder tablesNamesFinder = new TablesNamesFinder(); // 创建表名发现者对象
List<String> tableNameList = tablesNamesFinder.getTableList(statement); // 获取到表名列表
//一般这里应该只解析到一个表名,除非多个表名,取第一个
if (!CollectionUtils.isEmpty(tableNameList)) {
String tableName = tableNameList.get(0).trim();
classInfo.setTableName(tableName);
String className = StringUtilsPlus.upperCaseFirst(StringUtilsPlus.underlineToCamelCase(tableName));
if (className.contains("_")) {
className = className.replaceAll("_", "");
}
classInfo.setClassName(className);
classInfo.setClassComment(paramInfo.getTableSql());
}
//解析查询元素
Select select = null;
select = (Select) CCJSqlParserUtil.parse(paramInfo.getTableSql());
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
List<SelectItem<?>> selectItems = plainSelect.getSelectItems();
// field List
List<FieldInfo> fieldList = new ArrayList<FieldInfo>();
selectItems.forEach(t->{
FieldInfo fieldInfo = new FieldInfo();
String fieldName = ((Column)t.getExpression()).getColumnName();
String aliasName = t.getAlias() != null ? t.getAlias().getName() : ((Column)t.getExpression()).getColumnName();
//存储原始字段名
fieldInfo.setFieldComment(aliasName);fieldInfo.setColumnName(aliasName);
//处理字段名是t.xxx的情况
fieldName=fieldName.contains(".")?fieldName.substring(fieldName.indexOf(".")+1):fieldName;
//转换前
fieldInfo.setColumnName(fieldName);
switch ((String) paramInfo.getOptions().get("nameCaseType")) {
case ParamInfo.NAME_CASE_TYPE.CAMEL_CASE:
// 2024-1-27 L&J 适配任意(maybe)原始风格转小写驼峰
fieldName = StringUtilsPlus.toLowerCamel(aliasName);
break;
case ParamInfo.NAME_CASE_TYPE.UNDER_SCORE_CASE:
fieldName = StringUtilsPlus.toUnderline(aliasName, false);
break;
case ParamInfo.NAME_CASE_TYPE.UPPER_UNDER_SCORE_CASE:
fieldName = StringUtilsPlus.toUnderline(aliasName.toUpperCase(), true);
break;
default:
fieldName = aliasName;
break;
}
//转换后
fieldInfo.setFieldName(fieldName);
//无法推测类型所有都set为String
fieldInfo.setFieldClass("String");
fieldList.add(fieldInfo);
});
classInfo.setFieldList(fieldList);
log.info("classInfo:{}", JSON.toJSONString(classInfo));
return classInfo;
}
/**
* 解析DDL SQL生成类信息(默认模式|核心模式)
*

View File

@@ -1,3 +1,3 @@
spring:
profiles:
active: dev
active: bejson

View File

@@ -100,6 +100,15 @@
</div>
<div class="card-body">
<el-form-item label="生成引擎">
<el-select v-model="formData.options.dataType">
<el-option label="DDL SQL@自研SQL解析引擎" value="sql"></el-option>
<el-option label="SELECT SQL@JSqlParser引擎" value="select-sql"></el-option>
<el-option label="CREATE SQL@JSqlParser引擎" value="create-sql"></el-option>
<el-option label="JSON(Beta)" value="json"></el-option>
<el-option label="INSERT SQL" value="insert-sql"></el-option>
</el-select>
</el-form-item>
<el-form-item label="作者">
<el-input v-model="formData.options.authorName"></el-input>
</el-form-item>
@@ -115,14 +124,6 @@
<el-form-item label="忽略前缀">
<el-input v-model="formData.options.ignorePrefix"></el-input>
</el-form-item>
<el-form-item label="输入类型">
<el-select v-model="formData.options.dataType">
<el-option label="DDL SQL" value="sql"></el-option>
<el-option label="JSON" value="json"></el-option>
<el-option label="INSERT SQL" value="insert-sql"></el-option>
<el-option label="SELECT SQL by SQL Parser" value="select-sql"></el-option>
</el-select>
</el-form-item>
<el-form-item label="TinyInt转换">
<el-select v-model="formData.options.tinyintTransType">
<el-option value="boolean" label="boolean"></el-option>