配置 Java Spring boot 以使用 H2 进行测试并使用 MySQL 运行

问题描述 投票:0回答:1

我正在使用 Java Spring Boot 设置微服务。该应用程序在 Docker 容器中运行,并连接到本地计算机上的 MySQL 数据库。出于测试目的,我想避免 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

可以在此处找到具有我当前设置的存储库: GitHub 存储库:[apptraqer(ehc/ArchitectureOverview 分支)]1

配置 Spring Boot 使用 H2 进行单元测试,同时保留 MySQL 作为运行时配置的最佳方法是什么?我希望获得有关如何调整上述文件并解决这些错误的指导。

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,一个用于 Docker 容器,一个用于测试:

应用程序属性

# 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

应用程序测试.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"]
java spring-boot hibernate
1个回答
0
投票

以下是我必须做的更改才能正确构建应用程序。

为了运行单元测试而简单直接地配置 H2,我们可以使用 @AutoConfigureTestDatabase,如 Spring Boot 文档中自动配置数据 JPA 测试部分末尾所述。这将根据我们在类路径中添加的内容(在本例中为 H2)配置测试数据源。 这是我们如何在代码中添加注释的示例

@SpringBootTest
@AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
class UserTest{}

请注意,上述内容仅用于替换我们在应用程序中创建的数据源 bean。但是,可以将数据源属性移动到单独的配置文件中,分别用于测试和应用程序,例如将 MySQL 数据源配置放在名为 application-docker.yaml 的文件中,而不在任何其他 yaml 文件中提供任何数据源配置。这样,当我们的数据源自动配置时,它并不总是知道 MySQL 配置(除非配置文件 docker 处于活动状态)。在此设置中,Spring Boot 可以检测类路径上的 H2 并自动为我们配置 H2。部署时,我们可以在 docker 中运行应用程序,配置文件 docker active 指示 spring boot 连接到 MySQL。同样,当运行单元测试时,我们不会激活配置文件 docker,然后 Spring Boot 将配置 H2。

我能找到的其他一些更正是。

  • Spring boot 3.4 不再依赖 javax JPA API 必须删除此依赖项
    implementation 'javax.persistence:javax.persistence-api:2.2'
    并且 JPA 导入必须来自 jakarta 包,该包由 spring boot starter jpa 间接拉取
  • 由于我们在 application.properties 中配置 MySQL 方言,在 application-test.properties 中配置 H2 方言,以便 Spring Boot 使用 application-test.properties 配置,因此我们需要在运行单元测试时激活测试配置文件。我知道的一种简单方法是在测试类上方添加 @ActiveProfiles 注释,如下所示
    @ActiveProfiles(profiles = {"test"})

希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.