0%

日常工作中,有一些功能如状态更新等需要遍历表中数据,如果数据量比较少的情况下,我们可以正常的使用数据库如mysql提供的limit和offset来实现分页的功能,但是如果数据量比较大,这时候就会有深分页的问题,产生慢SQL, 为了解决这个问题,一种实现方式就是通过主键ID+查询条件来过滤数据,使用类似如下语句

1
SELECT * FROM task WHERE id > $minId AND status = 1 ORDER BY id LIMIT 200

如果有其他条件导致需要扫描很大的行数才能扫描到的话,可能还需要限制id上限

1
SELECT * FROM task WHERE id > $minId AND id < $maxId AND status = 1 ORDER BY id LIMIT 200

之后每次使用查询的最大值更新变量minId,直到查询不出数据为止,具体对应到Java代码中大致如下:

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
public class TaskStatusUpdater {
// 执行入口
public void execute() {
int limit = 200;
int minId = 0;

while (true) {
// 查询
List<Task> tasks = taskDao.listByMinId(minId, limit);
if (CollectionUtils.isEmpty(tasks)) {
break;
}
// 更新查询参数, 获取查询到的最大的ID
minId = tasks.stream()
.max(Comparator.comparing(Task::getId))
.map(Task::getId)
.get();
for (Task task : tasks) {
// 处理
operateTask(task);
}
}
}

private void operateTask(Task task) {
// 进行业务逻辑处理
}
}

这里可以看到,执行方法中大部分都是一些业务无关的控制代码,如果有不同的处理逻辑需要遍历,那么都要复制一下这一大坨的控制代码,是否有更好的写法呢?

阅读全文 »

之前介绍过 JSONPath 的使用,但是它只能支持一些简单场景下快速从json中获取指定的值,这次我们来看一款更强大的工具 JSONata,它是一种针对 JSON 数据的轻量级查询和转换语言,可以实现一些比较复杂的逻辑,现在我们就来看一下如何进行使用

阅读全文 »

类GPT对于程序员来说比较大的用途之一就是可以用来生成代码,这次我们使用chatGpt来实现一个word的模版功能,也就是先使用一个word模版,然后使用对应的语法来将需要替换的地方使用占位符进行占位,在渲染的时候根据入参的数据进行替换,生成最终需要的word文件

预期需要支持一下几种模版语法功能:

  1. 简单字符串的替换
  2. 数字类型的小数截取功能,要支持位数不足时是否需要补0
  3. 需要支持日期的格式化
  4. 需要支持图片占位符的替换
  5. 需要支持使用对象集合,渲染word中表格多行的数据部分(暂未实现)

运行效果

先来看一下最终呈现的效果

word模版内容

模版使用数据

1
2
3
4
5
6
7
8
Map<String, Object> placeholderMap = new HashMap<>();
placeholderMap.put("name", "张三");
placeholderMap.put("profession", "里斯");
placeholderMap.put("ssname", "John Doe");
placeholderMap.put("price", String.valueOf(123.45));
placeholderMap.put("date", new Date());
placeholderMap.put("image1", "https://zhengw-tech.com/images/netty-server.png");
placeholderMap.put("image2", "https://zhengw-tech.com/images/jvm-class.png");

最终word内容

阅读全文 »

在介绍Java agent之前,我们先来介绍一下一个比较关键的概念 - 字节码,这个如果大家已经比较熟悉了,可以直接跳到 java agent部分

字节码

我们知道Java编写的程序是可以不做任何修改的在不同的操作系统上面运行,也就是跨平台的,但是要想实现跨平台,就是需要能屏蔽掉不同操作系统之间api等的差异

比如说常见的创建线程,linux和window系统提供的接口就不一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 不同操作系统下,使用c语言创建线程的API
// linux
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void*), void *arg);

// windows
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);

除了创建线程,其实还有很多的差异,比如说如果用C语言来实现既能在windows下运行,又能在linux下指定的代码,那么就需要针对有差异的地方,根据不同的操作系统来编写不同的代码,然后在编译的时候根据需要编译成对应系统下的二进制指令,这无疑是很痛苦和低效的方式

而Java下编译并不会生成目标平台的二进制文件,而是生成一个与平台无关的字节码文件,由不同平台下Java虚拟机负责加载执行,操作系统的差异就需要由虚拟机来进行屏蔽,对开发人员是无感知的

我们只需要将源代码编译成字节码(而不是操作系统下的二进制格式),剩下的就可以交给虚拟机来识别执行了

其实这样还有一个额外的好处,那就是在Java虚拟机上,不仅仅只能支持Java语言,理论上只要是符合规范的字节码文件,它都能执行,至于这个字节码文件是Java语言编译过来的,还是其他语言(如kotlin, groovy)编译过来的并不重要,甚至我们都可以手写字节码来执行~

阅读全文 »

重试在项目中还是比较常见的一个场景,比如调用外部服务因为网络等原因的异常,重试一次可能就成功了,而不需要立即给用户反馈错误,提高体验

假设我们自己来写一个最简单异常重试的话,可能代码是这样子的

1
2
3
4
5
6
7
8
9
int retryTime = 3;
for (int i = 0; i < retryTime; i++) {
try {
// 方法调用
return service.query();
} catch (Throwable e) {
log.info("调用异常,进行重试");
}
}

这里可能有几个问题需要考虑

  1. 不仅仅是异常需要重试,有时接口返回的特定错误码也是需要重试的
  2. 不是所有的异常都可以重试,需要根据情况判断异常原因才能重试
  3. 有时失败不能立即重试,需要等待一小段时间,比如短时网络波动
  4. 重试停止不一定只需要次数,有时也需要判断整体用的时间等因素

这么一看,需要考虑的地方还挺多,而重试功能又是一个非常通用的功能,所以完全可以包装一下做成通用的能力

而这个目前已经有一些现成的工具供我们使用,这次我们就先看下 guava-retrying

阅读全文 »

Groovy本身是一门完整的编程语言,同时也可以作为脚本集成到我们的应用中,为静态语言提供一些动态的能力,比如将 groovy 的脚本放到一些动态配置中或者数据库中进行管理,实现一些动态的规则计算等,在规则新增或变更的时候可以实时更新而无需开发代码上线,这次主要介绍一下Java中如何使用Groovy脚本

关于在应用中集成Groovy,官方文档已经提供了相关的资料,本次基于此文档进行一下说明

首先需要引入相应的包,这里基于的版本是3.0.16

1
2
3
4
5
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>3.0.16</version>
</dependency>
阅读全文 »

java语言下,进行json处理的工具类jackson, fastjson, gson等,使用起来比较简单,就不介绍了,这次我们就来探索一下其中gson的具体实现,其核心处理类为JsonReader和JsonWriter进行json格式数据的读取和写入,上层TypeAdapter使用其进行json和具体数据类型的转换,而Gson调用时会根据类型获取到具体的TypeAdapter进行使用

阅读全文 »

MyBatis允许我们在其执行过程中对特定的一些方法进行拦截代理,实现一些特定的通用功能,如分页插件或者租户插件,允许进行拦截的类及对应方法基本如下

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)
阅读全文 »

我们平时在使用关系数据库存储数据时,在某种情况下可能会有使用字符串类的字段来存储json格式的数据,然后在代码里面进行解析处理等,这样比较方便扩展,但是在使用数据进行分析等场景下就会很不方便,这时候就需要对json进行一些解析的操作,这次主要看下如何使用Hive来解析json格式的数据

阅读全文 »

有时候我们需要对于同一个类的两个实例对象进行一下数据内容的比对,找出他们的差异,比如用来做单测或者同一份数据多版本的内容比对,这里主要介绍使用apache的commons-lang3的工具类来进行功能实现

阅读全文 »