Spring Boot JPA findById()重启应用后失效问题求助
问题描述
首次启动应用时,数据库无对应表,Spring自动创建表,此时save()、findById()等操作均正常。但重启应用后,首次执行findById()语句时应用卡住,无后续响应。
使用技术栈
- Spring Boot 3.0.1
- MariaDB 10.9
- Java 17
相关代码
pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.mariadb.jdbc</groupId> <artifactId>mariadb-java-client</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> . . . </dependencies>
application.properties
spring.jpa.generate-ddl=true spring.jpa.hibernate.ddl-auto=update spring.datasource.url=jdbc:mariadb://${MYSQL_HOST:localhost}:3306/database spring.datasource.username=USERNAME spring.datasource.password=PASSWORD spring.datasource.driver-class-name=org.mariadb.jdbc.Driver spring.jpa.open-in-view=false
Entity类(GuildEntity)
@Entity @Table(name = "guild_entity") @Getter @Setter public class GuildEntity { @Id @Column(name = "guild_id") private String guildId; @Column(name = "bot_control_role") private String botControlRole; @Column(name = "command_prefix") @Builder.Default private String commandPrefix = "varchar(1) default '&'"; @Column(name = "language") @Builder.Default private String language = "varchar(2) default 'EN'"; @Column(name = "voice_control_role") private String voiceControlRole; @Column(name = "volume") @Builder.Default private Integer volume = 20; @Column(name = "player_shuffle") @Builder.Default private Boolean shuffle = false; @Column(name = "player_repeat") @Builder.Default private Boolean repeat = false; @Column(name = "write_channel") private String writeChannel; @Column(name = "enable_support_system") @Builder.Default private Boolean enableSupportSystem = false; @Column(name = "enable_welcome_messages") @Builder.Default private Boolean enableWelcomeMessages = false; @Column(name = "welcome_channel") private String welcomeChannel; @Column(name = "welcome_message") @Builder.Default private String welcomeMessage = "varchar(255) default ':wave: Welcome to %server%, %user%!'"; }
EntityRepository接口
@Repository public interface GuildEntityRepository extends JpaRepository<GuildEntity, String> { }
SQLHandler服务类
@Service @RequiredArgsConstructor public class SQLHandler { @Autowired private final GuildEntityRepository guildEntityRepository; public GuildEntity getGuild(String guildId) { NeptuneBot.getLogger().debug(guildId, "Get guild", LogType.SQL); return guildEntityRepository.findById(guildId).orElse(null); } public void saveAllGuilds(Iterable<GuildEntity> guildEntities) { NeptuneBot.getLogger().debugSQL("Save all guild entities..."); guildEntityRepository.saveAll(guildEntities); NeptuneBot.getLogger().debugSQL("Done!"); } }
重启后的日志
[2m2022-12-31T03:07:38.523+01:00[0;39m [32m INFO[0;39m [35m1751594[0;39m [2m---[0;39m [2m[0.1-8080-exec-1][0;39m [36mo.a.c.c.C.[Tomcat].[localhost].[/] [0;39m [2m:[0;39m Initializing Spring DispatcherServlet 'dispatcherServlet' [2m2022-12-31T03:07:38.523+01:00[0;39m [32m INFO[0;39m [35m1751594[0;39m [2m---[0;39m [2m[0.1-8080-exec-1][0;39m [36mo.s.web.servlet.DispatcherServlet [0;39m [2m:[0;39m Initializing Servlet 'dispatcherServlet' [2m2022-12-31T03:07:38.525+01:00[0;39m [32m INFO[0;39m [35m1751594[0;39m [2m---[0;39m [2m[0.1-8080-exec-1][0;39m [36mo.s.web.servlet.DispatcherServlet [0;39m [2m:[0;39m Completed initialization in 1 ms [2m2022-12-31T03:07:38.907+01:00[0;39m [32m INFO[0;39m [35m1751594[0;39m [2m---[0;39m [2m[ main][0;39m [36mnet.dv8tion.jda.api.JDA [0;39m [2m:[0;39m Login Successful! [2m2022-12-31T03:07:39.198+01:00[0;39m [32m INFO[0;39m [35m1751594[0;39m [2m---[0;39m [2m[inWS-ReadThread][0;39m [36mn.d.j.internal.requests.WebSocketClient [0;39m [2m:[0;39m Connected to WebSocket [2m2022-12-31T03:07:39.608+01:00[0;39m [32m INFO[0;39m [35m1751594[0;39m [2m---[0;39m [2m[inWS-ReadThread][0;39m [36mnet.dv8tion.jda.api.JDA [0;39m [2m:[0;39m Finished Loading! [2022.12.31 03:07:42]: SQL | Get guild Hibernate: select g1_0.guild_id, g1_0.bot_control_role, g1_0.command_prefix, g1_0.enable_support_system, g1_0.enable_welcome_messages, g1_0.language, g1_0.player_repeat, g1_0.player_shuffle, g1_0.voice_control_role, g1_0.volume, g1_0.welcome_channel, g1_0.welcome_message, g1_0.write_channel from guild_entity g1_0 where g1_0.guild_id=?
问题分析与解决方案
核心问题定位
问题出在Entity类的默认值配置错误:
GuildEntity中commandPrefix、language、welcomeMessage字段的@Builder.Default注解后,赋值的是SQL字段定义语句(如"varchar(1) default '&'"),而非实际业务默认值。- 首次启动建表时,Hibernate会将这些字符串当作字段默认值写入表结构;但重启后查询时,错误的默认值会干扰Hibernate的元数据校验,触发首次查询时的线程阻塞或无限等待。
具体修复步骤
修正Entity类默认值
将错误的SQL语句替换为实际业务值,若需数据库层面的默认值,通过@Column的columnDefinition配置:@Column(name = "command_prefix", columnDefinition = "varchar(1) default '&'") @Builder.Default private String commandPrefix = "&"; @Column(name = "language", columnDefinition = "varchar(2) default 'EN'") @Builder.Default private String language = "EN"; @Column(name = "welcome_message", columnDefinition = "varchar(255) default ':wave: Welcome to %server%, %user%!'") @Builder.Default private String welcomeMessage = ":wave: Welcome to %server%, %user%!";重建数据库表
手动删除guild_entity表,或临时将spring.jpa.hibernate.ddl-auto改为create,启动一次应用后改回update,确保表结构正确。优化数据库连接配置
给连接URL添加必要参数,避免时区、SSL等问题导致连接阻塞:spring.datasource.url=jdbc:mariadb://${MYSQL_HOST:localhost}:3306/database?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true排查线程阻塞
应用卡住时用jstack命令查看线程堆栈,若为连接池问题,调整连接池参数:spring.datasource.hikari.maximum-pool-size=10 spring.datasource.hikari.connection-timeout=30000
验证效果
修复后重启应用,首次findById()操作可正常执行,无阻塞情况,后续操作也能稳定运行。
内容的提问来源于stack exchange,提问作者Jes Müller
相关产品推荐
相关产品推荐

