配置Spring Boot测试用H2、运行用MySQL及解决构建错误
问题与解决方案:Spring Boot测试环境H2配置及MySQL方言错误修复
问题背景
使用Java Spring Boot搭建微服务,部署在Docker容器中,运行环境连接本地MySQL数据库,单元测试希望用H2内存数据库降低开销。涉及build.gradle、application.properties、application-test.properties和Dockerfile等配置文件,但构建时出现错误。
错误日志
java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@44976b08 testClass = com.apptraqer.apptraqerauthservice.ApptraqerAuthServiceApplicationTests, locations = [], classes = [com.apptraqer.apptraqerauthservice.ApptraqerAuthServiceApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceDescriptors = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.autoconfigure.OnFailureConditionReportContextCustomizerFactory$OnFailureConditionReportContextCustomizer@5023bb8b, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@604f2bd2, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@30b19518, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@62e20a76, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@28e8dde3, org.springframework.boot.test.web.reactor.netty.DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory$DisableReactorResourceFactoryGlobalResourcesContextCustomizerCustomizer@59252cb6, org.springframework.test.context.support.DynamicPropertiesContextCustomizer@0, org.springframework.boot.test.context.SpringBootTestAnnotation@cc9a5aaa], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null] at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:180) <snip> at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] due to: Unable to resolve name [org.hibernate.dialect.MySQL5Dialect] as strategy [org.hibernate.dialect.Dialect] at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1802) at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601) <snip> at app//org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) at app//org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) at app//org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) ... 19 more Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] due to: Unable to resolve name [org.hibernate.dialect.MySQL5Dialect] as strategy [org.hibernate.dialect.Dialect] at app//org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:276) at app//org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:238) <snip> at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1849) at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1798) ... 39 more Caused by: org.hibernate.boot.registry.selector.spi.StrategySelectionException: Unable to resolve name [org.hibernate.dialect.MySQL5Dialect] as strategy [org.hibernate.dialect.Dialect] at app//org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.selectStrategyImplementor(StrategySelectorImpl.java:154) at app//org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.resolveStrategy(StrategySelectorImpl.java:236) <snip> at app//org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:130) at app//org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263) ... 54 more Caused by: org.hibernate.boot.registry.classloading.spi.ClassLoadingException: Unable to load class [org.hibernate.dialect.MySQL5Dialect] at app//org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.classForName(ClassLoaderServiceImpl.java:126) at app//org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.selectStrategyImplementor(StrategySelectorImpl.java:150) ... 64 more Caused by: java.lang.ClassNotFoundException: Could not load requested class : org.hibernate.dialect.MySQL5Dialect at org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader.findClass(AggregatedClassLoader.java:216) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:592) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:467) at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.classForName(ClassLoaderServiceImpl.java:123) ... 65 more
当前配置文件
build.gradle
plugins { id 'java' id 'org.springframework.boot' version '3.4.0' id 'io.spring.dependency-management' version '1.1.6' id 'application' } application { mainClass = 'com.apptraqer.apptraqerauthservice.ApptraqerAuthServiceApplication' } bootJar { mainClass = 'com.apptraqer.apptraqerauthservice.ApptraqerAuthServiceApplication' } group = 'com.apptraqer' version = '0.0.1-SNAPSHOT' java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } repositories { mavenCentral() } dependencies { // Spring Boot Starters implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' // OAuth2 and Security Enhancements implementation 'org.springframework.security:spring-security-oauth2-jose' // JSON Web Token (JJWT) implementation 'io.jsonwebtoken:jjwt-api:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-orgjson:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6' // Database Connector runtimeOnly 'mysql:mysql-connector-java:8.0.26' // Testing Dependencies testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' testImplementation 'com.h2database:h2' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' implementation 'javax.persistence:javax.persistence-api:2.2' implementation 'javax.xml.bind:jaxb-api:2.3.1' } tasks.named('test') { useJUnitPlatform() } tasks.withType(JavaCompile) { options.compilerArgs << "-Xlint:deprecation" }
application.properties
# application.properties (Common Settings for All Environments) # Application Name and Port (could be overridden per environment if needed) spring.application.name=apptraqer-auth-service server.port=8081 # Database Configuration (default to MySQL for non-test environments) spring.datasource.url=jdbc:mysql://apptraqer-mysql:3306/apptraqerdata spring.datasource.username=${SPRING_DATASOURCE_USERNAME} spring.datasource.password=${SPRING_DATASOURCE_PASSWORD} spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # Hibernate/JPA Settings (should be the same across all environments) spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
application-test.properties
# application-test.properties (Test Environment Overrides) # Override Database Configuration for Test with H2 (or whatever you use in tests) spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE spring.datasource.driver-class-name=org.h2.Driver spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect #H2 settings spring.h2.console.enabled=true spring.h2.console.path=/h2-console
Dockerfile
FROM openjdk:17-jdk-slim as build WORKDIR /app COPY build.gradle settings.gradle ./ COPY gradlew ./ COPY gradle/wrapper/ ./gradle/wrapper/ ENV SPRING_PROFILES_ACTIVE=test RUN ./gradlew build --no-daemon COPY src ./src RUN ./gradlew build FROM openjdk:17-jdk-slim WORKDIR /app COPY --from=build /app/build/libs/apptraqer-auth-service.jar apptraqer-auth-service.jar EXPOSE 8081 CMD ["java", "-jar", "apptraqer-auth-service.jar"]
解决方案
1. 修复MySQL方言版本问题
Spring Boot 3.4.0对应的Hibernate版本已移除MySQL5Dialect,改用适配MySQL 8.0+的MySQL8Dialect。修改application.properties中的方言配置:
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
2. 激活测试环境Profile
在测试类上添加@ActiveProfiles("test")注解,确保单元测试时加载application-test.properties的H2配置:
@SpringBootTest @ActiveProfiles("test") class ApptraqerAuthServiceApplicationTests { // 测试代码 }
3. 调整Dockerfile构建逻辑
构建阶段无需激活test profile,移除ENV SPRING_PROFILES_ACTIVE=test,并优化构建步骤避免重复执行build:
FROM openjdk:17-jdk-slim as build WORKDIR /app COPY gradlew ./ COPY gradle/wrapper/ ./gradle/wrapper/ COPY build.gradle settings.gradle ./ # 下载依赖(依赖不变时可缓存) RUN ./gradlew dependencies --no-daemon COPY src ./src # 构建jar包,跳过测试(如需跑测试可去掉-x test) RUN ./gradlew bootJar -x test --no-daemon FROM openjdk:17-jdk-slim WORKDIR /app COPY --from=build /app/build/libs/apptraqer-auth-service.jar apptraqer-auth-service.jar EXPOSE 8081 # 运行时默认使用生产环境配置,如需测试环境可在此添加ENV SPRING_PROFILES_ACTIVE=test CMD ["java", "-jar", "apptraqer-auth-service.jar"]
4. 优化依赖配置
移除冲突/冗余依赖:javax.persistence:javax.persistence-api和javax.xml.bind:jaxb-api,Spring Boot Starter已包含对应Jakarta版本的依赖,避免版本冲突:
dependencies { // Spring Boot Starters implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' // OAuth2 and Security Enhancements implementation 'org.springframework.security:spring-security-oauth2-jose' // JSON Web Token (JJWT) implementation 'io.jsonwebtoken:jjwt-api:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-orgjson:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6' // Database Connector runtimeOnly 'mysql:mysql-connector-java:8.0.26' // Testing Dependencies testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' testImplementation 'com.h2database:h2' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' }
验证修改
- 执行单元测试:
./gradlew test,确认H2数据库正常加载,无方言错误。 - 构建Docker镜像:
docker build -t apptraqer-auth-service .,运行容器时通过环境变量传入MySQL账号密码,验证生产环境连接正常。
内容的提问来源于stack exchange,提问作者Sam Carleton
相关产品推荐
相关产品推荐

