mirror of
https://github.com/moshowgame/SpringBootCodeGenerator.git
synced 2026-03-22 07:28:25 +08:00
Create SQL by JSqlParser Engine升级
This commit is contained in:
@@ -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> |
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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生成类信息(默认模式|核心模式)
|
||||
*
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
active: bejson
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user