This commit is contained in:
2025-10-17 10:11:04 +08:00
commit 9618d5cfa1
2012 changed files with 163764 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jeelowcode-framework</artifactId>
<groupId>com.jeelowcode</groupId>
<version>${jeelowcode.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jeelowcode-ai</artifactId>
<name>${project.artifactId}</name>
<version>${jeelowcode.version}</version>
<packaging>jar</packaging>
<description> JeeLowCode低代码平台 - ai模块 </description>
<dependencies>
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>jeelowcode-utils</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,97 @@
package com.jeelowcode.framework.ai;
import com.jeelowcode.framework.ai.config.DeepSeekConfig;
import com.jeelowcode.framework.ai.config.KimiConfig;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
import com.jeelowcode.framework.ai.platform.chat.tool.ChatMessage;
import com.jeelowcode.framework.ai.platform.chat.tool.Choice;
import com.jeelowcode.framework.ai.service.Configuration;
import com.jeelowcode.framework.ai.service.IChatService;
import com.jeelowcode.framework.ai.service.PlatformType;
import com.jeelowcode.framework.ai.service.factor.AiService;
import com.jeelowcode.framework.ai.template.CreateTableTemplate;
import com.jeelowcode.framework.utils.utils.FuncBase;
import lombok.Data;
import okhttp3.OkHttpClient;
import org.junit.Before;
import org.junit.Test;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class TestUtils {
private IChatService chatService;
@Before
public void test_init() throws NoSuchAlgorithmException, KeyManagementException {
KimiConfig kimiConfig = new KimiConfig();
kimiConfig.setApiKey("sk-EE98wvDeTFvxRcBxDQmWhKQlewkGFIrfyCxvyNxDj8Vaf701");
DeepSeekConfig deepseekConfig = new DeepSeekConfig();
deepseekConfig.setApiKey("sk-b31fcf4e27bf45fbb65ace5bbc627d5d");
Configuration configuration = new Configuration();
configuration.setKimiConfig(kimiConfig);
configuration.setDeepSeekConfig(deepseekConfig);
OkHttpClient okHttpClient = new OkHttpClient
.Builder()
.connectTimeout(300, TimeUnit.SECONDS)
.writeTimeout(300, TimeUnit.SECONDS)
.readTimeout(300, TimeUnit.SECONDS)
.build();
configuration.setOkHttpClient(okHttpClient);
AiService aiService = new AiService(configuration);
chatService = aiService.getChatService(PlatformType.KIMI);
//chatService = aiService.getChatService(PlatformType.DEEPSEEK);
}
@Test
public void test_chatCompletions_common() throws Exception {
long time1 = System.currentTimeMillis();
ChatBaseRequest chatBaseRequest = ChatBaseRequest.builder()
//.model("deepseek-chat")
.messages(CreateTableTemplate.getMessageTemplateList())
.message(ChatMessage.withUser("物流信息,其中要有,物流单号,收件人,寄件人"))
.build();
ChatBaseResponse chatBaseResponse = chatService.chatCompletion(chatBaseRequest);
List<Choice> choices = chatBaseResponse.getChoices();
if(FuncBase.isEmpty(choices)){
return;
}
Choice choice = choices.get(0);
ChatMessage message = choice.getMessage();
if(FuncBase.isEmpty(message)){
return;
}
String content = message.getContent();
System.out.println("content===="+content);
CreateTableTemplate.RspModel rspModel = FuncBase.json2Bean(content, CreateTableTemplate.RspModel.class);
System.out.println(rspModel);
long time2 = System.currentTimeMillis();
System.out.println("time2-time1="+(time2-time1));
}
}

View File

@@ -0,0 +1,19 @@
package com.jeelowcode.framework.ai.config;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeepSeekConfig {
private String apiHost = "https://api.deepseek.com/";
private String apiKey = "";
private String model="deepseek-chat";
private String chatCompletionUrl = "chat/completions";
}

View File

@@ -0,0 +1,15 @@
package com.jeelowcode.framework.ai.config;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class KimiConfig {
private String apiHost = "https://api.moonshot.cn/";
private String apiKey = "";
private String model="moonshot-v1-8k";
private String chatCompletionUrl = "v1/chat/completions";
}

View File

@@ -0,0 +1,9 @@
package com.jeelowcode.framework.ai.convert;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
public interface ParameterConvert<T> {
T convertParam(ChatBaseRequest chatBaseRequest);
}

View File

@@ -0,0 +1,10 @@
package com.jeelowcode.framework.ai.convert;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
public interface ResultConvert<T> {
//统一格式输出
ChatBaseResponse convertResponse(T t);
}

View File

@@ -0,0 +1,118 @@
package com.jeelowcode.framework.ai.platform.chat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.jeelowcode.framework.ai.platform.chat.tool.ChatMessage;
import lombok.*;
import lombok.experimental.SuperBuilder;
import java.util.List;
import java.util.Map;
@Data
@SuperBuilder
@NoArgsConstructor
public class ChatBaseRequest {
private String model;
/**
* 消息内容
*/
@Singular
private List<ChatMessage> messages;
/**
* 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。
*/
@Builder.Default
@JsonProperty("frequency_penalty")
private Float frequencyPenalty = 0f;
/**
* 采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。
* 我们通常建议可以更改这个值或者更改 top_p但不建议同时对两者进行修改。
*/
@Builder.Default
private Float temperature = 1f;
/**
* 作为调节采样温度的替代方案,模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。
* 我们通常建议修改这个值或者更改 temperature但不建议同时对两者进行修改。
*/
@Builder.Default
@JsonProperty("top_p")
private Float topP = 1f;
/**
* 限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。
*/
@JsonProperty("max_tokens")
private Integer maxTokens;
/**
* 辅助属性
*/
@JsonIgnore
private List<String> functions;
/**
* 控制模型调用 tool 的行为。
* none 意味着模型不会调用任何 tool而是生成一条消息。
* auto 意味着模型可以选择生成一条消息或调用一个或多个 tool。
* 当没有 tool 时,默认值为 none。如果有 tool 存在,默认值为 auto。
*/
@JsonProperty("tool_choice")
private String toolChoice;
@Builder.Default
@JsonProperty("parallel_tool_calls")
private Boolean parallelToolCalls = true;
/**
* 一个 object指定模型必须输出的格式。
*
* 设置为 { "type": "json_object" } 以启用 JSON 模式,该模式保证模型生成的消息是有效的 JSON。
*
* 注意: 使用 JSON 模式时,你还必须通过系统或用户消息指示模型生成 JSON。
* 否则,模型可能会生成不断的空白字符,直到生成达到令牌限制,从而导致请求长时间运行并显得“卡住”。
* 此外,如果 finish_reason="length",这表示生成超过了 max_tokens 或对话超过了最大上下文长度,消息内容可能会被部分截断。
*/
@JsonProperty("response_format")
private Object responseFormat;
private String user;
@Builder.Default
private Integer n = 1;
/**
* 在遇到这些词时API 将停止生成更多的 token。
*/
private List<String> stop;
/**
* 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性。
*/
@Builder.Default
@JsonProperty("presence_penalty")
private Float presencePenalty = 0f;
@JsonProperty("logit_bias")
private Map logitBias;
/**
* 是否返回所输出 token 的对数概率。如果为 true则在 message 的 content 中返回每个输出 token 的对数概率。
*/
@Builder.Default
private Boolean logprobs = false;
/**
* 一个介于 0 到 20 之间的整数 N指定每个输出位置返回输出概率 top N 的 token且返回这些 token 的对数概率。指定此参数时logprobs 必须为 true。
*/
@JsonProperty("top_logprobs")
private Integer topLogprobs;
}

View File

@@ -0,0 +1,46 @@
package com.jeelowcode.framework.ai.platform.chat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.jeelowcode.framework.ai.platform.chat.tool.Choice;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
public class ChatBaseResponse {
/**
* 该对话的唯一标识符。
*/
private String id;
/**
* 对象的类型, 其值为 chat.completion 或 chat.completion.chunk
*/
private String object;
/**
* 创建聊天完成时的 Unix 时间戳(以秒为单位)。
*/
private Long created;
/**
* 生成该 completion 的模型名。
*/
private String model;
/**
* 该指纹代表模型运行时使用的后端配置。
*/
@JsonProperty("system_fingerprint")
private String systemFingerprint;
/**
* 模型生成的 completion 的选择列表。
*/
private List<Choice> choices;
}

View File

@@ -0,0 +1,34 @@
package com.jeelowcode.framework.ai.platform.chat;
import com.jeelowcode.framework.utils.utils.FuncBase;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Optional;
@Getter
@AllArgsConstructor
public enum ChatMessageType {
SYSTEM("system"),
USER("user"),
ASSISTANT("assistant"),
TOOL("tool"),
;
private final String role;
public static ChatMessageType getByRole(String role) {
// 使用流来查找匹配的
Optional<ChatMessageType> matchingEnum = Arrays.stream(ChatMessageType.values())
.filter(chatMessageType -> FuncBase.equals(chatMessageType.getRole(), role))
.findFirst(); // findFirst()会返回第一个匹配的元素或者如果找不到则返回一个空的Optional
// 检查是否找到了匹配的枚举项
if (matchingEnum.isPresent()) {
return matchingEnum.get();
}
return null;
}
}

View File

@@ -0,0 +1,99 @@
package com.jeelowcode.framework.ai.platform.chat.deepseek;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jeelowcode.framework.ai.config.DeepSeekConfig;
import com.jeelowcode.framework.ai.convert.ParameterConvert;
import com.jeelowcode.framework.ai.convert.ResultConvert;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
import com.jeelowcode.framework.ai.platform.chat.deepseek.entity.DeepSeekChatRequest;
import com.jeelowcode.framework.ai.platform.chat.deepseek.entity.DeepSeekChatResponse;
import com.jeelowcode.framework.ai.platform.chat.kimi.entity.KimiChatRequest;
import com.jeelowcode.framework.ai.service.Configuration;
import com.jeelowcode.framework.ai.service.IChatService;
import com.jeelowcode.framework.utils.utils.FuncBase;
import okhttp3.*;
public class DeepSeekChatService implements IChatService, ParameterConvert<DeepSeekChatRequest>, ResultConvert<DeepSeekChatResponse> {
private final DeepSeekConfig deepSeekConfig;
private final OkHttpClient okHttpClient;
public DeepSeekChatService(Configuration configuration) {
this.deepSeekConfig = configuration.getDeepSeekConfig();
this.okHttpClient = configuration.getOkHttpClient();
}
@Override
public DeepSeekChatRequest convertParam(ChatBaseRequest chatBaseRequest) {
DeepSeekChatRequest deepSeekChatRequest = DeepSeekChatRequest.builder()
.messages(chatBaseRequest.getMessages())
.frequencyPenalty(chatBaseRequest.getFrequencyPenalty())
.maxTokens(chatBaseRequest.getMaxTokens())
.presencePenalty(chatBaseRequest.getPresencePenalty())
.responseFormat(chatBaseRequest.getResponseFormat())
.stop(chatBaseRequest.getStop())
.temperature(chatBaseRequest.getTemperature() / 2)
.topP(chatBaseRequest.getTopP())
.functions(chatBaseRequest.getFunctions())
.toolChoice(chatBaseRequest.getToolChoice())
.build();
return deepSeekChatRequest;
}
@Override
public ChatBaseResponse convertResponse(DeepSeekChatResponse deepSeekChatResponse) {
ChatBaseResponse chatBaseResponse = new ChatBaseResponse();
chatBaseResponse.setId(deepSeekChatResponse.getId());
chatBaseResponse.setObject(deepSeekChatResponse.getObject());
chatBaseResponse.setCreated(deepSeekChatResponse.getCreated());
chatBaseResponse.setModel(deepSeekChatResponse.getModel());
chatBaseResponse.setSystemFingerprint(deepSeekChatResponse.getSystemFingerprint());
chatBaseResponse.setChoices(deepSeekChatResponse.getChoices());
return chatBaseResponse;
}
@Override
public ChatBaseResponse chatCompletion(String baseUrl, String apiKey, ChatBaseRequest chatBaseRequest) throws Exception {
baseUrl = FuncBase.isEmpty(baseUrl) ? deepSeekConfig.getApiHost() : baseUrl;
apiKey = FuncBase.isEmpty(apiKey) ? deepSeekConfig.getApiKey() : apiKey;
// 转换 请求参数
DeepSeekChatRequest deepSeekChatRequest = this.convertParam(chatBaseRequest);
deepSeekChatRequest.setModel(deepSeekConfig.getModel());
// 构造请求
ObjectMapper mapper = new ObjectMapper();
String requestString = mapper.writeValueAsString(deepSeekChatRequest);
Request request = new Request.Builder()
.header("Authorization", "Bearer " + apiKey)
.url(baseUrl + deepSeekConfig.getChatCompletionUrl())
.post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), requestString))
.build();
Response execute = okHttpClient.newCall(request).execute();
if (FuncBase.isEmpty(execute)) {
return null;
}
if (FuncBase.isEmpty(execute.body())) {
return null;
}
//转为统一格式
String rsp = execute.body().string();
DeepSeekChatResponse moonshotChatCompletionResponse = FuncBase.json2Bean(rsp, DeepSeekChatResponse.class);
return this.convertResponse(moonshotChatCompletionResponse);
}
@Override
public ChatBaseResponse chatCompletion(ChatBaseRequest chatBaseRequest) throws Exception {
return this.chatCompletion(null, null, chatBaseRequest);
}
}

View File

@@ -0,0 +1,23 @@
package com.jeelowcode.framework.ai.platform.chat.deepseek.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
import com.jeelowcode.framework.ai.platform.chat.tool.ChatMessage;
import lombok.*;
import lombok.experimental.SuperBuilder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Data
@SuperBuilder
@NoArgsConstructor
public class DeepSeekChatRequest extends ChatBaseRequest {
}

View File

@@ -0,0 +1,16 @@
package com.jeelowcode.framework.ai.platform.chat.deepseek.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 聊天返回结果
*/
@Data
public class DeepSeekChatResponse extends ChatBaseResponse {
}

View File

@@ -0,0 +1,97 @@
package com.jeelowcode.framework.ai.platform.chat.kimi;
import com.jeelowcode.framework.ai.config.KimiConfig;
import com.jeelowcode.framework.ai.convert.ParameterConvert;
import com.jeelowcode.framework.ai.convert.ResultConvert;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
import com.jeelowcode.framework.ai.platform.chat.kimi.entity.KimiChatRequest;
import com.jeelowcode.framework.ai.platform.chat.kimi.entity.KimiChatResponse;
import com.jeelowcode.framework.ai.service.Configuration;
import com.jeelowcode.framework.ai.service.IChatService;
import com.jeelowcode.framework.utils.utils.FuncBase;
import okhttp3.*;
public class KimiChatService implements IChatService, ParameterConvert<KimiChatRequest>, ResultConvert<KimiChatResponse> {
private final KimiConfig kimiConfig;
private final OkHttpClient okHttpClient;
public KimiChatService(Configuration configuration) {
this.kimiConfig = configuration.getKimiConfig();
this.okHttpClient = configuration.getOkHttpClient();
}
@Override
public KimiChatRequest convertParam(ChatBaseRequest chatBaseRequest) {
KimiChatRequest kimiChatRequest = KimiChatRequest.builder()
.messages(chatBaseRequest.getMessages())
.frequencyPenalty(chatBaseRequest.getFrequencyPenalty())
.maxTokens(chatBaseRequest.getMaxTokens())
.presencePenalty(chatBaseRequest.getPresencePenalty())
.responseFormat(chatBaseRequest.getResponseFormat())
.stop(chatBaseRequest.getStop())
.temperature(chatBaseRequest.getTemperature() / 2)
.topP(chatBaseRequest.getTopP())
.functions(chatBaseRequest.getFunctions())
.toolChoice(chatBaseRequest.getToolChoice())
.build();
return kimiChatRequest;
}
/**
* 回复内容参数转换
*/
@Override
public ChatBaseResponse convertResponse(KimiChatResponse kimiChatResponse) {
ChatBaseResponse chatBaseResponse = new ChatBaseResponse();
chatBaseResponse.setId(kimiChatResponse.getId());
chatBaseResponse.setObject(kimiChatResponse.getObject());
chatBaseResponse.setCreated(kimiChatResponse.getCreated());
chatBaseResponse.setModel(kimiChatResponse.getModel());
chatBaseResponse.setChoices(kimiChatResponse.getChoices());
return chatBaseResponse;
}
/**
* 同步请求
*/
@Override
public ChatBaseResponse chatCompletion(String baseUrl, String apiKey, ChatBaseRequest chatBaseRequest) throws Exception {
baseUrl = FuncBase.isEmpty(baseUrl) ? kimiConfig.getApiHost() : baseUrl;
apiKey = FuncBase.isEmpty(apiKey) ? kimiConfig.getApiKey() : apiKey;
// 转换 请求参数
KimiChatRequest kimiChatRequest = this.convertParam(chatBaseRequest);
kimiChatRequest.setModel(kimiConfig.getModel());
// 构造请求
String requestString = FuncBase.json2Str(kimiChatRequest);
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), requestString);
Request request = new Request.Builder()
.header("Authorization", "Bearer " + apiKey)
.url(baseUrl+ kimiConfig.getChatCompletionUrl())
.post(requestBody)
.build();
Response execute = okHttpClient.newCall(request).execute();
if (FuncBase.isEmpty(execute)) {
return null;
}
if (FuncBase.isEmpty(execute.body())) {
return null;
}
//转为统一格式
String rsp = execute.body().string();
KimiChatResponse kimiChatResponse = FuncBase.json2Bean(rsp, KimiChatResponse.class);
return this.convertResponse(kimiChatResponse);
}
@Override
public ChatBaseResponse chatCompletion(ChatBaseRequest chatBaseRequest) throws Exception {
return this.chatCompletion(null, null, chatBaseRequest);
}
}

View File

@@ -0,0 +1,17 @@
package com.jeelowcode.framework.ai.platform.chat.kimi.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
import lombok.*;
import lombok.experimental.SuperBuilder;
/**
*
*/
@Data
@SuperBuilder
public class KimiChatRequest extends ChatBaseRequest {
}

View File

@@ -0,0 +1,18 @@
package com.jeelowcode.framework.ai.platform.chat.kimi.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
import com.jeelowcode.framework.ai.platform.chat.tool.Choice;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
public class KimiChatResponse extends ChatBaseResponse {
}

View File

@@ -0,0 +1,61 @@
package com.jeelowcode.framework.ai.platform.chat.tool;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.jeelowcode.framework.ai.platform.chat.ChatMessageType;
import lombok.*;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.jeelowcode.framework.ai.platform.chat.ChatMessageType;
import lombok.*;
@Data
@Builder
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ChatMessage {
private String content;
private String role;
private String name;
private String refusal;
@JsonProperty("reasoning_content")
private String reasoningContent;
@JsonProperty("tool_call_id")
private String toolCallId;
public ChatMessage(String userMessage) {
this.role = ChatMessageType.USER.getRole();
this.content =userMessage;
}
public ChatMessage(ChatMessageType role, String message) {
this.role = role.getRole();
this.content = message;
}
public ChatMessage(String role, String message) {
this.role = role;
this.content = message;
}
public static ChatMessage withSystem(String content) {
return new ChatMessage(ChatMessageType.SYSTEM, content);
}
public static ChatMessage withUser(String content) {
return new ChatMessage(ChatMessageType.USER, content);
}
public static ChatMessage withAssistant(String content) {
return new ChatMessage(ChatMessageType.ASSISTANT, content);
}
}

View File

@@ -0,0 +1,34 @@
package com.jeelowcode.framework.ai.platform.chat.tool;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Choice {
private Integer index;
private ChatMessage delta;
private ChatMessage message;
private Object logprobs;
/**
* 模型停止生成 token 的原因。
*
* [stop, length, content_filter, tool_calls, insufficient_system_resource]
*
* stop模型自然停止生成或遇到 stop 序列中列出的字符串。
* length输出长度达到了模型上下文长度限制或达到了 max_tokens 的限制。
* content_filter输出内容因触发过滤策略而被过滤。
* tool_calls函数调用。
* insufficient_system_resource系统推理资源不足生成被打断。
*
*/
@JsonProperty("finish_reason")
private String finishReason;
}

View File

@@ -0,0 +1,87 @@
package com.jeelowcode.framework.ai.platform.chat.tool;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.*;
import java.util.ArrayList;
import java.util.List;
@ToString
public class Content {
private String text; // 纯文本时使用
private List<MultiModal> multiModals; // 多模态时使用
// 纯文本构造方法
public static Content ofText(String text) {
Content instance = new Content();
instance.text = text;
return instance;
}
// 多模态构造方法
public static Content ofMultiModals(List<MultiModal> parts) {
Content instance = new Content();
instance.multiModals = parts;
return instance;
}
// 序列化逻辑
@JsonValue
public Object toJson() {
if (text != null) {
return text; // 直接返回
} else if (multiModals != null) {
return multiModals;
}
throw new IllegalStateException("Invalid content state");
}
public String getText() { return text; }
public List<MultiModal> getMultiModals() { return multiModals; }
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class MultiModal {
private String type = Type.TEXT.type;
private String text;
@JsonProperty("image_url")
private ImageUrl imageUrl;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ImageUrl {
private String url;
}
@Getter
@AllArgsConstructor
public enum Type {
TEXT("text", "文本类型"),
IMAGE_URL("image_url", "图片类型可以为url或者base64"),
;
private final String type;
private final String info;
}
public static List<MultiModal> withMultiModal(String text, String... imageUrl) {
List<MultiModal> messages = new ArrayList<>();
messages.add(new MultiModal(Type.TEXT.getType(), text, null));
for (String url : imageUrl) {
messages.add(new MultiModal(Type.IMAGE_URL.getType(), null, new ImageUrl(url)));
}
return messages;
}
}
}

View File

@@ -0,0 +1,89 @@
package com.jeelowcode.framework.ai.properties;
import com.jeelowcode.framework.ai.config.DeepSeekConfig;
import com.jeelowcode.framework.ai.config.KimiConfig;
import com.jeelowcode.framework.ai.service.factor.AiService;
import okhttp3.OkHttpClient;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableConfigurationProperties({
DeepSeekConfigProperties.class,
KimiConfigProperties.class})
public class AiConfigAutoConfiguration {
private final DeepSeekConfigProperties deepSeekConfigProperties;
private final KimiConfigProperties kimiConfigProperties;
private com.jeelowcode.framework.ai.service.Configuration configuration = new com.jeelowcode.framework.ai.service.Configuration();
public AiConfigAutoConfiguration(DeepSeekConfigProperties deepSeekConfigProperties, KimiConfigProperties kimiConfigProperties) {
this.deepSeekConfigProperties = deepSeekConfigProperties;
this.kimiConfigProperties = kimiConfigProperties;
}
@Bean
public AiService aiService() {
return new AiService(configuration);
}
@PostConstruct
private void init() {
initOkHttp();
initDeepSeekConfig();
initMoonshotConfig();
}
private void initOkHttp() {
// 开启 Http 客户端
OkHttpClient.Builder okHttpBuilder = new OkHttpClient
.Builder()
.connectTimeout(300, TimeUnit.SECONDS)
.writeTimeout(300,TimeUnit.SECONDS)
.readTimeout(300, TimeUnit.SECONDS);
OkHttpClient okHttpClient = okHttpBuilder.build();
configuration.setOkHttpClient(okHttpClient);
}
/**
* 初始化DeepSeek 配置信息
*/
private void initDeepSeekConfig(){
DeepSeekConfig deepSeekConfig = new DeepSeekConfig();
deepSeekConfig.setApiHost(deepSeekConfigProperties.getApiHost());
deepSeekConfig.setApiKey(deepSeekConfigProperties.getApiKey());
deepSeekConfig.setModel(deepSeekConfigProperties.getModel());
deepSeekConfig.setChatCompletionUrl(deepSeekConfigProperties.getChatCompletionUrl());
configuration.setDeepSeekConfig(deepSeekConfig);
}
/**
* 初始化Moonshot 配置信息
*/
private void initMoonshotConfig() {
KimiConfig kimiConfig = new KimiConfig();
kimiConfig.setApiHost(kimiConfigProperties.getApiHost());
kimiConfig.setApiKey(kimiConfigProperties.getApiKey());
kimiConfig.setModel(kimiConfigProperties.getModel());
kimiConfig.setChatCompletionUrl(kimiConfigProperties.getChatCompletionUrl());
configuration.setKimiConfig(kimiConfig);
}
}

View File

@@ -0,0 +1,14 @@
package com.jeelowcode.framework.ai.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "jeelowcode.ai.deepseek")
public class DeepSeekConfigProperties {
private String apiHost = "https://api.deepseek.com/";
private String apiKey = "";
private String model="deepseek-chat";
private String chatCompletionUrl = "chat/completions";
}

View File

@@ -0,0 +1,19 @@
package com.jeelowcode.framework.ai.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @Author cly
* @Description Moonshot配置文件
* @Date 2024/8/30 15:56
*/
@Data
@ConfigurationProperties(prefix = "jeelowcode.ai.kimi")
public class KimiConfigProperties {
private String apiHost = "https://api.moonshot.cn/";
private String apiKey = "";
private String model = "moonshot-v1-8k";
private String chatCompletionUrl = "v1/chat/completions";
}

View File

@@ -0,0 +1,22 @@
package com.jeelowcode.framework.ai.service;
import com.jeelowcode.framework.ai.config.DeepSeekConfig;
import com.jeelowcode.framework.ai.config.KimiConfig;
import com.jeelowcode.framework.ai.websearch.searxng.SearXNGConfig;
import lombok.Data;
import okhttp3.OkHttpClient;
@Data
public class Configuration {
private OkHttpClient okHttpClient;
private DeepSeekConfig deepSeekConfig;
private KimiConfig kimiConfig;
private SearXNGConfig searXNGConfig;
}

View File

@@ -0,0 +1,14 @@
package com.jeelowcode.framework.ai.service;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
public interface IChatService {
//同步消息
ChatBaseResponse chatCompletion(String baseUrl, String apiKey, ChatBaseRequest chatBaseRequest) throws Exception;
ChatBaseResponse chatCompletion(ChatBaseRequest chatBaseRequest) throws Exception;
}

View File

@@ -0,0 +1,15 @@
package com.jeelowcode.framework.ai.service;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum PlatformType {
DEEPSEEK("deepseek"),
KIMI("kimi"),
;
private final String platform;
}

View File

@@ -0,0 +1,44 @@
package com.jeelowcode.framework.ai.service.factor;
import com.jeelowcode.framework.ai.platform.chat.deepseek.DeepSeekChatService;
import com.jeelowcode.framework.ai.platform.chat.kimi.KimiChatService;
import com.jeelowcode.framework.ai.service.Configuration;
import com.jeelowcode.framework.ai.service.IChatService;
import com.jeelowcode.framework.ai.service.PlatformType;
import com.jeelowcode.framework.ai.websearch.ChatWithWebSearchEnhance;
public class AiService {
private final Configuration configuration;
public AiService(Configuration configuration) {
this.configuration = configuration;
}
public IChatService getChatService(PlatformType platform) {
return createChatService(platform);
}
public IChatService webSearchEnhance(IChatService chatService) {
return new ChatWithWebSearchEnhance(chatService, configuration);
}
private IChatService createChatService(PlatformType platform) {
switch (platform) {
case KIMI:
return new KimiChatService(configuration);
case DEEPSEEK:
return new DeepSeekChatService(configuration);
default:
throw new IllegalArgumentException("Unknown platform: " + platform);
}
}
}

View File

@@ -0,0 +1,42 @@
package com.jeelowcode.framework.ai.template;
import com.jeelowcode.framework.ai.platform.chat.tool.ChatMessage;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
public class CreateTableTemplate {
//获取模板
public static List<ChatMessage> getMessageTemplateList(){
List<ChatMessage> sysMessageList=new ArrayList<>();
sysMessageList.add(ChatMessage.withSystem("你是一个mysql数据库设计专家你需要根据用户的需求理解设计出数据表然后按特定格式返回"));
sysMessageList.add(ChatMessage.withSystem("表信息解析tableName表名称其中必须是tbl_ 开头。 tableDescribe表中文描述"));
sysMessageList.add(ChatMessage.withSystem("字段解析fieldCode字段编码fieldName字段名称fieldLen字段长度fieldDefaultVal默认值fieldType字段类型"));
sysMessageList.add(ChatMessage.withSystem("其中fieldType字段类型的值有String=字符串类型Integer=整型Date=日期yyyy-MM-ddDateTime=日期时间yyyy-MM-dd HH:mm:ssTime=时间HH:mm:ssBigInt=长整型long,BigDecimal=小数LongText=长文本Blob=二进制"));
sysMessageList.add(ChatMessage.withSystem("其中必须有的字段未id,tenant_id,create_user,create_time,create_dept,update_user,update_time,is_deleted"));
sysMessageList.add(ChatMessage.withSystem("这个是返回格式一定要以json格式返回其他不必要的内容不要返回{\"tableName\":\"tbl_name\",\"tableDescribe\":\"学生表\",\"fieldModelList\":[{\"fieldCode\":\"name\",\"fieldName\":\"姓名\",\"fieldLen\":10,\"fieldPointLen\":0,\"fieldDefaultVal\":\"\",\"fieldType\":\"String\"},{\"fieldCode\":\"age\",\"fieldName\":\"年龄\",\"fieldLen\":10,\"fieldPointLen\":0,\"fieldDefaultVal\":\"\",\"fieldType\":\"String\"}]}\n"));
sysMessageList.add(ChatMessage.withSystem("返回格式[重要]只需要返回json格式即可 ,其他不必要的内容不要返回"));
return sysMessageList;
}
@Data
public static class RspModel{
private String tableName;//表名称
private String tableDescribe;//表描述
List<FieldModel> fieldModelList;
}
@Data
public static class FieldModel{
private String fieldCode;//字段编号 例如name
private String fieldName;//字段名称 例如:姓名
private Integer fieldLen;//字段长度
private Integer fieldPointLen;//字段小数位
private String fieldDefaultVal;//默认值
private String fieldType;//字段类型
}
}

View File

@@ -0,0 +1,98 @@
package com.jeelowcode.framework.ai.websearch;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseRequest;
import com.jeelowcode.framework.ai.platform.chat.ChatBaseResponse;
import com.jeelowcode.framework.ai.service.Configuration;
import com.jeelowcode.framework.ai.service.IChatService;
import com.jeelowcode.framework.ai.websearch.searxng.SearXNGConfig;
import com.jeelowcode.framework.ai.websearch.searxng.SearXNGRequest;
import com.jeelowcode.framework.ai.websearch.searxng.SearXNGResponse;
import com.jeelowcode.framework.exception.JeeLowCodeException;
import com.jeelowcode.framework.utils.utils.FuncBase;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class ChatWithWebSearchEnhance implements IChatService {
private final IChatService chatService;
private final SearXNGConfig searXNGConfig;
private final OkHttpClient okHttpClient;
public ChatWithWebSearchEnhance(IChatService chatService, Configuration configuration) {
this.chatService = chatService;
this.searXNGConfig = configuration.getSearXNGConfig();
this.okHttpClient = configuration.getOkHttpClient();
}
@Override
public ChatBaseResponse chatCompletion(String baseUrl, String apiKey, ChatBaseRequest chatBaseRequest) throws Exception {
return chatService.chatCompletion(baseUrl, apiKey, addWebSearchResults(chatBaseRequest));
}
@Override
public ChatBaseResponse chatCompletion(ChatBaseRequest chatBaseRequest) throws Exception {
return chatService.chatCompletion(addWebSearchResults(chatBaseRequest));
}
private ChatBaseRequest addWebSearchResults(ChatBaseRequest chatBaseRequest) {
int chatLen = chatBaseRequest.getMessages().size();
String prompt = chatBaseRequest.getMessages().get(chatLen - 1).getContent();
// 执行联网搜索并将结果附加到提示词中
String searchResults = performWebSearch(prompt);
chatBaseRequest.getMessages().get(chatLen - 1).setContent("我将提供一段来自互联网的资料信息, 请根据这段资料以及用户提出的问题来给出回答。请确保在回答中使用Markdown格式并在回答末尾列出参考资料。如果资料中的信息不足以回答用户的问题可以根据自身知识库进行补充或者说明无法提供确切的答案。\n" +
"网络资料:\n"
+ "============\n"
+ searchResults
+ "============\n"
+ "用户问题:\n"
+ "============\n"
+ prompt
+ "============\n");
return chatBaseRequest;
}
private String performWebSearch(String query) {
SearXNGRequest searXNGRequest = SearXNGRequest.builder()
.q(query)
.engines(searXNGConfig.getEngines())
.build();
if(StringUtils.isBlank(searXNGConfig.getUrl())){
throw new JeeLowCodeException("SearXNG url is not configured");
}
Request request = new Request.Builder()
.url(searXNGConfig.getUrl()+ "?format=json&q=" + query + "&engines=" + searXNGConfig.getEngines())
.get()
.build();
try(Response execute = okHttpClient.newCall(request).execute()) {
if (execute.isSuccessful() && execute.body() != null){
SearXNGResponse searXNGResponse = FuncBase.json2Bean(execute.body().string(), SearXNGResponse.class);
if(searXNGResponse.getResults().size() > searXNGConfig.getNums()) {
return FuncBase.json2Str(searXNGResponse.getResults().subList(0, searXNGConfig.getNums()));
}
return FuncBase.json2Str(searXNGResponse.getResults());
}else{
throw new Exception("SearXNG request failed");
}
} catch (Exception e) {
throw new JeeLowCodeException("SearXNG request failed");
}
}
}

View File

@@ -0,0 +1,11 @@
package com.jeelowcode.framework.ai.websearch.searxng;
import lombok.Data;
@Data
public class SearXNGConfig {
private String url;
private String engines = "duckduckgo,google,bing,brave,mojeek,presearch,qwant,startpage,yahoo,arxiv,crossref,google_scholar,internetarchivescholar,semantic_scholar";
private int nums = 20;
}

View File

@@ -0,0 +1,18 @@
package com.jeelowcode.framework.ai.websearch.searxng;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
public class SearXNGRequest {
@Builder.Default
private final String format = "json";
private String q;
@Builder.Default
private String engines = "duckduckgo,google,bing,brave,mojeek,presearch,qwant,startpage,yahoo,arxiv,crossref,google_scholar,internetarchivescholar,semantic_scholar";
}

View File

@@ -0,0 +1,29 @@
package com.jeelowcode.framework.ai.websearch.searxng;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
public class SearXNGResponse {
private String query;
@JsonProperty("number_of_results")
private String numberOfResults;
private List<Result> results;
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Result {
private String url;
private String title;
private String content;
}
}

View File

@@ -0,0 +1 @@
com.jeelowcode.framework.ai.properties.AiConfigAutoConfiguration

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jeelowcode-framework</artifactId>
<groupId>com.jeelowcode</groupId>
<version>${jeelowcode.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jeelowcode-excel</artifactId>
<name>${project.artifactId}</name>
<version>${jeelowcode.version}</version>
<packaging>jar</packaging>
<description> JeeLowCode低代码平台 - excel模块 </description>
<dependencies>
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>jeelowcode-utils</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,47 @@
package com.jeelowcode.framework.excel;
import com.jeelowcode.framework.excel.model.ExcelTitleModel;
import com.jeelowcode.framework.excel.read.ExcelImportUtils;
import com.jeelowcode.framework.excel.write.ExcelExportUtils;
import org.apache.commons.collections4.map.LinkedMap;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
public class JeeLowCodeExcelUtils {
/**
* 导出 Excel
* @param response
* @param sheetName
* @param headMap
* @param dataList
*/
public static void exportExcel(HttpServletResponse response, String sheetName, LinkedMap<String, ExcelTitleModel> headMap, List<Map<String, Object>> dataList) {
ExcelExportUtils.exportExcel(response,sheetName,headMap,dataList);
}
/**
* 导入Excel
* @param inputStream
* @param headerCou 表头所在的行数
* @return
*/
public static List<Map<String, Object>> importExcel(InputStream inputStream, Integer headerCou, Map<String, String> fieldNameAndCodeMap){
return ExcelImportUtils.importExcel(inputStream,headerCou,fieldNameAndCodeMap);
}
/**
* 导入Excel
* @param inputStream
* @param headerCou 表头所在的行数
* @return
*/
public static List<Map<String, Object>> importExcel(InputStream inputStream, Integer headerCou,Integer sheet, Map<String, String> fieldNameAndCodeMap){
return ExcelImportUtils.importExcel(inputStream,headerCou,fieldNameAndCodeMap,sheet);
}
}

View File

@@ -0,0 +1,30 @@
package com.jeelowcode.framework.excel.handler;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.xssf.streaming.SXSSFSheet;
/**
*
*/
public class CustomSheetWriteHandler implements SheetWriteHandler {
private static final Integer COLUMN = 26;
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
for (int i = 0; i < COLUMN; i++) {
// 设置为文本格式
SXSSFSheet sxssfSheet = (SXSSFSheet) writeSheetHolder.getSheet();
CellStyle cellStyle = writeWorkbookHolder.getCachedWorkbook().createCellStyle();
// 49为文本格式
cellStyle.setDataFormat((short) 49);
// i为列一整列设置为文本格式
sxssfSheet.setDefaultColumnStyle(i, cellStyle);
}
}
}

View File

@@ -0,0 +1,134 @@
package com.jeelowcode.framework.excel.handler;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.jeelowcode.framework.utils.utils.FuncBase;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.DefaultIndexedColorMap;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import java.awt.Color;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 单元格设置文本格式
*/
public class RowFormatSetTextHandler implements CellWriteHandler {
Map<Integer, List<String>> dropdownOptionsMap=new HashMap<>();
Map<Integer, CellStyle> cellStyleMap = new HashMap<>();
public RowFormatSetTextHandler() {
}
@Override
public void beforeCellCreate(CellWriteHandlerContext context) {
WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder();
Sheet sheet = writeSheetHolder.getSheet();
Integer rowIndex = context.getRowIndex();
if(rowIndex!=0){
return;
}
Integer columnIndex = context.getColumnIndex();
//下拉列表
List<String> dropdownOptionList = dropdownOptionsMap.get(columnIndex);
if(FuncBase.isEmpty(dropdownOptionList)){
return;
}
createDropdownList(sheet,dropdownOptionList,columnIndex);
}
@Override
public void afterCellDispose(CellWriteHandlerContext context) {
WriteCellData<?> cellData = context.getFirstCellData();
// 清除原有单元格颜色
WriteCellStyle writeCellStyle = cellData.getWriteCellStyle();
writeCellStyle.setFillForegroundColor(null);
// 重新设置自定义RGB颜色
CellStyle cellStyle;
int cellStyleKey;
Integer rowIndex = context.getRowIndex();
if (rowIndex == 0) {
// 第0行即表头
cellStyleKey = 0;
cellStyle = cellStyleMap.computeIfAbsent(cellStyleKey,
value -> getCellStyle(cellData, context, new Color(115, 174, 76)));
} else if ((rowIndex & 1) == 0) {
// 偶数行
cellStyleKey = 2;
cellStyle = cellStyleMap.computeIfAbsent(cellStyleKey,
value -> getCellStyle(cellData, context, new Color(255, 255, 255)));
} else {
// 奇数行
cellStyleKey = 1;
cellStyle = cellStyleMap.computeIfAbsent(cellStyleKey,
value -> getCellStyle(cellData, context, new Color(227, 239, 219)));
}
cellData.setOriginCellStyle(cellStyle);
}
/**
* 单元格样式
*/
private CellStyle getCellStyle(WriteCellData<?> cellData, CellWriteHandlerContext context, Color color) {
CellStyle style = cellData.getOriginCellStyle();
if (FuncBase.isEmpty(style)) {
style = context.getWriteWorkbookHolder().getWorkbook().createCellStyle();
}
XSSFColor xssfColor = new XSSFColor(color, new DefaultIndexedColorMap());
((XSSFCellStyle) style).setFillForegroundColor(xssfColor);
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
return style;
}
/**
* 下拉
* @param sheet
* @param dropdownOptionList
*/
public void createDropdownList( Sheet sheet, List<String> dropdownOptionList, int firstCol) {
if(FuncBase.isEmpty(dropdownOptionList)){
return;
}
DataValidationHelper helper = sheet.getDataValidationHelper();
String[] dropdownOptionsArray = dropdownOptionList.toArray(new String[0]);
// 创建下拉列表的约束
DataValidationConstraint constraint = helper.createExplicitListConstraint(dropdownOptionsArray);
// 设置下拉列表应用的单元格区域例如第2行到最后一行的第2列
int firstRow = 1; // 通常Excel中第一行是1这里假设第一行为表头
int lastRow=65536;
int lastCol = firstCol;
CellRangeAddressList regions = new CellRangeAddressList(firstRow, lastRow, firstCol, lastCol);
// 创建数据验证规则并添加到工作表
DataValidation validation = helper.createValidation(constraint, regions);
sheet.addValidationData(validation);
}
public Map<Integer, List<String>> getDropdownOptionsMap() {
return dropdownOptionsMap;
}
public void setDropdownOptionsMap(Map<Integer, List<String>> dropdownOptionsMap) {
this.dropdownOptionsMap = dropdownOptionsMap;
}
}

View File

@@ -0,0 +1,43 @@
package com.jeelowcode.framework.excel.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 自定义监听器
*/
public class JeeLowCodeExcelListener extends AnalysisEventListener<Map<Integer, String>> {
//表头数据(存储所有的表头数据)
private List<Map<Integer, String>> headList = new ArrayList<>();
//数据体
private List<Map<Integer, String>> dataList = new ArrayList<>();
@Override//这里会一行行的返回头
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
//存储全部表头数据
headList.add(headMap);
}
@Override// 处理每一行数据
public void invoke(Map<Integer, String> data, AnalysisContext context) {
dataList.add(data);
}
@Override// 全部处理结束执行
public void doAfterAllAnalysed(AnalysisContext context) {
}
public List<Map<Integer, String>> getHeadList() {
return headList;
}
public List<Map<Integer, String>> getDataList() {
return dataList;
}
}

View File

@@ -0,0 +1,33 @@
package com.jeelowcode.framework.excel.model;
import java.util.List;
public class ExcelTitleModel {
private String title;//标题
private List<String> dropdownOptionList;//下拉列表
public ExcelTitleModel() {
}
public ExcelTitleModel(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<String> getDropdownOptionList() {
return dropdownOptionList;
}
public void setDropdownOptionList(List<String> dropdownOptionList) {
this.dropdownOptionList = dropdownOptionList;
}
}

View File

@@ -0,0 +1,36 @@
package com.jeelowcode.framework.excel.model;
import org.apache.commons.collections4.map.LinkedMap;
import java.util.List;
import java.util.Map;
public class MoreSheetConfig {
private String sheetName;
private LinkedMap<String, ExcelTitleModel> headMap;
private List<Map<String, Object>> dataList;
public String getSheetName() {
return sheetName;
}
public void setSheetName(String sheetName) {
this.sheetName = sheetName;
}
public LinkedMap<String, ExcelTitleModel> getHeadMap() {
return headMap;
}
public void setHeadMap(LinkedMap<String, ExcelTitleModel> headMap) {
this.headMap = headMap;
}
public List<Map<String, Object>> getDataList() {
return dataList;
}
public void setDataList(List<Map<String, Object>> dataList) {
this.dataList = dataList;
}
}

View File

@@ -0,0 +1,93 @@
package com.jeelowcode.framework.excel.read;
import cn.hutool.core.util.IdUtil;
import com.jeelowcode.framework.excel.listener.JeeLowCodeExcelListener;
import com.jeelowcode.framework.exception.JeeLowCodeException;
import com.jeelowcode.framework.utils.constant.JeeLowCodeConstant;
import com.jeelowcode.framework.utils.utils.FuncBase;
import com.alibaba.excel.EasyExcelFactory;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 处理导入数据
*/
public class ExcelImportUtils {
/**
* 动态表头导入功能
*
* @param inputStream 文件
* @return
*/
public static List<Map<String, Object>> importExcel(InputStream inputStream,Integer headerCou, Map<String, String> fieldNameAndCodeMap) {
return importExcel(inputStream,headerCou,fieldNameAndCodeMap,0);
}
public static List<Map<String, Object>> importExcel(InputStream inputStream,Integer headerCou, Map<String, String> fieldNameAndCodeMap,Integer sheet) {
try {
// 首先校验传入文件是否为空
if (inputStream == null) {
throw new JeeLowCodeException("文件源为空");
}
// 引入监听器此处需注意监听器不可被Spring管理
JeeLowCodeExcelListener readListener = new JeeLowCodeExcelListener();
// 开始处理excel
EasyExcelFactory.read(inputStream, readListener)
.sheet(sheet)
.headRowNumber(headerCou)
.doRead();
// 获取表头(验空)
List<Map<Integer, String>> headList = readListener.getHeadList();
if (FuncBase.isEmpty(headList)) {
throw new JeeLowCodeException("Excel表头不能为空");
}
//获取头部,取最后一次解析的列头数据
Map<Integer, String> excelHeadIdxNameMap = headList.get(headList.size() - 1);
// 获取表数据(验空)
List<Map<Integer, String>> dataList = readListener.getDataList();
if (FuncBase.isEmpty(dataList)) {
throw new JeeLowCodeException("Excel数据内容不能为空");
}
//封装数据体
AtomicInteger atomicInteger= new AtomicInteger(1);
List<Map<String, Object>> excelDataList = new ArrayList<Map<String, Object>>();
for (Map<Integer, String> dataRow : dataList) {
HashMap<String, Object> rowData = new HashMap<>();
int step =atomicInteger.getAndIncrement();
rowData.put(JeeLowCodeConstant.EXCEL_IMPORT_STEP,step);//处理序号
rowData.put(JeeLowCodeConstant.EXCEL_IMPORT_ID,IdUtil.getSnowflakeNextId());//id
excelHeadIdxNameMap.entrySet().forEach(columnHead -> {
String value = columnHead.getValue();
String fieldCode = fieldNameAndCodeMap.get(value);
if(FuncBase.isNotEmpty(fieldCode)){
rowData.put(fieldCode, dataRow.get(columnHead.getKey()));
}
});
if(FuncBase.isEmpty(rowData)){
continue;
}
excelDataList.add(rowData);
}
return excelDataList;
} catch (Exception e) {
e.printStackTrace();
throw new JeeLowCodeException(e.getMessage());
}
}
}

View File

@@ -0,0 +1,124 @@
package com.jeelowcode.framework.excel.write;
import com.alibaba.excel.metadata.data.DataFormatData;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.*;
import java.util.ArrayList;
import java.util.List;
public class CellStyleUtils {
/**
* 对行使用相同的样式<br/>
* 头和内容分别使用不同样式
*/
public static HorizontalCellStyleStrategy getCellStyleStrategy(){
// List<WriteCellStyle> contentStyleList = new ArrayList<>();// 内容奇偶数行样式不一致
// contentStyleList.add(getOddRowContentStyle());
// contentStyleList.add(getEvenRowContentStyle());
return new HorizontalCellStyleStrategy(getHeadStyle(), getContentStyle());
}
/**
* 头样式
*/
public static WriteCellStyle getHeadStyle() {
WriteCellStyle style = new WriteCellStyle();
DataFormatData dataFormatData = new DataFormatData();
dataFormatData.setFormat("@");
style.setDataFormatData(dataFormatData);// 单元格格式
WriteFont font = new WriteFont();
font.setFontHeightInPoints((short) 12);
font.setBold(false);
font.setColor(IndexedColors.WHITE.getIndex());
style.setWriteFont(font);// 字体
style.setHorizontalAlignment(HorizontalAlignment.CENTER);// 水平对齐方式
style.setVerticalAlignment(VerticalAlignment.CENTER);// 垂直对齐方式
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);// 边框样式
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());// 边框颜色
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
return style;
}
/**
* 内容样式
*/
public static WriteCellStyle getContentStyle() {
WriteCellStyle style = new WriteCellStyle();
DataFormatData dataFormatData = new DataFormatData();
dataFormatData.setFormat("@");
style.setDataFormatData(dataFormatData);// 单元格格式
WriteFont font = new WriteFont();
font.setFontHeightInPoints((short) 12);
style.setWriteFont(font);// 字体
style.setHorizontalAlignment(HorizontalAlignment.CENTER);// 水平对齐方式
style.setVerticalAlignment(VerticalAlignment.CENTER);// 垂直对齐方式
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);// 边框样式
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());// 边框颜色
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
return style;
}
/**
* 奇数行内容样式
*/
public static WriteCellStyle getOddRowContentStyle() {
WriteCellStyle style = new WriteCellStyle();
DataFormatData dataFormatData = new DataFormatData();
dataFormatData.setFormat("@");
style.setDataFormatData(dataFormatData);// 单元格格式
WriteFont font = new WriteFont();
font.setFontHeightInPoints((short) 12);
style.setWriteFont(font);// 字体
style.setHorizontalAlignment(HorizontalAlignment.CENTER);// 水平对齐方式
style.setVerticalAlignment(VerticalAlignment.CENTER);// 垂直对齐方式
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);// 边框样式
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());// 边框颜色
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
return style;
}
/**
* 偶数行内容样式
*/
public static WriteCellStyle getEvenRowContentStyle() {
WriteCellStyle style = new WriteCellStyle();
WriteFont font = new WriteFont();
DataFormatData dataFormatData = new DataFormatData();
dataFormatData.setFormat("@");
style.setDataFormatData(dataFormatData);// 文本格式
font.setFontHeightInPoints((short) 12);
style.setWriteFont(font);// 字体
style.setHorizontalAlignment(HorizontalAlignment.CENTER);// 水平对齐方式
style.setVerticalAlignment(VerticalAlignment.CENTER);// 垂直对齐方式
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);// 边框样式
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());// 边框颜色
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
return style;
}
}

View File

@@ -0,0 +1,31 @@
package com.jeelowcode.framework.excel.write;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Sheet;
/**
* 列宽样式策略
*/
public class CustomColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {
@Override
protected void setColumnWidth(CellWriteHandlerContext context) {
// 只需设置首行
if (context.getRowIndex() != 0) {
return;
}
DataFormatter dataFormatter = new DataFormatter();
Cell cell = context.getCell();
int length = dataFormatter.formatCellValue(cell).length();
if (length < 6) {
length = 6;
}
Sheet sheet = context.getWriteSheetHolder().getSheet();
sheet.setColumnWidth(context.getColumnIndex(), length * 1000);
}
}

View File

@@ -0,0 +1,186 @@
package com.jeelowcode.framework.excel.write;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.jeelowcode.framework.excel.handler.CustomSheetWriteHandler;
import com.jeelowcode.framework.excel.handler.RowFormatSetTextHandler;
import com.jeelowcode.framework.excel.model.ExcelTitleModel;
import com.jeelowcode.framework.excel.model.MoreSheetConfig;
import com.jeelowcode.framework.exception.JeeLowCodeException;
import com.jeelowcode.framework.utils.utils.FuncBase;
import com.alibaba.excel.EasyExcel;
import org.apache.commons.collections4.map.LinkedMap;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.*;
public class ExcelExportUtils {
/**
* 封装表头信息
* @param headMap {name:姓名} {age:年龄}
* @return
*/
private static List<List<String>> getExcelHeaderList(LinkedMap<String, ExcelTitleModel> headMap){
//处理头部
List<List<String>> head = new ArrayList<>();
headMap.forEach((code, model) -> {
List<String> headList = new ArrayList<>();
headList.add(model.getTitle());
head.add(headList);
});
return head;
}
/**
* 封装表数据
* @param headMap
* @param dataList name:张三age:18
* @return
*/
private static List<List<Object>> getExcelDataList(LinkedMap<String, ExcelTitleModel> headMap, List<Map<String, Object>> dataList){
List<List<Object>> data=new ArrayList<>();
//处理数据
dataList.stream().forEach(dataMap->{
List<Object> tmpList=new ArrayList<>();
headMap.forEach((code,title)->{
Object tmpVal = dataMap.get(code);
try{
tmpList.add(FuncBase.json2Str(tmpVal));
}catch (Exception e){
tmpList.add(tmpVal);
}
});
data.add(tmpList);
});
return data;
}
/**
* 动态表头导出
* @param response
* @param sheetName
* @param headMap
* @param dataList
*/
public static void exportExcel(HttpServletResponse response, String sheetName, LinkedMap<String, ExcelTitleModel> headMap, List<Map<String, Object>> dataList) {
try {
if(FuncBase.isEmpty(headMap)){
throw new JeeLowCodeException("表头不允许为空");
}
//设置mime类型
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
//设置编码
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String outFileName = URLEncoder.encode(sheetName, "UTF-8");
//设置响应头信息 Content-disposition
response.setHeader("Content-disposition", "attachment;filename=" + outFileName + ".xlsx");
//处理头部
List<List<String>> head =getExcelHeaderList(headMap);
//处理表数据
List<List<Object>> data=getExcelDataList(headMap,dataList);
//处理下拉内容
Map<Integer, List<String>> dropdownOptionsMap=getDropdownOptionsMap(headMap);
RowFormatSetTextHandler rowFormatSetTextHandler = new RowFormatSetTextHandler();
rowFormatSetTextHandler.setDropdownOptionsMap(dropdownOptionsMap);
EasyExcel.write(response.getOutputStream())
.registerWriteHandler(rowFormatSetTextHandler)
.registerWriteHandler(new CustomSheetWriteHandler())// 初始化单元格为文本格式
.registerWriteHandler(new CustomColumnWidthStyleStrategy())// 单元格宽度
.registerWriteHandler(CellStyleUtils.getCellStyleStrategy())// 单元格样式
.head(head)
.sheet(sheetName).doWrite(data);
} catch (Exception e) {
throw new JeeLowCodeException(e.getMessage());
}
}
public static void exportExcel(HttpServletResponse response, String fileName,List<MoreSheetConfig> sheetConfigList) {
try {
if (FuncBase.isEmpty(sheetConfigList)) {
throw new JeeLowCodeException("sheets数据不允许为空");
}
// 设置mime类型
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
// 设置编码
response.setCharacterEncoding("utf-8");
// 防止中文乱码
String outFileName = URLEncoder.encode(fileName, "UTF-8");
// 设置响应头信息 Content-disposition
response.setHeader("Content-disposition", "attachment;filename=" + outFileName + ".xlsx");
// 创建ExcelWriter
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
// 遍历每个sheet
for(MoreSheetConfig moreSheetConfig:sheetConfigList){
List<Map<String, Object>> dataList = moreSheetConfig.getDataList();
LinkedMap<String, ExcelTitleModel> headMap = moreSheetConfig.getHeadMap();
String sheetName = moreSheetConfig.getSheetName();
if (FuncBase.isEmpty(headMap)) {
continue; // 跳过空表头的sheet
}
// 处理头部
List<List<String>> head = getExcelHeaderList(headMap);
// 处理表数据
List<List<Object>> data = getExcelDataList(headMap, dataList);
// 处理下拉内容
Map<Integer, List<String>> dropdownOptionsMap = getDropdownOptionsMap(headMap);
RowFormatSetTextHandler rowFormatSetTextHandler = new RowFormatSetTextHandler();
rowFormatSetTextHandler.setDropdownOptionsMap(dropdownOptionsMap);
// 创建WriteSheet
WriteSheet writeSheet = EasyExcel.writerSheet(sheetName)
.head(head)
.registerWriteHandler(rowFormatSetTextHandler)
.registerWriteHandler(new CustomSheetWriteHandler())
.registerWriteHandler(new CustomColumnWidthStyleStrategy())
.registerWriteHandler(CellStyleUtils.getCellStyleStrategy())
.build();
// 写入数据
excelWriter.write(data, writeSheet);
}
// 关闭ExcelWriter
excelWriter.finish();
} catch (Exception e) {
throw new JeeLowCodeException(e.getMessage());
}
}
/**
* 处理下拉列表
* @param headMap
* @return
*/
private static Map<Integer, List<String>> getDropdownOptionsMap(LinkedMap<String, ExcelTitleModel> headMap){
Map<Integer, List<String>> dropdownOptionsMap=new HashMap<>();
int index = 0; // 初始化序号变量
Iterator<Map.Entry<String, ExcelTitleModel>> iterator = headMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, ExcelTitleModel> entry = iterator.next();
ExcelTitleModel model = entry.getValue();
List<String> dropdownOptionList = model.getDropdownOptionList();
if(FuncBase.isNotEmpty(dropdownOptionList)){
dropdownOptionsMap.put(index,dropdownOptionList);
}
++index;
}
return dropdownOptionsMap;
}
}

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jeelowcode-framework</artifactId>
<groupId>com.jeelowcode</groupId>
<version>${jeelowcode.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jeelowcode-exception</artifactId>
<name>${project.artifactId}</name>
<version>${jeelowcode.version}</version>
<packaging>jar</packaging>
<description> JeeLowCode低代码平台 - 自定义异常模块 </description>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,29 @@
package com.jeelowcode.framework.code;
/**
*/
public class JeeLowCodeErrorCode {
/**
* 错误码
*/
private final Integer code;
/**
* 错误提示
*/
private final String msg;
public JeeLowCodeErrorCode(Integer code, String message) {
this.code = code;
this.msg = message;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}

View File

@@ -0,0 +1,38 @@
package com.jeelowcode.framework.constants;
import com.jeelowcode.framework.code.*;
import com.jeelowcode.framework.code.JeeLowCodeErrorCode;
/**
* 框架 错误码枚举类
* <p>
* 低代码 系统,使用 6-001-000-000 段
*/
public interface FrameErrorCodeConstants {
JeeLowCodeErrorCode SUCCESS = new JeeLowCodeErrorCode(0, "成功");
// ========== 框架相关 6-001-001-000 ============
JeeLowCodeErrorCode FRAME_PARAM_ERROR = new JeeLowCodeErrorCode(1_600_001_000, "系统开小差了");
JeeLowCodeErrorCode FRAME_NOT_ROLE_ERROR = new JeeLowCodeErrorCode(1_600_011_000, "无权限访问");
JeeLowCodeErrorCode FRAM_SELF_ERROR = new JeeLowCodeErrorCode(1_600_001_001, "自定义错误");
JeeLowCodeErrorCode FRAME_ISNOT_ADMINISTOR_ERROR = new JeeLowCodeErrorCode(1_600_001_002, "该功能只允许超级管理员操作");
JeeLowCodeErrorCode FRAME_OP_ERROR = new JeeLowCodeErrorCode(1_600_001_003, "操作失败");
JeeLowCodeErrorCode FRAME_ENHANCE_ERROR = new JeeLowCodeErrorCode(1_600_001_004, "增强已锁");
JeeLowCodeErrorCode FRAME_LOGIN_VIEW_ERROR = new JeeLowCodeErrorCode(1_600_001_005,"用户未登录");
JeeLowCodeErrorCode FRAME_TABLE_NAME_ILLEGAL= new JeeLowCodeErrorCode(1_600_001_006,"表名称不符合");
JeeLowCodeErrorCode FRAME_DESFORM_CORE_ISEXIT_ERROR = new JeeLowCodeErrorCode(1_600_001_07,"编码已存在");
JeeLowCodeErrorCode FRAME_PARAM_NULL_ERROR = new JeeLowCodeErrorCode(1_600_001_08,"参数不允许为空");
JeeLowCodeErrorCode FRAME_MAIN_TABLE_NOT = new JeeLowCodeErrorCode(1_600_001_09,"绑定的主表不存在");
JeeLowCodeErrorCode FRAME_IMPORT_EXCEL = new JeeLowCodeErrorCode(1_600_001_10,"请使用正确的Excel表导入数据");
JeeLowCodeErrorCode FRAME_DESFORM_IS_OPEN = new JeeLowCodeErrorCode(1_600_001_11,"该表单需要登录才可访问");
JeeLowCodeErrorCode FRAME_IMPORT_EXCEL_NOT_DATA = new JeeLowCodeErrorCode(1_600_001_12,"数据不存在");
JeeLowCodeErrorCode FRAME_IMPORT_EXCEL_STATE_ERROR = new JeeLowCodeErrorCode(1_600_001_13,"表状态不符合");
JeeLowCodeErrorCode FRAME_CORE_EXIT = new JeeLowCodeErrorCode(1_600_001_14,"编号已存在");
}

View File

@@ -0,0 +1,11 @@
package com.jeelowcode.framework.exception;
/**
* 自定义异常
*/
public class JeeLowCodeException extends RuntimeException {
public JeeLowCodeException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,31 @@
package com.jeelowcode.framework.exception;
import cn.hutool.json.JSONUtil;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义异常-详细错误信息
*/
public class JeeLowCodeMoreException extends RuntimeException {
public JeeLowCodeMoreException(String title,String message) {
if(message==null){
message="";
}
if(message.startsWith("\r\n")){
message=message.substring(2);
}
Map<String,String> map=new HashMap<>();
map.put("title",title);
map.put("e",message);
String jsonStr = JSONUtil.toJsonStr(map);
throw new JeeLowCodeMoreException(jsonStr);
}
private JeeLowCodeMoreException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jeelowcode-framework</artifactId>
<groupId>com.jeelowcode</groupId>
<version>${jeelowcode.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jeelowcode-global</artifactId>
<name>${project.artifactId}</name>
<version>${jeelowcode.version}</version>
<packaging>jar</packaging>
<description> JeeLowCode低代码平台 - 全局常量 </description>
<dependencies>
</dependencies>
</project>

View File

@@ -0,0 +1,17 @@
package com.jeelowcode.framework.global;
/**
* 全局公共
*/
public interface JeeLowCodeBaseConstant {
//多数据源
String DS_JEELOWCODE="jeelowcode";//低代码
String DS_MASTER="master";//主服务
String REQUEST_URL_START="/jeelowcode";//公共请求
String BASE_PACKAGES="com.jeelowcode";//公共包名称
String BASE_PACKAGES_CODE=BASE_PACKAGES+".core";//核心包名
String BASE_PACKAGES_MODULE = BASE_PACKAGES+".module";//个人业务模块
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jeelowcode-framework</artifactId>
<groupId>com.jeelowcode</groupId>
<version>${jeelowcode.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jeelowcode-plus</artifactId>
<name>${project.artifactId}</name>
<version>${jeelowcode.version}</version>
<packaging>jar</packaging>
<description>JeeLowCode低代码平台 - Sql封装</description>
<dependencies>
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>jeelowcode-utils</artifactId>
</dependency>
<dependency>
<groupId>com.jeelowcode</groupId>
<artifactId>jeelowcode-tenant</artifactId>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,427 @@
package com.jeelowcode.framework.plus;
import com.jeelowcode.framework.plus.build.ISQLExpression;
import com.jeelowcode.framework.plus.build.build.AndExpression;
import com.jeelowcode.framework.plus.build.build.ddl.*;
import com.jeelowcode.framework.plus.build.build.dql.BuildSelect;
import com.jeelowcode.framework.plus.build.buildmodel.SQLInterpretContext;
import com.jeelowcode.framework.plus.build.buildmodel.ddl.*;
import com.jeelowcode.framework.plus.build.buildmodel.dql.SqlInfoPublicModel;
import com.jeelowcode.framework.plus.build.buildmodel.wrapper.SqlInfoDeleteWrapper;
import com.jeelowcode.framework.plus.build.buildmodel.wrapper.SqlInfoInsertWrapper;
import com.jeelowcode.framework.plus.build.buildmodel.wrapper.SqlInfoQueryWrapper;
import com.jeelowcode.framework.plus.build.buildmodel.wrapper.SqlInfoUpdateWrapper;
import com.jeelowcode.framework.plus.chain.entity.TenantIdFilterEntity;
import com.jeelowcode.framework.plus.entity.DbColunmTypesEntity;
import com.jeelowcode.framework.plus.entity.FieldModel;
import com.jeelowcode.framework.plus.entity.SqlInfoEntity;
import com.jeelowcode.framework.plus.enums.DbTypeEnum;
import com.jeelowcode.framework.plus.flyweight.FlyweightDBFactory;
import com.jeelowcode.framework.plus.template.AbstractDbTemplate;
import com.jeelowcode.framework.plus.core.model.SqlFormatModel;
import com.jeelowcode.framework.utils.adapter.IJeeLowCodeAdapter;
import com.jeelowcode.framework.utils.enums.YNEnum;
import com.jeelowcode.framework.utils.tool.spring.SpringUtils;
import com.jeelowcode.framework.utils.utils.FuncBase;
import net.sf.jsqlparser.JSQLParserException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class SqlHelper {
//数据库类型
//private static String dbType="postgresql";
//private static String dbSchemaName="db_jeelowcode";
private static String dbType="mysql";
private static String dbSchemaName="db_jeelowcode";
//数据库类型
//private static String dbType="oracle";
//private static String dbSchemaName="c##jee_test";
public static void setDbType(String dbType) {
SqlHelper.dbType = dbType;
}
public static void setDbSchemaName(String dbSchemaName) {
SqlHelper.dbSchemaName = dbSchemaName;
}
private static AbstractDbTemplate getAbstractDbTemplate() {
DbTypeEnum dbTypeEnum = DbTypeEnum.getByDbCode(dbType);
AbstractDbTemplate template = FlyweightDBFactory.getDbType(dbTypeEnum);
return template;
}
public static DbColunmTypesEntity getDbColunmTypesEntity() {
DbTypeEnum dbTypeEnum = DbTypeEnum.getByDbCode(dbType);
AbstractDbTemplate template = FlyweightDBFactory.getDbType(dbTypeEnum);
SqlInfoEntity sqlInfoEntity=new SqlInfoEntity();
SQLInterpretContext context =new SQLInterpretContext(dbSchemaName);
sqlInfoEntity.setContext(context);
template.operation(sqlInfoEntity);
return sqlInfoEntity.getContext().getDbColunmTypes();
}
//获取租户权限
private static TenantIdFilterEntity getTenantIdFilterEntity() {
//处理租户
IJeeLowCodeAdapter adapter = SpringUtils.getBean(IJeeLowCodeAdapter.class);
Set<String> tenantIgnoreTable = adapter.getTenantIgnoreTable();//忽略的表
Set<String> tenantIgnoreUrl = adapter.getTenantIgnoreUrl();//忽略的表
boolean tenantEnable = adapter.getTenantEnable();
TenantIdFilterEntity tenantIdFilterEntity = new TenantIdFilterEntity();
tenantIdFilterEntity.setEnable(tenantEnable);
tenantIdFilterEntity.setTenantId(adapter.getTenantId());
tenantIdFilterEntity.setIgnore(adapter.getTenantIsIgnore());
tenantIdFilterEntity.setIgnoreTables(tenantIgnoreTable);
tenantIdFilterEntity.setIgnoreUrl(tenantIgnoreUrl);
return tenantIdFilterEntity;
}
//新建表
public static List<String> createTable(String tableName, String tableAlias, List<FieldModel> fieldModelList) throws JSQLParserException {
/**
* 创建表分三步
* 1.创建简单表 id varchar(10) not null
* 2.处理备注
* 3.处理主键
*/
//1.创建表
SqlInfoCreateModel<BuildCreateTable> createModel = SqlInfoCreateModel.builder()
.setTableName(tableName)
.setTableOptions(fieldModelList)
.build();
//2.处理字段备注
SqlInfoCommentModel<BuildComment> commentmodel = SqlInfoCommentModel.builder()
.setTableName(tableName)
.setTableAlias(tableAlias)
.setfieldModelList(fieldModelList)
.build();
//3.处理主键
List<String> primaryKeyList = fieldModelList.stream()
.filter(fieldModel -> !FuncBase.isEmpty(fieldModel.getIsPrimaryKey()) && !FuncBase.equals(fieldModel.getIsPrimaryKey(), YNEnum.N.getCode()))
.map(FieldModel::getFieldCode)
.collect(Collectors.toList());
//3.处理主键
SqlInfoPrimaryKeyModel primaryKeyModel = SqlInfoPrimaryKeyModel.builder()
.setTableName(tableName)
.setPrimaryKey(primaryKeyList)
.build();
//构建SQL上下文
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setCreateModel(createModel);
context.setCommentModel(commentmodel);
context.setPrimaryKeyModel(primaryKeyModel);
//多个
AndExpression expression = new AndExpression(createModel.getSqlExpression(), commentmodel.getSqlExpression(),primaryKeyModel.getSqlExpression());
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, expression);
List<String> ddlList = sqlFormatModelList.stream()
.map(SqlFormatModel::getSql)
.collect(Collectors.toList());
return ddlList;
}
//增加列
public static List<String> addColumn(String tableName, FieldModel fieldModel) {
/**
* 增加列分两步
* 1.创建简单表
* 2.处理备注
*/
//1.创建表
SqlInfoAlterModel<BuildAlter> addColumnModel = SqlInfoAlterModel.builder()
.setTableName(tableName)
.buildAddColumn(fieldModel);
//2.处理主键
SqlInfoPrimaryKeyModel primaryKeyModel =null;
if(FuncBase.equals(fieldModel.getFieldCode(),"id")){
primaryKeyModel = SqlInfoPrimaryKeyModel.builder()
.setTableName(tableName)
.setPrimaryKey(FuncBase.toStrList("id"))
.build();
}
List<FieldModel> fieldModelList = new ArrayList<>();
fieldModelList.add(fieldModel);
SqlInfoCommentModel<BuildComment> commentmodel = SqlInfoCommentModel.builder()
.setTableName(tableName)
.setfieldModelList(fieldModelList)
.build();
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setAlterModel(addColumnModel);
context.setCommentModel(commentmodel);
context.setPrimaryKeyModel(primaryKeyModel);//主键
//多个
AndExpression expression =null;
if(FuncBase.isNotEmpty(context.getPrimaryKeyModel())){
expression = new AndExpression(addColumnModel.getSqlExpression(), commentmodel.getSqlExpression(),primaryKeyModel.getSqlExpression());
}else{
expression = new AndExpression(addColumnModel.getSqlExpression(), commentmodel.getSqlExpression());
}
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, expression);
List<String> ddlList = sqlFormatModelList.stream()
.map(SqlFormatModel::getSql)
.collect(Collectors.toList());
return ddlList;
}
//修改列
public static List<String> modifyColumnSql(String tableName, FieldModel fieldModel) {
/**
* 增加列分两步
* 1.创建简单表
* 2.处理备注
*/
//修改表
SqlInfoAlterModel<BuildAlter> modifyColumnModel = SqlInfoAlterModel.builder()
.setTableName(tableName)
.buildUpdateColum(fieldModel);
List<FieldModel> fieldModelList = new ArrayList<>();
fieldModelList.add(fieldModel);
SqlInfoCommentModel<BuildComment> commentModel = SqlInfoCommentModel.builder()
.setTableName(tableName)
.setfieldModelList(fieldModelList)
.build();
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setAlterModel(modifyColumnModel);
context.setCommentModel(commentModel);
//多个
AndExpression expression = new AndExpression(modifyColumnModel.getSqlExpression(), commentModel.getSqlExpression());
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, expression);
List<String> ddlList = sqlFormatModelList.stream()
.map(SqlFormatModel::getSql)
.collect(Collectors.toList());
return ddlList;
}
//删除列
public static String getDropColumnSql(String tableName,String clumn) {
SqlInfoDropModel<BuildDrop> dropModel = SqlInfoDropModel.builder()
.buildDropColumn(tableName, clumn);
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setDropModel(dropModel);
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, dropModel.getSqlExpression());
return sqlFormatModelList.get(0).getSql();
}
//删除表
public static String getDropTableSql(String tableName) {
SqlInfoDropModel<BuildDrop> dropTableModel = SqlInfoDropModel.builder()
.buildDropTable(tableName);
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setDropModel(dropTableModel);
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, dropTableModel.getSqlExpression());
return sqlFormatModelList.get(0).getSql();
}
//创建索引
public static String createIndex(String tableName,String indexName,String indexType,List<String> fieldList) {
SqlInfoCreateIndexModel<BuildCreateIndex> createIndexModel = SqlInfoCreateIndexModel.builder()
.setTableName(tableName)
.setIndexName(indexName)
.setIndexType(indexType)
.setIndexFieldCodeList(fieldList)
.build();
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setCreateIndexModel(createIndexModel);
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, createIndexModel.getSqlExpression());
return sqlFormatModelList.get(0).getSql();
}
//删除索引
public static String getDropIndex(String tableName,String indexName) {
SqlInfoDropModel<BuildDrop> dropModel = SqlInfoDropModel.builder()
.buildDropIndex(tableName, indexName);
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setDropModel(dropModel);
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, dropModel.getSqlExpression());
return sqlFormatModelList.get(0).getSql();
}
//校验索引是否存在
public static String getIndexExistSql(String tableName,String indexName){
SqlInfoDdlModel<BuildDdl> ddlModel = SqlInfoDdlModel.builder().buildIndexExist(tableName, indexName);
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setDdlModel(ddlModel);
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, ddlModel.getSqlExpression());
return sqlFormatModelList.get(0).getSql();
}
//获取某一个表所有索引
public static String getIndexAllByTableSql(String tableName){
SqlInfoDdlModel<BuildDdl> ddlModel = SqlInfoDdlModel.builder().buildIndexAllByTable(tableName);
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setDdlModel(ddlModel);
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, ddlModel.getSqlExpression());
return sqlFormatModelList.get(0).getSql();
}
//校验表是否存在
public static String getTableExistSql(String tableName){
SqlInfoDdlModel<BuildDdl> ddlModel = SqlInfoDdlModel.builder().buildTableExist(tableName);
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setDdlModel(ddlModel);
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, ddlModel.getSqlExpression());
return sqlFormatModelList.get(0).getSql();
}
//校验表是否存在
public static String getAllTableNameSql(){
SqlInfoDdlModel<BuildDdl> ddlModel = SqlInfoDdlModel.builder().buildGetAllTableName();
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setDdlModel(ddlModel);
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, ddlModel.getSqlExpression());
return sqlFormatModelList.get(0).getSql();
}
//获取表字段备注
public static String getTableFieldComment(String tableName){
SqlInfoDdlModel<BuildDdl> ddlModel = SqlInfoDdlModel.builder().buildFieldComment(tableName);
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setDdlModel(ddlModel);
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, ddlModel.getSqlExpression());
return sqlFormatModelList.get(0).getSql();
}
//获取表备注
public static String getTableComment(String tableName){
SqlInfoDdlModel<BuildDdl> ddlModel = SqlInfoDdlModel.builder().buildTableComment(tableName);
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setDdlModel(ddlModel);
List<SqlFormatModel> sqlFormatModelList = ddlExpression(context, ddlModel.getSqlExpression());
return sqlFormatModelList.get(0).getSql();
}
//--------------dql---------------
//通用sql
public static String getPublicSql(String sql){
SqlInfoPublicModel<BuildSelect> publicModel = SqlInfoPublicModel.builder().setSql(sql).build();
SQLInterpretContext context = new SQLInterpretContext(dbSchemaName);
context.setPublicModel(publicModel);
List<SqlFormatModel> sqlFormatModelList = sqlExpression(context, publicModel.getSqlExpression());
return sqlFormatModelList.get(0).getSql();
}
//查询数据
public static SqlInfoQueryWrapper.Wrapper getQueryWrapper(){
SqlInfoQueryWrapper.Wrapper wrapper = SqlInfoQueryWrapper.wrapper(dbSchemaName);
return wrapper;
}
//新增数据
public static SqlInfoInsertWrapper.Wrapper getInsertWrapper(){
SqlInfoInsertWrapper.Wrapper wrapper = SqlInfoInsertWrapper.wrapper(dbSchemaName);
return wrapper;
}
//修改数据
public static SqlInfoUpdateWrapper.Wrapper getUpdateWrapper(){
SqlInfoUpdateWrapper.Wrapper wrapper = SqlInfoUpdateWrapper.wrapper(dbSchemaName);
return wrapper;
}
//删除数据
public static SqlInfoDeleteWrapper.Wrapper getDeleteWrapper(){
SqlInfoDeleteWrapper.Wrapper wrapper = SqlInfoDeleteWrapper.wrapper(dbSchemaName);
return wrapper;
}
//自定义 sql
public static List<SqlFormatModel> sqlExpression(SQLInterpretContext context, ISQLExpression expression) {
Boolean tenantIgnore = context.getTenantIgnore();
TenantIdFilterEntity tenantIdFilterEntity =null;
if(FuncBase.isEmpty(tenantIgnore) || !tenantIgnore){//不做多租户处理
tenantIdFilterEntity =getTenantIdFilterEntity();
}
SqlInfoEntity sqlInfoEntity = new SqlInfoEntity();
List<SqlFormatModel> sqlModelList = sqlExpression(sqlInfoEntity,context, expression, tenantIdFilterEntity);
return sqlModelList;
}
//自定义 ddl
public static List<SqlFormatModel> ddlExpression(SQLInterpretContext context, ISQLExpression expression) {
SqlInfoEntity sqlInfoEntity = new SqlInfoEntity();
List<SqlFormatModel> sqlModelList = sqlExpression(sqlInfoEntity,context, expression, null);
return sqlModelList;
}
//自定义 只需要使用sql解析
public static List<SqlFormatModel> parserExpression(SQLInterpretContext context, ISQLExpression expression) {
SqlInfoEntity sqlInfoEntity = new SqlInfoEntity();
sqlInfoEntity.setSqlParserLevel(2);
List<SqlFormatModel> sqlModelList = sqlExpression(sqlInfoEntity,context, expression, null);
return sqlModelList;
}
private static List<SqlFormatModel> sqlExpression(SqlInfoEntity sqlInfoEntity,SQLInterpretContext context, ISQLExpression expression, TenantIdFilterEntity tenantIdFilterEntity) {
AbstractDbTemplate template = getAbstractDbTemplate();
sqlInfoEntity.setContext(context);
sqlInfoEntity.setSqlExpression(expression);
if (FuncBase.isNotEmpty(tenantIdFilterEntity)) {
sqlInfoEntity.setTenantIdFilterEntity(tenantIdFilterEntity);
}
template.operation(sqlInfoEntity);
List<SqlFormatModel> sqlModelList = sqlInfoEntity.getSqlModelList();
if (FuncBase.isEmpty(sqlModelList)) {
return sqlModelList;
}
sqlModelList.forEach(model->{
String sql = model.getSql();
if(sql.endsWith(";")){
System.out.println(sql);
}else{
System.out.println(sql+";");
}
});
return sqlModelList;
}
}

View File

@@ -0,0 +1,35 @@
package com.jeelowcode.framework.plus.chain;
import com.jeelowcode.framework.plus.entity.SqlInfoEntity;
/**
* 创建抽象的记录器类。
*/
public abstract class AbstractSQLParser {
public static int levelStandard = 2;//标准解析
public static int levelTenant = 1;//租户过滤
protected int level;
protected AbstractSQLParser nextSQLParse;//下一个节点解析
public void setNextSQLParse(AbstractSQLParser nextSQLParse) {
this.nextSQLParse = nextSQLParse;
}
public void processed(int level, SqlInfoEntity sqlInfoEntity) {
if (this.level <= level) {
sqlParse(sqlInfoEntity);
}
if (nextSQLParse != null) {
nextSQLParse.processed(level, sqlInfoEntity);
}
}
abstract protected void sqlParse(SqlInfoEntity sqlInfoEntity);
}

View File

@@ -0,0 +1,26 @@
package com.jeelowcode.framework.plus.chain;
public class SQLParserChain {
private AbstractSQLParser sqlParserChain;
public AbstractSQLParser getSqlParserChain() {
return sqlParserChain;
}
public void setSqlParserChain(AbstractSQLParser... sqlParserChains) {
sqlParserChain = sqlParserChains[0];//第一个
// 首先,检查是否有至少两个解析器,因为我们需要至少两个来设置链
if (sqlParserChains == null || sqlParserChains.length < 2) {
return;
}
// 遍历解析器数组,设置每个解析器的下一个解析器
for (int i = 0; i < sqlParserChains.length - 1; i++) {
sqlParserChains[i].setNextSQLParse(sqlParserChains[i + 1]);
}
}
}

View File

@@ -0,0 +1,51 @@
package com.jeelowcode.framework.plus.chain;
import com.jeelowcode.framework.plus.chain.parser.StandardSQLParser;
import com.jeelowcode.framework.plus.chain.parser.TenantSQLParser;
import com.jeelowcode.framework.plus.entity.SqlInfoEntity;
import com.jeelowcode.framework.utils.utils.FuncBase;
/**
* 使用责任链
*/
public class SQLParserChainClient {
//处理sql
public static void sqlParser(SqlInfoEntity sqlInfoEntity){
//标准解析 -> 数据权限 -> 租户权限
//标准SQL
AbstractSQLParser standardSQLParser = new StandardSQLParser(AbstractSQLParser.levelStandard);
//租户权限
AbstractSQLParser tenantSQLParser = new TenantSQLParser(AbstractSQLParser.levelTenant);
//单独要某一个
Integer sqlParserLevel = sqlInfoEntity.getSqlParserLevel();
if(FuncBase.isNotEmpty(sqlParserLevel)){
switch (sqlParserLevel){
case 2:
standardSQLParser.processed(AbstractSQLParser.levelStandard,sqlInfoEntity);
break;
case 1:
tenantSQLParser.processed(AbstractSQLParser.levelTenant,sqlInfoEntity);
break;
}
return;
}
SQLParserChain sqlParserChain=new SQLParserChain();
sqlParserChain.setSqlParserChain(standardSQLParser,tenantSQLParser);
AbstractSQLParser sqlParser = sqlParserChain.getSqlParserChain();
//先走标准sql解析
sqlParser.processed(AbstractSQLParser.levelStandard, sqlInfoEntity);
}
}

View File

@@ -0,0 +1,17 @@
package com.jeelowcode.framework.plus.chain.entity;
/**
* 数据权限
*/
public class DataPermissionFilterEntity {
private boolean enable;///是否启动
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
}

View File

@@ -0,0 +1,62 @@
package com.jeelowcode.framework.plus.chain.entity;
import java.util.Set;
/**
* 租户权限相关
*/
public class TenantIdFilterEntity {
private boolean enable;///是否启动
private Long tenantId;//租户id
private Set<String> ignoreTables; //不过滤的表
private Set<String> ignoreUrl;//不过滤的url
private boolean ignore;
public Long getTenantId() {
return tenantId;
}
public void setTenantId(Long tenantId) {
this.tenantId = tenantId;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
public Set<String> getIgnoreTables() {
return ignoreTables;
}
public void setIgnoreTables(Set<String> ignoreTables) {
this.ignoreTables = ignoreTables;
}
public boolean isIgnore() {
return ignore;
}
public void setIgnore(boolean ignore) {
this.ignore = ignore;
}
public Set<String> getIgnoreUrl() {
return ignoreUrl;
}
public void setIgnoreUrl(Set<String> ignoreUrl) {
this.ignoreUrl = ignoreUrl;
}
}

View File

@@ -0,0 +1,61 @@
package com.jeelowcode.framework.plus.chain.parser;
import com.jeelowcode.framework.plus.build.ISQLExpression;
import com.jeelowcode.framework.plus.build.buildmodel.SQLInterpretContext;
import com.jeelowcode.framework.plus.chain.AbstractSQLParser;
import com.jeelowcode.framework.plus.entity.DbColunmTypesEntity;
import com.jeelowcode.framework.plus.entity.SqlInfoEntity;
import com.jeelowcode.framework.plus.core.model.SqlFormatModel;
import com.jeelowcode.framework.utils.utils.FuncBase;
import java.util.ArrayList;
import java.util.List;
/**
* 标准sql解析
*/
public class StandardSQLParser extends AbstractSQLParser {
public StandardSQLParser(int level) {
this.level = level;
}
@Override
protected void sqlParse(SqlInfoEntity sqlInfoEntity) {
try {
ISQLExpression<SqlFormatModel> sqlExpression = sqlInfoEntity.getSqlExpression();
if(FuncBase.isEmpty(sqlExpression)){
return;
}
SQLInterpretContext context = sqlInfoEntity.getContext();
if(FuncBase.isEmpty(context)){
return;
}
DbColunmTypesEntity dbColunmTypes = context.getDbColunmTypes();
//处理数据库类型
context.setDbColunmTypes(dbColunmTypes);
Object obj =sqlExpression.interpret(context);
List<SqlFormatModel> sqlFormatModelList=new ArrayList<>();
if (FuncBase.isEmpty(obj)) {
return;
}
if(obj instanceof SqlFormatModel){
sqlFormatModelList.add((SqlFormatModel)obj);
}else if(obj instanceof List){
sqlFormatModelList.addAll((List<SqlFormatModel>)obj);
}
sqlInfoEntity.setSqlModelList(sqlFormatModelList);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,68 @@
package com.jeelowcode.framework.plus.chain.parser;
import com.jeelowcode.framework.plus.chain.AbstractSQLParser;
import com.jeelowcode.framework.plus.chain.entity.TenantIdFilterEntity;
import com.jeelowcode.framework.plus.entity.SqlInfoEntity;
import com.jeelowcode.framework.plus.core.model.SqlFormatModel;
import com.jeelowcode.framework.tenant.utils.JeeLowCodeTenantUtils;
import com.jeelowcode.framework.utils.utils.FuncBase;
import java.util.Set;
/**
* 多租户
*/
public class TenantSQLParser extends AbstractSQLParser {
public TenantSQLParser(int level) {
this.level=level;
}
@Override
protected void sqlParse(SqlInfoEntity sqlInfoEntity) {
try{
TenantIdFilterEntity tenantIdFilterEntity = sqlInfoEntity.getTenantIdFilterEntity();
if(FuncBase.isEmpty(tenantIdFilterEntity)){
return;
}
SqlFormatModel sqlFormatModel = sqlInfoEntity.getSqlModelList().get(0);
if(FuncBase.isEmpty(sqlFormatModel)){
return;
}
String sql = sqlFormatModel.getSql();
Long tenantId = tenantIdFilterEntity.getTenantId();
if(FuncBase.isEmpty(tenantId)){
return;
}
boolean enable = tenantIdFilterEntity.isEnable();
if(!enable){//没有启动
return;
}
boolean ignore = tenantIdFilterEntity.isIgnore();
Set<String> ignoreTables = tenantIdFilterEntity.getIgnoreTables();
Set<String> ignoreUrl = tenantIdFilterEntity.getIgnoreUrl();
JeeLowCodeTenantUtils.initIgnoreTables(ignoreTables);//初始化
JeeLowCodeTenantUtils.initIgnoreUrl(ignoreUrl);
boolean threadIgnore = JeeLowCodeTenantUtils.isIgnore();
if(!threadIgnore){
JeeLowCodeTenantUtils.setIgnore(ignore);//本次
}
JeeLowCodeTenantUtils.setTenantId(tenantId);
String newSql = JeeLowCodeTenantUtils.parseTenantSql(sql);
sqlFormatModel.setSql(newSql);
}catch (Exception e){
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,296 @@
package com.jeelowcode.framework.plus.component;
import com.jeelowcode.framework.exception.JeeLowCodeException;
import com.jeelowcode.framework.plus.SqlHelper;
import com.jeelowcode.framework.plus.entity.DbColunmTypesEntity;
import com.jeelowcode.framework.plus.entity.FieldModel;
import com.jeelowcode.framework.plus.utils.PlusUtils;
import com.jeelowcode.framework.utils.enums.JeeLowCodeFieldTypeEnum;
import com.jeelowcode.framework.utils.enums.YNEnum;
import com.jeelowcode.framework.utils.utils.FuncBase;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;
@Component
@Order(2) // 指定DbManager的初始化顺序确保它在SqlSessionFactory之后初始化
@DependsOn("sqlSessionFactory") // 指定DbManager依赖于SqlSessionFactory
public class DbManager {
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Bean
public void initDbManager(){
//初始化type
initDbType();
//初始化schema
initDbSchema();
}
/**
* 获取数据库的元数据
* @param primaryKeyResultSet
* @param set
* @return
*/
public Map<String, FieldModel> formatMetaData(ResultSet primaryKeyResultSet, ResultSet set,Map<String,String> remarkMap) {
Map<String,FieldModel> resultMap=new LinkedHashMap<>();
try{
//获取主键
Set<String> primaryKeySet=new HashSet<>();
while (primaryKeyResultSet.next()) {
String columnName = primaryKeyResultSet.getString("COLUMN_NAME");
if(FuncBase.isEmpty(columnName)){
continue;
}
primaryKeySet.add(columnName.toLowerCase());
}
DbColunmTypesEntity dbColunmTypesEntity = SqlHelper.getDbColunmTypesEntity();
while (set.next()) {
FieldModel fieldModel=new FieldModel();
String fieldCode = set.getString("COLUMN_NAME").toLowerCase();
fieldModel.setFieldCode(fieldCode);//字段
if(FuncBase.isNotEmpty(remarkMap)){
fieldModel.setFieldName(remarkMap.get(fieldCode));//字段名称
}else{
String fieldName = set.getString("REMARKS");
fieldModel.setFieldName(fieldName);//字段名称
}
Integer fieldLen = set.getInt("COLUMN_SIZE");//大小
fieldModel.setFieldLen(fieldLen);//长度
Integer fieldPointLen = set.getInt("DECIMAL_DIGITS");//小数
fieldModel.setFieldPointLen(FuncBase.isEmpty(fieldPointLen)?0:fieldPointLen);
String fieldDefaultVal = set.getString("COLUMN_DEF");
fieldModel.setFieldDefaultVal(fieldDefaultVal);//字段默认值
String dbType = set.getString("TYPE_NAME");
List<DbColunmTypesEntity.TypeEntity> fieldTypeList = PlusUtils.dbType2JeelowCodeTypeList(dbType, dbColunmTypesEntity);
List<JeeLowCodeFieldTypeEnum> fieldTypeEnumList=new ArrayList<>();
for(DbColunmTypesEntity.TypeEntity entityType:fieldTypeList){
JeeLowCodeFieldTypeEnum fieldType = JeeLowCodeFieldTypeEnum.getByFieldType(entityType.getJeeLowCodeType());
fieldTypeEnumList.add(fieldType);
}
fieldModel.setJavaFieldTypeList(fieldTypeEnumList);
fieldModel.setMaxFieldLen(fieldTypeList.get(0).getDbMaxLen());//数据库类型一样,长度也是一样
fieldModel.setMaxPointLen(fieldTypeList.get(0).getDbMaxPointLen());//小数位
fieldModel.setIsPrimaryKey(primaryKeySet.contains(fieldCode)? YNEnum.Y.getCode():YNEnum.N.getCode());//是否是主键
String isNull = set.getInt("NULLABLE") == 1 ? YNEnum.Y.getCode() : YNEnum.N.getCode();//是
fieldModel.setIsNull(isNull);
resultMap.put(fieldCode,fieldModel);
}
}catch (Exception e){
e.printStackTrace();
}
return resultMap;
}
/**
* 获取现有数据库的元数据
* @param tableName
* @return
*/
public Map<String, FieldModel> getDbColumnMetaMap(String tableName,Map<String,String> remarkMap) {
DbColunmTypesEntity dbColunmTypesEntity = SqlHelper.getDbColunmTypesEntity();
Boolean upperFlag = dbColunmTypesEntity.getUpperFlag();
if(FuncBase.isNotEmpty(upperFlag) && upperFlag){
tableName=tableName.toUpperCase();
}
try (SqlSession sqlSession = sqlSessionFactory.openSession();
Connection connection = sqlSession.getConnection()) {
DatabaseMetaData metaData = connection.getMetaData();
//获取主键
ResultSet primaryKeyResultSet = metaData.getPrimaryKeys(connection.getCatalog(), connection.getSchema(), tableName);
ResultSet set = metaData.getColumns(connection.getCatalog(), connection.getSchema(), tableName, "%");
Map<String, FieldModel> metaDataMap = formatMetaData(primaryKeyResultSet, set,remarkMap);
return metaDataMap;
} catch (SQLException e) {
throw new JeeLowCodeException(e.getMessage());
}
}
/**
* 获取新增列
* @param tableName
* @param fieldModelList
* @param columnMetaMap
* @return
*/
public List<String> getAddColumnDdl(String tableName, List<FieldModel> fieldModelList, Map<String, FieldModel> columnMetaMap) {
List<String> ddlList = new ArrayList<>();
for (FieldModel fieldModel : fieldModelList) {
String fieldCode = fieldModel.getFieldCode();
if (columnMetaMap.containsKey(fieldCode.toLowerCase())) {//已存在,不走新增
continue;
}
List<String> tmpDdlList = SqlHelper.addColumn(tableName, fieldModel);
ddlList.addAll(tmpDdlList);
}
return ddlList;
}
/**
* 获取修改ddl列表
* @param tableName
* @param fieldModelList
* @param columnMetaMap
* @return
*/
public List<String> getUpdateColumnDdl(String tableName, List<FieldModel> fieldModelList, Map<String, FieldModel> columnMetaMap) {
List<String> ddlList = new ArrayList<>();
for (FieldModel fieldModel : fieldModelList) {
String fieldCode = fieldModel.getFieldCode().toLowerCase();
Integer fieldLen = fieldModel.getFieldLen();
Integer fieldPointLen = fieldModel.getFieldPointLen();
if (!columnMetaMap.containsKey(fieldCode)) {//不存在
continue;
}
//数据库的
FieldModel dbFieldModel = columnMetaMap.get(fieldCode);
Integer maxFieldLen = dbFieldModel.getMaxFieldLen();//有最大长度
Integer maxPointLen = dbFieldModel.getMaxPointLen();//小数位
boolean lenFlag=false;//整型位长度
boolean pointLenFlag=false;//小数位长度
if(FuncBase.isNotEmpty(maxFieldLen) && maxFieldLen>0){
if(fieldLen>maxFieldLen){//保证和当前输入的一样长度
dbFieldModel.setFieldLen(fieldLen);
}
if(FuncBase.equals(dbFieldModel.getFieldPointLen(), fieldLen)){
lenFlag=true;
}
//校验小数位长度
if(FuncBase.isNotEmpty(maxPointLen)){//需要对长度进行校验
if(fieldPointLen>maxPointLen){//保证和当前输入的一样长度下面则是true
dbFieldModel.setMaxPointLen(maxPointLen);
}
if(FuncBase.equals(dbFieldModel.getFieldPointLen(), fieldPointLen)){
pointLenFlag=true;
}
}else{
pointLenFlag=true;
}
}else{//0说明不要对长度进行校验设置和输入的一样就可以了
lenFlag=true;//不用对长度进行校验
pointLenFlag=true;
}
fieldModel.setDbNowIsNull(dbFieldModel.getIsNull());//当前状态的
boolean typeFlag=false;
List<JeeLowCodeFieldTypeEnum> fieldTypeList = dbFieldModel.getJavaFieldTypeList();
for(JeeLowCodeFieldTypeEnum fieldTypeEnum:fieldTypeList){
if(FuncBase.equals(fieldTypeEnum, fieldModel.getFieldType())){
typeFlag=true;//一样
break;
}
}
//判断是否一致
if (!FuncBase.equals(dbFieldModel.getFieldName(), fieldModel.getFieldName()) ||
!lenFlag ||
!pointLenFlag ||
!FuncBase.equals(dbFieldModel.getFieldName(), fieldModel.getFieldName()) ||
!typeFlag ||
!FuncBase.equals(dbFieldModel.getIsPrimaryKey(), fieldModel.getIsPrimaryKey()) ||
!FuncBase.equals(dbFieldModel.getIsNull(), fieldModel.getIsNull())) {
List<String> tmpDdlList = SqlHelper.modifyColumnSql(tableName, fieldModel);
ddlList.addAll(tmpDdlList);
}
}
return ddlList;
}
/**
* 获取删除ddl列表
* @param tableName
* @param fieldModelList
* @param columnMetaMap
* @return
*/
public List<String> getDropColumnDdl(String tableName, List<FieldModel> fieldModelList, Map<String, FieldModel> columnMetaMap) {
fieldModelList.forEach(fieldModel -> {
fieldModel.setFieldCode(fieldModel.getFieldCode().toLowerCase());
});
Set<String> fieldCodeSet = fieldModelList.stream()
.map(FieldModel::getFieldCode)
.collect(Collectors.toCollection(LinkedHashSet::new));
List<String> ddlList = columnMetaMap.entrySet().stream()
.filter(entry -> !fieldCodeSet.contains(entry.getKey()))
.map(entry ->SqlHelper.getDropColumnSql(tableName,entry.getKey()))
.collect(Collectors.toList());
return ddlList;
}
private void initDbType(){
String dbType="mysql";
try (SqlSession sqlSession = sqlSessionFactory.openSession();
Connection connection = sqlSession.getConnection()) {
DatabaseMetaData metaData = connection.getMetaData();
String databaseProductName = metaData.getDatabaseProductName();
dbType = databaseProductName.toLowerCase();
}catch (Exception e){
throw new JeeLowCodeException("获取数据库类型失败");
}
SqlHelper.setDbType(dbType);
}
private void initDbSchema(){
try (SqlSession sqlSession = sqlSessionFactory.openSession();
Connection connection = sqlSession.getConnection()) {
String dbSchemaName = connection.getCatalog();
if(FuncBase.isEmpty(dbSchemaName)){
dbSchemaName = connection.getSchema();
}
if(FuncBase.isEmpty(dbSchemaName)){
throw new JeeLowCodeException("获取数据库 schema 失败");
}
SqlHelper.setDbSchemaName(dbSchemaName);
}catch (Exception e){
throw new JeeLowCodeException("获取数据库 schema 失败");
}
}
}

View File

@@ -0,0 +1,163 @@
package com.jeelowcode.framework.plus.component;
import com.jeelowcode.framework.global.JeeLowCodeBaseConstant;
import com.jeelowcode.framework.plus.SqlHelper;
import com.jeelowcode.framework.utils.utils.FuncBase;
import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static com.jeelowcode.framework.global.JeeLowCodeBaseConstant.BASE_PACKAGES_CODE;
@Component
@Intercepts({@Signature(
type = Executor.class,
method = "update",
args = {MappedStatement.class, Object.class}
), @Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
public class JeelowCodePlusInterceptor implements Interceptor {
private List<String> getSkipMapperList(){//todo 后面需要优化
String SKIP_MAPPER_CODE = BASE_PACKAGES_CODE+".framework.mapper";//不做租户拦截的mapper
List<String> skipMapperList=new ArrayList<>();
skipMapperList.add(SKIP_MAPPER_CODE);
return skipMapperList;
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget();
Object[] args = invocation.getArgs();
if (!(target instanceof Executor)) {
return invocation.proceed();
}
Executor executor = (Executor) target;
Object parameter = args[1];
boolean isUpdate = args.length == 2;
MappedStatement ms = (MappedStatement) args[0];
String id = ms.getId();
List<String> skipMapperList = getSkipMapperList();
for(String skipMapper:skipMapperList){
if(id.startsWith(skipMapper)){
return invocation.proceed();
}
}
if (!id.startsWith(BASE_PACKAGES_CODE)) {//直接跳过,如果不是低代码平台,则直接跳过
return invocation.proceed();
}
if (!isUpdate && ms.getSqlCommandType() == SqlCommandType.SELECT) {//查询相关
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler resultHandler = (ResultHandler) args[3];
BoundSql boundSql;
if (args.length == 4) {
boundSql = ms.getBoundSql(parameter);
} else {
boundSql = (BoundSql) args[5];
}
String publicSql = SqlHelper.getPublicSql(boundSql.getSql());
Object additionalParametersObj = boundSql.getAdditionalParameters();
boundSql = new BoundSql(ms.getConfiguration(), publicSql, boundSql.getParameterMappings(), boundSql.getParameterObject());
if (FuncBase.isNotEmpty(additionalParametersObj)) {
Map<String,Object> additionalParameters = (Map<String,Object>) additionalParametersObj;
Iterator<Map.Entry<String, Object>> iterator = additionalParameters.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> next = iterator.next();
String key = next.getKey();
Object value = next.getValue();
boundSql.setAdditionalParameter(key, value);
}
}
CacheKey cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
List<Object> query = null;
try {
query = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
} catch (Exception e) {
e.printStackTrace();
}
return query;
}
if (isUpdate) {
SqlSource sqlSource = ms.getSqlSource();
try {
BoundSql boundSql = sqlSource.getBoundSql(parameter);
String publicSql = SqlHelper.getPublicSql(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
SqlSource newSqlSource = new StaticSqlSource(ms.getConfiguration(), publicSql, parameterMappings);
MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
this.copyBuilder(builder, ms);
MappedStatement build = builder.build();
return executor.update(build, parameter);
} catch (Exception e) {
//e.printStackTrace();
}
}
return invocation.proceed();
}
//复制属性
private void copyBuilder(MappedStatement.Builder builder, MappedStatement ms) {
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.timeout(ms.getTimeout());
builder.statementType(ms.getStatementType());
builder.resultSetType(ms.getResultSetType());
builder.cache(ms.getCache());
builder.parameterMap(ms.getParameterMap());
builder.resultMaps(ms.getResultMaps());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
builder.resultOrdered(ms.isResultOrdered());
builder.keyGenerator(ms.getKeyGenerator());
if (FuncBase.isNotEmpty(ms.getKeyProperties())) {
builder.keyProperty(ms.getKeyProperties()[0]);
}
if (FuncBase.isNotEmpty(ms.getKeyColumns())) {
builder.keyColumn(ms.getKeyColumns()[0]);
}
builder.databaseId(ms.getDatabaseId());
builder.lang(ms.getLang());
if (FuncBase.isNotEmpty(ms.getResultSets())) {
builder.resultSets(ms.getResultSets()[0]);
}
builder.dirtySelect(ms.isDirtySelect());
}
}

View File

@@ -0,0 +1,753 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.conditions;
import com.jeelowcode.framework.plus.core.conditions.interfaces.Compare;
import com.jeelowcode.framework.plus.core.conditions.interfaces.Func;
import com.jeelowcode.framework.plus.core.conditions.interfaces.Join;
import com.jeelowcode.framework.plus.core.conditions.interfaces.Nested;
import com.jeelowcode.framework.plus.core.conditions.segments.ColumnSegment;
import com.jeelowcode.framework.plus.core.conditions.segments.MergeSegments;
import com.jeelowcode.framework.plus.core.enums.SqlKeyword;
import com.jeelowcode.framework.plus.core.enums.SqlLike;
import com.jeelowcode.framework.plus.core.enums.WrapperKeyword;
import com.jeelowcode.framework.plus.core.toolkit.*;
import com.jeelowcode.framework.plus.core.toolkit.sql.SqlScriptUtils;
import com.jeelowcode.framework.plus.core.toolkit.sql.SqlUtils;
import com.jeelowcode.framework.utils.utils.FuncBase;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.stream.Collectors.joining;
/**
* 查询条件封装
*
* @author hubin miemie HCL
* @since 2017-05-26
*/
@SuppressWarnings({"unchecked"})
public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T, R, Children>> extends Wrapper<T>
implements Compare<Children, R>, Nested<Children, Children>, Join<Children>, Func<Children, R> {
/**
* 占位符
*/
protected final Children typedThis = (Children) this;
/**
* 必要度量
*/
protected AtomicInteger paramNameSeq;
protected Map<String, Object> paramNameValuePairs;
/**
* 其他
*/
protected SharedString paramAlias;
protected SharedString lastSql;
/**
* SQL注释
*/
protected SharedString sqlComment;
/**
* SQL起始语句
*/
protected SharedString sqlFirst;
/**
* 数据库表映射实体类
*/
private T entity;
protected MergeSegments expression;
/**
* 实体类型(主要用于确定泛型以及取TableInfo缓存)
*/
private Class<T> entityClass;
@Override
public T getEntity() {
return entity;
}
public Children setEntity(T entity) {
this.entity = entity;
return typedThis;
}
public Class<T> getEntityClass() {
if (entityClass == null && entity != null) {
entityClass = (Class<T>) entity.getClass();
}
return entityClass;
}
public Children setEntityClass(Class<T> entityClass) {
if (entityClass != null) {
this.entityClass = entityClass;
}
return typedThis;
}
@Override
public <V> Children allEq(boolean condition, Map<R, V> params, boolean null2IsNull) {
if (condition && CollectionUtils.isNotEmpty(params)) {
params.forEach((k, v) -> {
if (StringUtils.checkValNotNull(v)) {
eq(k, v);
} else {
if (null2IsNull) {
isNull(k);
}
}
});
}
return typedThis;
}
@Override
public <V> Children allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) {
if (condition && CollectionUtils.isNotEmpty(params)) {
params.forEach((k, v) -> {
if (filter.test(k, v)) {
if (StringUtils.checkValNotNull(v)) {
eq(k, v);
} else {
if (null2IsNull) {
isNull(k);
}
}
}
});
}
return typedThis;
}
@Override
public Children eq(boolean condition, R column, Object val) {
return addCondition(condition, column, SqlKeyword.EQ, val);
}
@Override
public Children ne(boolean condition, R column, Object val) {
return addCondition(condition, column, SqlKeyword.NE, val);
}
@Override
public Children gt(boolean condition, R column, Object val) {
return addCondition(condition, column, SqlKeyword.GT, val);
}
@Override
public Children ge(boolean condition, R column, Object val) {
return addCondition(condition, column, SqlKeyword.GE, val);
}
@Override
public Children lt(boolean condition, R column, Object val) {
return addCondition(condition, column, SqlKeyword.LT, val);
}
@Override
public Children le(boolean condition, R column, Object val) {
return addCondition(condition, column, SqlKeyword.LE, val);
}
@Override
public Children like(boolean condition, R column, Object val) {
return likeValue(condition, SqlKeyword.LIKE, column, val, SqlLike.DEFAULT);
}
@Override
public Children notLike(boolean condition, R column, Object val) {
return likeValue(condition, SqlKeyword.NOT_LIKE, column, val, SqlLike.DEFAULT);
}
@Override
public Children likeLeft(boolean condition, R column, Object val) {
return likeValue(condition, SqlKeyword.LIKE, column, val, SqlLike.LEFT);
}
@Override
public Children likeRight(boolean condition, R column, Object val) {
return likeValue(condition, SqlKeyword.LIKE, column, val, SqlLike.RIGHT);
}
@Override
public Children notLikeLeft(boolean condition, R column, Object val) {
return likeValue(condition, SqlKeyword.NOT_LIKE, column, val, SqlLike.LEFT);
}
@Override
public Children notLikeRight(boolean condition, R column, Object val) {
return likeValue(condition, SqlKeyword.NOT_LIKE, column, val, SqlLike.RIGHT);
}
@Override
public Children between(boolean condition, R column, Object val1, Object val2) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.BETWEEN,
() -> formatParam(null, val1), SqlKeyword.AND, () -> formatParam(null, val2)));
}
@Override
public Children notBetween(boolean condition, R column, Object val1, Object val2) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.NOT_BETWEEN,
() -> formatParam(null, val1), SqlKeyword.AND, () -> formatParam(null, val2)));
}
@Override
public Children and(boolean condition, Consumer<Children> consumer) {
return and(condition).addNestedCondition(condition, consumer);
}
@Override
public Children or(boolean condition, Consumer<Children> consumer) {
return or(condition).addNestedCondition(condition, consumer);
}
@Override
public Children nested(boolean condition, Consumer<Children> consumer) {
return addNestedCondition(condition, consumer);
}
@Override
public Children not(boolean condition, Consumer<Children> consumer) {
return not(condition).addNestedCondition(condition, consumer);
}
@Override
public Children or(boolean condition) {
return maybeDo(condition, () -> appendSqlSegments(SqlKeyword.OR));
}
@Override
public Children apply(boolean condition, String applySql, Object... values) {
return maybeDo(condition, () -> appendSqlSegments(WrapperKeyword.APPLY, () -> formatSqlMaybeWithParam(applySql, values)));
}
@Override
public Children last(boolean condition, String lastSql) {
if (condition) {
this.lastSql.setStringValue(StringPool.SPACE + lastSql);
}
return typedThis;
}
@Override
public Children comment(boolean condition, String comment) {
if (condition) {
this.sqlComment.setStringValue(comment);
}
return typedThis;
}
@Override
public Children first(boolean condition, String firstSql) {
if (condition) {
this.sqlFirst.setStringValue(firstSql);
}
return typedThis;
}
@Override
public Children exists(boolean condition, String existsSql, Object... values) {
return maybeDo(condition, () -> appendSqlSegments(SqlKeyword.EXISTS,
() -> String.format("(%s)", formatSqlMaybeWithParam(existsSql, values))));
}
@Override
public Children notExists(boolean condition, String existsSql, Object... values) {
return not(condition).exists(condition, existsSql, values);
}
@Override
public Children isNull(boolean condition, R column) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.IS_NULL));
}
@Override
public Children isNotNull(boolean condition, R column) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.IS_NOT_NULL));
}
@Override
public Children in(boolean condition, R column, Collection<?> coll) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.IN, inExpression(coll)));
}
@Override
public Children in(boolean condition, R column, Object... values) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.IN, inExpression(values)));
}
@Override
public Children notIn(boolean condition, R column, Collection<?> coll) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.NOT_IN, inExpression(coll)));
}
@Override
public Children notIn(boolean condition, R column, Object... values) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.NOT_IN, inExpression(values)));
}
@Override
public Children inSql(boolean condition, R column, String inValue) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.IN,
() -> String.format("(%s)", inValue)));
}
@Override
public Children gtSql(boolean condition, R column, String inValue) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.GT,
() -> String.format("(%s)", inValue)));
}
@Override
public Children geSql(boolean condition, R column, String inValue) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.GE,
() -> String.format("(%s)", inValue)));
}
@Override
public Children ltSql(boolean condition, R column, String inValue) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.LT,
() -> String.format("(%s)", inValue)));
}
@Override
public Children leSql(boolean condition, R column, String inValue) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.LE,
() -> String.format("(%s)", inValue)));
}
@Override
public Children notInSql(boolean condition, R column, String inValue) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), SqlKeyword.NOT_IN,
() -> String.format("(%s)", inValue)));
}
@Override
public Children groupBy(boolean condition, R column, R... columns) {
return doGroupBy(condition, column, CollectionUtils.toList(columns));
}
@Override
public Children groupBy(boolean condition, R column, List<R> columns) {
return doGroupBy(condition, column, columns);
}
public Children doGroupBy(boolean condition, R column, List<R> columns) {
return maybeDo(condition, () -> {
String one = StringPool.EMPTY;
if (column != null) {
one = columnToString(column);
}
if (CollectionUtils.isNotEmpty(columns)) {
one += column != null ? StringPool.COMMA + columnsToString(columns) : columnsToString(columns);
}
final String finalOne = one;
appendSqlSegments(SqlKeyword.GROUP_BY, () -> finalOne);
});
}
public Children doOrderBy(boolean condition, boolean isAsc, R column, List<R> columns) {
return maybeDo(condition, () -> {
final SqlKeyword mode = isAsc ? SqlKeyword.ASC : SqlKeyword.DESC;
if (column != null) {
appendSqlSegments(SqlKeyword.ORDER_BY, columnToSqlSegment(column), mode);
}
if (CollectionUtils.isNotEmpty(columns)) {
columns.forEach(c -> appendSqlSegments(SqlKeyword.ORDER_BY,
columnToSqlSegment(c), mode));
}
});
}
@Override
public Children orderBy(boolean condition, boolean isAsc, R column, R... columns) {
return doOrderBy(condition, isAsc, column, CollectionUtils.toList(columns));
}
@Override
public Children orderBy(boolean condition, boolean isAsc, R column, List<R> columns) {
return doOrderBy(condition, isAsc, column, columns);
}
@Override
public Children groupBy(boolean condition, R column) {
return maybeDo(condition, () -> appendSqlSegments(SqlKeyword.GROUP_BY, () -> columnToString(column)));
}
@Override
public Children groupBy(boolean condition, List<R> columns) {
return maybeDo(condition, () -> appendSqlSegments(SqlKeyword.GROUP_BY, () -> columnsToString(columns)));
}
@Override
public Children orderBy(boolean condition, boolean isAsc, R column) {
return maybeDo(condition, () -> appendSqlSegments(SqlKeyword.ORDER_BY, columnToSqlSegment(column),
isAsc ? SqlKeyword.ASC : SqlKeyword.DESC));
}
@Override
public Children orderBy(boolean condition, boolean isAsc, List<R> columns) {
return maybeDo(condition, () -> columns.forEach(c -> appendSqlSegments(SqlKeyword.ORDER_BY,
columnToSqlSegment(c), isAsc ? SqlKeyword.ASC : SqlKeyword.DESC)));
}
@Override
public Children having(boolean condition, String sqlHaving, Object... params) {
return maybeDo(condition, () -> appendSqlSegments(SqlKeyword.HAVING, () -> formatSqlMaybeWithParam(sqlHaving, params)));
}
@Override
public Children func(boolean condition, Consumer<Children> consumer) {
return maybeDo(condition, () -> consumer.accept(typedThis));
}
/**
* 内部自用
* <p>NOT 关键词</p>
*/
protected Children not(boolean condition) {
return maybeDo(condition, () -> appendSqlSegments(SqlKeyword.NOT));
}
/**
* 内部自用
* <p>拼接 AND</p>
*/
protected Children and(boolean condition) {
return maybeDo(condition, () -> appendSqlSegments(SqlKeyword.AND));
}
/**
* 内部自用
* <p>拼接 LIKE 以及 值</p>
*/
protected Children likeValue(boolean condition, SqlKeyword keyword, R column, Object val, SqlLike sqlLike) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), keyword,
() -> formatParam(null, SqlUtils.concatLike(val, sqlLike))));
}
/**
* 普通查询条件
*
* @param condition 是否执行
* @param column 属性
* @param sqlKeyword SQL 关键词
* @param val 条件值
*/
protected Children addCondition(boolean condition, R column, SqlKeyword sqlKeyword, Object val) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(column), sqlKeyword,
() -> formatParam(null, val)));
}
/**
* 多重嵌套查询条件
*
* @param condition 查询条件值
*/
protected Children addNestedCondition(boolean condition, Consumer<Children> consumer) {
return maybeDo(condition, () -> {
final Children instance = instance();
consumer.accept(instance);
appendSqlSegments(WrapperKeyword.APPLY, instance);
});
}
/**
* 子类返回一个自己的新对象
*/
protected abstract Children instance();
/**
* 格式化 sql
* <p>
* 支持 "{0}" 这种,或者 "sql {0} sql" 这种
* 也支持 "sql {0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler} sql" 这种
*
* @param sqlStr 可能是sql片段
* @param params 参数
* @return sql片段
*/
@SuppressWarnings("SameParameterValue")
protected final String formatSqlMaybeWithParam(String sqlStr, Object... params) {
if (StringUtils.isBlank(sqlStr)) {
return null;
}
if (FuncBase.isNotEmpty(params)) {
for (int i = 0; i < params.length; ++i) {
String target = Constants.LEFT_BRACE + i + Constants.RIGHT_BRACE;
if (sqlStr.contains(target)) {
sqlStr = sqlStr.replace(target, formatParam(null, params[i]));
} else {
Matcher matcher = Pattern.compile("[{]" + i + ",[a-zA-Z0-9.,=]+}").matcher(sqlStr);
if (!matcher.find()) {
throw ExceptionUtils.mpe("Please check the syntax correctness! sql not contains: \"%s\"", target);
}
String group = matcher.group();
sqlStr = sqlStr.replace(group, formatParam(group.substring(target.length(), group.length() - 1), params[i]));
}
}
}
return sqlStr;
}
/**
* 处理入参
*
* @param mapping 例如: "javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler" 这种
* @param param 参数
* @return value
*/
protected final String formatParam(String mapping, Object param) {
final String genParamName = Constants.WRAPPER_PARAM + paramNameSeq.incrementAndGet();
final String paramStr = getParamAlias() + Constants.WRAPPER_PARAM_MIDDLE + genParamName;
paramNameValuePairs.put(genParamName, param);
return SqlScriptUtils.safeParam(paramStr, mapping);
}
/**
* 函数化的做事
*
* @param condition 做不做
* @param something 做什么
* @return Children
*/
protected final Children maybeDo(boolean condition, DoSomething something) {
if (condition) {
something.doIt();
}
return typedThis;
}
/**
* 获取in表达式 包含括号
*
* @param value 集合
*/
protected ISqlSegment inExpression(Collection<?> value) {
if (CollectionUtils.isEmpty(value)) {
return () -> "()";
}
return () -> value.stream().map(i -> formatParam(null, i))
.collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET));
}
/**
* 获取in表达式 包含括号
*
* @param values 数组
*/
protected ISqlSegment inExpression(Object[] values) {
if (FuncBase.isEmpty(values)) {
return () -> "()";
}
return () -> Arrays.stream(values).map(i -> formatParam(null, i))
.collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET));
}
/**
* 必要的初始化
*/
protected void initNeed() {
paramNameSeq = new AtomicInteger(0);
paramNameValuePairs = new HashMap<>(16);
expression = new MergeSegments();
lastSql = SharedString.emptyString();
sqlComment = SharedString.emptyString();
sqlFirst = SharedString.emptyString();
}
@Override
public void clear() {
entity = null;
paramNameSeq.set(0);
paramNameValuePairs.clear();
expression.clear();
lastSql.toEmpty();
sqlComment.toEmpty();
sqlFirst.toEmpty();
}
/**
* 添加 where 片段
*
* @param sqlSegments ISqlSegment 数组
*/
protected void appendSqlSegments(ISqlSegment... sqlSegments) {
expression.add(sqlSegments);
}
/**
* 是否使用默认注 排序
*
* @return true 使用 false 不使用
*/
public boolean isUseAnnotationOrderBy() {
final String _sqlSegment = this.getSqlSegment();
if (StringUtils.isBlank(_sqlSegment)) {
return true;
}
final String _sqlSegmentUpper = _sqlSegment.toUpperCase();
return !(_sqlSegmentUpper.contains(Constants.ORDER_BY) || _sqlSegmentUpper.contains(Constants.LIMIT));
}
@Override
public String getSqlSegment() {
return expression.getSqlSegment() + lastSql.getStringValue();
}
@Override
public String getSqlComment() {
if (StringUtils.isNotBlank(sqlComment.getStringValue())) {
return "/*" + sqlComment.getStringValue() + "*/";
}
return null;
}
@Override
public String getSqlFirst() {
if (StringUtils.isNotBlank(sqlFirst.getStringValue())) {
return sqlFirst.getStringValue();
}
return null;
}
@Override
public MergeSegments getExpression() {
return expression;
}
public String getParamAlias() {
return paramAlias == null ? Constants.WRAPPER : paramAlias.getStringValue();
}
/**
* 参数别名设置,初始化时优先设置该值、重复设置异常
*
* @param paramAlias 参数别名
* @return Children
*/
@SuppressWarnings("unused")
public Children setParamAlias(String paramAlias) {
Assert.notEmpty(paramAlias, "paramAlias can not be empty!");
Assert.isEmpty(paramNameValuePairs, "Please call this method before working!");
Assert.isNull(this.paramAlias, "Please do not call the method repeatedly!");
this.paramAlias = new SharedString(paramAlias);
return typedThis;
}
/**
* 获取 columnName
*/
protected final ColumnSegment columnToSqlSegment(R column) {
return () -> columnToString(column);
}
/**
* 获取 columnName
*/
protected String columnToString(R column) {
return (String) column;
}
/**
* 获取 columnNames
*/
protected String columnsToString(R... columns) {
return Arrays.stream(columns).map(this::columnToString).collect(joining(StringPool.COMMA));
}
/**
* 多字段转换为逗号 "," 分割字符串
*
* @param columns 多字段
*/
protected String columnsToString(List<R> columns) {
return columns.stream().map(this::columnToString).collect(joining(StringPool.COMMA));
}
@Override
@SuppressWarnings("all")
public Children clone() {
return SerializationUtils.clone(typedThis);
}
/**
* 做事函数
*/
@FunctionalInterface
public interface DoSomething {
void doIt();
}
public Children getTypedThis() {
return typedThis;
}
public AtomicInteger getParamNameSeq() {
return paramNameSeq;
}
public void setParamNameSeq(AtomicInteger paramNameSeq) {
this.paramNameSeq = paramNameSeq;
}
public Map<String, Object> getParamNameValuePairs() {
return paramNameValuePairs;
}
public void setParamNameValuePairs(Map<String, Object> paramNameValuePairs) {
this.paramNameValuePairs = paramNameValuePairs;
}
public void setParamAlias(SharedString paramAlias) {
this.paramAlias = paramAlias;
}
public SharedString getLastSql() {
return lastSql;
}
public void setLastSql(SharedString lastSql) {
this.lastSql = lastSql;
}
public void setSqlComment(SharedString sqlComment) {
this.sqlComment = sqlComment;
}
public void setSqlFirst(SharedString sqlFirst) {
this.sqlFirst = sqlFirst;
}
public void setExpression(MergeSegments expression) {
this.expression = expression;
}
}

View File

@@ -0,0 +1,20 @@
package com.jeelowcode.framework.plus.core.conditions;
import java.io.Serializable;
/**
* @author JX
* @create 2024-03-26 9:51
* @dedescription:
*/
@FunctionalInterface
public interface ISqlSegment extends Serializable {
/**
* SQL 片段
*/
String getSqlSegment();
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.conditions;
import com.jeelowcode.framework.plus.core.toolkit.StringPool;
import java.io.Serializable;
/**
* 共享查询字段
*
* @author miemie
* @since 2018-11-20
*/
public class SharedString implements Serializable {
private static final long serialVersionUID = -1536422416594422874L;
/**
* 共享的 string 值
*/
private String stringValue;
/**
* SharedString 里是 ""
*/
public static SharedString emptyString() {
return new SharedString(StringPool.EMPTY);
}
/**
* 置 empty
*
* @since 3.3.1
*/
public void toEmpty() {
stringValue = StringPool.EMPTY;
}
/**
* 置 null
*
* @since 3.3.1
*/
public void toNull() {
stringValue = null;
}
public SharedString() {
}
public SharedString(String stringValue) {
this.stringValue = stringValue;
}
public String getStringValue() {
return stringValue;
}
public void setStringValue(String stringValue) {
this.stringValue = stringValue;
}
}

View File

@@ -0,0 +1,161 @@
package com.jeelowcode.framework.plus.core.conditions;
import com.jeelowcode.framework.plus.core.conditions.segments.MergeSegments;
import com.jeelowcode.framework.plus.core.conditions.segments.NormalSegmentList;
import com.jeelowcode.framework.plus.core.toolkit.Constants;
import com.jeelowcode.framework.plus.core.toolkit.StringPool;
import com.jeelowcode.framework.plus.core.toolkit.StringUtils;
import org.springframework.util.CollectionUtils;
import java.util.Objects;
/**
* @author JX
* @create 2024-03-26 9:53
* @dedescription:
*/
public abstract class Wrapper<T> implements ISqlSegment {
/**
* 实体对象(子类实现)
*
* @return 泛型 T
*/
public abstract T getEntity();
public String getSqlSelect() {
return null;
}
public String getSqlSet() {
return null;
}
public String getSqlComment() {
return null;
}
public String getSqlFirst() {
return null;
}
/**
* 获取 MergeSegments
*/
public abstract MergeSegments getExpression();
/**
* 获取自定义SQL 简化自定义XML复杂情况
* <p>
* 使用方法: `select xxx from table` + ${ew.customSqlSegment}
* <p>
* 注意事项:
* 1. 逻辑删除需要自己拼接条件 (之前自定义也同样)
* 2. 不支持wrapper中附带实体的情况 (wrapper自带实体会更麻烦)
* 3. 用法 ${ew.customSqlSegment} (不需要where标签包裹,切记!)
* 4. ew是wrapper定义别名,不能使用其他的替换
*/
public String getCustomSqlSegment() {
MergeSegments expression = getExpression();
if (Objects.nonNull(expression)) {
NormalSegmentList normal = expression.getNormal();
String sqlSegment = getSqlSegment();
if (StringUtils.isNotBlank(sqlSegment)) {
if (normal.isEmpty()) {
return sqlSegment;
} else {
return Constants.WHERE + StringPool.SPACE + sqlSegment;
}
}
}
return StringPool.EMPTY;
}
public String getHavingCustomSqlSegment() {
MergeSegments expression = getExpression();
if (Objects.nonNull(expression)) {
NormalSegmentList normal = expression.getNormal();
String sqlSegment = getSqlSegment();
if (StringUtils.isNotBlank(sqlSegment)) {
if (normal.isEmpty()) {
return sqlSegment;
} else {
return Constants.HAVING + StringPool.SPACE + sqlSegment;
}
}
}
return StringPool.EMPTY;
}
//和上面一样只是不返回where ,直接原样凭接
public String appendCustomSqlSegment() {
MergeSegments expression = getExpression();
if (Objects.nonNull(expression)) {
String sqlSegment = getSqlSegment();
sqlSegment=sqlSegment.substring(1,sqlSegment.length()-1);//去掉左右括号()
if (StringUtils.isNotBlank(sqlSegment)) {
return sqlSegment;
}
}
return StringPool.EMPTY;
}
/**
* 查询条件为空(包含entity)
*/
public boolean isEmptyOfWhere() {
return isEmptyOfNormal() ;
}
/**
* 查询条件不为空(包含entity)
*/
public boolean isNonEmptyOfWhere() {
return !isEmptyOfWhere();
}
@Deprecated
public boolean nonEmptyOfWhere() {
return isNonEmptyOfWhere();
}
/**
* 查询条件为空(不包含entity)
*/
public boolean isEmptyOfNormal() {
return CollectionUtils.isEmpty(getExpression().getNormal());
}
/**
* 查询条件为空(不包含entity)
*/
public boolean isNonEmptyOfNormal() {
return !isEmptyOfNormal();
}
@Deprecated
public boolean nonEmptyOfNormal() {
return isNonEmptyOfNormal();
}
/**
* 获取格式化后的执行sql
*
* @return sql
* @since 3.3.1
*/
public String getTargetSql() {
return getSqlSegment().replaceAll("#\\{.+?}", "?");
}
/**
* 条件清空
*
* @since 3.3.1
*/
abstract public void clear();
}

View File

@@ -0,0 +1,394 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.conditions.interfaces;
import java.io.Serializable;
import java.util.Map;
import java.util.function.BiPredicate;
/**
* 查询条件封装
* <p>比较值</p>
*
* @author hubin miemie HCL
* @since 2017-05-26
*/
public interface Compare<Children, R> extends Serializable {
/**
* map 所有非空属性等于 =
*
* @param params map 类型的参数, key 是字段名, value 是字段值
* @return children
*/
default <V> Children allEq(Map<R, V> params) {
return allEq(params, true);
}
/**
* map 所有非空属性等于 =
*
* @param params map 类型的参数, key 是字段名, value 是字段值
* @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段
* @return children
*/
default <V> Children allEq(Map<R, V> params, boolean null2IsNull) {
return allEq(true, params, null2IsNull);
}
/**
* map 所有非空属性等于 =
*
* @param condition 执行条件
* @param params map 类型的参数, key 是字段名, value 是字段值
* @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段
* @return children
*/
<V> Children allEq(boolean condition, Map<R, V> params, boolean null2IsNull);
/**
* 字段过滤接口,传入多参数时允许对参数进行过滤
*
* @param filter 返回 true 来允许字段传入比对条件中
* @param params map 类型的参数, key 是字段名, value 是字段值
* @return children
*/
default <V> Children allEq(BiPredicate<R, V> filter, Map<R, V> params) {
return allEq(filter, params, true);
}
/**
* 字段过滤接口,传入多参数时允许对参数进行过滤
*
* @param filter 返回 true 来允许字段传入比对条件中
* @param params map 类型的参数, key 是字段名, value 是字段值
* @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段
* @return children
*/
default <V> Children allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) {
return allEq(true, filter, params, null2IsNull);
}
/**
* 字段过滤接口,传入多参数时允许对参数进行过滤
*
* @param condition 执行条件
* @param filter 返回 true 来允许字段传入比对条件中
* @param params map 类型的参数, key 是字段名, value 是字段值
* @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段
* @return children
*/
<V> Children allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull);
/**
* 等于 =
*
* @param column 字段
* @param val 值
* @return children
*/
default Children eq(R column, Object val) {
return eq(true, column, val);
}
/**
* 等于 =
*
* @param condition 执行条件
* @param column 字段
* @param val 值
* @return children
*/
Children eq(boolean condition, R column, Object val);
/**
* 不等于 &lt;&gt;
*
* @param column 字段
* @param val 值
* @return children
*/
default Children ne(R column, Object val) {
return ne(true, column, val);
}
/**
* 不等于 &lt;&gt;
*
* @param condition 执行条件
* @param column 字段
* @param val 值
* @return children
*/
Children ne(boolean condition, R column, Object val);
/**
* 大于 &gt;
*
* @param column 字段
* @param val 值
* @return children
*/
default Children gt(R column, Object val) {
return gt(true, column, val);
}
/**
* 大于 &gt;
*
* @param condition 执行条件
* @param column 字段
* @param val 值
* @return children
*/
Children gt(boolean condition, R column, Object val);
/**
* 大于等于 &gt;=
*
* @param column 字段
* @param val 值
* @return children
*/
default Children ge(R column, Object val) {
return ge(true, column, val);
}
/**
* 大于等于 &gt;=
*
* @param condition 执行条件
* @param column 字段
* @param val 值
* @return children
*/
Children ge(boolean condition, R column, Object val);
/**
* 小于 &lt;
*
* @param column 字段
* @param val 值
* @return children
*/
default Children lt(R column, Object val) {
return lt(true, column, val);
}
/**
* 小于 &lt;
*
* @param condition 执行条件
* @param column 字段
* @param val 值
* @return children
*/
Children lt(boolean condition, R column, Object val);
/**
* 小于等于 &lt;=
*
* @param column 字段
* @param val 值
* @return children
*/
default Children le(R column, Object val) {
return le(true, column, val);
}
/**
* 小于等于 &lt;=
*
* @param condition 执行条件
* @param column 字段
* @param val 值
* @return children
*/
Children le(boolean condition, R column, Object val);
/**
* BETWEEN 值1 AND 值2
*
* @param column 字段
* @param val1 值1
* @param val2 值2
* @return children
*/
default Children between(R column, Object val1, Object val2) {
return between(true, column, val1, val2);
}
/**
* BETWEEN 值1 AND 值2
*
* @param condition 执行条件
* @param column 字段
* @param val1 值1
* @param val2 值2
* @return children
*/
Children between(boolean condition, R column, Object val1, Object val2);
/**
* NOT BETWEEN 值1 AND 值2
*
* @param column 字段
* @param val1 值1
* @param val2 值2
* @return children
*/
default Children notBetween(R column, Object val1, Object val2) {
return notBetween(true, column, val1, val2);
}
/**
* NOT BETWEEN 值1 AND 值2
*
* @param condition 执行条件
* @param column 字段
* @param val1 值1
* @param val2 值2
* @return children
*/
Children notBetween(boolean condition, R column, Object val1, Object val2);
/**
* LIKE '%值%'
*
* @param column 字段
* @param val 值
* @return children
*/
default Children like(R column, Object val) {
return like(true, column, val);
}
/**
* LIKE '%值%'
*
* @param condition 执行条件
* @param column 字段
* @param val 值
* @return children
*/
Children like(boolean condition, R column, Object val);
/**
* NOT LIKE '%值%'
*
* @param column 字段
* @param val 值
* @return children
*/
default Children notLike(R column, Object val) {
return notLike(true, column, val);
}
/**
* NOT LIKE '%值%'
*
* @param condition 执行条件
* @param column 字段
* @param val 值
* @return children
*/
Children notLike(boolean condition, R column, Object val);
/**
* NOT LIKE '%值'
*
* @param column 字段
* @param val 值
* @return children
*/
default Children notLikeLeft(R column, Object val) {
return notLikeLeft(true, column, val);
}
/**
* NOT LIKE '%值'
*
* @param condition 执行条件
* @param column 字段
* @param val 值
* @return children
*/
Children notLikeLeft(boolean condition, R column, Object val);
/**
* NOT LIKE '值%'
*
* @param column 字段
* @param val 值
* @return children
*/
default Children notLikeRight(R column, Object val) {
return notLikeRight(true, column, val);
}
/**
* NOT LIKE '值%'
*
* @param condition 执行条件
* @param column 字段
* @param val 值
* @return children
*/
Children notLikeRight(boolean condition, R column, Object val);
/**
* LIKE '%值'
*
* @param column 字段
* @param val 值
* @return children
*/
default Children likeLeft(R column, Object val) {
return likeLeft(true, column, val);
}
/**
* LIKE '%值'
*
* @param condition 执行条件
* @param column 字段
* @param val 值
* @return children
*/
Children likeLeft(boolean condition, R column, Object val);
/**
* LIKE '值%'
*
* @param column 字段
* @param val 值
* @return children
*/
default Children likeRight(R column, Object val) {
return likeRight(true, column, val);
}
/**
* LIKE '值%'
*
* @param condition 执行条件
* @param column 字段
* @param val 值
* @return children
*/
Children likeRight(boolean condition, R column, Object val);
}

View File

@@ -0,0 +1,672 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.conditions.interfaces;
import com.jeelowcode.framework.plus.core.toolkit.CollectionUtils;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
/**
* 查询条件封装
*
* @author hubin miemie HCL
* @since 2017-05-26
*/
@SuppressWarnings("unchecked")
public interface Func<Children, R> extends Serializable {
/**
* 字段 IS NULL
* <p>例: isNull("name")</p>
*
* @param column 字段
* @return children
*/
default Children isNull(R column) {
return isNull(true, column);
}
/**
* 字段 IS NULL
* <p>例: isNull(true, "name")</p>
*
* @param condition 执行条件
* @param column 字段
* @return children
*/
Children isNull(boolean condition, R column);
/**
* 字段 IS NOT NULL
* <p>例: isNotNull("name")</p>
*
* @param column 字段
* @return children
*/
default Children isNotNull(R column) {
return isNotNull(true, column);
}
/**
* 字段 IS NOT NULL
* <p>例: isNotNull(true, "name")</p>
*
* @param condition 执行条件
* @param column 字段
* @return children
*/
Children isNotNull(boolean condition, R column);
/**
* 字段 IN (value.get(0), value.get(1), ...)
* <p>例: in("id", Arrays.asList(1, 2, 3, 4, 5))</p>
*
* <li> 注意!当集合为 空或null 时, sql会拼接为WHERE (字段名 IN ()), 执行时报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param column 字段
* @param coll 数据集合
* @return children
*/
default Children in(R column, Collection<?> coll) {
return in(true, column, coll);
}
/**
* 字段 IN (value.get(0), value.get(1), ...)
* <p>例: in(true, "id", Arrays.asList(1, 2, 3, 4, 5))</p>
*
* <li> 注意!当集合为 空或null 时, sql会拼接为WHERE (字段名 IN ()), 执行时报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param condition 执行条件
* @param column 字段
* @param coll 数据集合
* @return children
*/
Children in(boolean condition, R column, Collection<?> coll);
/**
* 字段 IN (v0, v1, ...)
* <p>例: in("id", 1, 2, 3, 4, 5)</p>
*
* <li> 注意!当数组为 空或null 时, sql会拼接为WHERE (字段名 IN ()), 执行时报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param column 字段
* @param values 数据数组
* @return children
*/
default Children in(R column, Object... values) {
return in(true, column, values);
}
/**
* 字段 IN (v0, v1, ...)
* <p>例: in(true, "id", 1, 2, 3, 4, 5)</p>
*
* <li> 注意!当数组为 空或null 时, sql会拼接为WHERE (字段名 IN ()), 执行时报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param condition 执行条件
* @param column 字段
* @param values 数据数组
* @return children
*/
Children in(boolean condition, R column, Object... values);
/**
* 字段 NOT IN (value.get(0), value.get(1), ...)
* <p>例: notIn("id", Arrays.asList(1, 2, 3, 4, 5))</p>
*
* <li> 注意!当集合为 空或null 时, sql会拼接为WHERE (字段名 NOT IN ()), 执行时报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param column 字段
* @param coll 数据集合
* @return children
*/
default Children notIn(R column, Collection<?> coll) {
return notIn(true, column, coll);
}
/**
* 字段 NOT IN (value.get(0), value.get(1), ...)
* <p>例: notIn(true, "id", Arrays.asList(1, 2, 3, 4, 5))</p>
*
* <li> 注意!当集合为 空或null 时, sql会拼接为WHERE (字段名 NOT IN ()), 执行时报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param condition 执行条件
* @param column 字段
* @param coll 数据集合
* @return children
*/
Children notIn(boolean condition, R column, Collection<?> coll);
/**
* 字段 NOT IN (v0, v1, ...)
* <p>例: notIn("id", 1, 2, 3, 4, 5)</p>
*
* <li> 注意!当数组为 空或null 时, sql会拼接为WHERE (字段名 NOT IN ()), 执行时报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param column 字段
* @param values 数据数组
* @return children
*/
default Children notIn(R column, Object... values) {
return notIn(true, column, values);
}
/**
* 字段 NOT IN (v0, v1, ...)
* <p>例: notIn(true, "id", 1, 2, 3, 4, 5)</p>
*
* <li> 注意!当数组为 空或null 时, sql会拼接为WHERE (字段名 NOT IN ()), 执行时报错</li>
* <li> 若要在特定条件下不拼接, 可在 condition 条件中判断 </li>
*
* @param condition 执行条件
* @param column 字段
* @param values 数据数组
* @return children
*/
Children notIn(boolean condition, R column, Object... values);
/**
* 字段 IN ( sql语句 )
* <p>!! sql 注入方式的 in 方法 !!</p>
* <p>例1: inSql("id", "1")</p>
* <p>例2: inSql("id", "select id from table where id &lt; 3")</p>
*
* @param column 字段
* @param sql sql语句
* @return children
*/
default Children inSql(R column, String sql) {
return inSql(true, column, sql);
}
/**
* 字段 IN ( sql语句 )
* <p>!! sql 注入方式的 in 方法 !!</p>
* <p>例1: inSql(true, "id", "1")</p>
* <p>例2: inSql(true, "id", "select id from table where id &lt; 3")</p>
*
* @param condition 执行条件
* @param column 字段
* @param sql sql语句
* @return children
*/
Children inSql(boolean condition, R column, String sql);
/**
* 字段 &gt; ( sql语句 )
* <p>例1: gtSql(true, "id", "1")</p>
* <p>例1: gtSql(true, "id", "select id from table where name = 'JunJun'")</p>
*
* @param condition 执行条件
* @param column 字段
* @param sql sql语句
* @return children
*/
Children gtSql(boolean condition, R column, String sql);
/**
* 字段 &gt; ( sql语句 )
* <p>例1: gtSql("id", "1")</p>
* <p>例1: gtSql("id", "select id from table where name = 'JunJun'")</p>
*
* @param column 字段
* @param sql sql语句
* @return children
*/
default Children gtSql(R column, String sql) {
return gtSql(true, column, sql);
}
/**
* 字段 >= ( sql语句 )
* <p>例1: geSql(true, "id", "1")</p>
* <p>例1: geSql(true, "id", "select id from table where name = 'JunJun'")</p>
*
* @param condition 执行条件
* @param column 字段
* @param sql sql语句
* @return children
*/
Children geSql(boolean condition, R column, String sql);
/**
* 字段 >= ( sql语句 )
* <p>例1: geSql("id", "1")</p>
* <p>例1: geSql("id", "select id from table where name = 'JunJun'")</p>
*
* @param column 字段
* @param sql sql语句
* @return children
*/
default Children geSql(R column, String sql) {
return geSql(true, column, sql);
}
/**
* 字段 &lt; ( sql语句 )
* <p>例1: ltSql(true, "id", "1")</p>
* <p>例1: ltSql(true , "id", "select id from table where name = 'JunJun'")</p>
*
* @param condition 执行条件
* @param column 字段
* @param sql sql语句
* @return children
*/
Children ltSql(boolean condition, R column, String sql);
/**
* 字段 &lt; ( sql语句 )
* <p>例1: ltSql("id", "1")</p>
* <p>例1: ltSql("id", "select id from table where name = 'JunJun'")</p>
*
* @param column 字段
* @param sql sql语句
* @return children
*/
default Children ltSql(R column, String sql) {
return ltSql(true, column, sql);
}
/**
* 字段 <= ( sql语句 )
* <p>例1: leSql(true, "id", "1")</p>
* <p>例1: leSql(true ,"id", "select id from table where name = 'JunJun'")</p>
*
* @param condition 执行条件
* @param column 字段
* @param sql sql语句
* @return children
*/
Children leSql(boolean condition, R column, String sql);
/**
* 字段 <= ( sql语句 )
* <p>例1: leSql("id", "1")</p>
* <p>例1: leSql("id", "select id from table where name = 'JunJun'")</p>
*
* @param column 字段
* @param inValue sql语句
* @return children
*/
default Children leSql(R column, String inValue) {
return leSql(true, column, inValue);
}
/**
* 字段 NOT IN ( sql语句 )
* <p>!! sql 注入方式的 not in 方法 !!</p>
* <p>例1: notInSql("id", "1, 2, 3, 4, 5, 6")</p>
* <p>例2: notInSql("id", "select id from table where id &lt; 3")</p>
*
* @param column 字段
* @param inValue sql语句 ---&gt; 1,2,3,4,5,6 或者 select id from table where id &lt; 3
* @return children
*/
default Children notInSql(R column, String inValue) {
return notInSql(true, column, inValue);
}
/**
* 字段 NOT IN ( sql语句 )
* <p>!! sql 注入方式的 not in 方法 !!</p>
* <p>例1: notInSql(true, "id", "1, 2, 3, 4, 5, 6")</p>
* <p>例2: notInSql(true, "id", "select id from table where id &lt; 3")</p>
*
* @param condition 执行条件
* @param column 字段
* @param inValue sql语句 ---&gt; 1,2,3,4,5,6 或者 select id from table where id &lt; 3
* @return children
*/
Children notInSql(boolean condition, R column, String inValue);
/**
* 分组GROUP BY 字段, ...
* <p>例: groupBy(true, "id")</p>
*
* @param condition 执行条件
* @param column 单个字段
* @return children
*/
Children groupBy(boolean condition, R column);
/**
* 分组GROUP BY 字段, ...
* <p>例: groupBy("id")</p>
*
* @param column 单个字段
* @return children
*/
default Children groupBy(R column) {
return groupBy(true, column);
}
/**
* 分组GROUP BY 字段, ...
* <p>例: groupBy(true, Arrays.asList("id", "name"))</p>
*
* @param condition 执行条件
* @param columns 字段数组
* @return children
*/
Children groupBy(boolean condition, List<R> columns);
/**
* 分组GROUP BY 字段, ...
* <p>例: groupBy(Arrays.asList("id", "name"))</p>
*
* @param columns 字段数组
* @return children
*/
default Children groupBy(List<R> columns) {
return groupBy(true, columns);
}
/**
* 分组GROUP BY 字段, ...
* <p>例: groupBy("id", "name")</p>
*
* @param column 单个字段
* @param columns 字段数组
* @return children
*/
default Children groupBy(R column, R... columns) {
return groupBy(true, column, columns);
}
/**
* 分组GROUP BY 字段, ...
* <p>例: groupBy(true, "id", "name")</p>
*
* @param condition 执行条件
* @param column 单个字段
* @param columns 字段数组
* @return children
*/
Children groupBy(boolean condition, R column, R... columns);
/**
* 分组GROUP BY 字段, ...
* <p>例: groupBy(true, "id", Arrays.asList("name"))</p>
*
* @param condition 执行条件
* @param column 单个字段
* @param columns 字段数组
* @return children
* @since 3.5.4
*/
Children groupBy(boolean condition, R column, List<R> columns);
/**
* 排序ORDER BY 字段, ... ASC
* <p>例: orderByAsc(true, "id")</p>
*
* @param condition 执行条件
* @param column 单个字段
* @return children
*/
default Children orderByAsc(boolean condition, R column) {
return orderBy(condition, true, column);
}
/**
* 排序ORDER BY 字段, ... ASC
* <p>例: orderByAsc("id")</p>
*
* @param column 单个字段
* @return children
*/
default Children orderByAsc(R column) {
return orderByAsc(true, column);
}
/**
* 排序ORDER BY 字段, ... ASC
* <p>例: orderByAsc(true, Arrays.asList("id", "name"))</p>
*
* @param condition 执行条件
* @param columns 字段数组
* @return children
*/
default Children orderByAsc(boolean condition, List<R> columns) {
return orderBy(condition, true, columns);
}
/**
* 排序ORDER BY 字段, ... ASC
* <p>例: orderByAsc(Arrays.asList("id", "name"))</p>
*
* @param columns 字段数组
* @return children
*/
default Children orderByAsc(List<R> columns) {
return orderByAsc(true, columns);
}
/**
* 排序ORDER BY 字段, ... ASC
*
* @param column 字段
* @param columns 字段数组
* @return children
*/
default Children orderByAsc(R column, R... columns) {
return orderByAsc(true, column, columns);
}
/**
* 排序ORDER BY 字段, ... ASC
*
* @param condition 执行条件
* @param column 字段
* @param columns 字段数组
*/
default Children orderByAsc(boolean condition, R column, R... columns) {
return orderBy(condition, true, column, columns);
}
/**
* 排序ORDER BY 字段, ... ASC
* <p>例: orderByAsc(true, Arrays.asList("id", "name"))</p>
*
* @param condition 执行条件
* @param columns 字段数组
* @return children
* @since 3.5.4
*/
default Children orderByAsc(boolean condition, R column, List<R> columns) {
return orderBy(condition, true, column, columns);
}
/**
* 排序ORDER BY 字段, ... DESC
* <p>例: orderByDesc(true, "id")</p>
*
* @param condition 执行条件
* @param column 字段
* @return children
*/
default Children orderByDesc(boolean condition, R column) {
return orderBy(condition, false, column);
}
/**
* 排序ORDER BY 字段, ... DESC
* <p>例: orderByDesc("id")</p>
*
* @param column 字段
* @return children
*/
default Children orderByDesc(R column) {
return orderByDesc(true, column);
}
/**
* 排序ORDER BY 字段, ... DESC
* <p>例: orderByDesc(true, Arrays.asList("id", "name"))</p>
*
* @param condition 执行条件
* @param columns 字段列表
* @return children
*/
default Children orderByDesc(boolean condition, List<R> columns) {
return orderBy(condition, false, columns);
}
/**
* 排序ORDER BY 字段, ... DESC
*
* @param columns 字段列表
*/
default Children orderByDesc(List<R> columns) {
return orderByDesc(true, columns);
}
/**
* 排序ORDER BY 字段, ... DESC
*
* @param column 单个字段
* @param columns 字段列表
*/
default Children orderByDesc(R column, R... columns) {
return orderByDesc(true, column, columns);
}
/**
* 排序ORDER BY 字段, ... DESC
*
* @param condition 执行条件
* @param column 单个字段
* @param columns 字段列表
*/
default Children orderByDesc(boolean condition, R column, R... columns) {
return orderBy(condition, false, column, CollectionUtils.toList(columns));
}
/**
* 排序ORDER BY 字段, ... DESC
*
* @param condition 执行条件
* @param column 单个字段
* @param columns 字段列表
* @since 3.5.4
*/
default Children orderByDesc(boolean condition, R column, List<R> columns) {
return orderBy(condition, false, column, columns);
}
/**
* 排序ORDER BY 字段, ...
* <p>例: orderBy(true, "id")</p>
*
* @param condition 执行条件
* @param isAsc 是否是 ASC 排序
* @param column 单个字段
* @return children
*/
Children orderBy(boolean condition, boolean isAsc, R column);
/**
* 排序ORDER BY 字段, ...
* <p>例: orderBy(true, Arrays.asList("id", "name"))</p>
*
* @param condition 执行条件
* @param isAsc 是否是 ASC 排序
* @param columns 字段列表
* @return children
*/
Children orderBy(boolean condition, boolean isAsc, List<R> columns);
/**
* 排序ORDER BY 字段, ...
*
* @param condition 执行条件
* @param isAsc 是否是 ASC 排序
* @param columns 字段列表
* @return children
*/
Children orderBy(boolean condition, boolean isAsc, R column, R... columns);
/**
* 排序ORDER BY 字段, ...
*
* @param condition 执行条件
* @param isAsc 是否是 ASC 排序
* @param columns 字段列表
* @return children
* @since 3.5.4
*/
Children orderBy(boolean condition, boolean isAsc, R column, List<R> columns);
/**
* HAVING ( sql语句 )
* <p>例1: having("sum(age) &gt; 10")</p>
* <p>例2: having("sum(age) &gt; {0}", 10)</p>
*
* @param sqlHaving sql 语句
* @param params 参数数组
* @return children
*/
default Children having(String sqlHaving, Object... params) {
return having(true, sqlHaving, params);
}
/**
* HAVING ( sql语句 )
* <p>例1: having(true, "sum(age) &gt; 10")</p>
* <p>例2: having(true, "sum(age) &gt; {0}", 10)</p>
*
* @param condition 执行条件
* @param sqlHaving sql 语句
* @param params 参数数组
* @return children
*/
Children having(boolean condition, String sqlHaving, Object... params);
/**
* 消费函数
*
* @param consumer 消费函数
* @return children
*/
default Children func(Consumer<Children> consumer) {
return func(true, consumer);
}
/**
* 消费函数
*
* @param condition 执行条件
* @param consumer 消费函数
* @return children
* @since 3.3.1
*/
Children func(boolean condition, Consumer<Children> consumer);
}

View File

@@ -0,0 +1,198 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.conditions.interfaces;
import java.io.Serializable;
/**
* 查询条件封装
* <p>拼接</p>
*
* @author hubin miemie HCL
* @since 2017-05-26
*/
public interface Join<Children> extends Serializable {
/**
* 拼接 OR
*
* @return children
*/
default Children or() {
return or(true);
}
/**
* 拼接 OR
*
* @param condition 执行条件
* @return children
*/
Children or(boolean condition);
/**
* 拼接 sql
* <p>!! 会有 sql 注入风险 !!</p>
* <p>例1: apply("id = 1")</p>
* <p>例2: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")</p>
* <p>例3: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", LocalDate.now())</p>
* <p>例4: apply("name={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}", "老王")</p>
*
* @param values 数据数组
* @return children
*/
default Children apply(String applySql, Object... values) {
return apply(true, applySql, values);
}
/**
* 拼接 sql
* <p>!! 会有 sql 注入风险 !!</p>
* <p>例1: apply("id = 1")</p>
* <p>例2: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")</p>
* <p>例3: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", LocalDate.now())</p>
* <p>例4: apply("name={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}", "老王")</p>
*
* @param condition 执行条件
* @param values 数据数组
* @return children
*/
Children apply(boolean condition, String applySql, Object... values);
/**
* 无视优化规则直接拼接到 sql 的最后(有sql注入的风险,请谨慎使用)
* <p>例: last("limit 1")</p>
* <p>注意只能调用一次,多次调用以最后一次为准</p>
*
* @param lastSql sql语句
* @return children
*/
default Children last(String lastSql) {
return last(true, lastSql);
}
/**
* 无视优化规则直接拼接到 sql 的最后(有sql注入的风险,请谨慎使用)
* <p>例: last("limit 1")</p>
* <p>注意只能调用一次,多次调用以最后一次为准</p>
*
* @param condition 执行条件
* @param lastSql sql语句
* @return children
*/
Children last(boolean condition, String lastSql);
/**
* sql 注释(会拼接在 sql 的最后面)
* <p>
* 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)}
* </p>
*
* @param comment sql注释
* @return children
*/
default Children comment(String comment) {
return comment(true, comment);
}
/**
* sql 注释(会拼接在 sql 的最后面)
* <p>
* 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)}
* </p>
*
* @param condition 执行条件
* @param comment sql注释
* @return children
*/
Children comment(boolean condition, String comment);
/**
* sql 起始句会拼接在SQL语句的起始处
* <p>
* 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)}
* </p>
*
* @param firstSql 起始语句
* @return children
*/
default Children first(String firstSql) {
return first(true, firstSql);
}
/**
* sql 起始句会拼接在SQL语句的起始处
* <p>
* 自3.5.6开始,默认不处理转义字符,如有需要,调用{@link com.baomidou.mybatisplus.core.toolkit.sql.StringEscape#escapeRawString(String)}
* </p>
*
* @param condition 执行条件
* @param firstSql 起始语句
* @return children
* @since 3.3.1
*/
Children first(boolean condition, String firstSql);
/**
* 拼接 EXISTS ( sql语句 )
* <p>!! sql 注入方法 !!</p>
* <p>例: exists("select id from table where age = 1")</p>
*
* @param existsSql sql语句
* @param values 数据数组
* @return children
*/
default Children exists(String existsSql, Object... values) {
return exists(true, existsSql, values);
}
/**
* 拼接 EXISTS ( sql语句 )
* <p>!! sql 注入方法 !!</p>
* <p>例: exists("select id from table where age = 1")</p>
*
* @param condition 执行条件
* @param existsSql sql语句
* @param values 数据数组
* @return children
*/
Children exists(boolean condition, String existsSql, Object... values);
/**
* 拼接 NOT EXISTS ( sql语句 )
* <p>!! sql 注入方法 !!</p>
* <p>例: notExists("select id from table where age = 1")</p>
*
* @param existsSql sql语句
* @param values 数据数组
* @return children
*/
default Children notExists(String existsSql, Object... values) {
return notExists(true, existsSql, values);
}
/**
* 拼接 NOT EXISTS ( sql语句 )
* <p>!! sql 注入方法 !!</p>
* <p>例: notExists("select id from table where age = 1")</p>
*
* @param condition 执行条件
* @param existsSql sql语句
* @param values 数据数组
* @return children
*/
Children notExists(boolean condition, String existsSql, Object... values);
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.conditions.interfaces;
import java.io.Serializable;
import java.util.function.Consumer;
/**
* 查询条件封装
* <p>嵌套</p>
* <li>泛型 Param 是具体需要运行函数的类(也是 wrapper 的子类)</li>
*
* @author hubin miemie HCL
* @since 2017-05-26
*/
public interface Nested<Param, Children> extends Serializable {
/**
* AND 嵌套
* <p>
* 例: and(i -&gt; i.eq("name", "李白").ne("status", "活着"))
* </p>
*
* @param consumer 消费函数
* @return children
*/
default Children and(Consumer<Param> consumer) {
return and(true, consumer);
}
/**
* AND 嵌套
* <p>
* 例: and(i -&gt; i.eq("name", "李白").ne("status", "活着"))
* </p>
*
* @param condition 执行条件
* @param consumer 消费函数
* @return children
*/
Children and(boolean condition, Consumer<Param> consumer);
/**
* OR 嵌套
* <p>
* 例: or(i -&gt; i.eq("name", "李白").ne("status", "活着"))
* </p>
*
* @param consumer 消费函数
* @return children
*/
default Children or(Consumer<Param> consumer) {
return or(true, consumer);
}
/**
* OR 嵌套
* <p>
* 例: or(i -&gt; i.eq("name", "李白").ne("status", "活着"))
* </p>
*
* @param condition 执行条件
* @param consumer 消费函数
* @return children
*/
Children or(boolean condition, Consumer<Param> consumer);
/**
* 正常嵌套 不带 AND 或者 OR
* <p>
* 例: nested(i -&gt; i.eq("name", "李白").ne("status", "活着"))
* </p>
*
* @param consumer 消费函数
* @return children
*/
default Children nested(Consumer<Param> consumer) {
return nested(true, consumer);
}
/**
* 正常嵌套 不带 AND 或者 OR
* <p>
* 例: nested(i -&gt; i.eq("name", "李白").ne("status", "活着"))
* </p>
*
* @param condition 执行条件
* @param consumer 消费函数
* @return children
*/
Children nested(boolean condition, Consumer<Param> consumer);
/**
* not嵌套
* <p>
* 例: not(i -&gt; i.eq("name", "李白").ne("status", "活着"))
* </p>
*
* @param consumer 消费函数
* @return children
*/
default Children not(Consumer<Param> consumer) {
return not(true, consumer);
}
/**
* not嵌套
* <p>
* 例: not(i -&gt; i.eq("name", "李白").ne("status", "活着"))
* </p>
*
* @param condition 执行条件
* @param consumer 消费函数
* @return children
*/
Children not(boolean condition, Consumer<Param> consumer);
}

View File

@@ -0,0 +1,109 @@
package com.jeelowcode.framework.plus.core.conditions.segments;
import com.jeelowcode.framework.plus.core.conditions.ISqlSegment;
import com.jeelowcode.framework.plus.core.toolkit.StringPool;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author JX
* @create 2024-03-26 10:02
* @dedescription:
*/
public abstract class AbstractISegmentList extends ArrayList<ISqlSegment> implements ISqlSegment, StringPool {
/**
* 最后一个值
*/
ISqlSegment lastValue = null;
/**
* 刷新 lastValue
*/
boolean flushLastValue = false;
/**
* 结果集缓存
*/
private String sqlSegment = EMPTY;
/**
* 是否缓存过结果集
*/
private boolean cacheSqlSegment = true;
/**
* 重写方法,做个性化适配
*
* @param c 元素集合
* @return 是否添加成功
*/
@Override
public boolean addAll(Collection<? extends ISqlSegment> c) {
List<ISqlSegment> list = new ArrayList<>(c);
boolean goon = transformList(list, list.get(0), list.get(list.size() - 1));
if (goon) {
cacheSqlSegment = false;
if (flushLastValue) {
this.flushLastValue(list);
}
return super.addAll(list);
}
return false;
}
/**
* 在其中对值进行判断以及更改 list 的内部元素
*
* @param list 传入进来的 ISqlSegment 集合
* @param firstSegment ISqlSegment 集合里第一个值
* @param lastSegment ISqlSegment 集合里最后一个值
* @return true 是否继续向下执行; false 不再向下执行
*/
protected abstract boolean transformList(List<ISqlSegment> list, ISqlSegment firstSegment, ISqlSegment lastSegment);
/**
* 刷新属性 lastValue
*/
private void flushLastValue(List<ISqlSegment> list) {
lastValue = list.get(list.size() - 1);
}
/**
* 删除元素里最后一个值</br>
* 并刷新属性 lastValue
*/
void removeAndFlushLast() {
remove(size() - 1);
flushLastValue(this);
}
@Override
public String getSqlSegment() {
if (cacheSqlSegment) {
return sqlSegment;
}
cacheSqlSegment = true;
sqlSegment = childrenSqlSegment();
return sqlSegment;
}
/**
* 只有该类进行过 addAll 操作,才会触发这个方法
* <p>
* 方法内可以放心进行操作
*
* @return sqlSegment
*/
protected abstract String childrenSqlSegment();
@Override
public void clear() {
super.clear();
lastValue = null;
sqlSegment = EMPTY;
cacheSqlSegment = true;
}
}

View File

@@ -0,0 +1,14 @@
package com.jeelowcode.framework.plus.core.conditions.segments;
import com.jeelowcode.framework.plus.core.conditions.ISqlSegment;
/**
* @author JX
* @create 2024-03-26 10:03
* @dedescription:
*/
@FunctionalInterface
public interface ColumnSegment extends ISqlSegment {
}

View File

@@ -0,0 +1,34 @@
package com.jeelowcode.framework.plus.core.conditions.segments;
import com.jeelowcode.framework.plus.core.conditions.ISqlSegment;
import com.jeelowcode.framework.plus.core.enums.SqlKeyword;
import com.jeelowcode.framework.plus.core.toolkit.StringPool;
import java.util.List;
import static java.util.stream.Collectors.joining;
/**
* @author JX
* @create 2024-03-26 10:10
* @dedescription:
*/
public class GroupBySegmentList extends AbstractISegmentList {
@Override
protected boolean transformList(List<ISqlSegment> list, ISqlSegment firstSegment, ISqlSegment lastSegment) {
list.remove(0);
return true;
}
@Override
protected String childrenSqlSegment() {
if (isEmpty()) {
return StringPool.EMPTY;
}
return this.stream().map(ISqlSegment::getSqlSegment).collect(joining(StringPool.COMMA, StringPool.SPACE + SqlKeyword.GROUP_BY.getSqlSegment() + StringPool.SPACE, StringPool.EMPTY));
}
}

View File

@@ -0,0 +1,36 @@
package com.jeelowcode.framework.plus.core.conditions.segments;
import com.jeelowcode.framework.plus.core.conditions.ISqlSegment;
import com.jeelowcode.framework.plus.core.enums.SqlKeyword;
import com.jeelowcode.framework.plus.core.toolkit.StringPool;
import java.util.List;
import static java.util.stream.Collectors.joining;
/**
* @author JX
* @create 2024-03-26 10:13
* @dedescription:
*/
public class HavingSegmentList extends AbstractISegmentList {
@Override
protected boolean transformList(List<ISqlSegment> list, ISqlSegment firstSegment, ISqlSegment lastSegment) {
if (!isEmpty()) {
this.add(SqlKeyword.AND);
}
list.remove(0);
return true;
}
@Override
protected String childrenSqlSegment() {
if (isEmpty()) {
return StringPool.EMPTY;
}
return this.stream().map(ISqlSegment::getSqlSegment).collect(joining(StringPool.SPACE, StringPool.SPACE + SqlKeyword.HAVING.getSqlSegment() + StringPool.SPACE, StringPool.EMPTY));
}
}

View File

@@ -0,0 +1,41 @@
package com.jeelowcode.framework.plus.core.conditions.segments;
import com.jeelowcode.framework.plus.core.conditions.ISqlSegment;
import com.jeelowcode.framework.plus.core.enums.SqlKeyword;
import com.jeelowcode.framework.plus.core.enums.WrapperKeyword;
import java.util.function.Predicate;
/**
* @author JX
* @create 2024-03-26 10:07
* @dedescription:
*/
public enum MatchSegment {
GROUP_BY(i -> i == SqlKeyword.GROUP_BY),
ORDER_BY(i -> i == SqlKeyword.ORDER_BY),
NOT(i -> i == SqlKeyword.NOT),
AND(i -> i == SqlKeyword.AND),
OR(i -> i == SqlKeyword.OR),
AND_OR(i -> i == SqlKeyword.AND || i == SqlKeyword.OR),
EXISTS(i -> i == SqlKeyword.EXISTS),
HAVING(i -> i == SqlKeyword.HAVING),
APPLY(i -> i == WrapperKeyword.APPLY);
private final Predicate<ISqlSegment> predicate;
public boolean match(ISqlSegment segment) {
return getPredicate().test(segment);
}
MatchSegment(Predicate<ISqlSegment> predicate) {
this.predicate = predicate;
}
public Predicate<ISqlSegment> getPredicate() {
return predicate;
}
}

View File

@@ -0,0 +1,100 @@
package com.jeelowcode.framework.plus.core.conditions.segments;
import com.jeelowcode.framework.plus.core.conditions.ISqlSegment;
import com.jeelowcode.framework.plus.core.toolkit.StringPool;
import java.util.Arrays;
import java.util.List;
/**
* @author JX
* @create 2024-03-26 10:09
* @dedescription:
*/
public class MergeSegments implements ISqlSegment {
private final NormalSegmentList normal = new NormalSegmentList();
private final GroupBySegmentList groupBy = new GroupBySegmentList();
private final HavingSegmentList having = new HavingSegmentList();
private final OrderBySegmentList orderBy = new OrderBySegmentList();
private String sqlSegment = StringPool.EMPTY;
private boolean cacheSqlSegment = true;
public void add(ISqlSegment... iSqlSegments) {
List<ISqlSegment> list = Arrays.asList(iSqlSegments);
ISqlSegment firstSqlSegment = list.get(0);
if (MatchSegment.ORDER_BY.match(firstSqlSegment)) {
orderBy.addAll(list);
} else if (MatchSegment.GROUP_BY.match(firstSqlSegment)) {
groupBy.addAll(list);
} else if (MatchSegment.HAVING.match(firstSqlSegment)) {
having.addAll(list);
} else {
normal.addAll(list);
}
cacheSqlSegment = false;
}
@Override
public String getSqlSegment() {
if (cacheSqlSegment) {
return sqlSegment;
}
cacheSqlSegment = true;
if (normal.isEmpty()) {
if (!groupBy.isEmpty() || !orderBy.isEmpty()) {
sqlSegment = groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment();
}
} else {
sqlSegment = normal.getSqlSegment() + groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment();
}
return sqlSegment;
}
/**
* 清理
*
* @since 3.3.1
*/
public void clear() {
sqlSegment = StringPool.EMPTY;
cacheSqlSegment = true;
normal.clear();
groupBy.clear();
having.clear();
orderBy.clear();
}
public NormalSegmentList getNormal() {
return normal;
}
public GroupBySegmentList getGroupBy() {
return groupBy;
}
public HavingSegmentList getHaving() {
return having;
}
public OrderBySegmentList getOrderBy() {
return orderBy;
}
public void setSqlSegment(String sqlSegment) {
this.sqlSegment = sqlSegment;
}
public boolean isCacheSqlSegment() {
return cacheSqlSegment;
}
public void setCacheSqlSegment(boolean cacheSqlSegment) {
this.cacheSqlSegment = cacheSqlSegment;
}
}

View File

@@ -0,0 +1,86 @@
package com.jeelowcode.framework.plus.core.conditions.segments;
import com.jeelowcode.framework.plus.core.conditions.ISqlSegment;
import com.jeelowcode.framework.plus.core.enums.SqlKeyword;
import com.jeelowcode.framework.plus.core.toolkit.StringPool;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author JX
* @create 2024-03-26 10:06
* @dedescription:
*/
public class NormalSegmentList extends AbstractISegmentList {
/**
* 是否处理了的上个 not
*/
private boolean executeNot = true;
public NormalSegmentList() {
this.flushLastValue = true;
}
@Override
protected boolean transformList(List<ISqlSegment> list, ISqlSegment firstSegment, ISqlSegment lastSegment) {
if (list.size() == 1) {
/* 只有 and() 以及 or() 以及 not() 会进入 */
if (!MatchSegment.NOT.match(firstSegment)) {
//不是 not
if (isEmpty()) {
//sqlSegment是 and 或者 or 并且在第一位,不继续执行
return false;
}
boolean matchLastAnd = MatchSegment.AND.match(lastValue);
boolean matchLastOr = MatchSegment.OR.match(lastValue);
if (matchLastAnd || matchLastOr) {
//上次最后一个值是 and 或者 or
if (matchLastAnd && MatchSegment.AND.match(firstSegment)) {
return false;
} else if (matchLastOr && MatchSegment.OR.match(firstSegment)) {
return false;
} else {
//和上次的不一样
removeAndFlushLast();
}
}
} else {
executeNot = false;
return false;
}
} else {
if (MatchSegment.APPLY.match(firstSegment)) {
list.remove(0);
}
if (!MatchSegment.AND_OR.match(lastValue) && !isEmpty()) {
add(SqlKeyword.AND);
}
if (!executeNot) {
list.add(0, SqlKeyword.NOT);
executeNot = true;
}
}
return true;
}
@Override
protected String childrenSqlSegment() {
if (MatchSegment.AND_OR.match(lastValue)) {
removeAndFlushLast();
}
final String str = this.stream().map(ISqlSegment::getSqlSegment).collect(Collectors.joining(StringPool.SPACE));
return (StringPool.LEFT_BRACKET + str + StringPool.RIGHT_BRACKET);
}
@Override
public void clear() {
super.clear();
flushLastValue = true;
executeNot = true;
}
}

View File

@@ -0,0 +1,38 @@
package com.jeelowcode.framework.plus.core.conditions.segments;
import com.jeelowcode.framework.plus.core.conditions.ISqlSegment;
import com.jeelowcode.framework.plus.core.enums.SqlKeyword;
import com.jeelowcode.framework.plus.core.toolkit.StringPool;
import java.util.ArrayList;
import java.util.List;
import static java.util.stream.Collectors.joining;
/**
* @author JX
* @create 2024-03-26 10:14
* @dedescription:
*/
public class OrderBySegmentList extends AbstractISegmentList {
@Override
protected boolean transformList(List<ISqlSegment> list, ISqlSegment firstSegment, ISqlSegment lastSegment) {
list.remove(0);
final List<ISqlSegment> sqlSegmentList = new ArrayList<>(list);
list.clear();
list.add(() -> sqlSegmentList.stream().map(ISqlSegment::getSqlSegment).collect(joining(StringPool.SPACE)));
return true;
}
@Override
protected String childrenSqlSegment() {
if (isEmpty()) {
return StringPool.EMPTY;
}
return this.stream().map(ISqlSegment::getSqlSegment).collect(joining(StringPool.COMMA, StringPool.SPACE + SqlKeyword.ORDER_BY.getSqlSegment() + StringPool.SPACE, StringPool.EMPTY));
}
}

View File

@@ -0,0 +1,180 @@
package com.jeelowcode.framework.plus.core.ddl.alter;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.alter.Alter;
import net.sf.jsqlparser.statement.alter.AlterExpression;
import net.sf.jsqlparser.statement.alter.AlterOperation;
import net.sf.jsqlparser.statement.create.table.ColDataType;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.create.table.Index;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author JX
* @create 2024-03-27 13:38
* @dedescription:
*/
public class JeeLowCodeAlterTable {
private String tableName;
private AlterOperation alterOperation;
private List<Index> indices = new ArrayList<>();
private List<ColumnDefinition> columnDefinitions = new ArrayList<>();
private List<AlterExpression> expressions = new ArrayList<>();
private Alter alter ;
public void setTableName(String tableName){
this.tableName = tableName;
alter = new Alter();
alter.setTable(new Table(tableName));
}
public void setAlterOperation(AlterOperation alterOperation){
this.alterOperation = alterOperation;
}
/**
* 新增字段
* @param columnName
* @param dataType
* @param specs
*/
public void addCoulumn(String columnName, String dataType,String... specs){
AlterExpression alterExpression = new AlterExpression();
alterExpression.setOperation(AlterOperation.ADD);
AlterExpression.ColumnDataType columnDataType = new AlterExpression.ColumnDataType(columnName,false,new ColDataType(dataType),Arrays.asList(specs));
alterExpression.addColDataType(columnDataType);
expressions.add(alterExpression);
}
public void addIndex(String indexName, String... columns){
Index index = new Index();
index.setName(indexName);
index.setColumnsNames(Arrays.asList(columns));
index.setType("index");
AlterExpression alterExpression = new AlterExpression();
alterExpression.setOperation(AlterOperation.ADD);
alterExpression.setIndex(index);
expressions.add(alterExpression);
}
public void dropColumn(String columnName){
AlterExpression alterExpression = new AlterExpression();
alterExpression.setOperation(AlterOperation.DROP);
alterExpression.setColumnName(columnName);
expressions.add(alterExpression);
}
public void dropIndex(String indexName){
Index index = new Index();
index.setName(indexName);
index.setType("index");
AlterExpression alterExpression = new AlterExpression();
alterExpression.setIndex(index);
alterExpression.setOperation(AlterOperation.DROP);
expressions.add(alterExpression);
}
public void alterColumn(String columnName, String dataType, AlterOperation alterOperation,String... specs){
AlterExpression alterExpression = new AlterExpression();
alterExpression.setColumnName(columnName);
AlterExpression.ColumnDataType columnDataType = new AlterExpression.ColumnDataType(false);
columnDataType.setColumnName(columnName);
columnDataType.setColDataType(new ColDataType(dataType));
columnDataType.setColumnSpecs(Arrays.asList(specs));
alterExpression.addColDataType(columnDataType);
alterExpression.setOperation(alterOperation);
expressions.add(alterExpression);
}
//字段属性
public void modifyColumn(String columnName, String dateType , String... specs){
AlterExpression.ColumnDataType columnDataType = new AlterExpression.ColumnDataType(false);
columnDataType.setColumnName(columnName);
columnDataType.setColDataType(new ColDataType(dateType));
columnDataType.setColumnSpecs(Arrays.asList(specs));
AlterExpression alterExpression = new AlterExpression();
alterExpression.addColDataType(columnDataType);
alterExpression.setOperation(AlterOperation.MODIFY);
expressions.add(alterExpression);
}
//字段改变
public void change(String oldName, String newName, String dataType, String... specs){
AlterExpression.ColumnDataType columnDataType = new AlterExpression.ColumnDataType(false);
columnDataType.setColumnName(newName);
columnDataType.setColumnSpecs(Arrays.asList(specs));
columnDataType.setColDataType(new ColDataType(dataType));
AlterExpression alterExpression = new AlterExpression();
alterExpression.setColumnOldName(oldName);
alterExpression.setOperation(AlterOperation.CHANGE);
alterExpression.addColDataType(columnDataType);
expressions.add(alterExpression);
}
public String getFullSQL(){
alter.setAlterExpressions(expressions);
return alter.toString();
}
@Test
public void dropTable(){
JeeLowCodeAlterTable alterTable=new JeeLowCodeAlterTable();
alterTable.setTableName("tbl_lin2");
alterTable.setAlterOperation(AlterOperation.DROP);
System.out.println(alterTable.getFullSQL());
}
@Test
public void addCoulumn(){
JeeLowCodeAlterTable alterTable=new JeeLowCodeAlterTable();
alterTable.setTableName("tbl_lin2");
alterTable.setAlterOperation(AlterOperation.ALTER);
alterTable.addCoulumn("name","varchar(20)","not null","COMMENT '姓名'");
System.out.println(alterTable.getFullSQL());
}
@Test
public void test_modifyCoulumn(){
JeeLowCodeAlterTable alterTable=new JeeLowCodeAlterTable();
alterTable.setTableName("tbl_lin2");
alterTable.modifyColumn("name","varchar(20)","not null","COMMENT '姓名'");
System.out.println(alterTable.getFullSQL());
}
@Test
public void test_dropColumn(){
JeeLowCodeAlterTable alterTable=new JeeLowCodeAlterTable();
alterTable.setTableName("tbl_lin2");
alterTable.dropColumn("name");
System.out.println(alterTable.getFullSQL());
}
@Test
public void test_addIndex(){
JeeLowCodeAlterTable alterTable=new JeeLowCodeAlterTable();
alterTable.setTableName("tbl_lin2");
alterTable.addIndex("ind_name1","id","name");
System.out.println(alterTable.getFullSQL());
}
@Test
public void test_dropIndex(){
JeeLowCodeAlterTable alterTable=new JeeLowCodeAlterTable();
alterTable.setTableName("tbl_lin2");
alterTable.dropIndex("ind_name1");
System.out.println(alterTable.getFullSQL());
}
}

View File

@@ -0,0 +1,73 @@
package com.jeelowcode.framework.plus.core.ddl.create;
import com.jeelowcode.framework.utils.utils.FuncBase;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.create.table.ColDataType;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import net.sf.jsqlparser.statement.create.table.Index;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
* @author JX
* @create 2024-03-27 10:40
* @dedescription:
*/
public class JeeLowCodeCreateTable {
//表名
private String tableName;
//别名
private List<String> tableOptions;
//字段
private List<ColumnDefinition> columnDefinitions=new ArrayList<>();
//索引
private List<Index> indices = new ArrayList<>();
public void setTableName(String tableName){
this.tableName = tableName;
}
public void setTableOptions(List<String> tableOptions) {
this.tableOptions = tableOptions;
}
public void addCoulumn(String columnName, String dataType, String... specs){
ColumnDefinition columnDefinition = new ColumnDefinition(columnName,new ColDataType(dataType));
if (!FuncBase.isEmpty(specs)){
columnDefinition.addColumnSpecs(specs);
}
this.columnDefinitions.add(columnDefinition);
}
public String getDDL(){
CreateTable createTable = new CreateTable();
createTable.setTable(new Table(tableName));
createTable.setTableOptionsStrings(tableOptions);
createTable.setColumnDefinitions(columnDefinitions);
createTable.setIndexes(indices);
return createTable.toString();
}
@Test
public void testCreate(){
JeeLowCodeCreateTable createTable=new JeeLowCodeCreateTable();
createTable.setTableName("tbl_lin2");
createTable.addCoulumn("id","varchar(12)","PRIMARY KEY","COMMENT 'id'","not null");
createTable.addCoulumn("name","varchar(20)","not null","COMMENT '姓名'");
System.out.println(createTable.getDDL());
}
}

View File

@@ -0,0 +1,41 @@
package com.jeelowcode.framework.plus.core.ddl.drop;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.drop.Drop;
import org.junit.Test;
/**
* 删除表
* @author JX
* @create 2024-03-27 13:38
* @dedescription:
*/
public class JeeLowCodeDropTable {
private String tableName;
private Drop drop ;
public void setTableName(String tableName){
this.tableName = tableName;
drop = new Drop();
drop.setName(new Table(tableName));
drop.setType("TABLE");
}
public String getFullSQL(){
return drop.toString();
}
@Test
public void dropTable(){
JeeLowCodeDropTable dropTable=new JeeLowCodeDropTable();
dropTable.setTableName("tbl_lin2");
// alterTable.setAlterOperation(AlterOperation.);
System.out.println(dropTable.getFullSQL());
}
}

View File

@@ -0,0 +1,56 @@
package com.jeelowcode.framework.plus.core.dql.delete;
import com.jeelowcode.framework.plus.core.conditions.AbstractWrapper;
import com.jeelowcode.framework.plus.core.conditions.SharedString;
import com.jeelowcode.framework.plus.core.conditions.segments.MergeSegments;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author JX
* @create 2024-03-27 17:46
* @dedescription:
*/
public class JeeLowCodeDeleteWrapper<T> extends AbstractWrapper<T, String, JeeLowCodeDeleteWrapper<T>> {
public JeeLowCodeDeleteWrapper() {
this((T) null);
}
public JeeLowCodeDeleteWrapper(T entity) {
super.setEntity(entity);
super.initNeed();
}
public JeeLowCodeDeleteWrapper(AtomicInteger paramNameSeq,
Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,
SharedString lastSql, SharedString sqlComment){
this.paramNameSeq = paramNameSeq;
this.paramNameValuePairs = paramNameValuePairs;
this.paramAlias = paramAlias;
this.lastSql = lastSql;
this.sqlComment = sqlComment;
this.expression = new MergeSegments();
}
@Override
protected JeeLowCodeDeleteWrapper instance() {
return new JeeLowCodeDeleteWrapper( paramNameSeq , paramNameValuePairs , expression , paramAlias , SharedString.emptyString() , SharedString.emptyString());
}
public void orderby(){
return;
}
public void groupBy(){
}
}

View File

@@ -0,0 +1,95 @@
package com.jeelowcode.framework.plus.core.dql.insert;
import com.jeelowcode.framework.plus.core.conditions.AbstractWrapper;
import com.jeelowcode.framework.plus.core.conditions.SharedString;
import com.jeelowcode.framework.plus.core.conditions.segments.MergeSegments;
import com.jeelowcode.framework.plus.core.dql.update.Update;
import com.jeelowcode.framework.plus.core.toolkit.CollectionUtils;
import com.jeelowcode.framework.plus.core.toolkit.Constants;
import com.jeelowcode.framework.plus.core.toolkit.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author JX
* @create 2024-03-27 17:47
* @dedescription:
*/
public class JeeLowCodeInsertWrapper<T> extends AbstractWrapper<T, String, JeeLowCodeInsertWrapper<T>>
implements Update<JeeLowCodeInsertWrapper<T>, String> {
private final List<String> sqlSet;
public JeeLowCodeInsertWrapper() {
// 如果无参构造函数,请注意实体 NULL 情况 SET 必须有否则 SQL 异常
this(null);
}
public JeeLowCodeInsertWrapper(T entity) {
super.setEntity(entity);
super.initNeed();
this.sqlSet = new ArrayList<>();
}
private JeeLowCodeInsertWrapper(T entity, List<String> sqlSet, AtomicInteger paramNameSeq,
Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,
SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
super.setEntity(entity);
this.sqlSet = sqlSet;
this.paramNameSeq = paramNameSeq;
this.paramNameValuePairs = paramNameValuePairs;
this.expression = mergeSegments;
this.paramAlias = paramAlias;
this.lastSql = lastSql;
this.sqlComment = sqlComment;
this.sqlFirst = sqlFirst;
}
@Override
public String getSqlSet() {
if (CollectionUtils.isEmpty(sqlSet)) {
return null;
}
return String.join(Constants.COMMA, sqlSet);
}
@Override
public JeeLowCodeInsertWrapper<T> set(boolean condition, String column, Object val, String mapping) {
return maybeDo(condition, () -> {
String sql = formatParam(mapping, val);
sqlSet.add(column + Constants.EQUALS + sql);
});
}
@Override
public JeeLowCodeInsertWrapper<T> setSql(boolean condition, String setSql, Object... params) {
if (condition && StringUtils.isNotBlank(setSql)) {
sqlSet.add(formatSqlMaybeWithParam(setSql, params));
}
return typedThis;
}
@Override
protected JeeLowCodeInsertWrapper<T> instance() {
return new JeeLowCodeInsertWrapper<>(getEntity(), null, paramNameSeq, paramNameValuePairs, new MergeSegments(),
paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
}
@Override
public void clear() {
super.clear();
sqlSet.clear();
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.dql.select;
import com.jeelowcode.framework.plus.core.conditions.AbstractWrapper;
import com.jeelowcode.framework.plus.core.conditions.SharedString;
import com.jeelowcode.framework.plus.core.conditions.segments.MergeSegments;
import com.jeelowcode.framework.plus.core.exceptions.JeelowcodeSqlException;
import com.jeelowcode.framework.plus.core.toolkit.CollectionUtils;
import com.jeelowcode.framework.plus.core.toolkit.StringPool;
import com.jeelowcode.framework.plus.core.toolkit.sql.SqlInjectionUtils;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Entity 对象封装操作类
*
* @author hubin miemie HCL
* @since 2018-05-25
*/
public class JeeLowCodeQueryWrapper<T> extends AbstractWrapper<T, String, JeeLowCodeQueryWrapper<T>>
implements Query<JeeLowCodeQueryWrapper<T>, T, String> {
/**
* 查询字段
*/
protected final SharedString sqlSelect = new SharedString();
public JeeLowCodeQueryWrapper() {
this((T) null);
}
public JeeLowCodeQueryWrapper(T entity) {
super.setEntity(entity);
super.initNeed();
}
public JeeLowCodeQueryWrapper(Class<T> entityClass) {
super.setEntityClass(entityClass);
super.initNeed();
}
public JeeLowCodeQueryWrapper(T entity, String... columns) {
super.setEntity(entity);
super.initNeed();
this.select(Arrays.asList(columns));
}
/**
* 非对外公开的构造方法,只用于生产嵌套 sql
*
* @param entityClass 本不应该需要的
*/
private JeeLowCodeQueryWrapper(T entity, Class<T> entityClass, AtomicInteger paramNameSeq,
Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,
SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
super.setEntity(entity);
super.setEntityClass(entityClass);
this.paramNameSeq = paramNameSeq;
this.paramNameValuePairs = paramNameValuePairs;
this.expression = mergeSegments;
this.paramAlias = paramAlias;
this.lastSql = lastSql;
this.sqlComment = sqlComment;
this.sqlFirst = sqlFirst;
}
private JeeLowCodeQueryWrapper(T entity, Class<T> entityClass, AtomicInteger paramNameSeq,
Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,
SharedString lastSql, SharedString sqlComment, SharedString sqlFirst, String tableName) {
super.setEntity(entity);
super.setEntityClass(entityClass);
this.paramNameSeq = paramNameSeq;
this.paramNameValuePairs = paramNameValuePairs;
this.expression = mergeSegments;
this.paramAlias = paramAlias;
this.lastSql = lastSql;
this.sqlComment = sqlComment;
this.sqlFirst = sqlFirst;
}
/**
* 检查 SQL 注入过滤
*/
private boolean checkSqlInjection;
/**
* 开启检查 SQL 注入
*/
public JeeLowCodeQueryWrapper<T> checkSqlInjection() {
this.checkSqlInjection = true;
return this;
}
@Override
protected String columnToString(String column) {
if (checkSqlInjection && SqlInjectionUtils.check(column)) {
throw new JeelowcodeSqlException("Discovering SQL injection column: " + column);
}
return column;
}
@Override
public JeeLowCodeQueryWrapper<T> select(boolean condition, List<String> columns) {
if (condition && CollectionUtils.isNotEmpty(columns)) {
this.sqlSelect.setStringValue(String.join(StringPool.COMMA, columns));
}
return typedThis;
}
@Override
public String getSqlSelect() {
return sqlSelect.getStringValue();
}
/**
* 用于生成嵌套 sql
* <p>
* 故 sqlSelect 不向下传递
* </p>
*/
@Override
protected JeeLowCodeQueryWrapper<T> instance() {
return new JeeLowCodeQueryWrapper<>(getEntity(), getEntityClass(), paramNameSeq, paramNameValuePairs, new MergeSegments(),
paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
}
@Override
public void clear() {
super.clear();
sqlSelect.toNull();
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.dql.select;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
/**
* @author miemie
* @since 2018-12-12
*/
public interface Query<Children, T, R> extends Serializable {
/**
* 指定查询字段
*
* @param columns 字段列表
* @return children
*/
@SuppressWarnings("unchecked")
default Children select(R... columns) {
return select(true, columns);
}
/**
* 指定查询字段
*
* @param condition 执行条件
* @param columns 字段列表
* @return children
*/
@SuppressWarnings("unchecked")
default Children select(boolean condition, R... columns) {
return select(condition, Arrays.asList(columns));
}
/**
* 指定查询字段
*
* @param columns 字段列表
* @return children
*/
default Children select(List<R> columns) {
return select(true, columns);
}
/**
* 指定查询字段
*
* @param condition 执行条件
* @param columns 字段列表
* @return children
*/
Children select(boolean condition, List<R> columns);
/**
* 过滤查询的字段信息(主键除外!)
* <p>注意只有内部有 entity 才能使用该方法</p>
*/
// default Children select(Predicate<TableFieldInfo> predicate) {
// return select(null, predicate);
// }
/**
* 过滤查询的字段信息(主键除外!)
* <p>例1: 只要 java 字段名以 "test" 开头的 -> select(i -> i.getProperty().startsWith("test"))</p>
* <p>例2: 只要 java 字段属性是 CharSequence 类型的 -> select(TableFieldInfo::isCharSequence)</p>
* <p>例3: 只要 java 字段没有填充策略的 -> select(i -> i.getFieldFill() == FieldFill.DEFAULT)</p>
* <p>例4: 要全部字段 -> select(i -> true)</p>
* <p>例5: 只要主键字段 -> select(i -> false)</p>
*
* @param predicate 过滤方式
* @return children
*/
// Children select(Class<T> entityClass, Predicate<TableFieldInfo> predicate);
/**
* 查询条件 SQL 片段
*/
String getSqlSelect();
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.dql.update;
import com.jeelowcode.framework.plus.core.conditions.AbstractWrapper;
import com.jeelowcode.framework.plus.core.conditions.SharedString;
import com.jeelowcode.framework.plus.core.conditions.segments.MergeSegments;
import com.jeelowcode.framework.plus.core.toolkit.CollectionUtils;
import com.jeelowcode.framework.plus.core.toolkit.Constants;
import com.jeelowcode.framework.plus.core.toolkit.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Update 条件封装
*
* @author hubin miemie HCL
* @since 2018-05-30
*/
public class JeeLowCodeUpdateWrapper<T> extends AbstractWrapper<T, String, JeeLowCodeUpdateWrapper<T>>
implements Update<JeeLowCodeUpdateWrapper<T>, String> {
/**
* SQL 更新字段内容例如name='1', age=2
*/
private final List<String> sqlSet;
public JeeLowCodeUpdateWrapper() {
// 如果无参构造函数,请注意实体 NULL 情况 SET 必须有否则 SQL 异常
this(null);
}
public JeeLowCodeUpdateWrapper(T entity) {
super.setEntity(entity);
super.initNeed();
this.sqlSet = new ArrayList<>();
}
private JeeLowCodeUpdateWrapper(T entity, List<String> sqlSet, AtomicInteger paramNameSeq,
Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias,
SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
super.setEntity(entity);
this.sqlSet = sqlSet;
this.paramNameSeq = paramNameSeq;
this.paramNameValuePairs = paramNameValuePairs;
this.expression = mergeSegments;
this.paramAlias = paramAlias;
this.lastSql = lastSql;
this.sqlComment = sqlComment;
this.sqlFirst = sqlFirst;
}
@Override
public String getSqlSet() {
if (CollectionUtils.isEmpty(sqlSet)) {
return null;
}
return String.join(Constants.COMMA, sqlSet);
}
@Override
public JeeLowCodeUpdateWrapper<T> set(boolean condition, String column, Object val, String mapping) {
return maybeDo(condition, () -> {
String sql = formatParam(mapping, val);
sqlSet.add(column + Constants.EQUALS + sql);
});
}
@Override
public JeeLowCodeUpdateWrapper<T> setSql(boolean condition, String setSql, Object... params) {
if (condition && StringUtils.isNotBlank(setSql)) {
sqlSet.add(formatSqlMaybeWithParam(setSql, params));
}
return typedThis;
}
@Override
protected JeeLowCodeUpdateWrapper<T> instance() {
return new JeeLowCodeUpdateWrapper<>(getEntity(), null, paramNameSeq, paramNameValuePairs, new MergeSegments(),
paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
}
@Override
public void clear() {
super.clear();
sqlSet.clear();
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.dql.update;
import java.io.Serializable;
/**
* @author miemie
* @since 2018-12-12
*/
public interface Update<Children, R> extends Serializable {
/**
* 设置 更新 SQL 的 SET 片段
*
* @param column 字段
* @param val 值
* @return children
*/
default Children set(R column, Object val) {
return set(true, column, val);
}
/**
* 设置 更新 SQL 的 SET 片段
*
* @param condition 是否加入 set
* @param column 字段
* @param val 值
* @return children
*/
default Children set(boolean condition, R column, Object val) {
return set(condition, column, val, null);
}
/**
* 设置 更新 SQL 的 SET 片段
*
* @param column 字段
* @param val 值
* @param mapping 例: javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler
* @return children
*/
default Children set(R column, Object val, String mapping) {
return set(true, column, val, mapping);
}
/**
* 设置 更新 SQL 的 SET 片段
*
* @param condition 是否加入 set
* @param column 字段
* @param val 值
* @param mapping 例: javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler
* @return children
*/
Children set(boolean condition, R column, Object val, String mapping);
/**
* 设置 更新 SQL 的 SET 片段
*
* @param setSql set sql
* 例1: setSql("id=1")
* 例2: apply("dateColumn={0}", LocalDate.now())
* 例3: apply("dateColumn={0}", LocalDate.now())
* 例4: apply("name={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}", "老王")
* @return children
*/
default Children setSql(String setSql, Object... params) {
return setSql(true, setSql, params);
}
/**
* 设置 更新 SQL 的 SET 片段
*
* @param condition 执行条件
* @param setSql set sql
* 例1: setSql("id=1")
* 例2: apply("dateColumn={0}", LocalDate.now())
* 例3: apply("dateColumn={0}", LocalDate.now())
* 例4: apply("name={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}", "老王")
* @return children
*/
Children setSql(boolean condition, String setSql, Object... params);
/**
* 获取 更新 SQL 的 SET 片段
*/
String getSqlSet();
}

View File

@@ -0,0 +1,54 @@
package com.jeelowcode.framework.plus.core.enums;
import com.jeelowcode.framework.plus.core.conditions.ISqlSegment;
import com.jeelowcode.framework.plus.core.toolkit.StringPool;
/**
* @author JX
* @create 2024-03-26 10:01
* @dedescription:
*/
public enum SqlKeyword implements ISqlSegment {
AND("AND"),
OR("OR"),
NOT("NOT"),
IN("IN"),
NOT_IN("NOT IN"),
LIKE("LIKE"),
NOT_LIKE("NOT LIKE"),
EQ(StringPool.EQUALS),
NE("<>"),
GT(StringPool.RIGHT_CHEV),
GE(">="),
LT(StringPool.LEFT_CHEV),
LE("<="),
IS_NULL("IS NULL"),
IS_NOT_NULL("IS NOT NULL"),
GROUP_BY("GROUP BY"),
HAVING("HAVING"),
ORDER_BY("ORDER BY"),
EXISTS("EXISTS"),
NOT_EXISTS("NOT EXISTS"),
BETWEEN("BETWEEN"),
NOT_BETWEEN("NOT BETWEEN"),
ASC("ASC"),
DESC("DESC");
private final String keyword;
@Override
public String getSqlSegment() {
return this.keyword;
}
SqlKeyword(String keyword) {
this.keyword = keyword;
}
public String getKeyword() {
return keyword;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.enums;
/**
* SQL like 枚举
*
* @author Caratacus
* @since 2016-12-4
*/
public enum SqlLike {
/**
* %值
*/
LEFT,
/**
* 值%
*/
RIGHT,
/**
* %值%
*/
DEFAULT
}

View File

@@ -0,0 +1,32 @@
package com.jeelowcode.framework.plus.core.enums;
import com.jeelowcode.framework.plus.core.conditions.ISqlSegment;
/**
* @author JX
* @create 2024-03-26 10:08
* @dedescription:
*/
public enum WrapperKeyword implements ISqlSegment {
/**
* 只用作于辨识,不用于其他
*/
APPLY(null);
private final String keyword;
@Override
public String getSqlSegment() {
return keyword;
}
public String getKeyword() {
return keyword;
}
WrapperKeyword(String keyword) {
this.keyword = keyword;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.exceptions;
/**
* MybatisPlus 异常类
*
* @author hubin
* @since 2016-01-23
*/
public class JeelowcodeSqlException extends RuntimeException {
private static final long serialVersionUID = 1L;
public JeelowcodeSqlException(String message) {
super(message);
}
public JeelowcodeSqlException(Throwable throwable) {
super(throwable);
}
public JeelowcodeSqlException(String message, Throwable throwable) {
super(message, throwable);
}
}

View File

@@ -0,0 +1,30 @@
package com.jeelowcode.framework.plus.core.model;
public class OrderByModel {
/**
* 默认排序字段
*/
private String column;
/**
* 默认排序类型
*/
private String order;
public String getColumn() {
return column;
}
public void setColumn(String column) {
this.column = column;
}
public String getOrder() {
return order;
}
public void setOrder(String order) {
this.order = order;
}
}

View File

@@ -0,0 +1,44 @@
package com.jeelowcode.framework.plus.core.model;
import java.util.Map;
public class SqlFormatModel {
private String sql;
private Map<String,Object> dataMap;
public SqlFormatModel() {
}
public SqlFormatModel(String sql, Map<String, Object> dataMap) {
this.sql = sql;
this.dataMap = dataMap;
}
public SqlFormatModel(String sql) {
this.sql = sql;
}
public String getSql() {
return sql;
}
public String getEwSql() {
String replacedSql = sql.replaceAll("(?<!#\\{ew\\.)\\#\\{(\\w+)}", "#{ew.$1}");
return replacedSql;
}
public void setSql(String sql) {
this.sql = sql;
}
public Map<String, Object> getDataMap() {
return dataMap;
}
public void setDataMap(Map<String, Object> dataMap) {
this.dataMap = dataMap;
}
}

View File

@@ -0,0 +1,37 @@
package com.jeelowcode.framework.plus.core.model;
import com.jeelowcode.framework.utils.enums.JeeLowCodeFieldTypeEnum;
/**
* 创建表-字段
*/
public class TableFieldModel {
String fieldCode;
JeeLowCodeFieldTypeEnum fieldType;//字段类型
String defaultValSql;
public String getFieldCode() {
return fieldCode;
}
public void setFieldCode(String fieldCode) {
this.fieldCode = fieldCode;
}
public JeeLowCodeFieldTypeEnum getFieldType() {
return fieldType;
}
public void setFieldType(JeeLowCodeFieldTypeEnum fieldType) {
this.fieldType = fieldType;
}
public String getDefaultValSql() {
return defaultValSql;
}
public void setDefaultValSql(String defaultValSql) {
this.defaultValSql = defaultValSql;
}
}

View File

@@ -0,0 +1,17 @@
package com.jeelowcode.framework.plus.core.model;
import java.util.List;
public class WhereModel {
private List<String> whereList;
public List<String> getWhereList() {
return whereList;
}
public void setWhereList(List<String> whereList) {
this.whereList = whereList;
}
}

View File

@@ -0,0 +1,116 @@
package com.jeelowcode.framework.plus.core.toolkit;
import com.jeelowcode.framework.utils.utils.FuncBase;
import java.util.Collection;
import java.util.Map;
/**
* @author JX
* @create 2024-03-26 10:26
* @dedescription:
*/
public final class Assert {
/**
* 断言这个 boolean 为 true
* <p>为 false 则抛出异常</p>
*
* @param expression boolean 值
* @param message 消息
*/
public static void isTrue(boolean expression, String message, Object... params) {
if (!expression) {
throw ExceptionUtils.mpe(message, params);
}
}
/**
* 断言这个 boolean 为 false
* <p>为 true 则抛出异常</p>
*
* @param expression boolean 值
* @param message 消息
*/
public static void isFalse(boolean expression, String message, Object... params) {
isTrue(!expression, message, params);
}
/**
* 断言这个 object 为 null
* <p>不为 null 则抛异常</p>
*
* @param object 对象
* @param message 消息
*/
public static void isNull(Object object, String message, Object... params) {
isTrue(object == null, message, params);
}
/**
* 断言这个 object 不为 null
* <p>为 null 则抛异常</p>
*
* @param object 对象
* @param message 消息
*/
public static void notNull(Object object, String message, Object... params) {
isTrue(object != null, message, params);
}
/**
* 断言这个 value 不为 empty
* <p>为 empty 则抛异常</p>
*
* @param value 字符串
* @param message 消息
*/
public static void notEmpty(String value, String message, Object... params) {
isTrue(StringUtils.isNotBlank(value), message, params);
}
/**
* 断言这个 collection 不为 empty
* <p>为 empty 则抛异常</p>
*
* @param collection 集合
* @param message 消息
*/
public static void notEmpty(Collection<?> collection, String message, Object... params) {
isTrue(CollectionUtils.isNotEmpty(collection), message, params);
}
/**
* 断言这个 map 不为 empty
* <p>为 empty 则抛异常</p>
*
* @param map 集合
* @param message 消息
*/
public static void notEmpty(Map<?, ?> map, String message, Object... params) {
isTrue(CollectionUtils.isNotEmpty(map), message, params);
}
/**
* 断言这个 map 为 empty
* <p>为 empty 则抛异常</p>
*
* @param map 集合
* @param message 消息
*/
public static void isEmpty(Map<?, ?> map, String message, Object... params) {
isTrue(CollectionUtils.isEmpty(map), message, params);
}
/**
* 断言这个 数组 不为 empty
* <p>为 empty 则抛异常</p>
*
* @param array 数组
* @param message 消息
*/
public static void notEmpty(Object[] array, String message, Object... params) {
isTrue(FuncBase.isNotEmpty(array), message, params);
}
}

View File

@@ -0,0 +1,226 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.toolkit;
import java.util.*;
import java.util.function.Function;
/**
* Collection工具类
*
* @author Caratacus
* @since 2016-09-19
*/
public class CollectionUtils {
private static final int MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2);
private static boolean isJdk8;
static {
// Java 8
// Java 9+: 9,11,17
try {
isJdk8 = System.getProperty("java.version").startsWith("1.8.");
} catch (Exception ignore) {
isJdk8 = true;
}
}
/**
* 校验集合是否为空
*
* @param coll 入参
* @return boolean
*/
public static boolean isEmpty(Collection<?> coll) {
return (coll == null || coll.isEmpty());
}
/**
* 校验集合是否不为空
*
* @param coll 入参
* @return boolean
*/
public static boolean isNotEmpty(Collection<?> coll) {
return !isEmpty(coll);
}
/**
* 判断Map是否为空
*
* @param map 入参
* @return boolean
*/
public static boolean isEmpty(Map<?, ?> map) {
return (map == null || map.isEmpty());
}
/**
* 判断Map是否不为空
*
* @param map 入参
* @return boolean
*/
public static boolean isNotEmpty(Map<?, ?> map) {
return !isEmpty(map);
}
/**
* 创建默认HashMap
*
* @param <K> K
* @param <V> V
* @return HashMap
* @see com.google.common.collect.Maps#newHashMap()
* @since 3.4.0
*/
public static <K, V> HashMap<K, V> newHashMap() {
return new HashMap<>();
}
/**
* 根据预期大小创建HashMap.
*
* @param expectedSize 预期大小
* @param <K> K
* @param <V> V
* @return HashMap
* @see com.google.common.collect.Maps#newHashMapWithExpectedSize
* @since 3.4.0
*/
public static <K, V> HashMap<K, V> newHashMapWithExpectedSize(int expectedSize) {
return new HashMap<>(capacity(expectedSize));
}
/**
* 用来过渡下Jdk1.8下ConcurrentHashMap的性能bug
* https://bugs.openjdk.java.net/browse/JDK-8161372
*
* A temporary workaround for Java 8 ConcurrentHashMap#computeIfAbsent specific performance issue: JDK-8161372.</br>
* @see <a href="https://bugs.openjdk.java.net/browse/JDK-8161372">https://bugs.openjdk.java.net/browse/JDK-8161372</a>
*
* @param concurrentHashMap ConcurrentHashMap 没限制类型了非ConcurrentHashMap就别调用这方法了
* @param key key
* @param mappingFunction function
* @param <K> k
* @param <V> v
* @return V
* @since 3.4.0
*/
public static <K, V> V computeIfAbsent(Map<K, V> concurrentHashMap, K key, Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
if (isJdk8) {
V v = concurrentHashMap.get(key);
if (null == v) {
// issue#11986 lock bug
// v = map.computeIfAbsent(key, func);
// this bug fix methods maybe cause `func.apply` multiple calls.
v = mappingFunction.apply(key);
if (null == v) {
return null;
}
final V res = concurrentHashMap.putIfAbsent(key, v);
if (null != res) {
// if pre value present, means other thread put value already, and putIfAbsent not effect
// return exist value
return res;
}
// if pre value is null, means putIfAbsent effected, return current value
}
return v;
} else {
return concurrentHashMap.computeIfAbsent(key, mappingFunction);
}
}
/**
* Returns a capacity that is sufficient to keep the map from being resized as
* long as it grows no larger than expectedSize and the load factor is >= its
* default (0.75).
*
* @see com.google.common.collect.Maps#capacity(int)
* @since 3.4.0
*/
private static int capacity(int expectedSize) {
if (expectedSize < 3) {
if (expectedSize < 0) {
throw new IllegalArgumentException("expectedSize cannot be negative but was: " + expectedSize);
}
return expectedSize + 1;
}
if (expectedSize < MAX_POWER_OF_TWO) {
// This is the calculation used in JDK8 to resize when a putAll
// happens; it seems to be the most conservative calculation we
// can make. 0.75 is the default load factor.
return (int) ((float) expectedSize / 0.75F + 1.0F);
}
return Integer.MAX_VALUE; // any large value
}
// 提供处理Map多key取值工具方法
/**
* 批量取出Map中的值
*
* @param map map
* @param keys 键的集合
* @param <K> key的泛型
* @param <V> value的泛型
* @return value的泛型的集合
*/
public static <K, V> List<V> getCollection(Map<K, V> map, Iterable<K> keys) {
List<V> result = new ArrayList<>();
if (map != null && !map.isEmpty() && keys != null) {
keys.forEach(key -> Optional.ofNullable(map.get(key)).ifPresent(result::add));
}
return result;
}
/**
* 批量取出Map中的值
*
* @param map map
* @param keys 键的集合
* @param comparator 排序器
* @param <K> key的泛型
* @param <V> value的泛型
* @return value的泛型的集合
*/
public static <K, V> List<V> getCollection(Map<K, V> map, Iterable<K> keys, Comparator<V> comparator) {
Objects.requireNonNull(comparator);
List<V> result = getCollection(map, keys);
Collections.sort(result, comparator);
return result;
}
/**
* 构建List
*
* @since 3.5.4
*/
@SafeVarargs
public static <T> List<T> toList(T... t) {
if (t != null) {
return Arrays.asList(t);
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,187 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.toolkit;
import java.io.Serializable;
/**
* mybatis_plus 自用常量集中管理
*
* @author miemie
* @since 2018-07-22
*/
public interface Constants extends StringPool, Serializable {
/**
* project name
*/
String MYBATIS_PLUS = "mybatis-plus";
/**
* MD5
*/
String MD5 = "MD5";
/**
* AES
*/
String AES = "AES";
/**
* AES 算法
*/
String AES_CBC_CIPHER = "AES/CBC/PKCS5Padding";
/**
* as
*/
String AS = " AS ";
/**
* 实体类
*/
String ENTITY = "et";
/**
* 实体类 带后缀 ==> .
*/
String ENTITY_DOT = ENTITY + DOT;
/**
* wrapper 类
*/
String WRAPPER = "ew";
/**
* wrapper 类 带后缀 ==> .
*/
String WRAPPER_DOT = WRAPPER + DOT;
/**
* wrapper 类的属性 entity
*/
String WRAPPER_ENTITY = WRAPPER_DOT + "entity";
/**
* wrapper 类的属性 sqlSegment
*/
String WRAPPER_SQLSEGMENT = WRAPPER_DOT + "sqlSegment";
/**
* wrapper 类的属性 emptyOfNormal
*/
String WRAPPER_EMPTYOFNORMAL = WRAPPER_DOT + "emptyOfNormal";
/**
* wrapper 类的属性 nonEmptyOfNormal
*/
String WRAPPER_NONEMPTYOFNORMAL = WRAPPER_DOT + "nonEmptyOfNormal";
/**
* wrapper 类的属性 nonEmptyOfEntity
*/
String WRAPPER_NONEMPTYOFENTITY = WRAPPER_DOT + "nonEmptyOfEntity";
/**
* wrapper 类的属性 emptyOfWhere
*/
String WRAPPER_EMPTYOFWHERE = WRAPPER_DOT + "emptyOfWhere";
/**
* wrapper 类的判断属性 nonEmptyOfWhere
*/
String WRAPPER_NONEMPTYOFWHERE = WRAPPER_DOT + "nonEmptyOfWhere";
/**
* wrapper 类的属性 entity 带后缀 ==> .
*/
String WRAPPER_ENTITY_DOT = WRAPPER_DOT + "entity" + DOT;
/**
* wrapper 类的属性 expression 下级属性 order
*/
String WRAPPER_EXPRESSION_ORDER = WRAPPER_DOT + "useAnnotationOrderBy";
/**
* UpdateWrapper 类的属性 sqlSet
*/
String U_WRAPPER_SQL_SET = WRAPPER_DOT + "sqlSet";
/**
* QueryWrapper 类的属性 sqlSelect
*/
String Q_WRAPPER_SQL_SELECT = WRAPPER_DOT + "sqlSelect";
/**
* wrapper 类的属性 sqlComment
*/
String Q_WRAPPER_SQL_COMMENT = WRAPPER_DOT + "sqlComment";
/**
* wrapper 类的属性 sqlFirst
*/
String Q_WRAPPER_SQL_FIRST = WRAPPER_DOT + "sqlFirst";
/**
* columnMap
*/
@Deprecated
String COLUMN_MAP = "cm";
/**
* columnMap.isEmpty
*/
String COLUMN_MAP_IS_EMPTY = COLUMN_MAP + DOT + "isEmpty";
/**
* collection
*
* @see #COLL
* @deprecated 3.5.2 后面修改成collection
*/
@Deprecated
String COLLECTION = "coll";
/**
* @since 3.5.2
*/
String COLL = "coll";
/**
* list
*
* @since 3.5.0
*/
String LIST = "list";
/**
* where
*/
String WHERE = "WHERE";
/*
* having
*/
String HAVING = "HAVING";
/**
* limit
*/
String LIMIT = "LIMIT";
/**
* @since 3.5.2
*/
String ARRAY = "array";
/**
* order by
*/
String ORDER_BY = "ORDER BY";
/**
* asc
*/
String ASC = "ASC";
/**
* desc
*/
String DESC = "DESC";
/**
* 乐观锁字段
*/
String MP_OPTLOCK_VERSION_ORIGINAL = "MP_OPTLOCK_VERSION_ORIGINAL";
/**
* wrapper 内部参数相关
*/
String WRAPPER_PARAM = "MPGENVAL";
String WRAPPER_PARAM_MIDDLE = ".paramNameValuePairs" + DOT;
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.toolkit;
import com.jeelowcode.framework.plus.core.exceptions.JeelowcodeSqlException;
/**
* 异常辅助工具类
*
* @author HCL
* @since 2018-07-24
*/
public final class ExceptionUtils {
private ExceptionUtils() {
}
/**
* 返回一个新的异常,统一构建,方便统一处理
*
* @param msg 消息
* @param t 异常信息
* @return 返回异常
*/
public static JeelowcodeSqlException mpe(String msg, Throwable t, Object... params) {
return new JeelowcodeSqlException(String.format(msg, params), t);
}
/**
* 重载的方法
*
* @param msg 消息
* @return 返回异常
*/
public static JeelowcodeSqlException mpe(String msg, Object... params) {
return new JeelowcodeSqlException(String.format(msg, params));
}
/**
* 重载的方法
*
* @param t 异常
* @return 返回异常
*/
public static JeelowcodeSqlException mpe(Throwable t) {
return new JeelowcodeSqlException(t);
}
public static void throwMpe(boolean condition, String msg, Object... params) {
if (condition) {
throw mpe(msg, params);
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.toolkit;
import java.io.*;
/**
* <p> copy from spring-core#org.springframework.util.SerializationUtils version 5.2.8 </p>
*
* @since 1.0
*/
public class SerializationUtils {
/**
* Deep clone an {@code Object} using serialization.
* <p>This is many times slower than writing clone methods by hand
* on all objects in your object graph. However, for complex object
* graphs, or for those that don't support deep cloning this can
* be a simple alternative implementation. Of course all the objects
* must be {@code Serializable}.</p>
*
* @param <T> the type of the object involved
* @param object the {@code Serializable} object to clone
* @return the cloned object
*/
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(final T object) {
if (object == null) {
return null;
}
final byte[] objectData = serialize(object);
return (T) deserialize(objectData);
}
/**
* Serialize the given object to a byte array.
*
* @param object the object to serialize
* @return an array of bytes representing the object in a portable fashion
*/
public static byte[] serialize(Object object) {
if (object == null) {
return null;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(object);
oos.flush();
} catch (IOException ex) {
throw new IllegalArgumentException("Failed to serialize object of type: " + object.getClass(), ex);
}
return baos.toByteArray();
}
/**
* Deserialize the byte array into an object.
*
* @param bytes a serialized object
* @return the result of deserializing the bytes
*/
public static Object deserialize(byte[] bytes) {
if (bytes == null) {
return null;
}
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
return ois.readObject();
} catch (IOException ex) {
throw new IllegalArgumentException("Failed to deserialize object", ex);
} catch (ClassNotFoundException ex) {
throw new IllegalStateException("Failed to deserialize object type", ex);
}
}
}

View File

@@ -0,0 +1,86 @@
package com.jeelowcode.framework.plus.core.toolkit;
/**
* @author JX
* @create 2024-03-26 9:55
* @dedescription:
*/
public interface StringPool {
String AMPERSAND = "&";
String AND = "and";
String AT = "@";
String ASTERISK = "*";
String STAR = ASTERISK;
String BACK_SLASH = "\\";
String COLON = ":";
String COMMA = ",";
String DASH = "-";
String DOLLAR = "$";
String DOT = ".";
String DOTDOT = "..";
String DOT_CLASS = ".class";
String DOT_JAVA = ".java";
String DOT_XML = ".xml";
String EMPTY = "";
String EQUALS = "=";
String FALSE = "false";
String SLASH = "/";
String HASH = "#";
String HAT = "^";
String LEFT_BRACE = "{";
String LEFT_BRACKET = "(";
String LEFT_CHEV = "<";
String DOT_NEWLINE = ",\n";
String NEWLINE = "\n";
String N = "n";
String NO = "no";
String NULL = "null";
String NUM = "NUM";
String OFF = "off";
String ON = "on";
String PERCENT = "%";
String PIPE = "|";
String PLUS = "+";
String QUESTION_MARK = "?";
String EXCLAMATION_MARK = "!";
String QUOTE = "\"";
String RETURN = "\r";
String TAB = "\t";
String RIGHT_BRACE = "}";
String RIGHT_BRACKET = ")";
String RIGHT_CHEV = ">";
String SEMICOLON = ";";
String SINGLE_QUOTE = "'";
String BACKTICK = "`";
String SPACE = " ";
String SQL = "sql";
String TILDA = "~";
String LEFT_SQ_BRACKET = "[";
String RIGHT_SQ_BRACKET = "]";
String TRUE = "true";
String UNDERSCORE = "_";
String UTF_8 = "UTF-8";
String US_ASCII = "US-ASCII";
String ISO_8859_1 = "ISO-8859-1";
String Y = "y";
String YES = "yes";
String ONE = "1";
String ZERO = "0";
String DOLLAR_LEFT_BRACE = "${";
String HASH_LEFT_BRACE = "#{";
String CRLF = "\r\n";
String HTML_NBSP = "&nbsp;";
String HTML_AMP = "&amp";
String HTML_QUOTE = "&quot;";
String HTML_LT = "&lt;";
String HTML_GT = "&gt;";
// ---------------------------------------------------------------- array
String[] EMPTY_ARRAY = new String[0];
byte[] BYTES_NEW_LINE = StringPool.NEWLINE.getBytes();
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2011-2023, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jeelowcode.framework.plus.core.toolkit;
import java.util.regex.Pattern;
/**
* String 工具类
*
* @author D.Yang, hcl
* @author hcl
* @since 2016-08-18
*/
public final class StringUtils {
/**
* 字符串 is
*/
public static final String IS = "is";
/**
* 验证字符串是否是数据库字段
*/
private static final Pattern P_IS_COLUMN = Pattern.compile("^\\w\\S*[\\w\\d]*$");
/**
* 判断字符串中是否全是空白字符
*
* @param cs 需要判断的字符串
* @return 如果字符串序列是 null 或者全是空白,返回 true
*/
public static boolean isBlank(CharSequence cs) {
if (cs != null) {
int length = cs.length();
for (int i = 0; i < length; i++) {
if (!Character.isWhitespace(cs.charAt(i))) {
return false;
}
}
}
return true;
}
/**
* @see #isBlank(CharSequence)
*/
public static boolean isNotBlank(CharSequence cs) {
return !isBlank(cs);
}
public static boolean isEmpty(CharSequence cs) {
return cs == null || cs.length() == 0;
}
public static boolean isNotEmpty(CharSequence cs) {
return !isEmpty(cs);
}
/**
* 判断字符串是否符合数据库字段的命名
*
* @param str 字符串
* @return 判断结果
*/
public static boolean isNotColumnName(String str) {
return !P_IS_COLUMN.matcher(str).matches();
}
/**
* 获取真正的字段名
*
* @param column 字段名
* @return 字段名
*/
public static String getTargetColumn(String column) {
if (isNotColumnName(column)) {
return column.substring(1, column.length() - 1);
}
return column;
}
/**
* 判断对象是否不为空
*
* @param object ignore
* @return ignore
*/
public static boolean checkValNotNull(Object object) {
if (object instanceof CharSequence) {
return isNotEmpty((CharSequence) object);
}
return object != null;
}
}

Some files were not shown because too many files have changed in this diff Show More