Spring-Boot

解决 Spring Boot H2 JdbcSQLSyntaxErrorException “Table not found”

1、简介 H2 是一个简单的轻量级内存数据库,Spring Boot 可以自动对其进行配置,使开发人员可以轻松测试数据访问逻辑。 通常情况下,org.h2.jdbc.JdbcSQLSyntaxErrorException 是用于表示与 SQL 语法相关的错误的异常。“Table not found” 表示 H2 无法找到指定的表。 本文将带你了解 H2 抛出 JdbcSQLSyntaxErrorException 异常的原因以及解决办法。 2、示例 既然知道了异常背后的根本原因,来看看如何重现异常。 2.1、H2 配置 Spring Boot 会配置应用使用用户名 sa 和空密码连接到可嵌入的数据库 H2。将这些属性添加到 application.properties 文件中: spring.datasource.url=jdbc:h2:mem:mydb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= 现在,假设有一个名为 person 的表。在此,使用一个基本的 SQL 脚本为数据库添加数据。默认情况下,Spring Boot 会加载 data.sql 文件: INSERT INTO "person" VALUES (1, 'Abderrahim', 'Azhrioun'); INSERT INTO "person" VALUES (2, 'David', 'Smith'); INSERT INTO "person" VALUES (3, 'Jean', 'Anderson'); 2.2、对象关系映射 接下来,使用 JPA 注解将表 person 映射到一个实体。

一种极简单的 Spring Boot 单元测试方法

本文主要介绍了一种单元测试方法,力求零基础人员可以从本文中受到启发,可以搭建一套好用的单元测试环境,并能切实提高交付代码的质量。极简体现在除了 POM 依赖和单元测试类之外,其他什么都不需要引入,只需要一个本地能启动的 Spring Boot 项目。 1、POM依赖 Springboot版本: 2.6.6 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>3.12.4</version> </dependency> 2、单元测试类示例 主要有两种。 第一种,偏集成测试 需要启动项目,需要连接数据库、RPC 注册中心等。 主要注解:@SpringBootTest + @RunWith(SpringRunner.class) + @Transactional + @Resource + @SpyBean + @Test @SpringBootTest + @RunWith(SpringRunner.class) 启动了一套 Spring Boot 的测试环境; @Transactional 对于一些修改数据库的操作,会执行回滚,能测试执行 sql,但是又不会真正的修改测试库的数据; @Resource 主要引入被测试的类; @SpyBean Spring Boot 环境下 mock 依赖的 Bean,可以搭配 Mockito.doAnswer(...).when(xxServiceImpl).xxMethod(any()) Mock 特定方法的返回值; @Test 标识一个测试方法; TIP:对于打桩有这几个注解 @Mock @Spy @MockBean @SpyBean,每一个都有其对应的搭配,简单说 @Mock 和 @Spy 要搭配 @InjectMocks 去使用,@MockBean 和 @SpyBean 搭配 @SpringBootTest + @RunWith(SpringRunner.

在 Spring Boot 应用中设置默认时区(Timezone)

1、概览 有时,我们希望能够指定应用使用的时区。我们可以通过几种不同的方法来实现这一目标。一种方法是在执行应用时使用 JVM 参数。另一种方法是在启动生命周期的不同阶段以编程式在代码中进行更改。 本文将带你了解在 Spring Boot 应用中设置默认时区的几种方法。 2、主要概念 TimeZone 的默认值基于运行 JVM 的机器的操作系统。我们可以: 通过使用 user.timezone 参数传递 JVM 参数,可以根据运行任务或 JAR 的不同情况,以不同的方式传递参数。 在程序中使用 Bean 生命周期配置选项(在创建 Bean 时/创建 Bean 前),甚至在类内执行过程中使用这些选项。 在 Spring Boot 应用中设置默认 TimeZone 会影响不同的组件,如日志的时间戳、调度程序(Scheduler)、JPA/Hibernate 时间戳等。这意味着我们选择在何处执行取决于何时需要它生效。例如,是希望在创建某个 Bean 时生效,还是在初始化 WebApplicationContext 后生效? 精确地确定何时设置该值非常重要,因为这可能会导致不必要的应用行为。例如,警报服务可能会在时区更改生效前设置警报,从而导致警报在错误的时间启动。 在决定采用哪种方案之前,另一个需要考虑的因素是可测试性。使用 JVM 参数是比较简单的选择,但测试起来可能比较麻烦,也更容易出现错误。我们无法保证单元测试能以与生产部署相同的 JVM 参数运行。 3、设置 bootRun 任务的默认时区 如果使用 bootRun 任务运行应用,我们可以在命令行中使用 JVM 参数传递默认 TimeZone。 在这种情况下,我们设置的值从一开始执行就可用: mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Duser.timezone=Europe/Athens" 4、在执行 JAR 时设置默认时区 与运行 bootRun 任务类似,我们可以在执行 JAR 文件时在命令行中传递默认的 TimeZone 值。 同样,我们设置的值在执行之初就可用: java -Duser.timezone=Europe/Athens -jar spring-core-4-0.

Spring Boot 中 Spring Security 自动配置

1、概览 本文将带你了解 Spring Boot 中 Spring Security 的自动配置、默认安全配置,以及如何在需要时禁用或自定义它。 2、默认的 Spring Security 设置 首先添加 security starter 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> 这包含初始/默认 Security 配置的 SecurityAutoConfiguration 类。 这里没有指定版本,因为项目使用了 spring-boot-starter-parent 作为 parent。 默认情况下,应用会启用身份验证,内容协商(Content Negotiation)用于确定应使用 basic 还是 formLogin。 有一些预定义的配置属性: spring.security.user.name= spring.security.user.password= 如果不使用预定义属性 spring.security.user.password 配置密码并启动应用,默认密码将随机生成并打印在控制台日志中: Using default security password: c8be15da-4489-4491-9dc6-fab3f91435c7 有关更多默认值,请参阅 Spring Boot 中文文档中的 属性配置。 3、禁用自动配置 要禁止 Security 自动配置并添加我们的自定义配置,需要排除 SecurityAutoConfiguration 自动配置类。 可以通过 @SpringBootApplication 注解中的 exclude 属性来实现: @SpringBootApplication(exclude = { SecurityAutoConfiguration.class }) public class SpringBootSecurityApplication { public static void main(String[] args) { SpringApplication.

Spring Boot 使用 Grafana Loki 来收集和显示日志

1、简介 Grafana 实验室受 Prometheus 的启发开发了开源日志聚合系统 Loki。该系统的目的是存储日志数据并编制索引,从而方便高效地查询和分析由不同应用和系统生成的日志。 本文将带你了解如何在 Spring Boot 中使用 Loki 收集和汇总日志,并使用 Grafana 显示日志。 2、运行 Loki 和 Grafana 服务 首先以 Docker 容器的方式启动 Loki 和 Grafana 服务,以便收集和观察日志。 在 docker-compose 文件中定义 Loki 和 Grafana 服务: version: "3" networks: loki: services: loki: image: grafana/loki:2.9.0 ports: - "3100:3100" command: -config.file=/etc/loki/local-config.yaml networks: - loki grafana: environment: - GF_PATHS_PROVISIONING=/etc/grafana/provisioning - GF_AUTH_ANONYMOUS_ENABLED=true - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin entrypoint: - sh - -euc - | mkdir -p /etc/grafana/provisioning/datasources cat <<EOF > /etc/grafana/provisioning/datasources/ds.

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 缓存中:

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 Boot Filter 中获取响应体

1、简介 本文将带你了解如何在 Spring Boot Filter(过滤器)中获取 ServletResponse 的响应体。 2、场景 在使用 Spring Boot 中使用 Filter 时,从 ServletResponse 访问响应体非常麻烦。这是因为响应体不是随时可用的,它是在 Filter 链执行完毕后才写入输出流的。 但是,有些操作(如生成哈希签名)需要在发送给客户端之前读取完整的响应正文的内容。因此,需要找到读取响应体内容的方法。 3、使用 ContentCachingResponseWrapper 创建一个自定义 Filter,并使用 Spring 提供的 ContentCachingResponseWrapper 类进行包装: @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { ContentCachingResponseWrapper responseCacheWrapperObject = new ContentCachingResponseWrapper((HttpServletResponse) servletResponse); filterChain.doFilter(servletRequest, responseCacheWrapperObject); byte[] responseBody = responseCacheWrapperObject.getContentAsByteArray(); MessageDigest md5Digest = MessageDigest.getInstance("MD5"); byte[] md5Hash = md5Digest.digest(responseBody); String md5HashString = DatatypeConverter.printHexBinary(md5Hash); responseCacheWrapperObject.getResponse().setHeader("Response-Body-MD5", md5HashString); // ... } 简而言之,wrapper 类允许我们封装 HttpServletResponse 以缓存响应正文内容,并调用 doFilter() 将请求传递给下一个 Filter。

Spring Boot 使用 git-commit-id-maven-plugin 打包应用

原文地址:https://www.cnblogs.com/Naylor/p/18024689 简介 git-commit-id-maven-plugin 是一个maven 插件,用来在打包的时候将 git-commit 信息打进 Jar 中。 这样做的好处是可以将发布的某版本和对应的代码关联起来,方便查阅和线上项目的维护。至于它的作用,用官方说法,这个功能对于大型分布式项目来说是无价的。 功能 你是否经常遇到这样的问题: 测试提交了一个bug,开发人员无法确认是哪个版本有这个问题?当前测试环境部署的是某个版本吗?生产环境会不会也有这个问题? 公司内部的项目,总共几十、几百个服务,每天都有服务的生产环境部署,一个服务甚至一天上线好几次,对于项目管理来说无法清晰了解某一时刻某个服务的版本 如何验证我的代码是否已经上线? 。。。。。。 以上种种,都有一个共同的诉求,就是我希望在打包的时候将最后一次 git commit id 和当前 jar 关联起来并可试试查询 jar 对应的 git commit id 。 实践 引入插件 本例 Spring Boot 版本为 2.7.6,java 版本为 11 此插件已经上传到中央仓库(https://central.sonatype.com/artifact/io.github.git-commit-id/git-commit-id-maven-plugin?smo=true) 在项目pom.xml 中引入如下插件 <project> ...... <build> <plugins> <!-- git-commit-id-maven-plugin :打包的时候携带git提交信息 --> <plugin> <groupId>io.github.git-commit-id</groupId> <artifactId>git-commit-id-maven-plugin</artifactId> <version>5.0.0</version> <executions> <execution> <id>get-the-git-infos</id> <goals> <goal>revision</goal> </goals> <phase>initialize</phase> </execution> </executions> <configuration> <generateGitPropertiesFile>true</generateGitPropertiesFile> <generateGitPropertiesFilename>${project.build.outputDirectory}/git.</generateGitPropertiesFilename> <includeOnlyProperties> <includeOnlyProperty>^git.build.(time|version)$</includeOnlyProperty> <includeOnlyProperty>^git.commit.id.(abbrev|full)$</includeOnlyProperty> </includeOnlyProperties> <format>txt</format> <commitIdGenerationMode>full</commitIdGenerationMode> </configuration> </plugin> </plugins> </build> </project> generateGitPropertiesFilename:用于指定生成的 gitCommitInfo 存放到哪个位置,后缀可以任意指定,如果不指定将使用 format 的值 format:指定文件后缀,一般为 properties、json commitIdGenerationMode:记录完整信息,若 format 为 json,此值必须为 full 此外为了能成功打出 jar 包,还需要如下插件的配合:

在 Spring Boot 中动态管理 Kafka Listener

1、概览 本文将带你了解如何在 Spring Boot 应用中动态地启动和停止 Kafka Listener。 2、依赖 首先,添加 spring-kafka 依赖: <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> <version>3.1.2</version> </dependency> 3、配置 Kafka 消费者 生产者是向 Kafka Topic 发布(写入)事件的应用。 在本教程中,我们使用单元测试来模拟生产者向 Kafka Topic 发送事件。订阅 Topic 并处理事件流的消费者由应用中的 Listener(监听器)表示。该 Listener 被配置为处理来自 Kafka 的传入消息。 通过 KafkaConsumerConfig 类来配置 Kafka 消费者,其中包括 Kafka broker 的地址、消费者组 ID 以及 Key 和 Value 的反序列化器: @Bean public DefaultKafkaConsumerFactory<String, UserEvent> consumerFactory() { Map<String, Object> props = new HashMap<>(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class); props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); props.put(JsonDeserializer.TRUSTED_PACKAGES, "com.baeldung.spring.kafka.start.stop.consumer"); return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(), new JsonDeserializer<>(UserEvent.