教程

ClickHouse 数据库简介

1、简介 通过使用联机分析处理(Online Analytical Processing,OLAP),企业可以深入了解当前的运营情况,并确定改进趋势。这通常是通过对汇总的业务数据进行复杂的分析来实现的。 ClickHouse 是一个开源的、列式 OLAP 数据库,因其出色的 性能 而大受欢迎。 本文将带你了解如何在 Spring Boot 中整合、使用 ClickHouse。 2、项目设置 在开始与 ClickHouse 数据库交互之前,我们需要添加一些 SDK 依赖,并正确配置我们的应用。 2.1、依赖 首先,在项目的 pom.xml 中添加必要的依赖: <dependency> <groupId>com.clickhouse</groupId> <artifactId>clickhouse-jdbc</artifactId> <version>0.7.1</version> </dependency> <dependency> <groupId>org.lz4</groupId> <artifactId>lz4-java</artifactId> <version>1.8.0</version> </dependency> clickhouse-jdbc 依赖提供了 JDBC API 的实现,使我们能够与 ClickHouse 数据库建立连接并进行交互。 默认情况下,ClickHouse 使用 LZ4 压缩来存储数据,为此我们添加了 lz4-java 依赖项。 2.2、使用 Flyway 定义数据表 接下来,定义数据库表,并对其执行操作。 使用 Flyway 来管理数据库迁移。这需要添加 flyway-core 和 flyway-database-clickhouse 依赖: <dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> </dependency> <dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-database-clickhouse</artifactId> <version>10.16.3</version> </dependency> 将这些依赖添加到 pom.xml 之后,在 src/main/resources/db/migration 目录中创建一个名为 V001__create_table.

Spring Filter 中的 chain.doFilter() 方法

1、简介 本文将带你了解 Spring 中 chain.doFilter() 方法的作用。 2、什么是 Spring Filter 在 Spring 应用中,Filter 过滤器以 Java Servlet Filter 为基础,后者代表拦截请求和响应的对象。Filter 是 Java Servlet API 的一部分,在 Web 应用中扮演着重要角色,因为它们位于客户端和服务器处理逻辑之间。 利用它们,我们可以在请求到达 servlet 之前或生成响应之后执行任务。过滤器的常见用例包括 认证和授权 审计和日志 修改请求/响应 过滤器虽然不是 Spring 框架的一部分,但与 Spring 框架完全兼容。我们可以将它们注册为 Spring Bean,并在应用中使用它们。Spring 提供了一些过滤器的实现,其中常见的包括 OncePerRequestFilter 和 CorsFilter。 3、理解 chain.doFilter() 方法 在了解 chain.doFilter() 方法之前,首先要了解过滤器链(Filter Chain)的概念及其在过滤过程中的作用。 过滤器链表示应用于传入请求或传出响应的处理逻辑顺序流。换句话说,它是用于预处理请求或后处理响应的过滤器集合。这些过滤器以明确定义的顺序排列,确保每个过滤器都能在将请求或响应传递到链中的下一阶段之前执行其处理逻辑。 要定义过滤器链,可以使用了 Java Servlet API 中的 FilterChain 接口,其中包含了我们感兴趣的方法。查看该方法的签名,就会发现请求(request)和响应(response)对象被定义为输入参数: void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException; chain.doFilter() 方法将请求和响应传递给链中的下一个过滤器。如果过滤器链中没有过滤器,则会将请求转发给目标资源(通常是 Servlet),并将响应发送给客户端。 3.1、为什么调用 chain.doFilter() 非常重要? 我们可以看到,chain.

在 Spring Data JPA 中创建动态查询

1、概览 在使用 Spring Data 开发应用时,我们经常需要根据选择条件构建动态查询,以便从数据库中获取数据。 本文将带你了解在 Spring Data JPA Repository 中创建动态查询的三种方法:Example 查询、Specification 查询和 Querydsl 查询。 2、示例 创建 School 和 Student 两个实体。这两个实体类之间的关系是一对多,即一个 School 可以有多个 Student: @Entity @Table public class School { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column private Long id; @Column private String name; @Column private String borough; @OneToMany(mappedBy = "school") private List<Student> studentList; // 构造函数、Getter、Setter 省略 } @Entity @Table public class Student { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column private Long id; @Column private String name; @Column private Integer age; @ManyToOne private School school; // 构造函数、Getter、Setter 省略 } 除了实体类,还要为 Student 实体定义一个 Spring Data Repository:

正式推出 Spring AI MCP:用于 MCP(模型上下文协议)的 Java SDK

我们很高兴推出 Spring AI MCP,它是模型上下文协议(Model Context Protocol,MCP)的强大 Java SDK 实现。Spring AI 生态系统的这一新成员为 Java 平台带来了标准化的 AI 模型集成能力。 MCP 是什么? 模型上下文协议(MCP)是一种开放式协议,它规范了应用程序为大型语言模型(LLM)提供上下文的方式。MCP 提供了一种将人工智能模型连接到不同数据源和工具的标准化方法,使集成无缝且一致。它可以帮助你在 LLM 的基础上构建代理和复杂的工作流程。LLM 经常需要与数据和工具集成,而 MCP 提供了以下功能: 一个不断扩充的预构建集成列表,你的大语言模型(LLM)可以直接接入使用。 在不同的大语言模型(LLM)提供商和供应商之间灵活切换的能力。 总体结构 MCP 的核心是客户端-服务器(CS)架构,一个应用可以连接多个服务器。 Spring AI MCP 采用模块化架构,包含以下组件: Spring AI 应用: 使用 Spring AI 框架构建希望通过 MCP 访问数据的生成式 AI 应用。 Spring MCP 客户端:与服务器保持 1:1 连接的 MCP 协议的 Spring AI 实现。 MCP 服务器:轻量级程序,每个程序都通过标准化的模型上下文协议公开特定功能。 本地数据源:MCP 服务器可安全访问的计算机文件、数据库和服务。 远程服务:MCP 服务器可通过互联网(如 API)连接的外部系统。 该架构支持广泛的用例,从简单的文件系统访问到复杂的多模型人工智能与数据库和互联网连接的交互。 入门 Spring AI MCP GitHub:https://github.com/spring-projects-experimental/spring-ai-mcp Maven 依赖 在 Maven 项目中添加以下依赖之一

把 Future 转换为 Completablefuture

1、简介 本文将带你了解如何将 Future 转换为 CompletableFuture。通过这种转换,我们可以利用 CompletableFuture 的高级功能,如非阻塞操作、链式任务和更好的错误处理,同时仍可使用返回 Future 的 API 或库。 2、为什么要把 Future 转换为 CompletableFuture? Java 中的 Future 接口表示异步计算的结果。它提供了检查计算是否完成、等待计算完成和检索结果的方法。 不过,Future 也有其局限性,比如阻塞调用需要使用 get() 来获取结果。此外,它还不支持链式调用多个异步任务或处理回调。 而,Java 8 中引入的 CompletableFuture 解决了这些缺陷。它通过用于任务链和回调的 thenApply() 和 thenAccept() 等方法支持非阻塞操作,并使用 exceptionally() 进行错误处理。 通过将 Future 转换为 CompletableFuture,我们可以在使用返回 Future 的 API 或库时利用这些功能。 3、逐步转换 来看看如何将 Future 转换为 CompletableFuture。 3.1、使用 ExecutorService 模拟 Future 要了解 Future 如何工作,我们首先要使用 ExecutorService 模拟异步计算。ExecutorService 是一个用于管理和调度独立线程中任务的框架。这将有助于我们理解 Future 的阻塞特性: @Test void givenFuture_whenGet_thenBlockingCall() throws ExecutionException, InterruptedException { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<String> future = executor.

JVM 中的高级选项

1、概览 Java 虚拟机(JVM)是驱动 Java 应用程序的强大引擎。它具有高度可定制性,通过标准选项提供基本配置,通过非标准选项进行一般性性能调优,以及通过高级选项实现精确控制。 高级选项允许开发人员对性能进行微调,诊断问题,并尝试最前沿的功能。 本文将带你了解最著名的高级 JVM 选项以及如何使用它们,从而对 JVM 行为进行更精细的控制。 2、JVM 选项的分类 JVM 参数可分为三大类: 标准选项(-version、-help) 非标准选项(-X: options) 高级选项(-XX: options) 3、理解高级 JVM 选项 高级选项不仅限于基本配置,还可以设置 JVM 的更低级属性。这些选项允许我们调整性能关键参数,如垃圾回收、内存管理和运行时诊断。 其中一些高级选项也是常用的最重要的 JVM 参数。不过,由于它们可以针对特定应用场景进行微调,我们必须谨慎使用。在不清楚应用程序行为的情况下过度定制,可能会导致性能低下、崩溃或意外行为。 此外,高级 JVM 选项并不能保证为所有 JVM 实现所支持,而且可能会发生变化。因此,由于这些选项会随着 JVM 的更新而变化,有些选项可能会过时,或者在较新版本中表现不同。 例如,Java 并发标记和清理垃圾收集(CMS)算法就曾出现过这种情况,该算法在 Java 9 中被弃用,并在 Java 14 中被删除。通过关注官方文档,我们可以在发生任何变化之前及时了解情况。 4、垃圾收集调整 垃圾回收对内存管理至关重要,但也会带来影响性能的停顿。高级选项可控制垃圾回收行为,确保应用程序运行更流畅。 从 Java 9 开始,默认情况下使用垃圾优先的垃圾收集器(G1),旨在平衡吞吐量和延迟。 为了克服 G1 的延迟限制,JDK12 引入了 Shenandoah GC,可以使用 -XX:+UseShenandoahGC 选项启用它。Shenandoah 的可用性取决于 JDK 供应商和版本。 还可以根据专门的工作负载使用其他实现方式。Epsilon 垃圾收集器也非常适合用于性能调优,以检查垃圾收集是否会影响我们程序的性能。 5、内存管理 如上所述,垃圾回收是内存管理的重要组成部分,但它只是 JVM 中更大的内存管理生态系统的一部分。 要实现最佳性能,同样重要的是配置内存分配、管理堆大小以及了解堆外内存的工作原理,特别是对于内存密集型应用或有特定性能要求的系统。 回顾一下与内存管理相关的一些高级 JVM 选项:

使用 Java 在 PostgreSQL 中存储日期和时间

1、简介 在数据库中存储日期(Date)和时间(Time)信息是软件开发中的一项常见任务。由于有许多不同的格式、时区和存储格式,处理日期和时间可能是一项复杂的任务,如果处理不慎,可能会导致许多问题。 本文将带你了解 Java Date / Time API 提供的日期和时间类,以及 PostgreSQL 如何持久化这些类。 2、设置 本文使用 Spring Boot 和 Spring Data JPA 在 PostgreSQL 数据库中持久化日期和时间值。 首先,创建一个实体,其中包含 Java Date / Time API 中不同日期和时间类的字段: @Entity public class DateTimeValues { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private Date date; private LocalDate localDate; private LocalDateTime localDateTime; private Instant instant; private ZonedDateTime zonedDateTime; private LocalTime localTime; private OffsetDateTime offsetDateTime; private java.sql.Date sqlDate; // Getter / Setter 省略 } 此外,还要添加一个默认构造函数,以固定时间初始化所有日期/时间字段:

JsonMappingException:Can not deserialize instance of java.util.HashMap out of START_ARRAY token

1、概览 本文将带你了解如何解决 Jackson 异常:JsonMappingException: Can not deserialize instance of java.util.HashMap out of START_ARRAY token。 2、理解异常 简而言之,Jackson 在反序列化 JSON 字符串时抛出 JsonMappingException 来指示映射错误。而,“Can not deserialize instance of java.util.HashMap out of START_ARRAY token” 异常消息表示预期的数据结构与实际的 JSON 字符串不匹配。 这里出现不匹配是因为 Jackson 期望的是 List 而不是 HashMap。反序列化过程不知道如何将指定的 JSON 数组转换为 HashMap。因此出现异常。 3、示例 来看一个会导致 Jackson 出现 JsonMappingException 异常的例子。 为了简单起见,假设我们有一个 JSON 数组,其中每个元素都代表一个由名(firstName)和姓(lastName)定义的人: [ { "firstName":"Abderrahim", "lastName":"Azhrioun" }, { "firstName":"Nicole", "lastName":"Smith" } ] 现在,如果尝试将 JSON 数组直接反序列化为 Map,将导致 JsonMappingException: @Test public void givenJsonArray_whenDeserializingToMap_thenThrowException() { final String json = "[{\"firstName\":\"Abderrahim\",\"lastName\":\"Azhrioun\"}, {\"firstName\":\"Nicole\",\"lastName\":\"Smith\"}]"; final ObjectMapper mapper = new ObjectMapper(); Exception exception = assertThrows(JsonMappingException.

MongoDB 字段级加密

1、简介 本文将带你了解如何使用 MongoDB 的客户端字段级加密(或 CSFLE)来加密文档中的指定字段,主要介绍显式/自动加密和显式/自动解密,以及加密算法之间的差异。 2、场景与设置 MongoDB Atlas 和 MongoDB Enterprise 都支持自动加密。MongoDB Atlas 有一个 永久免费的集群,我们可以用它来测试所有功能。 此外,需要注意的是,字段级加密有别于静态存储,后者会对整个数据库或磁盘进行加密。通过有选择地加密特定字段,我们可以更好地保护敏感数据,同时实现高效查询和索引。 本文将从一个简单的 Spring Boot 应用开始,使用 Spring Data MongoDB 插入和检索数据。 首先,我们要创建一个包含未加密字段和加密字段混合的文档类。我们将从手动加密开始,然后看看如何通过 自动加密 实现同样的效果。对于手动加密,我们需要一个中间对象来表示加密的 POJO,并创建方法来加密/解密每个字段。 2.1. Spring Boot Starter 和加解密模块 首先,需要使用 spring-boot-starter-data-mongodb 来连接到 MongoDB: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> 然后,还需要 mongodb-crypt,以启用加密功能: <dependency> <groupId>org.mongodb</groupId> <artifactId>mongodb-crypt</artifactId> <version>1.7.3</version> </dependency> 由于我们使用的是 Spring Boot,所以只需上面两个依赖就足够了。 2.2、创建 Master Key Master Key(主密钥)用于加密和解密数据。任何拥有它的人都可以读取我们的数据。因此,确保主密钥的安全至关重要。 MongoDB 建议使用 远程密钥管理服务。不过,为了简单起见,本文使用本地密钥管理器,如下: public class LocalKmsUtils { public static byte[] createMasterKey(String path) { byte[] masterKey = new byte[96]; new SecureRandom().

Spring AI Advisor 指南

1、概览 AI 驱动的应用已成为我们的新现实。我们正在广泛实现各种 RAG 应用和提示 API,并使用 LLM 创建令人印象深刻的项目。借助 Spring AI,我们可以更快、更稳定地完成这些任务。 本文将带你了解 Spring AI Advisor 这一功能,它可以为我们处理各种常规任务。 2、Spring AI Advisor 是什么? Advisors 是在 AI 应用程序中处理请求和响应的拦截器。我们可以使用它们为提示流程设置额外的功能。例如,可以建立聊天历史、排除敏感词或为每个请求添加额外的上下文。 该功能的核心组件是 CallAroundAdvisor 接口。我们通过实现该接口来创建 Advisor 链,从而影响我们的请求或响应。Advisor 流程如下图所示: 我们会将提示(prompt)发送到一个聊天模型,该模型关联了一个 Advisor 链。在发送提示之前,链上的每个 Advisor 都会执行其 before 操作。同样,在收到聊天模型的回复之前,每个 Advisor 都会调用自己的 after 操作。 3、ChatMemoryAdvisor ChatMemoryAdvisor 是一组非常有用的 Advisor 实现。我们可以使用这些 Advisor 提供与聊天提示的交流历史,从而提高聊天回复的准确性。 3.1、MessageChatMemoryAdvisor 使用 MessageChatMemoryAdvisor,我们可以通过 messages 属性提供聊天客户端调用的聊天历史记录。我们可以将所有消息保存在 ChatMemory 实现中,并控制历史记录的大小。 示例如下: @SpringBootTest(classes = ChatModel.class) @EnableAutoConfiguration @ExtendWith(SpringExtension.class) public class SpringAILiveTest { @Autowired @Qualifier("openAiChatModel") ChatModel chatModel; ChatClient chatClient; @BeforeEach void setup() { chatClient = ChatClient.