我正在编写集成测试并遇到问题。当我启动应用程序时,一切正常。但是,在运行测试时,我遇到了异常:
org.hibernate.MappingException:无法确定 SQL 类型名称 表“some_table”的列“id”,因为没有类型映射 org.hibernate.type.SqlTypes 代码:1111(其他)
某些实体使用以下标识符映射:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", unique = true, nullable = false)
@Access(AccessType.PROPERTY)
@Type(value = SomeUUIDType.class, parameters = @Parameter(name = "column", value = "id"))
private UUID id;
类
SomeUUIDType
只是从Hypersistence扩展了
MutableType
,并由于某些原因添加了(我不知道为什么 - 它就在那里):
import io.hypersistence.utils.hibernate.type.MutableType;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.type.descriptor.java.UUIDJavaType;
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
import org.hibernate.usertype.EnhancedUserType;
import org.hibernate.usertype.ParameterizedType;
import java.util.Properties;
import java.util.UUID;
public class SomeUUIDType extends MutableType<UUID, UUIDJdbcType, UUIDJavaType> implements ParameterizedType, EnhancedUserType<UUID> {
public SomeUUIDType() {
super(UUID.class, UUIDJdbcType.INSTANCE, UUIDJavaType.INSTANCE);
}
@Override
public UUID fromStringValue(CharSequence seq) throws HibernateException {
return UUID.fromString(seq.toString());
}
@Override
public <X, Y> int forEachDisassembledJdbcValue(
Object value,
int offset,
X x,
Y y,
JdbcValuesBiConsumer<X, Y> consumer,
SharedSessionContractImplementor session
) {
return 0;
}
@Override
public int forEachJdbcType(int offset, IndexedConsumer<JdbcMapping> indexedConsumer) {
return 0;
}
@Override
public void setParameterValues(Properties params) {
}
@Override
public String toSqlLiteral(UUID value) {
return value.toString();
}
@Override
public String toString(UUID value) throws HibernateException {
return value.toString();
}
}
我首先编写一些基本测试。这里我创建了一个实体并尝试保存它:
@Testcontainers
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@EntityScan(basePackages = "some.package")
@EnableAutoConfiguration
@TestPropertySource("/test-application.properties")
public class SomeTest {
@Container
private static final PostgreSQLContainer<?> testContainer = new PostgreSQLContainer<>("postgres:latest");
@Autowired
private ApplicationContext context;
@Autowired
private TestEntityManager em;
@DynamicPropertySource
private static void overrideProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", testContainer::getJdbcUrl);
registry.add("spring.datasource.username", testContainer::getUsername);
registry.add("spring.datasource.password", testContainer::getPassword);
registry.add("spring.datasource.driverClassName", testContainer::getDriverClassName);
}
@Test
void test() {
assertTrue(testContainer.isCreated());
assertTrue(testContainer.isRunning());
assertNotNull(context);
assertNotNull(em);
SomeEntity entity = new SomeEntity();
entity.setId(UUID.randomUUID());
em.persistAndFlush(entity);
}
}
我的测试应用程序.属性:
spring.datasource.url=
spring.datasource.driverClassName=
spring.datasource.username=
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.datasource.initialization-mode=always
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql = true
pom.xml 包含以下依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>3.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
<scope>runtime</scope>
</dependency>
查了资料,发现有必要定义一个自定义方言。像这样的东西:
public class CustomPostgreSQLDialect extends PostgreSQLDialect {
@Override
protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
super.registerColumnTypes(typeContributions, serviceRegistry);
DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
ddlTypeRegistry.addDescriptor(Types.OTHER, ....);
}
}
然后插入即可:
spring.jpa.properties.hibernate.dialect=some.package.CustomPostgreSQLDialect
但是我不清楚我到底需要在这里重写什么。这就是原因吗?
我实际上只是编写测试,无法更改现有代码。我想可以配置自定义方言来防止异常。
经过研究后,问题似乎与让 Hibernate 创建表有关。使用 Flyway,就不会出现错误。
我在使用您的测试类时遇到了一些困难,因此我展示了测试中使用的所有代码。
src/test/resources/db/migration/V1__Create_Tables.sql
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE some_entity
(
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
);
依赖关系 对于较新版本的 Spring Boot,请使用
testRuntimeOnly("org.flywaydb:flyway-database-postgresql")
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("io.hypersistence:hypersistence-utils-hibernate-63:3.9.0")
testRuntimeOnly("org.postgresql:postgresql")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testImplementation("org.flywaydb:flyway-core:9.0.0")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.boot:spring-boot-testcontainers")
testImplementation("org.testcontainers:postgresql")
}
应用程序测试.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/test_db
username: test
password: test
driver-class-name: org.postgresql.Driver
jpa:
hibernate.ddl-auto: none
show-sql: true
flyway:
enabled: true
locations: classpath:db/migration
PostgresTestContainerConfig
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.testcontainers.containers.PostgreSQLContainer;
@TestConfiguration
public class PostgresTestContainerConfig {
@Bean
@ServiceConnection
public PostgreSQLContainer<?> postgreSQLContainer() {
return new PostgreSQLContainer<>("postgres:15-alpine");
}
}
SomeEntityRepository
import org.springframework.data.repository.CrudRepository;
import java.util.UUID;
public interface SomeEntityRepository extends CrudRepository<SomeEntity, UUID> { }
一些测试
replace = AutoConfigureTestDatabase.Replace.NONE
可以使用 Spring Boot 3.4.0 删除。
import no.myapp.database.PostgresTestContainerConfig;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.context.annotation.Import;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import(PostgresTestContainerConfig.class)
public class SomeTest {
@Autowired
TestEntityManager em;
@Autowired
SomeEntityRepository sut;
@Test
void testSave() {
SomeEntity someEntity = new SomeEntity();
sut.save(someEntity);
assertEquals(1, getItemCountInDb());
}
long getItemCountInDb() {
return (long) em.getEntityManager()
.createQuery("SELECT COUNT(1) FROM SomeEntity")
.getSingleResult();
}
}