0%

概述

java.lang.SuppressWarnings是J2SE5.0中标准的Annotation之一。可以标注在类、字段、方法、参数、构造方法,以及局部变量上。

1
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE})

目的

告诉编译器忽略指定的警告,不用在编译完成后出现警告信息。

使用方式

1
2
3
@SuppressWarnings(“”)
@SuppressWarnings({})
@SuppressWarnings(value = {})

使用示例

@SuppressWarnings(“serial”)

警告信息:The serializable class WmailCalendar does notdeclare a static final serialVersionUID field of type long
使用这个注释将警告信息去掉。

@SuppressWarnings(“unused”)

编码时出现变量未被使用的警告提示

可以在方法前添加 @SuppressWarnings("unused") 去除这些“感叹号”。

抑制警告的关键字

*关键字* *用途*
all to suppress all warnings(抑制所有警告)
boxing to suppress warnings relative to boxing/unboxing operations(要抑制与箱/非装箱操作相关的警告)
cast to suppress warnings relative to cast operations(为了抑制与强制转换操作相关的警告)
dep-ann to suppress warnings relative to deprecated annotation(要抑制相对于弃用注释的警告)
deprecation to suppress warnings relative to deprecation(要抑制相对于弃用的警告)
fallthrough to suppress warnings relative to missing breaks in switch statements(在switch语句中,抑制与缺失中断相关的警告)
finally to suppress warnings relative to finally block that don’t return(为了抑制警告,相对于最终阻止不返回的警告)
hiding to suppress warnings relative to locals that hide variable(为了抑制本地隐藏变量的警告)
incomplete-switch to suppress warnings relative to missing entries in a switch statement (enum case)(为了在switch语句(enum案例)中抑制相对于缺失条目的警告)
nls to suppress warnings relative to non-nls string literals(要抑制相对于非nls字符串字面量的警告)
null to suppress warnings relative to null analysis(为了抑制与null分析相关的警告)
rawtypes to suppress warnings relative to un-specific types when using generics on class params(在类params上使用泛型时,要抑制相对于非特异性类型的警告)
restriction to suppress warnings relative to usage of discouraged or forbidden references(禁止使用警告或禁止引用的警告)
serial to suppress warnings relative to missing serialVersionUID field for a serializable class(为了一个可串行化的类,为了抑制相对于缺失的serialVersionUID字段的警告)
static-access o suppress warnings relative to incorrect static access(o抑制与不正确的静态访问相关的警告)
synthetic-access to suppress warnings relative to unoptimized access from inner classes(相对于内部类的未优化访问,来抑制警告)
unchecked to suppress warnings relative to unchecked operations(相对于不受约束的操作,抑制警告)
unqualified-field-access to suppress warnings relative to field access unqualified(为了抑制与现场访问相关的警告)
unused to suppress warnings relative to unused code(抑制没有使用过代码的警告)

本文地址: https://github.com/maxzhao-it/blog/post/40046/

概述

@ServerEndpoint类下的@Resource@Autowired无效

下面的注入并没有成功

1
2
3
4
5
6
7
8
9
10
11

@Component
@Slf4j
public class WebSocketServer {

/**
* 聊天信息分发
*/
@Resource(name = "socketRouter")
private ISocketRouter socketRouter;
}

原因 1

在使用@ServerEndpoint 是多例的,与 spring 单例模式冲突,所以在当前类中只会注入一次.

解决方法

在加载当前 bean时,set方法会再次执行一次.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@Component
@Slf4j
public class WebSocketServer {

/**
* 聊天信息分发
*/
private static ISocketRouter socketRouter;

@Resource(name = "socketRouter")
public void setEmployeeServer(ISocketRouter socketRouter) {
this.socketRouter = socketRouter;
}
}

本文地址: https://github.com/maxzhao-it/blog/post/58297/

概述

@Component类下的@Resource@Autowired无效

下面的注入并没有成功

1
2
3
4
5
6
7
8
9
10
11

@Component
@Slf4j
public class WebSocketServer {

/**
* 聊天信息分发
*/
@Resource(name = "socketRouter")
private ISocketRouter socketRouter;
}

原因 1

在使用@Component注解将bean实例化到spring容器内的时候,@Resource@Autowired是在这个bean之中的,@Resource@Autowired
还未完成自动装载,所以导致servicenull

解决方法

在加载当前 bean时,set方法会再次执行一次.

注意,参数为静态变量,因为这里的 set 方法只会执行一次.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@Component
@Slf4j
public class WebSocketServer {

/**
* 聊天信息分发
*/
private static ISocketRouter socketRouter;

@Resource(name = "socketRouter")
public void setEmployeeServer(ISocketRouter socketRouter) {
this.socketRouter = socketRouter;
}
}

本文地址: https://github.com/maxzhao-it/blog/post/27993/

都说MySql8.x比5.7快两倍,可以参考一下。
但不支持从 MySQL 8.0 降级到 MySQL 5.7(或从某个 MySQL 8.0 版本降级到任意一个更早的 MySQL 8.0 版本)。数据备份方式还是可以的。

  • 注意MySql8 的用户安全策略的改变
  • 注意MySql8 编码格式

安装

先查看有没有 mariadb

1
sudo pacman -Qs mariadb

有就强制卸载

1
sudo pacman -R xxx

安装

1
sudo pacman -S mysql

初始化配置

1
2
3
4
5
6
# 初始化,最后会输出密码
sudo mysqld --initialize --user=mysql --basedir=/usr --datadir=/var/lib/mysql
chown mysql:mysql /var/lib/mysql -R
systemctl start mysqld.service
# 开机自起
systemctl enable mysqld

查看安装日志中的密码

1
cat /var/log/mysqld.log | grep password

登录

1
mysql -uroot -p

比如这个uQ3sw%Cu;IKk复制粘贴就可以了。

修改密码

1
2
3
4
5
6
alter user 'root'@'localhost' identified  by "123456";
# alter user 'root'@'localhost' identified WITH caching_sha2_password by "123456";
#创建远程连接
create user root@'%' identified by '123456';
grant all privileges on *.* to root@'%';
flush privileges;

无密码的方式,可用于找回密码。

输入一百次也输入不对默认密码 配置文件 MySQL 免密码登录 编辑 MySQL 的配置文件

1
2
3
vim /etc/my.cnf
#在 datadir开头的下面一行加入下面这句
skip-grant-tables

修改密码(记得删除my.cnf文件的东西)

1
2
3
bin/mysql 
use mysql;
update user SET Password = 'new-password' WHERE User = 'root';

基本配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[mysqld]
basedir=/var/lib/mysql
datadir=/var/lib/mysql
port=3336
character-set-server=utf8mb4
# 下面这个选项是可以选择的
#default_authentication_plugin=mysql_native_password

#排序规则
collation-server=utf8mb4_0900_ai_ci
#utf8mb4_0900_ai_ci 排序规则:ai 口音不敏感 ci 不区分大小写 ,默认支持表情符号
#utf8mb4_0900_ai_ci 属于 utf8mb4_unicode_ci 中的一种
#utf8mb4_general_ci 没有实现utf8mb4_unicode_ci 的排序规则。没有utf8mb4_unicode_ci 准备。但是比较和排序的时候更快
[mysql]
default-character-set=utf8mb4
# 不使用密码进入
# skip-grant-table

[client]
default-character-set=utf8mb4

问题

问题1 NO_ZERO_DATE

问题描述:安装mysql执行初始化命令mysqld –initialize时出现如下错误:

1
2
3
4
5
6
2021-04-12T05:57:03.198202Z 0 [Warning] [MY-010915] [Server] 'NO_ZERO_DATE', 'NO_ZERO_IN_DATE' and 'ERROR_FOR_DIVISION_BY_ZERO' sql modes should be used with strict mode. They will be merged with strict mode in a future release.
2021-04-12T05:57:03.198234Z 0 [System] [MY-013169] [Server] /usr/bin/mysqld (mysqld 8.0.23) initializing of server in progress as process 6289
2021-04-12T05:57:03.199359Z 0 [ERROR] [MY-010457] [Server] --initialize specified but the data directory has files in it. Aborting.
2021-04-12T05:57:03.199363Z 0 [ERROR] [MY-013236] [Server] The designated data directory /var/lib/mysql/ is unusable. You can remove all files that the server added to it.
2021-04-12T05:57:03.199405Z 0 [ERROR] [MY-010119] [Server] Aborting
2021-04-12T05:57:03.199471Z 0 [System] [MY-010910] [Server] /usr/bin/mysqld: Shutdown complete (mysqld 8.0.23) Source distribution.

问题原因:根目录下data文件夹问题。一般在初始化前手动建立data文件夹会有问题,需要初始化时自动创建。

解决办法:将根目录下data文件夹删除或修改名字,再次执行初始化命令时会自动简历data文件夹。

问题2 libicuuc.so.65

下载icu4c-65_1-src.zip

1
2
3
4
解压icu4c-65_1-src.zip
cd icu/source
# 这里如果执行失败,就看一下文件中有没有 \n\r,如果有删除\r
./configure

本文地址: Centos7 解压安装MYSQL8

推荐

MySQL8.0创建用户及其配置
MySQL8.0新特性-新的索引方式
MySQL8.0新特性-通用表表达式(CTE)
MySQL8.0新特性-窗口函数
MySQL8.0新特性-InnoDB增强
MySQL8.0新特性-JSON增强
[官方介绍]([https://dev.mysql.com/doc/refman/8.0/en/create-index.html#create-index-functional-key-parts

本文地址: https://github.com/maxzhao-it/blog/post/20858/

概述

当前服务第一次适用于聊天项目。

目的

  • 方便后期切换 socket 或其它连接技术。
  • 更好的扩展使用场景。

整体思路

  • 消息收发与业务分离;
  • 连接方式动态扩展(位置共享、聊天多设备等);
  • socket技术切换不影响实现;

配置

ServerEndpointExporter

1
2
3
4
5
6
7
8

@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

主要依赖

1
2
3
4
5
6
7
8
9
10
11
<!--websocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

SocketServer

socket 服务

socket服务一般使用

  • onOpen 打开连接
  • onClose 关闭连接
  • onError 错误
  • onMessage 接收消息
1
2
3
4
5
6
7

@Component
@ServerEndpoint("/test/{userId}/{userKey}")
@Slf4j
public class WebSocketServer {
/* ************************************************** */
}
  • test 用于标记路径,与 Api 一样(不一样在于 socket长连接,不能像 Api一样可以方便的切换路径)
  • /{userId}/{userKey} 动态参数
  • 这里使用 userIduserKey 用来标记连接,目的是:
    • 一个 userId 标识连接的客户身份;
    • 一个 userKey 标识连接的客户设备;
    • 一个用户可以使用多个设备连接(具体参考 连接存储);

onOpen 打开连接

1
2
3
4
5
6
7
8
9
10
11

public class WebSocketServer {
@OnOpen
public void onOpen(Session session,
@PathParam(value = "userId") String userId,
@PathParam(value = "userKey") String userKey) {
log.info("client login => {}", session.getId());
log.info("client login userId => {}", userId);
log.info("client login userKey => {}", userKey);
}
}

@PathParam(value = "userId") String userId 用户获取连接路径中的动态参数

onClose 关闭连接

1
2
3
4
5
6
7
8
9
public class WebSocketServer {
@OnClose
public void onClose(Session session,
@PathParam(value = "userId") String userId,
@PathParam(value = "userKey") String userKey) {
log.info("client logout => {}", session.getId());
socketSessionStorage.outLine(userId);
}
}

onError 错误

1
2
3
4
5
public class WebSocketServer {
@OnError
public void onError(Throwable throwable) {
}
}

onMessage 接收消息

1
2
3
4
5
6
7
public class WebSocketServer {
@OnMessage
public void onMessage(Session session, String msg,
@PathParam(value = "userId") String currentUserId,
@PathParam(value = "userKey") String userKey) {
}
}

连接存储

连接存储使用多种类型的存储。

统一接口

下面是存储的接口(省略无用注释);

这里还使用了动态参数,主要是因为上面考虑客户可以使用多个设备的情况,这样实现就可以兼容 同时在线一人多设备在线、**不同类型设备同时在线 ** 等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import javax.websocket.Session;

/** * 连接存储模式 */
public interface ISocketSessionStorage {
/** * 用户上线 *
* @param keys 关键字
* @param session socket session
*/
default void onLine(Session session, String... keys) {
}

/** * 用户上线 *
* @param keys 关键字
*/
default void outLine(String... keys) {
}

/** * 用户是否在线 *
* @param keys 关键字
* @return true 在线
*/
default boolean isLine(String... keys) {
return false;
}

/** * 获取 socket session *
* @param keys 关键字
* @return socket session
*/
default Session getSession(String... keys) {
return null;
}
}

举例:同时在线一人

这里对于同时在线一人的处理,并不是像 QQ微信 一样让之前的人下线,而是让之前的人收不到消息。想让之前的人下线,需要执行 outLine(keys);或者有自己的处理。

代码参考

消息收发

接收消息

消息接收后,直接交给消息分发接口(代码参考)

在消息处理后,根据需要发送消息给前端(代码参考)

WebSocketServer 服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class WebSocketServer {
/** * 聊天信息分发 */
@Resource(name = "socketRouter")
private ISocketRouter socketRouter;

@OnMessage
public void onMessage(Session session, String msg,
@PathParam(value = "userId") String currentUserId,
@PathParam(value = "userKey") String userKey) {
SocketResult socketResult = chatRouter.goRoute(currentUserId, msg);
/*发送消息*/
sendMsg(socketResult);
}
}

发送消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class WebSocketServer {
/** * 发送消息 *
* @param socketResult 待发送的消息
*/
private void sendMsg(SocketResult socketResult) {
/*所有待发送的消息*/
List<SocketResultMsg> msgList = socketResult.getMsgs();
if (msgList == null || msgList.isEmpty()) {
/*没有消息*/
return;
}
Session sessionTemp;
for (SocketResultMsg socketResultMsg : msgList) {
if (!socketSessionStorage.isLine(socketResultMsg.getSocketKey())) {
/*用户不在线*/
continue;
}
sessionTemp = socketSessionStorage.getSession(socketResultMsg.getSocketKey());
try {
sessionTemp.getBasicRemote().sendText(socketResultMsg.getMessage());
} catch (IOException e) {
log.error("消息发送失败! socketKey={} e:{} \nStackTrace={}", socketResultMsg.getSocketKey(), e.getMessage(), e.getStackTrace());
}
}
}
}

消息处理

在消息服务中 (WebSocketServer) 这是一种比较简单的消息处理方式。

这种方式不用关心业务,业务处理只需要继承 ISocketRouter接口,并实现 goRoute方法.

SocketResult(代码参考) 是对响应结果的封装

ISocketRouter消息分发

1
2
3
4
5
6
7
8
9
10
11
/** * 聊天信息分发处理中心 */
public interface ISocketRouter {
/** * 消息分发中心 *
* @param currentUserId NotNull:当前连接用户主键
* @param message NotNull:消息
* @return 返回结果
*/
default SocketResult goRoute(String currentUserId, String message) {
return null;
}
}

其它代码

SocketSignSessionStorage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.websocket.Session;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
* 单连接存储模式
* <p>一个连接 keys 只能存储一个用户</p>
*
* @author maxzhao
* @date 2021-04-09 10:04
*/
@Slf4j
@Service("socketSignSessionStorage")
public class SocketSignSessionStorage implements ISocketSessionStorage {
/** * key 不存在则抛出异常 */
public static final String SOCKET_KEYS_IS_NULL = "Socket keys is null ";
/** * 在线人数 * <p>线程安全</p>*/
private static final AtomicInteger ON_LINE_NUM = new AtomicInteger();
/** * 在线长连接 * <p>线程安全</p>*/
private static final Map<String, Session> CLIENTS = new ConcurrentHashMap<>();

@Override
public void onLine(Session session, String[] keys) {
checkKeys(keys);
/*让之前的登录下线*/
/*outLine(keys);*/
CLIENTS.put(keys[0], session);
ON_LINE_NUM.addAndGet(1);
}

@Override
public void outLine(String[] keys) {
checkKeys(keys);
Session session = CLIENTS.get(keys[0]);
if (session != null && session.isOpen()) {
try {
session.close();
} catch (IOException e) {
log.warn("Socket close fail; keys : {} ", keys[0]);
}
}
CLIENTS.remove(keys[0]);
ON_LINE_NUM.addAndGet(-1);
}

@Override
public boolean isLine(String[] keys) {
checkKeys(keys);
Session session = CLIENTS.get(keys[0]);
if (session == null || !session.isOpen()) {
/*当前 socket session 不存在*/
CLIENTS.remove(keys[0]);
ON_LINE_NUM.addAndGet(-1);
return false;
}
return true;
}

@Override
public Session getSession(String[] keys) {
checkKeys(keys);
return CLIENTS.get(keys[0]);
}

/** * 校验 keys *
* @param keys socket 关键字
*/
private static void checkKeys(String[] keys) {
if (keys == null || keys.length == 0) {
throw new IllegalArgumentException(SOCKET_KEYS_IS_NULL);
}
}
}

SocketResult

1
2
3
4
5
6
7
8

/** * 消息响应结果 */
@Data
public class SocketResult {
/** * 要响应的所有消息 */
List<SocketResultMsg> msgs;
/* ************************** */
}

SocketResultMsg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/** * 消息响应消息 */
@Data
public class SocketResultMsg {
/** * socket 连接关键字 */
private String socketKey;
/** * 消息内容 */
private String message;
/* ************************** */

/** * 判断当前对象是否为 null *
* @return null 为 true
*/
public boolean isEmpty() {
return this.socketKey == null || this.message == null;
}
}

本文地址: https://github.com/maxzhao-it/blog/post/61051/

前言

一般情况下,在 SpringBoot 中,生成的代码默认在 resources 目录下,Mapper.xml 的路径也是固定的。

当前路径不建议修改,因为在打包时,Mapper.xmlMapper.class 是在一个文件夹下的。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.OracleTypeConvert;
import com.baomidou.mybatisplus.generator.config.converts.PostgreSqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
* 代码生成器
* <p>当前使用 freemarker 引擎需要 pom 依赖</p>
*
* @author maxzhao
* @date 2021-04-08
*/
public class CodeGenerator {
/**
* 文件存放位置
* <p>当前为当前项目位置</p>
*/
private static final String PROJECT_PATH = System.getProperty("user.dir");
/**
* 包的起始路径
*/
private static final String PACKAGE_PARENT = "boot";
/**
* 模块名
*/
private static final String MODULE_NAME = "chats";
/**
* 文件注释下的 auther 名称
*/
public static final String AUTHOR = "maxzhao";
/**
* 表名
*/
private static final String[] TABLE_NAMES = "chat_group,chat_group_user,chat_history,chat_history_status,chat_session,chat_user,chat_user_grouping,chat_user_grouping_user".split(",");

/**
* 连接地址
* oracle = "jdbc:mysql://127.0.0.1:3306/boot?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true";
* postgresql = "jdbc:postgresql://127.0.0.1:5432/postgres?charSet=utf8&currentSchema=pg_schema";
* mysql8 = "jdbc:oracle:thin:@32.1.6.219:1521/orcl";
*/
private static final String DS_URL = "jdbc:mysql://127.0.0.1:3306/boot?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true";
/**
* 驱动
* oracle = "oracle.jdbc.driver.OracleDriver";
* postgresql = "org.postgresql.Driver";
* mysql8 = "com.mysql.cj.jdbc.Driver";
*/
private static final String DS_DRIVER_NAME = "com.mysql.cj.jdbc.Driver";
private static final String DS_USERNAME = "root";
private static final String DS_PASSWORD = "maxzhao";

/**
* PostgreSql 的 schema(其它数据库不需要)
*/
public static final String POSTGRESQL_SCHEMA = "pg_schema";

/**
* RUN
*/
public static void main(String[] args) {
codeGenerate();
}

/**
* 代码生成器
*/
private static void codeGenerate() {
/*代码生成器*/
AutoGenerator mpg = new AutoGenerator();
/*全局配置*/
mpg.setGlobalConfig(getGlobalConfig());
/*数据源配置*/
mpg.setDataSource(getDataSourceConfig());
/*跟包相关的配置项*/
mpg.setPackageInfo(getPackageConfig());
/*自定义配置*/
mpg.setCfg(getInjectionConfig());
mpg.setTemplate(new TemplateConfig().setXml(null));
/*策略配置*/
mpg.setStrategy(getStrategyConfig());
// 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}

/**
* 全局配置
*/
private static GlobalConfig getGlobalConfig() {
GlobalConfig gc = new GlobalConfig();
/*生成文件的输出目录【默认 D 盘根目录】*/
gc.setOutputDir(PROJECT_PATH + File.separator + "src" + File.separator + "main" + File.separator + "java");
/*是否覆盖已有文件*/
gc.setFileOverride(false);
/*是否打开输出目录*/
gc.setOpen(false);
gc.setAuthor(AUTHOR);
gc.setSwagger2(true);
return gc;
}

/**
* 数据库配置
*/
private static DataSourceConfig getDataSourceConfig() {
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl(DS_URL);
dsc.setDriverName(DS_DRIVER_NAME);
dsc.setUsername(DS_USERNAME);
dsc.setPassword(DS_PASSWORD);
if (DS_DRIVER_NAME.contains("postgresql")) {
initPGSqlDataSourceConfig(dsc);
} else if (DS_DRIVER_NAME.contains("mysql")) {
} else if (DS_DRIVER_NAME.contains("oracle")) {
initOracleDataSourceConfig(dsc);
}
return dsc;
}

/**
* PostgreSQL数据库连接
*/
private static void initPGSqlDataSourceConfig(DataSourceConfig dsc) {
dsc.setSchemaName(POSTGRESQL_SCHEMA);
dsc.setTypeConvert(new PostgreSqlTypeConvert());
}

/**
* ORACLE 数据库连接
*/
private static void initOracleDataSourceConfig(DataSourceConfig dsc) {
dsc.setTypeConvert(new OracleTypeConvert() {
@Override
public DbColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) {
/*可以参考 super.processTypeConvert*/
return (DbColumnType) super.processTypeConvert(globalConfig, fieldType);
}
});
}

/**
* 跟包相关的配置项
*/
private static PackageConfig getPackageConfig() {
PackageConfig pc = new PackageConfig();
/*父包名。如果为空,将下面子包名必须写全部, 否则就只需写子包名*/
pc.setParent(PACKAGE_PARENT);
/*父包模块名*/
pc.setModuleName(MODULE_NAME);
/*Entity包名*/
pc.setEntity("model.entity");
/*Controller包名*/
pc.setController("api");
return pc;
}

/**
* 自定义配置
*/
private static InjectionConfig getInjectionConfig() {
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
List<FileOutConfig> focList = new ArrayList<>();
/*配置 Mapper.xml的生成位置*/
focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输入文件名称
return PROJECT_PATH + File.separator +
"src" + File.separator +
"main" + File.separator +
"resources" + File.separator +
PACKAGE_PARENT.replace(".", File.separator) +
File.separator +
MODULE_NAME.replace(".", File.separator) +
File.separator + "mapper" + File.separator +
tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
return cfg;
}

/**
* 策略配置
*/
private static StrategyConfig getStrategyConfig() {
StrategyConfig strategy = new StrategyConfig();
/*数据库表映射到实体的命名策略,下划线转驼峰*/
strategy.setNaming(NamingStrategy.underline_to_camel);
/*数据库表字段映射到实体的命名策略,未指定按照 naming 执行*/
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
/*表前缀*/
strategy.setTablePrefix("t_", "sys_", "p_");
/*字段前缀*/
strategy.setFieldPrefix("is", "has");
/*需要包含的表名,允许正则表达式(与exclude二选一配置)*/
strategy.setInclude(TABLE_NAMES);
/*实体是否生成 serialVersionUID*/
strategy.setEntitySerialVersionUID(true);
/*【实体】是否为lombok模型(默认 false)*/
strategy.setEntityLombokModel(true);
/*Boolean类型字段是否移除is前缀(默认 false) 比如 : 数据库字段名称 : 'is_xxx',类型为 : tinyint.
在映射实体的时候则会去掉is,在实体类中映射最终结果为 xxx*/
strategy.setEntityBooleanColumnRemoveIsPrefix(true);
/*是否生成实体时,生成字段注解*/
strategy.setEntityTableFieldAnnotationEnable(true);
/*生成 @RestController 控制器*/
strategy.setRestControllerStyle(true);
/*驼峰转连字符*/
strategy.setControllerMappingHyphenStyle(false);
/*是否生成实体时,生成字段注解*/
strategy.setEntityTableFieldAnnotationEnable(true);
return strategy;
}
}

本文地址: https://github.com/maxzhao-it/blog/post/49279/

描述

当自定义@SELECT语句后,直接传wrapper到方法上不行.

解决思路

普通用法

1
2
@select(“select*from user where name = #{name}”)
List selectUserByName(String name)

like 用法

1
2
3
4

@Select(“select*from userf where name like CONCAT(’%’,#{s},’%’)”)
List selectLikename(String s);

concat拼接字符串

CONCAT(’%’,#{0},’%’)”) 不能直接用 ‘%#{s}%’

本文地址: https://github.com/maxzhao-it/blog/post/47890/

描述

当自定义@SELECT语句后,直接传wrapper到方法上不行.

解决思路

MybatisPlus官方文档
上有一个方法:

注意事项:
需要mybatis-plus版本 >= 3.0.7 param 参数名要么叫ew,
要么加上注解@Param(Constants.WRAPPER) 使用
${ew.customSqlSegment} 不支持 Wrapper 内的entity生成where语句

用注解

1
2
@Select("select * from mysql_data ${ew.customSqlSegment}")
List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);

用XML

1
List<MysqlData> getAll(Wrapper ew);
1
2
3
4

<select id="getAll" resultType="MysqlData">
SELECT * FROM mysql_data ${ew.customSqlSegment}
</select>

链式调用 lambda 式

1
2
3
4
5
6
7
8
9
10
11
12
13
// 区分:
// 链式调用 普通
UpdateChainWrapper<T> update();
// 链式调用 lambda 式。注意:不支持 Kotlin
LambdaUpdateChainWrapper<T> lambdaUpdate();

// 等价示例:
query().eq("id",value).one();
lambdaQuery().eq(Entity::getId,value).one();

// 等价示例:
update().eq("id",value).remove();
lambdaUpdate().eq(Entity::getId,value).remove();

本文地址: https://github.com/maxzhao-it/blog/post/26362/

整体思路:使用debtap工具进行解包

首先查看电脑上是否安装过

1
sudo pacman -Q debtap

配置 arch

1
sudo vim /etc/pacman.conf
1
2
3
[archlinuxcn]
SigLevel = Optional TrustAll
Server = https://mirrors.ustc.edu.cn/archlinuxcn/$arch

安装yay工具

1
sudo pacman -S yay

安装解包打包工具debtap

1
yay -S debtap

如果这里无法安装,可以到 github debtap下载,然后编译安装

配置 debtap

解决同步数据慢的问题

1
sudo vim  /usr/bin/debtap

替换:http://ftp.debian.org/debian/dists
https://mirrors.ustc.edu.cn/debian/dists

替换:http://archive.ubuntu.com/ubuntu/dists
https://mirrors.ustc.edu.cn/ubuntu/dists/

升级debtap

1
sudo debtap -u

解包

1
2
3
# 至少执行一次
sudo debtap -u xxxx.deb
sudo debtap -q xxxx.deb

-q会略过除了编辑元数据之外的所有问题。

比如

安装

1
sudo pacman -U x.tar.xz

本文地址: https://github.com/maxzhao-it/blog/post/51898/