如何扩展 PostgreSQL 方言以添加对某些自定义列类型的支持? org.hibernate.type.SqlTypes 代码没有类型映射:1111(其他)

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

我正在编写集成测试并遇到问题。当我启动应用程序时,一切正常。但是,在运行测试时,我遇到了异常:

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

但是我不清楚我到底需要在这里重写什么。这就是原因吗?

我实际上只是编写测试,无法更改现有代码。我想可以配置自定义方言来防止异常。

postgresql spring-boot hibernate jpa spring-data-jpa
1个回答
0
投票

经过研究后,问题似乎与让 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();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.