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 省略 } 此外,还要添加一个默认构造函数,以固定时间初始化所有日期/时间字段:
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.
HTTP/3 是 Hypertext Transfer Protocol(超文本传输协议,即 HTTP)的最新主要版本,其规范已于 2022 年 6 月定稿。该版本旨在提高性能、可靠性和安全性。与之前的版本不同,HTTP/3 使用 QUIC 而不是 TCP 作为传输层。QUIC 是一种基于 UDP 的多路复用安全传输协议,内置 TLS 1.3 加密,默认情况下 QUIC 是加密的。
要进一步了解 HTTP/3 的性能和优势,可以查看 《什么是 HTTP/3》。
有关浏览器采用情况的信息,请参阅《检查 HTTP/3 使用情况》,其中还提供了不同浏览器使用的 HTTP 版本的原始数据。
Reactor Netty 1.2(Reactor 2024.0 发布系列的一部分)添加了对 HTTP/3 的实验性支持。通过这个新版本的 Reactor Netty,你可以配置你的 Spring Boot 应用和 Spring Cloud Gateway 来支持 HTTP/3。
下面让我们看看如何配置 HTTP/3 支持。
配置 Reactor BOM Version Spring Boot 3.4 默认集成了 Reactor 2024.0 发布系列! 如果你运行的是旧版本的 Spring Boot,你可以将 Reactor BOM 升级到 2024.
从 Spring Boot 3.3 升级 RestClient 和 RestTemplate 已添加对自动配置 RestClient 和 RestTemplate 以使用 Reactor Netty 的 HttpClient 或 JDK 的 HttpClient 的支持。按优先顺序,现在支持的客户端如下:
Apache HTTP Components(HttpComponentsClientHttpRequestFactory) Jetty Client (JettyClientHttpRequestFactory) Reactor Netty HttpClient (ReactorClientHttpRequestFactory) JDK HttpClient (JdkClientHttpRequestFactory) Simple JDK HttpURLConnection (SimpleClientHttpRequestFactory) 需要注意的是,如果类路径上没有 HTTP Client 库,就可能会使用 JdkClientHttpRequestFactory,而之前使用的是 SimpleClientHttpRequestFactory。可以通过设置 spring.http.client.factory 来选择特定的客户端。支持的值包括 http-components、jetty、reactor、jdk 和 simple。
默认情况下,所有五个客户端都会自动跟随重定向。要禁用此行为,可将 spring.http.client.redirects 设置为 dont-follow 。
Apache HTTP Components 和 Envoy Apache HTTP Components(组件)更改了 HttpClient 中有关 HTTP/1.1 TLS 升级的默认设置。大多数代理服务器都能顺利处理升级,但 Envoy 或 Istio 可能会遇到问题。
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().
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.
1、简介 Spring Webflux 是一个非阻塞的 Web 框架,从底层开始构建,旨在利用多核、下一代处理器的优势,处理大量并发连接(既然是非阻塞框架,线程就不应该被阻塞)。
本文将带你了解在使用 Spring Webflux 时常犯的一个错误。
2、Spring Webflux 线程模型 为了更好地理解这个问题,我们需要了解 Spring Webflux 的线程模型。
在 Spring Webflux 中,一个小型工作线程池负责处理传入请求。这与 Servlet 模型不同,在 Servlet 模型中,每个请求都有一个专用线程。因此,框架会保护(隔离)这些接受(处理)请求的线程。
理解了这一点后,继续往下看。
3、通过线程阻塞了解 IllegalStateException 让我们通过一个示例来了解 Spring Webflux 中何时以及为何会出现异常:“java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread”。
以文件搜索 API 为例。该应用从文件系统读取文件,并在文件中搜索用户提供的文本。
3.1、FileService 先定义一个 FileService 类,它能以字符串形式读取文件内容:
@Service public class FileService { @Value("${files.base.dir:/tmp/bael-7724}") private String filesBaseDir; public Mono<String> getFileContentAsString(String fileName) { return DataBufferUtils.read(Paths.get(filesBaseDir + "/" + fileName), DefaultDataBufferFactory.sharedInstance, DefaultDataBufferFactory.DEFAULT_INITIAL_CAPACITY) .
1、概览 本文将带你了解 Java 中的 IncompatibleClassChangeError 异常,这是一种运行时异常,当 JVM 检测到类的更改与之前加载的类不兼容时就会发生。
本文将带你了解出现这个异常的原因以及解决办法。
2、IncompatibleClassChangeError 类 IncompatibleClassChangeError 是 Java 中的一种 LinkageError。该异常通常表示一个或多个依赖类出现了问题。
IncompatibleClassChangeError(不兼容类变更错误)是 LinkageError 的子类,当一个或多个从属类的类定义发生不兼容变更时会导致该异常。
注意,这是 Error 的子类,因此不应该试图 catch 这些异常,因为这意味着应用程序或运行时出现异常需要进行处理。
3、异常的产生 接下来,让我们模拟一种会导致 IncompatibleClassChangeError 的情况。
3.1、预定义一个第三方库 首先创建一个简单的库(项目),其中有一个父类 Dinosaur 和一个子类 Coelophysis,后者继承自 Dinosaur:
public class Dinosaur { public void species(String sp) { if(sp == null) { System.out.println("I am a generic Dinosaur"); } else { System.out.println(sp); } } } public class Coelophysis extends Dinosaur { public void mySpecies() { species("My species is Coelophysis of the Triassic Period"); } public static void main(String[] args) { Coelophysis coelophysis = new Coelophysis(); coelophysis.
在开发时候,发现有很多需要用到时间的地方,例如记录操作的时间、比较时间判断产品是否有效等。总而言之,时间是我们业务开发必须关注、时刻注意的点。但目前工程的代码中使用了非常多时间的工具类,一会儿用 java.util.Date 记录时间,一会用 java.time.LocalDateTime 记录时间,怎么才能在 Java 中优雅的操纵时间呢,我整理了相关的概念和工具类,希望帮助大家在代码开发的过程中对对时间的使用更加优雅。
这里先写一个结论:
建议使用 java8 的时间 API,在安全性和易用性上都远高于 java.util.Date。 目前比较流行的封装 java API 的时间工具类大都基于 java.util.Date,建议在开发过程中根据业务需要基于 java.time.* 的方法封装工具类(文末给出了一个简单的实现)。 时间在计算机中的存储和展示 时间以整数的方式进行存储:时间在计算机中存储的本质是一个整数,称为 Epoch Time(时间戳),计算从 1970 年 1 月 1 日零点(格林威治时间/GMT+00:00)到现在所经历的秒数。
在 java 程序中,时间戳通常使用 long 表示毫秒数,通过 System.currentTimeMillis() 可以获取时间戳。时间戳对我们人来说是不易理解的,因此需要将其转换为易读的时间,例如,2024-10-7 20:21:59(实际上说的是本地时间),而同一时刻不同时区的人看到的本地时间是不一样,所以在时间展示的时候需要加上时区的信息,才能精准的找到对应的时刻。
时区与世界时间标准相关:
世界时间的标准在 1972 年发生了变化,但我们在开发程序的时候可以忽略 GMT 和 UTC 的差异, 因为计算机的时钟在联网的时候会自动与时间服务器同步时间。 本地时间等于我们所在(或者所使用)时区内的当地时间,它由与世界标准时间(UTC)之间的偏移量来定义。这个偏移量可以表示为 UTC- 或 UTC+,后面接上偏移的小时和分钟数。 例如:GMT+08:00 或者 UTC+08:00 表示东八区,2024-10-7 20:21:59 UTC+08:00 便可以精准的定位一个时刻。
日期 API JDK 以版本 8 为界,有两套处理日期/时间的 API。
简单的比较如下:
特性 java.util.Date java.util.Date.Calendar java.time.LocalDateTime 线程安全 ❌ ❌ ✅ 时间运算 ❌ ✅ ✅ 可读性 Tue Oct 08 00:11:16 CST 2024 易读性较低 ❌不易读 ✅ yyyy-MM-dd’T’HH:mm:ss 常量设计 需要对获取的年份(+1900)月份(0-11)进行处理 需要对获月份(0-11)进行处理 ✅ 不需要额外处理,符合常识 时间精度 精确到毫秒 精确到毫秒 精确到纳秒 时区 具体的时间调用 不 - 特性 java.
1、简介 本文将带你了解 SessionFactory 和 EntityManagerFactory 之间的异同。
顾名思义,这两个类都是用于创建数据库通信对象的工厂类。除了创建对象,它们还提供了其他功能,帮助我们与数据库进行交互。
2、EntityManagerFactory 是什么? Java 持久化 API(JPA)是 Java 应用管理持久化数据的规范。它提供了一种与关系数据库交互的标准方式。EntityManager(实体管理器)作为 JPA 的核心接口,用于与持久化上下文交互并管理实体的生命周期。它为基本的 CRUD 操作提供了具有方法的轻量级实例。
EntityManagerFactory 是一个 JPA 接口,用于创建 EntityManager 实例,以线程安全的方式与持久化上下文(Persistence Context)交互。
2.1、示例 首先,先定义实体:
@Entity(name = "persons") public class Person { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) private Integer id; private String name; private String email; // Getter / Setter 省略 } 设置配置有多种方法,这里使用 persistence.xml 配置文件的方法。首先,需要在 resource/META-INF 文件夹中创建该配置文件,并定义连接配置:
<persistence-unit name="com.baeldung.sfvsemf.persistence_unit" transaction-type="RESOURCE_LOCAL"> <description>Persistence Unit for SessionFactory vs EntityManagerFactory code example</description> <class>com.baeldung.sfvsemf.entity.Person</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> <properties> <property name="hibernate.