Spring Data JPA 实现 updateOrInsert(更新或保存)

1、简介 在应用开发中,执行 “更新或插入” 操作(也称为 “upsert”)的需要是很常见的。这个操作涉及将新记录存入数据库表中,如果记录不存在,则插入新记录;如果记录已经存在,则更新现有记录。 本文将带你了解使用 Spring Data JPA 执行 “更新或插入” 操作的不同方法。 2、实体类 定义 CreditCard 实体类用于演示: @Entity @Table(name="credit_card") public class CreditCard { @Id @GeneratedValue(strategy= GenerationType.SEQUENCE, generator = "credit_card_id_seq") @SequenceGenerator(name = "credit_card_id_seq", sequenceName = "credit_card_id_seq", allocationSize = 1) private Long id; private String cardNumber; private String expiryDate; private Long customerId; // Get / Set 方法省略 } 3、实现 本文介绍三种不同的方法来实现 “更新或插入”。 3.1、使用 Repository 方法 使用从 CrudRepository 接口继承的 save(entity) 方法在 Repository 中编写一个带事务的 default 方法。

Spring Boot 在测试时禁用 @Cacheable 缓存

1、简介 缓存是一种有效的策略,当执行结果在一段已知时间内没有变化时,可以避免重复执行逻辑,从而提高性能。 Spring Boot 提供了 @Cacheable 注解,可以在方法上定义该注解,它就会缓存方法的结果。在某些情况下,例如在测试环境中进行测试时,我们可能需要禁用缓存来观察某些修改后的行为。 本文将带你了解如何配置 Spring Boot 中的缓存,以及如何在需要时禁用缓存。 2、缓存配置 设置一个简单的用例,通过 ISBN(国际标准书号)查询图书评论,并在某个逻辑中使用 @Cacheable 对该方法返回的结果进行缓存。 实体类 BookReview 如下,它包含 bookRating、isbn 等信息: @Entity @Table(name="BOOK_REVIEWS") public class BookReview { @Id @GeneratedValue(strategy= GenerationType.SEQUENCE, generator = "book_reviews_reviews_id_seq") @SequenceGenerator(name = "book_reviews_reviews_id_seq", sequenceName = "book_reviews_reviews_id_seq", allocationSize = 1) private Long reviewsId; private String userId; private String isbn; private String bookRating; // Get / Set 方法省略 } 在 BookRepository 中添加一个简单的 findByIsbn() 方法,用于按 isbn 查询书评: public interface BookRepository extends JpaRepository<BookReview, Long> { List<BookReview> findByIsbn(String isbn); } BookReviewsLogic 类包含一个在 BookRepository 中调用 findByIsbn() 的方法。我们添加了 @Cacheable 注解,将指定 isbn 的结果缓存在 book_reviews 缓存中:

解决 PostgreSQL 的 PSQLException: “FATAL: sorry, too many clients already” 异常

1、概览 当 PostgreSQL 服务器无法接受客户端应用的连接请求时,就会抛出 PostgreSQL PSQLException:FATAL: sorry, too many clients already 这个异常。 本文将带你了解如何解决以及防止这个异常。 2、理解问题 DB 服务器启动时的连接数有限。有时,连接会用完。因此,数据库服务器无法提供新的连接。这时,它就会抛出异常:FATAL: sorry, too many clients already。 首先,来了解一下这个问题是如何、何时以及为何出现的。假设有四个 Client 应用连接到 PostgreSQL 数据库: 大多数情况下,应用会在启动时创建数据库连接池。假设数据库管理员为 PostgreSQL 数据库服务器配置了最多 90 个连接。需要注意的是,数据库会保留一些连接供内部使用。实际可供客户端使用的连接数甚至更少。运维为每个客户端应用配置的连接池大小为 30。 现在,假设运维从 Client-1 开始依次启动 Client 应用。当 Client-4 试图启动时,由于 Client-1、Client-2 和 Client-3 已经创建并阻塞了 90 个连接。因此,当 Client-4 向数据库服务器请求出创建 30 个连接时,服务器拒绝了请求,并显示错误 “FATAL: sorry, too many clients already”。 当开发人员尝试使用 psql、psgAdmin、DBeaver 等数据库管理工具连接 PostgreSQL 服务器时,也会出现该错误。因为这些工具也会尝试获取与数据库的连接,如果连接不足,它们也可能遇到同样的错误。 3、排除故障 首先,通过在 PostgreSQL 数据库中运行查询来确定最大连接数: show max_connections 默认情况下,该值为 100,可以通过修改数据库配置文件 postgresql.conf 中的 max_connections 属性来 设置 它:

新版 Spring Security 中的路径匹配机制

Spring Security 是一个功能强大且可高度定制的安全框架,它提供了一套完整的解决方案,用于保护基于 Spring 的应用。在 Spring Security 中,路径匹配是权限控制的核心部分,它决定了哪些请求可以访问特定的资源。本文将带你详细了解 Spring Security 中的路径匹配策略,并提供相应的代码示例。 在旧版的 Spring Security 中,路径匹配方法有很多,但是新版 Spring Security 对这些方法进行了统一的封装,都是调用 requestMatchers 方法进行处理: public C requestMatchers(RequestMatcher... requestMatchers) { Assert.state(!this.anyRequestConfigured, "Can't configure requestMatchers after anyRequest"); return chainRequestMatchers(Arrays.asList(requestMatchers)); } requestMatchers 方法接收一个 RequestMatcher 类型的参数,RequestMatcher 是一个接口,这个接口是一个用来确定 HTTP 请求是否与给定的模式匹配的工具。这个接口提供了一种灵活的方式来定义请求的匹配规则,从而可以对不同的请求执行不同的安全策略。 所以在新版 Spring Security 中,不同的路径匹配分方案实际上就是不同的 RequestMatcher 的实现类。 1. AntPathRequestMatcher AntPathRequestMatcher 是 Spring 中最常用的请求匹配器之一,它使用 Ant 风格的路径模式来匹配请求的 URI。 1.1 什么是 Ant 风格的路径模式 Ant 风格的路径模式(Ant Path Matching)是一种用于资源定位的模式匹配规则,它源自 Apache Ant 这个 Java 构建工具。在 Ant 中,这种模式被用来指定文件系统中的文件和目录。由于其简单性和灵活性,Ant 风格的路径模式也被其他许多框架和应用程序所采用,包括 Spring Security。

把 Google Protobuf Timestamp 转换为 LocalDate

1、概览 Protocol Buffer (Protobuf) 是一种用于序列化结构化数据的二进制格式。它是由 Google 开发的,并且被广泛应用于跨平台和跨语言的数据通信。Protocol Buffer 使用简洁、高效的编码方案,可以将结构化数据定义为消息类型,并生成针对不同编程语言的数据访问代码。这使得在不同的系统之间传输和解析数据变得更加简单和高效。Protocol Buffer 具有广泛的支持,包括 Java、C++、Python、Golang 等编程语言。 Protobuf Timestamp 类型代表一个时间点,与任何特定时区无关。本文将带你了解如何把 Protobuf Timestamp 实例转换为 Java 的本地时间类型,如 LocalDate。 2、Maven 依赖 在 pom.xml 中添加 protobuf-java 依赖: <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>4.26.1</version> </dependency> 该依赖提供了 Timestamp 和其他与 Protobuf 相关的类。 3、Timestamp 类 Protobuf Timestamp 类表示自 Unix 纪元以来的时间点。与时区或本地日历没有关系。 它表示的是某个时间点的秒数和纳秒数。下面是使用 Java Instant 对象计算当前时间戳的示例: Instant currentTimestamp = Instant.now(); Timestamp timestamp = Timestamp.newBuilder() .setSeconds(currentTimestamp.getEpochSecond()) .setNanos(currentTimestamp.getNano()) .build(); 如上,通过 Instant 对象计算时间戳。首先,创建一个 Instant 对象,表示给定点的日期和时间。然后,提取秒和纳秒,并将它们传递给 Timestamp 实例。 4、把 Timestamp 实例转换为 LocalDate 在将 Timestamp 转换为 LocalDate 时,必须考虑时区及其与 UTC 的相关偏移,以准确表示本地日期。要将 Timestamp 转换为 LocalDate,首先要创建一个具有特定秒和纳秒的 Timestamp 实例:

在 MongoRepository 中使用 Skip 和 Limit

1、概览 Spring Data MongoDB 的 MongoRepository 接口提供了一种简单的方式与 MongoDB Collection 进行交互。 本文将带你了解如何在 MongoRepository 中使用 limit 和 skip。 2、初始设置 首先,创建一个名为 StudentRepository 的 Repository,用于存储 Student 信息: public interface StudentRepository extends MongoRepository<Student, String> {} 然后,向该 Repository 添加一些 Student 示例数据: @Before public void setUp() { Student student1 = new Student("A", "Abraham", 15L); Student student2 = new Student("B", "Usman", 30L); Student student3 = new Student("C", "David", 20L); Student student4 = new Student("D", "Tina", 45L); Student student5 = new Student("E", "Maria", 33L); studentList = Arrays.

如何在 Spring Boot 中指定 logback.xml 文件的位置?

1、概览 日志记录是任何软件应用的重要组件,用于监控、调试和维护系统的健康状况。在 Spring Boot 生态系统中,Logback 作为默认的日志记录框架,提供了灵活和强大的功能。虽然 Spring Boot 简化了应用的许多方面,但有时仍然需要通过 logback.xml 配置文件来配置 Logback 以满足特定要求。 本文将会带你了解如何在 Java、Spring Boot 应用中指定 logback.xml 配置文件的位置。 2、logback.xml logback.xml 文件是 Logback 的配置文件,用于定义日志记录规则、appender 和日志格式(log format)。 默认情况下,Logback 会在 classpath 根目录中搜索该文件。这意味着将 logback.xml 文件放在 Spring Boot 项目的 src/main/resources 目录中就足够了,因为 Logback 会在运行时自动检测到它。不过,在某些情况下,自定义其位置也是必要的。 3、指定 logback.xml 位置 3.1、使用 System Properties 如果 logback.xml 配置文件不在打包后的 Jar 中,可以使用 System Properties 指定其位置。例如,在运行 Spring Boot 应用时,可以使用 JVM 参数: java -Dlogback.configurationFile=/path/to/logback.xml -jar application.jar 命令 -Dlogback.configurationFile=/path/to/logback.xml 将系统属性 logback.configurationFile 设置为指定路径,指定 Logback 使用提供的配置文件。 3.2、编程式配置 logback.

在 WebFlux 中拦截请求并添加自定义请求头

1、概览 Filter(拦截器/过滤器)是 Spring 提供的一个机制,可以在 Controller 处理请求或向客户端返回响应之前拦截并处理请求。 本文将带你了解如何使用 WebFlux 拦截客户端请求以及如何添加自定义 Header。 2、Maven 依赖 添加 spring-boot-starter-webflux 响应式 Web 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <version>3.1.5</version> </dependency> 3、拦截请求 Spring WebFlux Filter 可分为 WebFilter 和 HandlerFilterFunction 两类。可使用这些过滤器拦截 Web 请求,并添加新的自定义 Header 或修改现有 Header。 3.1、使用 WebFilter WebFilter 以链式拦截方式处理 Web 请求。WebFilter 在全局范围内生效,一旦启用,将拦截所有的请求和响应。 首先,定义基于注解的 Controller: @GetMapping(value= "/trace-annotated") public Mono<String> trace(@RequestHeader(name = "traceId") final String traceId) { return Mono.just("TraceId: ".concat(traceId)); } 然后,拦截 Web 请求,使用 TraceWebFilter 实现添加一个新的 Header traceId: @Component public class TraceWebFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { exchange.

Spring Boot 3.2.5 发布

🐞 Bug 修复 BindValidationFailureAnalyzer 使用了错误的 target #40364 Log4j2LoggingSystem 通过一个永远不会被移除的 SpringEnvironmentPropertySource 污染 Log4j2 的环境 #40326 使用 Maven 时,配置 spring-boot.excludes 或 spring-boot-includes 用户属性会导致构建失败,提示:Cannot find default setter #40323 @ServletComponentScan 无法在模拟的 Web 环境中注册 Servlet 组件 #40321 在将 Log4j2 配置为使用单个 JVM 范围日志记录器上下文的情况下部署到 Tomcat 时,加载自定义的 deny-all 过滤器可能会导致 StackOverflowError 错误 #40312 Jetty 支持不设置虚拟线程名称 #40152 3.2.0 之后,当 Hibernate Scanner 未禁用时,可执行 JAR 应用的启动速度变慢 #40125 线程中断时,LaunchedClassLoader 可能会抛出 NoClassDefFoundError 错误 #40096 📔 文档 Producible 的 javadoc 中 @WriteOperation 和 @DeleteOperation 的链接文本有误 #40386 明确参数和构造函数绑定的要求 #40157 🔨 依赖升级 升级到 ActiveMQ 5.

在 Spring 测试中禁用 @EnableScheduling

1、简介 本文将带你了解如何测试启用了定时任务(@EnableScheduling)的 Spring 应用,以及如何在测试过程中禁用定时任务。 2、示例 首先来看一个示例,假设我们有一个系统,允许公司的代表向客户发送通知。其中一些通知是时间敏感的,应该立即发送,但有些通知应该等到下一个工作日再发送。因此,我们需要一个机制来定期尝试发送这些通知: public class DelayedNotificationScheduler { private NotificationService notificationService; @Scheduled(fixedDelayString = "${notification.send.out.delay}", initialDelayString = "${notification.send.out.initial.delay}") public void attemptSendingOutDelayedNotifications() { notificationService.sendOutDelayedNotifications(); } } attemptSendingOutDelayedNotifications() 方法上添加了 @Scheduled 注解。当 initialDelayString 配置的时间过去后,该方法将被首次调用。执行结束后,Spring 会在 fixedDelayString 参数配置的时间过后再次调用该方法。该方法本身将实际逻辑委托给了 NotificationService。 当然,我们还需要开启调度功能。为此,需要在 @Configuration 类上添加 @EnableScheduling 注解。 3、集成测试中定时任务的问题 首先,为通知应用编写一个基本的集成测试: @SpringBootTest( classes = { ApplicationConfig.class, SchedulerTestConfiguration.class }, properties = { "notification.send.out.delay: 10", "notification.send.out.initial.delay: 0" } ) public class DelayedNotificationSchedulerIntegrationTest { @Autowired private Clock testClock; @Autowired private NotificationRepository repository; @Autowired private DelayedNotificationScheduler scheduler; @Test public void whenTimeIsOverNotificationSendOutTime_thenItShouldBeSent() { ZonedDateTime fiveMinutesAgo = ZonedDateTime.