🎨 引入spring-java-format插件,代码格式管理
This commit is contained in:
@@ -13,18 +13,24 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/6/12 19:35
|
||||
* @author lingting 2020/6/12 19:35
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class DingTalkParams {
|
||||
|
||||
@JsonProperty("msgtype")
|
||||
private String type;
|
||||
|
||||
private At at;
|
||||
|
||||
private ActionCard actionCard;
|
||||
|
||||
private Link link;
|
||||
|
||||
private Markdown markdown;
|
||||
|
||||
private Text text;
|
||||
|
||||
@Override
|
||||
@@ -36,43 +42,64 @@ public class DingTalkParams {
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class Text {
|
||||
|
||||
private String content;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class Markdown {
|
||||
|
||||
private String title;
|
||||
|
||||
private String text;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class Link {
|
||||
|
||||
private String text;
|
||||
|
||||
private String title;
|
||||
|
||||
private String picUrl;
|
||||
|
||||
private String messageUrl;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class ActionCard {
|
||||
|
||||
private String title;
|
||||
|
||||
private String text;
|
||||
|
||||
private String btnOrientation;
|
||||
|
||||
private String singleTitle;
|
||||
|
||||
@JsonProperty("singleURL")
|
||||
private String singleUrl;
|
||||
|
||||
@JsonProperty("btns")
|
||||
private List<DingTalkActionCardMessage.Button> buttons;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class At {
|
||||
|
||||
@JsonProperty("isAtAll")
|
||||
private boolean atAll;
|
||||
|
||||
private Set<String> atMobiles;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,21 +8,26 @@ import lombok.Setter;
|
||||
/**
|
||||
* 钉钉返回信息
|
||||
*
|
||||
* @author lingting 2020/6/11 0:23
|
||||
* @author lingting 2020/6/11 0:23
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class DingTalkResponse {
|
||||
|
||||
public static final String SUCCESS_CODE = "0";
|
||||
|
||||
private String errCode;
|
||||
|
||||
/**
|
||||
* 值为ok表示无异常
|
||||
*/
|
||||
private String errMsg;
|
||||
|
||||
/**
|
||||
* 钉钉返回信息
|
||||
*/
|
||||
private String response;
|
||||
|
||||
/**
|
||||
* 是否发送成功
|
||||
*/
|
||||
@@ -42,4 +47,5 @@ public class DingTalkResponse {
|
||||
public String toString() {
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,32 +17,34 @@ import java.nio.charset.StandardCharsets;
|
||||
/**
|
||||
* 订单消息发送
|
||||
*
|
||||
* @author lingting 2020/6/10 21:25
|
||||
* @author lingting 2020/6/10 21:25
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@RequiredArgsConstructor
|
||||
public class DingTalkSender {
|
||||
|
||||
/**
|
||||
* 请求路径
|
||||
*/
|
||||
private final String url;
|
||||
|
||||
/**
|
||||
* 密钥
|
||||
*/
|
||||
private String secret;
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* 根据参数值判断使用哪种发送方式
|
||||
* 发送消息 根据参数值判断使用哪种发送方式
|
||||
*
|
||||
* @author lingting 2020-06-11 00:05:51
|
||||
* @author lingting 2020-06-11 00:05:51
|
||||
*/
|
||||
@SneakyThrows
|
||||
public DingTalkResponse sendMessage(DingTalkMessage message) {
|
||||
if (StrUtil.isEmpty(secret)) {
|
||||
return sendNormalMessage(message);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return sendSecretMessage(message);
|
||||
}
|
||||
}
|
||||
@@ -50,16 +52,16 @@ public class DingTalkSender {
|
||||
/**
|
||||
* 未使用 加签 安全设置 直接发送
|
||||
*
|
||||
* @author lingting 2020-06-11 00:09:23
|
||||
* @author lingting 2020-06-11 00:09:23
|
||||
*/
|
||||
public DingTalkResponse sendNormalMessage(DingTalkMessage message) {
|
||||
return DingTalkResponse.getInstance(HttpUtil.post(url, message.generate()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 加签 安全设置 发送
|
||||
* 使用 加签 安全设置 发送
|
||||
*
|
||||
* @author lingting 2020-06-11 00:10:38
|
||||
* @author lingting 2020-06-11 00:10:38
|
||||
*/
|
||||
@SneakyThrows
|
||||
public DingTalkResponse sendSecretMessage(DingTalkMessage message) {
|
||||
@@ -69,7 +71,7 @@ public class DingTalkSender {
|
||||
/**
|
||||
* 获取签名后的请求路径
|
||||
*
|
||||
* @author lingting 2020-06-11 00:13:55
|
||||
* @author lingting 2020-06-11 00:13:55
|
||||
*/
|
||||
@SneakyThrows
|
||||
public String secret() {
|
||||
@@ -81,4 +83,5 @@ public class DingTalkSender {
|
||||
String encode = URLEncoder.encode(Base64.encode(signData), "UTF-8");
|
||||
return url + "×tamp=" + timestamp + "&sign=" + encode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,19 +4,21 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 跳转 ActionCard 类型 消息的按钮排列方式
|
||||
* 跳转 ActionCard 类型 消息的按钮排列方式
|
||||
*
|
||||
* @author lingting 2020/6/10 23:44
|
||||
* @author lingting 2020/6/10 23:44
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ActionBtnOrientationEnum {
|
||||
|
||||
/**
|
||||
* 按钮排列样式值 说明
|
||||
* 按钮排列样式值 说明
|
||||
*/
|
||||
VERTICAL("0", "按钮竖向排列"),
|
||||
HORIZONTAL("1", "按钮横向排列"),
|
||||
;
|
||||
VERTICAL("0", "按钮竖向排列"), HORIZONTAL("1", "按钮横向排列"),;
|
||||
|
||||
private final String val;
|
||||
|
||||
private final String text;
|
||||
|
||||
}
|
||||
|
||||
@@ -6,19 +6,20 @@ import lombok.Getter;
|
||||
/**
|
||||
* 钉钉消息类型
|
||||
*
|
||||
* @author lingting 2020/6/10 21:29
|
||||
* @author lingting 2020/6/10 21:29
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum MessageTypeEnum {
|
||||
|
||||
/**
|
||||
* 消息值 消息说明
|
||||
* 消息值 消息说明
|
||||
*/
|
||||
TEXT("text", "文本"),
|
||||
LINK("link", "链接"),
|
||||
MARKDOWN("markdown", "markdown"),
|
||||
ACTION_CARD("actionCard", "跳转 actionCard 类型"),
|
||||
;
|
||||
TEXT("text", "文本"), LINK("link", "链接"), MARKDOWN("markdown", "markdown"), ACTION_CARD("actionCard",
|
||||
"跳转 actionCard 类型"),;
|
||||
|
||||
private final String val;
|
||||
|
||||
private final String desc;
|
||||
|
||||
}
|
||||
|
||||
@@ -9,13 +9,15 @@ import java.util.Set;
|
||||
/**
|
||||
* 钉钉消息基础类
|
||||
*
|
||||
* @author lingting 2020/6/10 21:28
|
||||
* @author lingting 2020/6/10 21:28
|
||||
*/
|
||||
public abstract class AbstractDingTalkMessage implements DingTalkMessage {
|
||||
|
||||
/**
|
||||
* at 的人的手机号码
|
||||
*/
|
||||
private final Set<String> atPhones = new HashSet<>();
|
||||
|
||||
/**
|
||||
* 是否 at 所有人
|
||||
*/
|
||||
@@ -29,7 +31,7 @@ public abstract class AbstractDingTalkMessage implements DingTalkMessage {
|
||||
/**
|
||||
* 添加 at 对象的手机号
|
||||
*
|
||||
* @author lingting 2020-06-10 21:57:08
|
||||
* @author lingting 2020-06-10 21:57:08
|
||||
*/
|
||||
public AbstractDingTalkMessage addPhone(String phone) {
|
||||
atPhones.add(phone);
|
||||
@@ -38,28 +40,24 @@ public abstract class AbstractDingTalkMessage implements DingTalkMessage {
|
||||
|
||||
/**
|
||||
* 获取消息类型
|
||||
*
|
||||
* @return 返回消息类型
|
||||
* @author lingting 2020-06-10 22:12:30
|
||||
* @author lingting 2020-06-10 22:12:30
|
||||
*/
|
||||
public abstract MessageTypeEnum getType();
|
||||
|
||||
/**
|
||||
* 设置非公有属性
|
||||
*
|
||||
* @param params 已设置完公有参数的参数类
|
||||
* @return 已设置完成的参数类
|
||||
* @author lingting 2020-06-10 22:11:04
|
||||
* @author lingting 2020-06-10 22:11:04
|
||||
*/
|
||||
public abstract DingTalkParams put(DingTalkParams params);
|
||||
|
||||
|
||||
@Override
|
||||
public String generate() {
|
||||
DingTalkParams params = put(new DingTalkParams()
|
||||
.setType(getType().getVal())
|
||||
.setAt(new DingTalkParams.At().setAtAll(atAll).setAtMobiles(atPhones))
|
||||
);
|
||||
DingTalkParams params = put(new DingTalkParams().setType(getType().getVal())
|
||||
.setAt(new DingTalkParams.At().setAtAll(atAll).setAtMobiles(atPhones)));
|
||||
return params.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,25 +15,30 @@ import java.util.List;
|
||||
/**
|
||||
* 跳转 ActionCard类型
|
||||
*
|
||||
* @author lingting 2020/6/10 23:39
|
||||
* @author lingting 2020/6/10 23:39
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class DingTalkActionCardMessage extends AbstractDingTalkMessage {
|
||||
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private MarkdownBuilder text;
|
||||
|
||||
/**
|
||||
* 按钮排列样式 默认横
|
||||
*/
|
||||
private ActionBtnOrientationEnum orientation = ActionBtnOrientationEnum.HORIZONTAL;
|
||||
|
||||
/**
|
||||
* 单个按钮的标题
|
||||
*/
|
||||
private String singleTitle;
|
||||
|
||||
/**
|
||||
* 点击singleTitle按钮触发的URL
|
||||
*/
|
||||
@@ -47,7 +52,7 @@ public class DingTalkActionCardMessage extends AbstractDingTalkMessage {
|
||||
/**
|
||||
* 添加按钮
|
||||
*
|
||||
* @author lingting 2020-06-10 23:59:45
|
||||
* @author lingting 2020-06-10 23:59:45
|
||||
*/
|
||||
public DingTalkActionCardMessage addButton(String title, String url) {
|
||||
buttons.add(new Button(title, url));
|
||||
@@ -61,15 +66,14 @@ public class DingTalkActionCardMessage extends AbstractDingTalkMessage {
|
||||
|
||||
@Override
|
||||
public DingTalkParams put(DingTalkParams params) {
|
||||
DingTalkParams.ActionCard card = new DingTalkParams.ActionCard()
|
||||
.setTitle(title)
|
||||
.setText(text.build())
|
||||
DingTalkParams.ActionCard card = new DingTalkParams.ActionCard().setTitle(title).setText(text.build())
|
||||
.setBtnOrientation(orientation.getVal());
|
||||
|
||||
// 当 单按钮的 文本和链接都不为空时
|
||||
// 当 单按钮的 文本和链接都不为空时
|
||||
if (buttons.size() == 0) {
|
||||
card.setSingleTitle(singleTitle).setSingleUrl(singleUrl);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
card.setButtons(buttons);
|
||||
}
|
||||
return params.setActionCard(card);
|
||||
@@ -78,13 +82,17 @@ public class DingTalkActionCardMessage extends AbstractDingTalkMessage {
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class Button {
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private final String title;
|
||||
|
||||
/**
|
||||
* 跳转路径
|
||||
*/
|
||||
private final String actionURL;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,24 +7,28 @@ import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/6/10 22:13
|
||||
* @author lingting 2020/6/10 22:13
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class DingTalkLinkMessage extends AbstractDingTalkMessage {
|
||||
|
||||
/**
|
||||
* 文本
|
||||
*/
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 图片url
|
||||
*/
|
||||
private String picUrl;
|
||||
|
||||
/**
|
||||
* 消息链接
|
||||
*/
|
||||
@@ -37,10 +41,8 @@ public class DingTalkLinkMessage extends AbstractDingTalkMessage {
|
||||
|
||||
@Override
|
||||
public DingTalkParams put(DingTalkParams params) {
|
||||
return params.setLink(new DingTalkParams.Link()
|
||||
.setText(text)
|
||||
.setTitle(title)
|
||||
.setPicUrl(picUrl)
|
||||
.setMessageUrl(messageUrl));
|
||||
return params.setLink(
|
||||
new DingTalkParams.Link().setText(text).setTitle(title).setPicUrl(picUrl).setMessageUrl(messageUrl));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,16 +8,18 @@ import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/6/10 22:13
|
||||
* @author lingting 2020/6/10 22:13
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class DingTalkMarkDownMessage extends AbstractDingTalkMessage {
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
@@ -32,4 +34,5 @@ public class DingTalkMarkDownMessage extends AbstractDingTalkMessage {
|
||||
public DingTalkParams put(DingTalkParams params) {
|
||||
return params.setMarkdown(new DingTalkParams.Markdown().setTitle(title).setText(text.build()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package com.hccake.extend.dingtalk.message;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/6/11 21:58
|
||||
* @author lingting 2020/6/11 21:58
|
||||
*/
|
||||
public interface DingTalkMessage {
|
||||
|
||||
/**
|
||||
* 生成钉钉消息发送参数
|
||||
*
|
||||
* @return 钉钉文档要求的 jsonString
|
||||
* @author lingting 2020-06-12 19:56:54
|
||||
* @author lingting 2020-06-12 19:56:54
|
||||
*/
|
||||
String generate();
|
||||
|
||||
}
|
||||
|
||||
@@ -7,12 +7,13 @@ import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/6/10 22:13
|
||||
* @author lingting 2020/6/10 22:13
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(chain = true)
|
||||
public class DingTalkTextMessage extends AbstractDingTalkMessage {
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
@@ -27,4 +28,5 @@ public class DingTalkTextMessage extends AbstractDingTalkMessage {
|
||||
public DingTalkParams put(DingTalkParams params) {
|
||||
return params.setText(new DingTalkParams.Text().setContent(content));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,13 +26,17 @@ import java.util.regex.Pattern;
|
||||
/**
|
||||
* kafka Stream 流构建方法
|
||||
*
|
||||
* @author lingting 2020/6/23 19:31
|
||||
* @author lingting 2020/6/23 19:31
|
||||
*/
|
||||
public class KafkaStreamBuilder {
|
||||
|
||||
private static final String BOOTSTRAP_SERVERS_DELIMITER = ",";
|
||||
|
||||
@Getter
|
||||
private Topology topology = new Topology();
|
||||
|
||||
private final Properties properties = new Properties();
|
||||
|
||||
private final Set<String> bootstrapServers = new HashSet<>();
|
||||
|
||||
public KafkaStreamBuilder keySerde(Class<? extends Serde<?>> c) {
|
||||
@@ -52,9 +56,9 @@ public class KafkaStreamBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 kafka 路径 host:port
|
||||
* 添加 kafka 路径 host:port
|
||||
*
|
||||
* @author lingting 2020-06-19 16:30:03
|
||||
* @author lingting 2020-06-19 16:30:03
|
||||
*/
|
||||
public KafkaStreamBuilder addBootstrapServers(String uri) {
|
||||
bootstrapServers.add(uri);
|
||||
@@ -69,7 +73,7 @@ public class KafkaStreamBuilder {
|
||||
/**
|
||||
* 添加配置
|
||||
*
|
||||
* @author lingting 2020-06-19 16:30:50
|
||||
* @author lingting 2020-06-19 16:30:50
|
||||
*/
|
||||
public KafkaStreamBuilder put(Object key, Object val) {
|
||||
properties.put(key, val);
|
||||
@@ -79,7 +83,7 @@ public class KafkaStreamBuilder {
|
||||
/**
|
||||
* 添加配置
|
||||
*
|
||||
* @author lingting 2020-06-19 16:30:50
|
||||
* @author lingting 2020-06-19 16:30:50
|
||||
*/
|
||||
public KafkaStreamBuilder putAll(Properties properties) {
|
||||
this.properties.putAll(properties);
|
||||
@@ -101,62 +105,76 @@ public class KafkaStreamBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, String name, String... topics) {
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, String name,
|
||||
String... topics) {
|
||||
topology.addSource(offsetReset, name, topics);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, String name, Pattern topicPattern) {
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, String name,
|
||||
Pattern topicPattern) {
|
||||
topology.addSource(offsetReset, name, topicPattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addSource(TimestampExtractor timestampExtractor, String name, String... topics) {
|
||||
public synchronized KafkaStreamBuilder addSource(TimestampExtractor timestampExtractor, String name,
|
||||
String... topics) {
|
||||
topology.addSource(timestampExtractor, name, topics);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addSource(TimestampExtractor timestampExtractor, String name, Pattern topicPattern) {
|
||||
public synchronized KafkaStreamBuilder addSource(TimestampExtractor timestampExtractor, String name,
|
||||
Pattern topicPattern) {
|
||||
topology.addSource(timestampExtractor, name, topicPattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, TimestampExtractor timestampExtractor, String name, String... topics) {
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset,
|
||||
TimestampExtractor timestampExtractor, String name, String... topics) {
|
||||
topology.addSource(offsetReset, timestampExtractor, name, topics);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, TimestampExtractor timestampExtractor, String name, Pattern topicPattern) {
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset,
|
||||
TimestampExtractor timestampExtractor, String name, Pattern topicPattern) {
|
||||
topology.addSource(offsetReset, timestampExtractor, name, topicPattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addSource(String name, Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer, String... topics) {
|
||||
public synchronized KafkaStreamBuilder addSource(String name, Deserializer<?> keyDeserializer,
|
||||
Deserializer<?> valueDeserializer, String... topics) {
|
||||
topology.addSource(name, keyDeserializer, valueDeserializer, topics);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addSource(String name, Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer, Pattern topicPattern) {
|
||||
public synchronized KafkaStreamBuilder addSource(String name, Deserializer<?> keyDeserializer,
|
||||
Deserializer<?> valueDeserializer, Pattern topicPattern) {
|
||||
topology.addSource(name, keyDeserializer, valueDeserializer, topicPattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, String name, Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer, String... topics) {
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, String name,
|
||||
Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer, String... topics) {
|
||||
topology.addSource(offsetReset, name, keyDeserializer, valueDeserializer, topics);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, String name, Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer, Pattern topicPattern) {
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, String name,
|
||||
Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer, Pattern topicPattern) {
|
||||
topology.addSource(offsetReset, name, keyDeserializer, valueDeserializer, topicPattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, String name, TimestampExtractor timestampExtractor, Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer, String... topics) {
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, String name,
|
||||
TimestampExtractor timestampExtractor, Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer,
|
||||
String... topics) {
|
||||
topology.addSource(offsetReset, name, timestampExtractor, keyDeserializer, valueDeserializer, topics);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, String name, TimestampExtractor timestampExtractor, Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer, Pattern topicPattern) {
|
||||
public synchronized KafkaStreamBuilder addSource(Topology.AutoOffsetReset offsetReset, String name,
|
||||
TimestampExtractor timestampExtractor, Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer,
|
||||
Pattern topicPattern) {
|
||||
topology.addSource(offsetReset, name, timestampExtractor, keyDeserializer, valueDeserializer, topicPattern);
|
||||
return this;
|
||||
}
|
||||
@@ -166,42 +184,51 @@ public class KafkaStreamBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, String topic, StreamPartitioner<? super K, ? super V> partitioner, String... parentNames) {
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, String topic,
|
||||
StreamPartitioner<? super K, ? super V> partitioner, String... parentNames) {
|
||||
topology.addSink(name, topic, partitioner, parentNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, String topic, Serializer<K> keySerializer, Serializer<V> valueSerializer, String... parentNames) {
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, String topic, Serializer<K> keySerializer,
|
||||
Serializer<V> valueSerializer, String... parentNames) {
|
||||
topology.addSink(name, topic, keySerializer, valueSerializer, parentNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, String topic, Serializer<K> keySerializer, Serializer<V> valueSerializer, StreamPartitioner<? super K, ? super V> partitioner, String... parentNames) {
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, String topic, Serializer<K> keySerializer,
|
||||
Serializer<V> valueSerializer, StreamPartitioner<? super K, ? super V> partitioner, String... parentNames) {
|
||||
topology.addSink(name, topic, keySerializer, valueSerializer, partitioner, parentNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, TopicNameExtractor<K, V> topicExtractor, String... parentNames) {
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, TopicNameExtractor<K, V> topicExtractor,
|
||||
String... parentNames) {
|
||||
topology.addSink(name, topicExtractor, parentNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, TopicNameExtractor<K, V> topicExtractor, StreamPartitioner<? super K, ? super V> partitioner, String... parentNames) {
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, TopicNameExtractor<K, V> topicExtractor,
|
||||
StreamPartitioner<? super K, ? super V> partitioner, String... parentNames) {
|
||||
topology.addSink(name, topicExtractor, partitioner, parentNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, TopicNameExtractor<K, V> topicExtractor, Serializer<K> keySerializer, Serializer<V> valueSerializer, String... parentNames) {
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, TopicNameExtractor<K, V> topicExtractor,
|
||||
Serializer<K> keySerializer, Serializer<V> valueSerializer, String... parentNames) {
|
||||
topology.addSink(name, topicExtractor, keySerializer, valueSerializer, parentNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, TopicNameExtractor<K, V> topicExtractor, Serializer<K> keySerializer, Serializer<V> valueSerializer, StreamPartitioner<? super K, ? super V> partitioner, String... parentNames) {
|
||||
public synchronized <K, V> KafkaStreamBuilder addSink(String name, TopicNameExtractor<K, V> topicExtractor,
|
||||
Serializer<K> keySerializer, Serializer<V> valueSerializer,
|
||||
StreamPartitioner<? super K, ? super V> partitioner, String... parentNames) {
|
||||
topology.addSink(name, topicExtractor, keySerializer, valueSerializer, partitioner, parentNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addProcessor(String name, ProcessorSupplier<?, ?> supplier, String... parentNames) {
|
||||
public synchronized KafkaStreamBuilder addProcessor(String name, ProcessorSupplier<?, ?> supplier,
|
||||
String... parentNames) {
|
||||
topology.addProcessor(name, supplier, parentNames);
|
||||
return this;
|
||||
}
|
||||
@@ -211,17 +238,24 @@ public class KafkaStreamBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addGlobalStore(StoreBuilder<?> storeBuilder, String sourceName, Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer, String topic, String processorName, ProcessorSupplier<?, ?> stateUpdateSupplier) {
|
||||
topology.addGlobalStore(storeBuilder, sourceName, keyDeserializer, valueDeserializer, topic, processorName, stateUpdateSupplier);
|
||||
public synchronized KafkaStreamBuilder addGlobalStore(StoreBuilder<?> storeBuilder, String sourceName,
|
||||
Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer, String topic, String processorName,
|
||||
ProcessorSupplier<?, ?> stateUpdateSupplier) {
|
||||
topology.addGlobalStore(storeBuilder, sourceName, keyDeserializer, valueDeserializer, topic, processorName,
|
||||
stateUpdateSupplier);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder addGlobalStore(StoreBuilder<?> storeBuilder, String sourceName, TimestampExtractor timestampExtractor, Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer, String topic, String processorName, ProcessorSupplier<?, ?> stateUpdateSupplier) {
|
||||
topology.addGlobalStore(storeBuilder, sourceName, timestampExtractor, keyDeserializer, valueDeserializer, topic, processorName, stateUpdateSupplier);
|
||||
public synchronized KafkaStreamBuilder addGlobalStore(StoreBuilder<?> storeBuilder, String sourceName,
|
||||
TimestampExtractor timestampExtractor, Deserializer<?> keyDeserializer, Deserializer<?> valueDeserializer,
|
||||
String topic, String processorName, ProcessorSupplier<?, ?> stateUpdateSupplier) {
|
||||
topology.addGlobalStore(storeBuilder, sourceName, timestampExtractor, keyDeserializer, valueDeserializer, topic,
|
||||
processorName, stateUpdateSupplier);
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized KafkaStreamBuilder connectProcessorAndStateStores(String processorName, String... stateStoreNames) {
|
||||
public synchronized KafkaStreamBuilder connectProcessorAndStateStores(String processorName,
|
||||
String... stateStoreNames) {
|
||||
topology.connectProcessorAndStateStores(processorName, stateStoreNames);
|
||||
return this;
|
||||
}
|
||||
@@ -229,7 +263,7 @@ public class KafkaStreamBuilder {
|
||||
/**
|
||||
* 自定义的构筑方法, 传入 topology 和属性
|
||||
*
|
||||
* @author lingting 2020-06-23 20:24:52
|
||||
* @author lingting 2020-06-23 20:24:52
|
||||
*/
|
||||
public KafkaStreams build(BiFunction<Topology, Properties, KafkaStreams> biFunction) {
|
||||
return biFunction.apply(topology, getProperties());
|
||||
@@ -254,8 +288,12 @@ public class KafkaStreamBuilder {
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
bootstrapServers.addAll(ListUtil.toList(properties.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, StrUtil.EMPTY).split(BOOTSTRAP_SERVERS_DELIMITER)));
|
||||
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, String.join(BOOTSTRAP_SERVERS_DELIMITER, bootstrapServers));
|
||||
bootstrapServers
|
||||
.addAll(ListUtil.toList(properties.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, StrUtil.EMPTY)
|
||||
.split(BOOTSTRAP_SERVERS_DELIMITER)));
|
||||
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
String.join(BOOTSTRAP_SERVERS_DELIMITER, bootstrapServers));
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,20 +7,19 @@ import org.apache.kafka.streams.state.KeyValueStore;
|
||||
/**
|
||||
* 所有 kafka 流处理执行相关类的顶级父类
|
||||
*
|
||||
* @author lingting 2020/6/22 11:02
|
||||
* @author lingting 2020/6/22 11:02
|
||||
*/
|
||||
public abstract class AbstractKafka {
|
||||
|
||||
/**
|
||||
* 获取上下文
|
||||
*
|
||||
* @return content
|
||||
* @author lingting 2020-06-22 11:03:23
|
||||
* @author lingting 2020-06-22 11:03:23
|
||||
*/
|
||||
public abstract ProcessorContext getContext();
|
||||
|
||||
/**
|
||||
* 获取 KeyValueStore
|
||||
*
|
||||
* @return java.lang.String
|
||||
* @author lingting 2020-06-22 09:57:37
|
||||
*/
|
||||
|
||||
@@ -15,10 +15,11 @@ import java.time.Duration;
|
||||
/**
|
||||
* kafka 顶级 processor 类
|
||||
*
|
||||
* @author lingting 2020/6/16 22:27
|
||||
* @author lingting 2020/6/16 22:27
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractProcessor<K, V> extends AbstractKafka implements Processor<K, V> {
|
||||
|
||||
@Getter
|
||||
private ProcessorContext context;
|
||||
|
||||
@@ -29,10 +30,9 @@ public abstract class AbstractProcessor<K, V> extends AbstractKafka implements P
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于初始化窗口的方法
|
||||
* 子类如果需要 自己重写
|
||||
* 用于初始化窗口的方法 子类如果需要 自己重写
|
||||
*
|
||||
* @author lingting 2020-06-17 10:44:39
|
||||
* @author lingting 2020-06-17 10:44:39
|
||||
*/
|
||||
public void initSchedule(ProcessorContext context) {
|
||||
|
||||
@@ -41,7 +41,7 @@ public abstract class AbstractProcessor<K, V> extends AbstractKafka implements P
|
||||
/**
|
||||
* 用于构筑 Punctuator
|
||||
*
|
||||
* @author lingting 2020-06-21 13:58:34
|
||||
* @author lingting 2020-06-21 13:58:34
|
||||
*/
|
||||
public void schedule(Duration interval, PunctuationType type, AbstractPunctuator callback) {
|
||||
context.schedule(interval, type, callback);
|
||||
@@ -50,7 +50,7 @@ public abstract class AbstractProcessor<K, V> extends AbstractKafka implements P
|
||||
/**
|
||||
* 用于构筑 {@link PunctuationType#WALL_CLOCK_TIME} 类型的 Punctuator
|
||||
*
|
||||
* @author lingting 2020-06-21 13:58:53
|
||||
* @author lingting 2020-06-21 13:58:53
|
||||
*/
|
||||
public void schedule(Duration interval, AbstractPunctuator callback) {
|
||||
schedule(interval, PunctuationType.WALL_CLOCK_TIME, callback);
|
||||
@@ -58,11 +58,10 @@ public abstract class AbstractProcessor<K, V> extends AbstractKafka implements P
|
||||
|
||||
/**
|
||||
* 下发数据
|
||||
*
|
||||
* @param key key
|
||||
* @param value value
|
||||
* @param key key
|
||||
* @param value value
|
||||
* @param childName 目标名称
|
||||
* @author lingting 2020-06-17 19:44:45
|
||||
* @author lingting 2020-06-17 19:44:45
|
||||
*/
|
||||
public <KEY, VALUE> void forward(KEY key, VALUE value, String childName) {
|
||||
context.forward(key, value, To.child(childName));
|
||||
@@ -70,10 +69,9 @@ public abstract class AbstractProcessor<K, V> extends AbstractKafka implements P
|
||||
|
||||
/**
|
||||
* 下发数据
|
||||
*
|
||||
* @param key key
|
||||
* @param key key
|
||||
* @param value value
|
||||
* @param to 目标
|
||||
* @param to 目标
|
||||
* @author lingting 2020-06-17 19:47:55
|
||||
*/
|
||||
public <KEY, VALUE> void forward(KEY key, VALUE value, To to) {
|
||||
@@ -94,12 +92,14 @@ public abstract class AbstractProcessor<K, V> extends AbstractKafka implements P
|
||||
try {
|
||||
startLog(key, value);
|
||||
process(context, key, value);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
errLog(e);
|
||||
String errStr;
|
||||
try {
|
||||
errStr = new ObjectMapper().writeValueAsString(value);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.error("数据转json出错!msg: {}", ex.getMessage());
|
||||
errStr = Convert.toStr(value);
|
||||
}
|
||||
@@ -109,10 +109,9 @@ public abstract class AbstractProcessor<K, V> extends AbstractKafka implements P
|
||||
|
||||
/**
|
||||
* Process the record with the given key and value.
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param key the key for the record
|
||||
* @param value the value for the record
|
||||
* @param key the key for the record
|
||||
* @param value the value for the record
|
||||
*/
|
||||
public abstract void process(ProcessorContext context, K key, V value);
|
||||
|
||||
@@ -122,4 +121,5 @@ public abstract class AbstractProcessor<K, V> extends AbstractKafka implements P
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,10 +10,11 @@ import org.apache.kafka.streams.processor.Punctuator;
|
||||
/**
|
||||
* kafka 顶级 punctuator 类
|
||||
*
|
||||
* @author lingting 2020/6/17 14:02
|
||||
* @author lingting 2020/6/17 14:02
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractPunctuator extends AbstractKafka implements Punctuator {
|
||||
|
||||
@Getter
|
||||
private ProcessorContext context;
|
||||
|
||||
@@ -24,7 +25,6 @@ public abstract class AbstractPunctuator extends AbstractKafka implements Punctu
|
||||
|
||||
/**
|
||||
* 是否处理数据, 如果想依据条件执行,需要自己重写当前方法
|
||||
*
|
||||
* @return boolean true 表示有参数,可以执行处理
|
||||
* @author lingting 2020-06-17 14:05:01
|
||||
*/
|
||||
@@ -42,7 +42,7 @@ public abstract class AbstractPunctuator extends AbstractKafka implements Punctu
|
||||
/**
|
||||
* handle 开始日志
|
||||
*
|
||||
* @author lingting 2020-06-21 18:34:15
|
||||
* @author lingting 2020-06-21 18:34:15
|
||||
*/
|
||||
public void startLog() {
|
||||
log.debug("任务开始执行, 类名 {} ,{}", this.getClass().getSimpleName(), ProcessorContextUtil.toLogString(context));
|
||||
@@ -50,21 +50,22 @@ public abstract class AbstractPunctuator extends AbstractKafka implements Punctu
|
||||
|
||||
/**
|
||||
* handle 结束日志
|
||||
*
|
||||
* @param time 执行时长 单位 毫秒
|
||||
* @author lingting 2020-06-21 16:39:16
|
||||
* @param time 执行时长 单位 毫秒
|
||||
* @author lingting 2020-06-21 16:39:16
|
||||
*/
|
||||
public void endLog(long time) {
|
||||
log.debug("任务执行时长: {}, 类名 {}, {} ", time, this.getClass().getSimpleName(), ProcessorContextUtil.toLogString(context));
|
||||
log.debug("任务执行时长: {}, 类名 {}, {} ", time, this.getClass().getSimpleName(),
|
||||
ProcessorContextUtil.toLogString(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常日志
|
||||
*
|
||||
* @author lingting 2020-06-22 19:50:16
|
||||
* @author lingting 2020-06-22 19:50:16
|
||||
*/
|
||||
public void errLog(Throwable e) {
|
||||
log.error("punctuator 操作数据出错 类名 " + this.getClass().getSimpleName() + ", " + ProcessorContextUtil.toLogString(context), e);
|
||||
log.error("punctuator 操作数据出错 类名 " + this.getClass().getSimpleName() + ", "
|
||||
+ ProcessorContextUtil.toLogString(context), e);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,16 +80,17 @@ public abstract class AbstractPunctuator extends AbstractKafka implements Punctu
|
||||
clean();
|
||||
context.commit();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
errLog(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理聚合的数据
|
||||
*
|
||||
* @param timestamp 时间戳
|
||||
* @author lingting 2020-06-17 14:06:25
|
||||
*/
|
||||
public abstract void handle(long timestamp);
|
||||
|
||||
}
|
||||
|
||||
@@ -14,17 +14,16 @@ import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* kafka 扩展类
|
||||
* 自动注入 指定类型 指定名称的 store
|
||||
* Value 数据的类型
|
||||
* Values 存放数据的对象类型
|
||||
* kafka 扩展类 自动注入 指定类型 指定名称的 store Value 数据的类型 Values 存放数据的对象类型
|
||||
*
|
||||
* @author lingting 2020/6/19 10:21
|
||||
* @author lingting 2020/6/19 10:21
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractKeyValueStorePunctuator<K, V, R> extends AbstractPunctuator {
|
||||
|
||||
@Getter
|
||||
private KafkaKeyValueStore<K, V> store;
|
||||
|
||||
/**
|
||||
* 用来处理单个值得函数
|
||||
*/
|
||||
@@ -33,10 +32,12 @@ public abstract class AbstractKeyValueStorePunctuator<K, V, R> extends AbstractP
|
||||
@Override
|
||||
@Deprecated
|
||||
public AbstractKeyValueStorePunctuator<K, V, R> init(ProcessorContext context) {
|
||||
throw new RuntimeException("继承自 com.hccake.extend.kafka.stream.extend.AbstractKeyValueStorePunctuator 的类禁止使用当前方法进行初始化!");
|
||||
throw new RuntimeException(
|
||||
"继承自 com.hccake.extend.kafka.stream.extend.AbstractKeyValueStorePunctuator 的类禁止使用当前方法进行初始化!");
|
||||
}
|
||||
|
||||
public AbstractKeyValueStorePunctuator<K, V, R> init(ProcessorContext context, String storeName, BiFunction<K, V, R> signHandle) {
|
||||
public AbstractKeyValueStorePunctuator<K, V, R> init(ProcessorContext context, String storeName,
|
||||
BiFunction<K, V, R> signHandle) {
|
||||
super.init(context);
|
||||
store = getStore(storeName);
|
||||
this.signHandle = signHandle;
|
||||
@@ -46,7 +47,7 @@ public abstract class AbstractKeyValueStorePunctuator<K, V, R> extends AbstractP
|
||||
/**
|
||||
* 获取单批量处理数量
|
||||
*
|
||||
* @author lingting 2020-06-22 15:37:10
|
||||
* @author lingting 2020-06-22 15:37:10
|
||||
*/
|
||||
public long getHandleSize() {
|
||||
return 1000;
|
||||
@@ -71,10 +72,9 @@ public abstract class AbstractKeyValueStorePunctuator<K, V, R> extends AbstractP
|
||||
|
||||
/**
|
||||
* 执行数据处理方法
|
||||
*
|
||||
* @param list 数据
|
||||
* @param list 数据
|
||||
* @param timestamp 时间戳
|
||||
* @author lingting 2020-06-22 19:51:56
|
||||
* @author lingting 2020-06-22 19:51:56
|
||||
*/
|
||||
public void runHandle(long timestamp, List<R> list) {
|
||||
try {
|
||||
@@ -82,11 +82,14 @@ public abstract class AbstractKeyValueStorePunctuator<K, V, R> extends AbstractP
|
||||
log.debug("任务执行中,类名 {}, 操作数据量: {}", this.getClass().getSimpleName(), list.size());
|
||||
handle(timestamp, list);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
errLog(e);
|
||||
try {
|
||||
log.error("时间戳: {}, 类名: {}, 异常数据: {}", timestamp, this.getClass().getName(), new ObjectMapper().writeValueAsString(list));
|
||||
} catch (Exception exception) {
|
||||
log.error("时间戳: {}, 类名: {}, 异常数据: {}", timestamp, this.getClass().getName(),
|
||||
new ObjectMapper().writeValueAsString(list));
|
||||
}
|
||||
catch (Exception exception) {
|
||||
log.error("记录异常数据出错! 时间戳: {}, 类名: {}", timestamp, this.getClass().getName());
|
||||
log.error("数据转换异常! ", exception);
|
||||
}
|
||||
@@ -95,9 +98,8 @@ public abstract class AbstractKeyValueStorePunctuator<K, V, R> extends AbstractP
|
||||
|
||||
/**
|
||||
* 批量处理数据
|
||||
*
|
||||
* @param timestamp 时间戳
|
||||
* @param list 当前批数据
|
||||
* @param list 当前批数据
|
||||
* @author lingting 2020-06-22 15:35:38
|
||||
*/
|
||||
public abstract void handle(long timestamp, List<R> list);
|
||||
@@ -106,4 +108,5 @@ public abstract class AbstractKeyValueStorePunctuator<K, V, R> extends AbstractP
|
||||
public void clean() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@ import lombok.extern.slf4j.Slf4j;
|
||||
/**
|
||||
* String String 类型的 store
|
||||
*
|
||||
* @author lingting 2020/6/19 10:21
|
||||
* @author lingting 2020/6/19 10:21
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractStringStringStorePunctuator<R> extends AbstractKeyValueStorePunctuator<String, String, R> {
|
||||
public abstract class AbstractStringStringStorePunctuator<R>
|
||||
extends AbstractKeyValueStorePunctuator<String, String, R> {
|
||||
|
||||
}
|
||||
|
||||
@@ -14,10 +14,12 @@ import java.util.function.Supplier;
|
||||
/**
|
||||
* 使用 {@link KeyValueStore} 为具体实现的kafka数据缓存方法
|
||||
*
|
||||
* @author lingting 2020/6/22 10:24
|
||||
* @author lingting 2020/6/22 10:24
|
||||
*/
|
||||
public class KafkaKeyValueStore<K, V> implements KafkaWindow<V, KeyValueStore<K, V>> {
|
||||
|
||||
private KeyValueStore<K, V> store;
|
||||
|
||||
private Supplier<K> supplier;
|
||||
|
||||
private KafkaKeyValueStore() {
|
||||
@@ -30,7 +32,7 @@ public class KafkaKeyValueStore<K, V> implements KafkaWindow<V, KeyValueStore<K,
|
||||
|
||||
/**
|
||||
* @param supplier 生成key的方式
|
||||
* @author lingting 2020-06-22 10:43:34
|
||||
* @author lingting 2020-06-22 10:43:34
|
||||
*/
|
||||
public static <K, V> KafkaKeyValueStore<K, V> init(KeyValueStore<K, V> store, Supplier<K> supplier) {
|
||||
KafkaKeyValueStore<K, V> keyValueStore = new KafkaKeyValueStore<>();
|
||||
@@ -63,12 +65,10 @@ public class KafkaKeyValueStore<K, V> implements KafkaWindow<V, KeyValueStore<K,
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取插入数据的key
|
||||
*
|
||||
* @return 生成的key
|
||||
* @author lingting 2020-06-22 10:14:15
|
||||
* @author lingting 2020-06-22 10:14:15
|
||||
*/
|
||||
public K getKey() {
|
||||
return supplier.get();
|
||||
@@ -98,8 +98,7 @@ public class KafkaKeyValueStore<K, V> implements KafkaWindow<V, KeyValueStore<K,
|
||||
|
||||
/**
|
||||
* 直接插入数据
|
||||
*
|
||||
* @param v 值
|
||||
* @param v 值
|
||||
* @param kvKeyValueStore 目标
|
||||
* @author lingting 2020-06-22 10:36:53
|
||||
*/
|
||||
@@ -115,4 +114,5 @@ public class KafkaKeyValueStore<K, V> implements KafkaWindow<V, KeyValueStore<K,
|
||||
public V delete(K key) {
|
||||
return store.delete(key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,71 +4,68 @@ import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* kafka 数据缓存类的接口
|
||||
* @author lingting 2020/6/22 10:32
|
||||
*
|
||||
* @author lingting 2020/6/22 10:32
|
||||
*/
|
||||
public interface KafkaWindow<Value, Values> {
|
||||
/**
|
||||
* 数据通过校验才插入
|
||||
*
|
||||
* @param values 目标
|
||||
* @param value 值
|
||||
* @author lingting 2020-06-19 10:27:30
|
||||
*/
|
||||
default void pushValue(Value value, Values values) {
|
||||
if (check(value)) {
|
||||
forkPush(value, values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据通过校验才插入
|
||||
* @param values 目标
|
||||
* @param value 值
|
||||
* @author lingting 2020-06-19 10:27:30
|
||||
*/
|
||||
default void pushValue(Value value, Values values) {
|
||||
if (check(value)) {
|
||||
forkPush(value, values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入多个数据
|
||||
*
|
||||
* @param values 插入目标
|
||||
* @param iterable 需要插入的多个值
|
||||
* @author lingting 2020-06-19 11:05:00
|
||||
*/
|
||||
default void pushAll(Iterable<Value> iterable, Values values) {
|
||||
for (Value v : iterable) {
|
||||
pushValue(v, values);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 插入多个数据
|
||||
* @param values 插入目标
|
||||
* @param iterable 需要插入的多个值
|
||||
* @author lingting 2020-06-19 11:05:00
|
||||
*/
|
||||
default void pushAll(Iterable<Value> iterable, Values values) {
|
||||
for (Value v : iterable) {
|
||||
pushValue(v, values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接插入数据
|
||||
*
|
||||
* @param value 数据
|
||||
* @param values 存放所有数据的对象
|
||||
* @author lingting 2020-06-19 10:25:24
|
||||
*/
|
||||
void forkPush(Value value, Values values);
|
||||
/**
|
||||
* 直接插入数据
|
||||
* @param value 数据
|
||||
* @param values 存放所有数据的对象
|
||||
* @author lingting 2020-06-19 10:25:24
|
||||
*/
|
||||
void forkPush(Value value, Values values);
|
||||
|
||||
/**
|
||||
* 校验 value 是否可以插入
|
||||
*
|
||||
* @param value 数据
|
||||
* @return boolean true 可以插入
|
||||
* @author lingting 2020-06-19 10:27:17
|
||||
*/
|
||||
default boolean check(Value value) {
|
||||
if (!isInsertNull()) {
|
||||
// 不能插入空值,进行校验
|
||||
if (value instanceof String && StrUtil.isEmpty((String) value)) {
|
||||
// 空值, 结束方法
|
||||
return false;
|
||||
}
|
||||
return value != null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 校验 value 是否可以插入
|
||||
* @param value 数据
|
||||
* @return boolean true 可以插入
|
||||
* @author lingting 2020-06-19 10:27:17
|
||||
*/
|
||||
default boolean check(Value value) {
|
||||
if (!isInsertNull()) {
|
||||
// 不能插入空值,进行校验
|
||||
if (value instanceof String && StrUtil.isEmpty((String) value)) {
|
||||
// 空值, 结束方法
|
||||
return false;
|
||||
}
|
||||
return value != null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可以插入空值
|
||||
* @return true 可以插入空值
|
||||
* @author lingting 2020-06-19 10:28:52
|
||||
*/
|
||||
default boolean isInsertNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可以插入空值
|
||||
*
|
||||
* @return true 可以插入空值
|
||||
* @author lingting 2020-06-19 10:28:52
|
||||
*/
|
||||
default boolean isInsertNull() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.apache.kafka.streams.processor.internals.ProcessorContextImpl;
|
||||
/**
|
||||
* kafka 上下文工具类
|
||||
*
|
||||
* @author lingting 2020/6/23 18:00
|
||||
* @author lingting 2020/6/23 18:00
|
||||
*/
|
||||
public class ProcessorContextUtil {
|
||||
|
||||
@@ -15,9 +15,8 @@ public class ProcessorContextUtil {
|
||||
if (context instanceof ProcessorContextImpl) {
|
||||
res += " currentNode.name: " + ((ProcessorContextImpl) context).currentNode().name();
|
||||
}
|
||||
res += " topic: " + context.topic() +
|
||||
" offset: " + context.offset() +
|
||||
" taskId: " + context.taskId();
|
||||
res += " topic: " + context.topic() + " offset: " + context.offset() + " taskId: " + context.taskId();
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.hccake.extend.kafka;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/6/19 16:48
|
||||
* @author lingting 2020/6/19 16:48
|
||||
*/
|
||||
public class KafkaConstants {
|
||||
|
||||
public static final String BOOTSTRAP_SERVERS_DELIMITER = ",";
|
||||
|
||||
}
|
||||
|
||||
@@ -17,16 +17,17 @@ import java.util.function.Function;
|
||||
import static com.hccake.extend.kafka.KafkaConstants.BOOTSTRAP_SERVERS_DELIMITER;
|
||||
|
||||
/**
|
||||
* 消费者
|
||||
* 具体的配置请参考 {@link ConsumerConfig}
|
||||
* 这里只提供一些常用配置
|
||||
* 消费者 具体的配置请参考 {@link ConsumerConfig} 这里只提供一些常用配置
|
||||
*
|
||||
* @author lingting 2020/5/19 20:56
|
||||
* @author lingting 2020/5/19 20:56
|
||||
*/
|
||||
@Slf4j
|
||||
public class KafkaConsumerBuilder {
|
||||
|
||||
private final Properties properties = new Properties();
|
||||
|
||||
private final Set<String> bootstrapServers = new HashSet<>();
|
||||
|
||||
@Getter
|
||||
private final Set<String> topics = new HashSet<>();
|
||||
|
||||
@@ -47,9 +48,9 @@ public class KafkaConsumerBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 kafka 路径 host:port
|
||||
* 添加 kafka 路径 host:port
|
||||
*
|
||||
* @author lingting 2020-06-19 16:30:03
|
||||
* @author lingting 2020-06-19 16:30:03
|
||||
*/
|
||||
public KafkaConsumerBuilder addBootstrapServers(String uri) {
|
||||
bootstrapServers.add(uri);
|
||||
@@ -64,7 +65,7 @@ public class KafkaConsumerBuilder {
|
||||
/**
|
||||
* 添加配置
|
||||
*
|
||||
* @author lingting 2020-06-19 16:30:50
|
||||
* @author lingting 2020-06-19 16:30:50
|
||||
*/
|
||||
public KafkaConsumerBuilder put(Object key, Object val) {
|
||||
properties.put(key, val);
|
||||
@@ -74,7 +75,7 @@ public class KafkaConsumerBuilder {
|
||||
/**
|
||||
* 添加配置
|
||||
*
|
||||
* @author lingting 2020-06-19 16:30:50
|
||||
* @author lingting 2020-06-19 16:30:50
|
||||
*/
|
||||
public KafkaConsumerBuilder putAll(Properties properties) {
|
||||
this.properties.putAll(properties);
|
||||
@@ -84,7 +85,7 @@ public class KafkaConsumerBuilder {
|
||||
/**
|
||||
* 组id
|
||||
*
|
||||
* @author lingting 2020-06-19 16:46:32
|
||||
* @author lingting 2020-06-19 16:46:32
|
||||
*/
|
||||
public KafkaConsumerBuilder groupId(String groupId) {
|
||||
return put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
|
||||
@@ -120,8 +121,12 @@ public class KafkaConsumerBuilder {
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
bootstrapServers.addAll(ListUtil.toList(properties.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, StrUtil.EMPTY).split(BOOTSTRAP_SERVERS_DELIMITER)));
|
||||
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, String.join(BOOTSTRAP_SERVERS_DELIMITER, bootstrapServers));
|
||||
bootstrapServers
|
||||
.addAll(ListUtil.toList(properties.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, StrUtil.EMPTY)
|
||||
.split(BOOTSTRAP_SERVERS_DELIMITER)));
|
||||
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
String.join(BOOTSTRAP_SERVERS_DELIMITER, bootstrapServers));
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ import org.apache.kafka.common.header.Header;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* 生产者扩展
|
||||
* 提供了一些更加方便使用 生产者的方法
|
||||
* 生产者扩展 提供了一些更加方便使用 生产者的方法
|
||||
*
|
||||
* @author lingting 2020/6/23 17:11
|
||||
* @author lingting 2020/6/23 17:11
|
||||
*/
|
||||
public class KafkaExtendProducer<K, V> {
|
||||
|
||||
@Getter
|
||||
private final KafkaProducer<K, V> producer;
|
||||
|
||||
@@ -23,7 +23,8 @@ public class KafkaExtendProducer<K, V> {
|
||||
this.producer = producer;
|
||||
}
|
||||
|
||||
public ProducerRecord<K, V> record(String topic, Integer partition, Long timestamp, K key, V value, Iterable<Header> headers) {
|
||||
public ProducerRecord<K, V> record(String topic, Integer partition, Long timestamp, K key, V value,
|
||||
Iterable<Header> headers) {
|
||||
return new ProducerRecord<>(topic, partition, timestamp, key, value, headers);
|
||||
}
|
||||
|
||||
@@ -51,7 +52,8 @@ public class KafkaExtendProducer<K, V> {
|
||||
return producer.send(record, callback);
|
||||
}
|
||||
|
||||
public Future<RecordMetadata> send(String topic, Integer partition, Long timestamp, K key, V value, Iterable<Header> headers) {
|
||||
public Future<RecordMetadata> send(String topic, Integer partition, Long timestamp, K key, V value,
|
||||
Iterable<Header> headers) {
|
||||
return send(record(topic, partition, timestamp, key, value, headers), null);
|
||||
}
|
||||
|
||||
@@ -78,4 +80,5 @@ public class KafkaExtendProducer<K, V> {
|
||||
public Future<RecordMetadata> send(String topic, V value, Callback callback) {
|
||||
return send(record(topic, value), callback);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,15 +16,15 @@ import java.util.function.Function;
|
||||
import static com.hccake.extend.kafka.KafkaConstants.BOOTSTRAP_SERVERS_DELIMITER;
|
||||
|
||||
/**
|
||||
* 生产者
|
||||
* 具体的配置请参考 {@link ProducerConfig}
|
||||
* 这里只提供一些常用配置
|
||||
* 生产者 具体的配置请参考 {@link ProducerConfig} 这里只提供一些常用配置
|
||||
*
|
||||
* @author lingting 2020/5/19 20:56
|
||||
* @author lingting 2020/5/19 20:56
|
||||
*/
|
||||
@Slf4j
|
||||
public class KafkaProducerBuilder {
|
||||
|
||||
private final Properties properties = new Properties();
|
||||
|
||||
private final Set<String> bootstrapServers = new HashSet<>();
|
||||
|
||||
public KafkaProducerBuilder keySerializer(Class<? extends Serializer<?>> c) {
|
||||
@@ -44,9 +44,9 @@ public class KafkaProducerBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 kafka 路径 host:port
|
||||
* 添加 kafka 路径 host:port
|
||||
*
|
||||
* @author lingting 2020-06-19 16:30:03
|
||||
* @author lingting 2020-06-19 16:30:03
|
||||
*/
|
||||
public KafkaProducerBuilder addBootstrapServers(String uri) {
|
||||
bootstrapServers.add(uri);
|
||||
@@ -61,7 +61,7 @@ public class KafkaProducerBuilder {
|
||||
/**
|
||||
* 添加配置
|
||||
*
|
||||
* @author lingting 2020-06-19 16:30:50
|
||||
* @author lingting 2020-06-19 16:30:50
|
||||
*/
|
||||
public KafkaProducerBuilder put(Object key, Object val) {
|
||||
properties.put(key, val);
|
||||
@@ -71,7 +71,7 @@ public class KafkaProducerBuilder {
|
||||
/**
|
||||
* 添加配置
|
||||
*
|
||||
* @author lingting 2020-06-19 16:30:50
|
||||
* @author lingting 2020-06-19 16:30:50
|
||||
*/
|
||||
public KafkaProducerBuilder putAll(Properties properties) {
|
||||
this.properties.putAll(properties);
|
||||
@@ -96,8 +96,12 @@ public class KafkaProducerBuilder {
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
bootstrapServers.addAll(ListUtil.toList(properties.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, StrUtil.EMPTY).split(BOOTSTRAP_SERVERS_DELIMITER)));
|
||||
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, String.join(BOOTSTRAP_SERVERS_DELIMITER, bootstrapServers));
|
||||
bootstrapServers
|
||||
.addAll(ListUtil.toList(properties.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, StrUtil.EMPTY)
|
||||
.split(BOOTSTRAP_SERVERS_DELIMITER)));
|
||||
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
String.join(BOOTSTRAP_SERVERS_DELIMITER, bootstrapServers));
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,29 +5,26 @@ import org.apache.kafka.clients.consumer.KafkaConsumer;
|
||||
import org.apache.kafka.common.serialization.StringDeserializer;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/6/23 17:07
|
||||
* @author lingting 2020/6/23 17:07
|
||||
*/
|
||||
public class KafkaTest {
|
||||
public static void main(String[] args) {
|
||||
// KafkaProducer<String, String> producer = new KafkaProducerBuilder()
|
||||
KafkaConsumer<Object, Object> consumer = new KafkaConsumerBuilder()
|
||||
.keyDeserializer(StringDeserializer.class)
|
||||
.valueDeserializer(StringDeserializer.class)
|
||||
.groupId("group-id")
|
||||
.addTopic("first")
|
||||
// .keySerializer(StringSerializer.class)
|
||||
// .valueSerializer(StringSerializer.class)
|
||||
.addBootstrapServers("192.168.1.3:50211")
|
||||
.addBootstrapServers("192.168.1.3:50212")
|
||||
.build();
|
||||
|
||||
// producer.send("first", "测试消息");
|
||||
// while (true) {
|
||||
// ConsumerRecords<Object, Object> records = consumer.poll(Duration.ofSeconds(5));
|
||||
// records.forEach(record -> {
|
||||
// System.out.println(record.key());
|
||||
// System.out.println(record.value());
|
||||
// });
|
||||
// }
|
||||
public static void main(String[] args) {
|
||||
// KafkaProducer<String, String> producer = new KafkaProducerBuilder()
|
||||
KafkaConsumer<Object, Object> consumer = new KafkaConsumerBuilder().keyDeserializer(StringDeserializer.class)
|
||||
.valueDeserializer(StringDeserializer.class).groupId("group-id").addTopic("first")
|
||||
// .keySerializer(StringSerializer.class)
|
||||
// .valueSerializer(StringSerializer.class)
|
||||
.addBootstrapServers("192.168.1.3:50211").addBootstrapServers("192.168.1.3:50212").build();
|
||||
|
||||
// producer.send("first", "测试消息");
|
||||
// while (true) {
|
||||
// ConsumerRecords<Object, Object> records = consumer.poll(Duration.ofSeconds(5));
|
||||
// records.forEach(record -> {
|
||||
// System.out.println(record.key());
|
||||
// System.out.println(record.value());
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,19 +17,23 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
/**
|
||||
* 用于mybatis 自定义的 列
|
||||
*
|
||||
* @author lingting 2020/5/27 15:53
|
||||
* @author lingting 2020/5/27 15:53
|
||||
*/
|
||||
@Getter
|
||||
public class Columns<T> {
|
||||
|
||||
public static final String COLUMN_FLAG = "@";
|
||||
|
||||
/**
|
||||
* 缓存 全类名 方法名 字段名
|
||||
* 缓存 全类名 方法名 字段名
|
||||
*/
|
||||
private static final Map<String, ConcurrentHashMap<String, String>> COLUMN_CACHE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 自定义字段
|
||||
*/
|
||||
private final List<Column> list = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 类的所有字段
|
||||
*/
|
||||
@@ -68,8 +72,8 @@ public class Columns<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param val 自定义的替换sql {@link Columns#COLUMN_FLAG} 表示字段名
|
||||
* @author lingting 2020-05-27 17:57:35
|
||||
* @param val 自定义的替换sql {@link Columns#COLUMN_FLAG} 表示字段名
|
||||
* @author lingting 2020-05-27 17:57:35
|
||||
*/
|
||||
public Columns<T> add(SFunction<T, ?> sf, String val) {
|
||||
String column = getColumn(sf);
|
||||
@@ -77,10 +81,7 @@ public class Columns<T> {
|
||||
val = "VALUES(" + column + ")";
|
||||
}
|
||||
|
||||
list.add(new Column()
|
||||
.setName(column)
|
||||
.setVal(val.replaceAll(COLUMN_FLAG, column))
|
||||
);
|
||||
list.add(new Column().setName(column).setVal(val.replaceAll(COLUMN_FLAG, column)));
|
||||
back.remove(column);
|
||||
return this;
|
||||
}
|
||||
@@ -88,13 +89,14 @@ public class Columns<T> {
|
||||
/**
|
||||
* 获取方法所代表的表的字段名
|
||||
*
|
||||
* @author lingting 2020-05-27 17:56:40
|
||||
* @author lingting 2020-05-27 17:56:40
|
||||
*/
|
||||
public String getColumn(SFunction<T, ?> sf) {
|
||||
SerializedLambda lambda = SerializedLambda.resolve(sf);
|
||||
|
||||
if (!COLUMN_CACHE_MAP.containsKey(lambda.getImplClass().getName())) {
|
||||
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(LambdaUtils.getColumnMap(lambda.getImplClass()).size());
|
||||
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(
|
||||
LambdaUtils.getColumnMap(lambda.getImplClass()).size());
|
||||
LambdaUtils.getColumnMap(lambda.getImplClass()).forEach((k, v) -> {
|
||||
map.put(k, v.getColumn());
|
||||
});
|
||||
@@ -115,7 +117,7 @@ public class Columns<T> {
|
||||
/**
|
||||
* 忽略设置的字段
|
||||
*
|
||||
* @author lingting 2020-05-28 11:06:15
|
||||
* @author lingting 2020-05-28 11:06:15
|
||||
*/
|
||||
public Columns<T> ignore() {
|
||||
this.ignore = true;
|
||||
@@ -125,7 +127,7 @@ public class Columns<T> {
|
||||
/**
|
||||
* 设置不忽略设置的字段
|
||||
*
|
||||
* @author lingting 2020-05-28 11:05:59
|
||||
* @author lingting 2020-05-28 11:05:59
|
||||
*/
|
||||
public Columns<T> set() {
|
||||
this.ignore = false;
|
||||
@@ -135,8 +137,11 @@ public class Columns<T> {
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class Column {
|
||||
private String name;
|
||||
private String val;
|
||||
}
|
||||
}
|
||||
|
||||
private String name;
|
||||
|
||||
private String val;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,17 +9,14 @@ import org.apache.ibatis.annotations.Param;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 所有的 Mapper接口 都需要继承当前接口
|
||||
* 如果想自己定义其他的全局方法, 您的全局 BaseMapper 需要继承当前接口
|
||||
* 所有的 Mapper接口 都需要继承当前接口 如果想自己定义其他的全局方法, 您的全局 BaseMapper 需要继承当前接口
|
||||
*
|
||||
* @author lingting 2020/5/27 11:39
|
||||
* @author lingting 2020/5/27 11:39
|
||||
*/
|
||||
public interface ExtendBaseMapper<T> extends com.baomidou.mybatisplus.core.mapper.BaseMapper<T> {
|
||||
|
||||
/**
|
||||
* 批处理 如果重复则忽略
|
||||
* 实现类 {@link InsertIgnoreByBatch}
|
||||
*
|
||||
* 批处理 如果重复则忽略 实现类 {@link InsertIgnoreByBatch}
|
||||
* @param list 值列表
|
||||
* @return int
|
||||
* @author lingting 2020-05-27 11:41:28
|
||||
@@ -27,10 +24,8 @@ public interface ExtendBaseMapper<T> extends com.baomidou.mybatisplus.core.mappe
|
||||
int insertIgnoreByBatch(@Param("list") List<T> list);
|
||||
|
||||
/**
|
||||
* 批处理 如果重复则更新
|
||||
* 实现类 {@link InsertOrUpdateByBatch}
|
||||
*
|
||||
* @param list 值列表
|
||||
* 批处理 如果重复则更新 实现类 {@link InsertOrUpdateByBatch}
|
||||
* @param list 值列表
|
||||
* @param ignore 是否忽略全局配置的忽略字段 {@link StaticConfig#UPDATE_IGNORE_FIELDS}
|
||||
* @return int
|
||||
* @author lingting 2020-05-27 11:41:28
|
||||
@@ -39,7 +34,6 @@ public interface ExtendBaseMapper<T> extends com.baomidou.mybatisplus.core.mappe
|
||||
|
||||
/**
|
||||
* 批处理 如果重复则更新 直接调用本方法会 忽略全局配置的忽略字段 {@link StaticConfig#UPDATE_IGNORE_FIELDS}
|
||||
*
|
||||
* @param list 值列表
|
||||
* @return int
|
||||
* @author lingting 2020-05-27 11:41:28
|
||||
@@ -49,15 +43,13 @@ public interface ExtendBaseMapper<T> extends com.baomidou.mybatisplus.core.mappe
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义 如果重复 需要更新的 field
|
||||
* 当传入的 columns.ignore 属性为 true时
|
||||
* 会使用您传入的 字段值 去覆盖 不在 columns.list 中 字段 的值
|
||||
* 实现类 {@link InsertOrUpdateFieldByBatch}
|
||||
*
|
||||
* @param list 值列表
|
||||
* 自定义 如果重复 需要更新的 field 当传入的 columns.ignore 属性为 true时 会使用您传入的 字段值 去覆盖 不在 columns.list
|
||||
* 中 字段 的值 实现类 {@link InsertOrUpdateFieldByBatch}
|
||||
* @param list 值列表
|
||||
* @param columns 字段
|
||||
* @return int
|
||||
* @author lingting 2020-05-27 15:48:20
|
||||
*/
|
||||
int insertOrUpdateFieldByBatch(@Param("list") List<T> list, @Param("columns") Columns<T> columns);
|
||||
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/5/27 11:43
|
||||
* @author lingting 2020/5/27 11:43
|
||||
*/
|
||||
@Configuration
|
||||
public class MybatisConfig implements MybatisConfigurer {
|
||||
@@ -27,4 +27,5 @@ public class MybatisConfig implements MybatisConfigurer {
|
||||
list.add(new InsertOrUpdateByBatch());
|
||||
list.add(new InsertOrUpdateFieldByBatch());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,59 +9,56 @@ import org.apache.ibatis.mapping.SqlSource;
|
||||
/**
|
||||
* 所有插入自定义方法的父类
|
||||
*
|
||||
* @author lingting 2020/5/27 15:14
|
||||
* @author lingting 2020/5/27 15:14
|
||||
*/
|
||||
public abstract class BaseInsertBatch extends AbstractMethod {
|
||||
|
||||
@Override
|
||||
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
|
||||
SqlSource sqlSource = languageDriver.createSqlSource(configuration,
|
||||
String.format(getSql(), tableInfo.getTableName(),
|
||||
prepareFieldSql(tableInfo),
|
||||
prepareValuesSqlForMysqlBatch(tableInfo)
|
||||
),
|
||||
modelClass);
|
||||
return this.addInsertMappedStatement(mapperClass, modelClass, getId(), sqlSource, new NoKeyGenerator(), null, null);
|
||||
}
|
||||
@Override
|
||||
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
|
||||
SqlSource sqlSource = languageDriver.createSqlSource(configuration, String.format(getSql(),
|
||||
tableInfo.getTableName(), prepareFieldSql(tableInfo), prepareValuesSqlForMysqlBatch(tableInfo)),
|
||||
modelClass);
|
||||
return this.addInsertMappedStatement(mapperClass, modelClass, getId(), sqlSource, new NoKeyGenerator(), null,
|
||||
null);
|
||||
}
|
||||
|
||||
protected String prepareFieldSql(TableInfo tableInfo) {
|
||||
StringBuilder fieldSql = new StringBuilder();
|
||||
fieldSql.append(tableInfo.getKeyColumn()).append(",");
|
||||
tableInfo.getFieldList().forEach(x -> fieldSql.append(x.getColumn()).append(","));
|
||||
fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
|
||||
fieldSql.insert(0, "(");
|
||||
fieldSql.append(")");
|
||||
return fieldSql.toString();
|
||||
}
|
||||
protected String prepareFieldSql(TableInfo tableInfo) {
|
||||
StringBuilder fieldSql = new StringBuilder();
|
||||
fieldSql.append(tableInfo.getKeyColumn()).append(",");
|
||||
tableInfo.getFieldList().forEach(x -> fieldSql.append(x.getColumn()).append(","));
|
||||
fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
|
||||
fieldSql.insert(0, "(");
|
||||
fieldSql.append(")");
|
||||
return fieldSql.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注册的脚本
|
||||
*
|
||||
* @return java.lang.String
|
||||
* @author lingting 2020-06-09 20:38:54
|
||||
*/
|
||||
abstract protected String getSql();
|
||||
/**
|
||||
* 获取注册的脚本
|
||||
* @return java.lang.String
|
||||
* @author lingting 2020-06-09 20:38:54
|
||||
*/
|
||||
abstract protected String getSql();
|
||||
|
||||
/**
|
||||
* 获取脚本id 即 方法名
|
||||
*
|
||||
* @return java.lang.String
|
||||
* @author lingting 2020-06-09 20:39:30
|
||||
*/
|
||||
abstract protected String getId();
|
||||
/**
|
||||
* 获取脚本id 即 方法名
|
||||
* @return java.lang.String
|
||||
* @author lingting 2020-06-09 20:39:30
|
||||
*/
|
||||
abstract protected String getId();
|
||||
|
||||
protected String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) {
|
||||
return prepareValuesBuildSqlForMysqlBatch(tableInfo).toString();
|
||||
}
|
||||
|
||||
protected String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) {
|
||||
return prepareValuesBuildSqlForMysqlBatch(tableInfo).toString();
|
||||
}
|
||||
protected StringBuilder prepareValuesBuildSqlForMysqlBatch(TableInfo tableInfo) {
|
||||
final StringBuilder valueSql = new StringBuilder();
|
||||
valueSql.append(
|
||||
"<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
|
||||
valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
|
||||
tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
|
||||
valueSql.delete(valueSql.length() - 1, valueSql.length());
|
||||
valueSql.append("</foreach>");
|
||||
return valueSql;
|
||||
}
|
||||
|
||||
protected StringBuilder prepareValuesBuildSqlForMysqlBatch(TableInfo tableInfo) {
|
||||
final StringBuilder valueSql = new StringBuilder();
|
||||
valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
|
||||
valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
|
||||
tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
|
||||
valueSql.delete(valueSql.length() - 1, valueSql.length());
|
||||
valueSql.append("</foreach>");
|
||||
return valueSql;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
package com.hccake.extend.mybatis.plus.mysql.methods;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/5/27 11:47
|
||||
* @author lingting 2020/5/27 11:47
|
||||
*/
|
||||
public class InsertIgnoreByBatch extends BaseInsertBatch {
|
||||
@Override
|
||||
protected String getSql() {
|
||||
return "<script>insert ignore into %s %s values %s</script>";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getId() {
|
||||
return "insertIgnoreByBatch";
|
||||
}
|
||||
@Override
|
||||
protected String getSql() {
|
||||
return "<script>insert ignore into %s %s values %s</script>";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getId() {
|
||||
return "insertIgnoreByBatch";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@ import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||
import com.hccake.extend.mybatis.plus.config.StaticConfig;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/5/27 11:47
|
||||
* @author lingting 2020/5/27 11:47
|
||||
*/
|
||||
public class InsertOrUpdateByBatch extends BaseInsertBatch {
|
||||
|
||||
@Override
|
||||
protected String getSql() {
|
||||
return "<script>insert into %s %s values %s</script>";
|
||||
@@ -29,8 +30,10 @@ public class InsertOrUpdateByBatch extends BaseInsertBatch {
|
||||
// 默认忽略字段
|
||||
if (!StaticConfig.UPDATE_IGNORE_FIELDS.contains(field.getProperty())) {
|
||||
sql.append(field.getColumn()).append("=").append("VALUES(").append(field.getColumn()).append("),");
|
||||
} else {
|
||||
ignore.append(",").append(field.getColumn()).append("=").append("VALUES(").append(field.getColumn()).append(")");
|
||||
}
|
||||
else {
|
||||
ignore.append(",").append(field.getColumn()).append("=").append("VALUES(").append(field.getColumn())
|
||||
.append(")");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -42,4 +45,5 @@ public class InsertOrUpdateByBatch extends BaseInsertBatch {
|
||||
sql.append("<if test=\"!ignore\">").append(ignore).append("</if>");
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,39 +3,36 @@ package com.hccake.extend.mybatis.plus.mysql.methods;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/5/27 11:47
|
||||
* @author lingting 2020/5/27 11:47
|
||||
*/
|
||||
public class InsertOrUpdateFieldByBatch extends BaseInsertBatch {
|
||||
private static final String SQL = "<script>insert into %s %s values %s</script>";
|
||||
|
||||
@Override
|
||||
protected String getSql() {
|
||||
return "<script>insert into %s %s values %s</script>";
|
||||
}
|
||||
private static final String SQL = "<script>insert into %s %s values %s</script>";
|
||||
|
||||
@Override
|
||||
protected String getId() {
|
||||
return "insertOrUpdateFieldByBatch";
|
||||
}
|
||||
@Override
|
||||
protected String getSql() {
|
||||
return "<script>insert into %s %s values %s</script>";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) {
|
||||
StringBuilder sql = super.prepareValuesBuildSqlForMysqlBatch(tableInfo);
|
||||
sql.append(" ON DUPLICATE KEY UPDATE ")
|
||||
// 如果模式为 不忽略设置的字段
|
||||
.append("<if test=\"!columns.ignore\">")
|
||||
.append("<foreach collection=\"columns.list\" item=\"item\" index=\"index\" separator=\",\" >")
|
||||
.append("${item.name}=${item.val}")
|
||||
.append("</foreach>")
|
||||
.append("</if>");
|
||||
@Override
|
||||
protected String getId() {
|
||||
return "insertOrUpdateFieldByBatch";
|
||||
}
|
||||
|
||||
// 如果模式为 忽略设置的字段
|
||||
sql.append("<if test=\"columns.ignore\">")
|
||||
.append("<foreach collection=\"columns.back\" item=\"item\" index=\"index\" separator=\",\" >")
|
||||
.append("${item}=VALUES(${item})")
|
||||
.append("</foreach>")
|
||||
.append("</if>");
|
||||
return sql.toString();
|
||||
}
|
||||
@Override
|
||||
protected String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) {
|
||||
StringBuilder sql = super.prepareValuesBuildSqlForMysqlBatch(tableInfo);
|
||||
sql.append(" ON DUPLICATE KEY UPDATE ")
|
||||
// 如果模式为 不忽略设置的字段
|
||||
.append("<if test=\"!columns.ignore\">")
|
||||
.append("<foreach collection=\"columns.list\" item=\"item\" index=\"index\" separator=\",\" >")
|
||||
.append("${item.name}=${item.val}").append("</foreach>").append("</if>");
|
||||
|
||||
// 如果模式为 忽略设置的字段
|
||||
sql.append("<if test=\"columns.ignore\">")
|
||||
.append("<foreach collection=\"columns.back\" item=\"item\" index=\"index\" separator=\",\" >")
|
||||
.append("${item}=VALUES(${item})").append("</foreach>").append("</if>");
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,14 +15,16 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/6/9 19:59
|
||||
* @author lingting 2020/6/9 19:59
|
||||
*/
|
||||
@Slf4j
|
||||
@ComponentScan
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({MybatisProperties.class})
|
||||
@EnableConfigurationProperties({ MybatisProperties.class })
|
||||
public class MybatisAutoConfiguration {
|
||||
|
||||
private final List<MybatisConfigurer> configurers;
|
||||
|
||||
private final MybatisProperties properties;
|
||||
|
||||
public MybatisAutoConfiguration(List<MybatisConfigurer> configurers, MybatisProperties properties) {
|
||||
@@ -42,7 +44,8 @@ public class MybatisAutoConfiguration {
|
||||
for (Class<? extends AbstractMethod> c : properties.getMethods()) {
|
||||
try {
|
||||
list.add(c.newInstance());
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new MybatisMethodInstanceException("获取自定义全局方法实例出错 class: " + c.getName(), e);
|
||||
}
|
||||
}
|
||||
@@ -50,4 +53,5 @@ public class MybatisAutoConfiguration {
|
||||
configurers.forEach(mybatisConfigurer -> mybatisConfigurer.addGlobalMethods(list));
|
||||
return new SqlInjector(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ import lombok.RequiredArgsConstructor;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lingting 2020/5/27 11:46
|
||||
* @author lingting 2020/5/27 11:46
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class SqlInjector extends DefaultSqlInjector {
|
||||
private final List<AbstractMethod> list;
|
||||
|
||||
private final List<AbstractMethod> list;
|
||||
|
||||
@Override
|
||||
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
|
||||
@@ -20,4 +20,5 @@ public class SqlInjector extends DefaultSqlInjector {
|
||||
list.addAll(this.list);
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,15 +6,14 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 用于自定义 mybatis 配置
|
||||
* 自定义配置需 实现本类 并设置为bean
|
||||
* 用于自定义 mybatis 配置 自定义配置需 实现本类 并设置为bean
|
||||
*
|
||||
* @author lingting 2020/5/27 20:56
|
||||
* @author lingting 2020/5/27 20:56
|
||||
*/
|
||||
public interface MybatisConfigurer {
|
||||
|
||||
/**
|
||||
* 添加 生成 更新 sql 时 需要忽略的字段
|
||||
*
|
||||
* @param set 字段 set
|
||||
* @author lingting 2020-05-27 20:58:32
|
||||
*/
|
||||
@@ -23,10 +22,10 @@ public interface MybatisConfigurer {
|
||||
|
||||
/**
|
||||
* 添加自定义的 全局方法
|
||||
*
|
||||
* @param list 方法list
|
||||
* @author lingting 2020-05-27 23:47:10
|
||||
*/
|
||||
default void addGlobalMethods(List<AbstractMethod> list) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,11 +10,12 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lingting 2020-05-28 14:54:30
|
||||
* @author lingting 2020-05-28 14:54:30
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "ballcat.extend.mybatis-plus")
|
||||
public class MybatisProperties {
|
||||
|
||||
/**
|
||||
* 全局忽略更新的字段
|
||||
*/
|
||||
@@ -24,4 +25,5 @@ public class MybatisProperties {
|
||||
* 自定义方法
|
||||
*/
|
||||
private List<Class<? extends AbstractMethod>> methods = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
||||
@@ -6,11 +6,13 @@ import java.util.Set;
|
||||
/**
|
||||
* 用于配置 需要 动态加载,但是 静态来使用的变量
|
||||
*
|
||||
* @author lingting 2020/6/9 20:05
|
||||
* @author lingting 2020/6/9 20:05
|
||||
*/
|
||||
public class StaticConfig {
|
||||
|
||||
/**
|
||||
* 更新时忽略的字段
|
||||
*/
|
||||
public static final Set<String> UPDATE_IGNORE_FIELDS = new HashSet<>();
|
||||
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ package com.hccake.extend.mybatis.plus.exception;
|
||||
/**
|
||||
* mybatis plus 方法 实例化获取异常
|
||||
*
|
||||
* @author lingting 2020/5/28 15:06
|
||||
* @author lingting 2020/5/28 15:06
|
||||
*/
|
||||
public class MybatisMethodInstanceException extends RuntimeException {
|
||||
|
||||
public MybatisMethodInstanceException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user