org.hibernate.mapping.Table.getColumn 返回 null

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

我正在尝试进行junit测试和测试存储库。为此,我需要使用

h2
数据库创建测试数据库,测试类看起来:

PokemonRepositoryTest.java

@DataJpaTest
@AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
@TestPropertySource(properties = {
        "spring.flyway.enabled=false",
        "spring.jpa.hibernate.naming.physical-strategy=com.example.j2.config.H2PhysicalNamingStrategy"
})
public class PokemonRepositoryTest {
    @Autowired
    private PokemonRepository pokemonRepository;

    @Test
    public void saved_pokemon_is_not_null(){
        // arrange
        Pokemon pokemon = Pokemon.builder()
                .name("pikachu")
                .type("electric")
                .build();
        // act
        Pokemon saved = pokemonRepository.save(pokemon);
        // assert
        Assert.notNull(saved,"saved pokemon is null");
        Assert.isTrue(saved.getId() > 0, "saved pokemon is not grater then 0");
    }
}

现在我有一个名为

User
的实体,其物理(表)名称为
user
。然而,这是不允许的,因为
user
(未引用,因此H2将其大写为
USER
)是保留关键字。所以我决定实现
PhysicalNamingStrategy
来为 H2 创建的所有表添加前缀(同样,这是出于测试目的,在我的开发中,我使用的 mysql 没有
user
作为关键字,因此没问题):

H2PhysicalNamingStrategy.java

@Configuration
public class H2PhysicalNamingStrategy implements PhysicalNamingStrategy {
    private static final String TABLE_PREFIX = "_";
    @Override
    public Identifier toPhysicalCatalogName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return identifier;
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return identifier;
    }

    @Override
    public Identifier toPhysicalTableName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return Identifier.toIdentifier(TABLE_PREFIX + identifier.getText());
    }

    @Override
    public Identifier toPhysicalSequenceName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return identifier;
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return identifier;
    }
}

现在我收到错误:

...
Hibernate: drop table if exists _pokemon cascade 
Hibernate: drop table if exists _refresh_token cascade 
Hibernate: drop table if exists _review cascade 
Hibernate: drop table if exists _role cascade 
Hibernate: drop table if exists _user cascade 
Hibernate: drop table if exists _user_roles cascade 
Hibernate: create table _pokemon (id bigint generated by default as identity, name varchar(255), type varchar(255), primary key (id))
Hibernate: create table _refresh_token (id bigint generated by default as identity, user_id bigint, token varchar(255), primary key (id))
Hibernate: create table _review (stars integer, id bigint generated by default as identity, pokemon_id bigint, title varchar(255), content clob, primary key (id))
2024-01-07T01:31:07.894+01:00 ERROR 132222 --- [           main] j.LocalContainerEntityManagerFactoryBean : Failed to initialize JPA EntityManagerFactory: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Error creating SQL 'create' commands for table '_role' [Cannot invoke "org.hibernate.mapping.Column.isUnique()" because the return value of "org.hibernate.mapping.Table.getColumn(org.hibernate.mapping.Column)" is null]

它说

org.hibernate.mapping.Table.getColumn
为空。但为什么呢?

编辑: 将前缀更改为后缀(按照@Basil Bourque 在评论中的建议):

H2PhysicalNamingStrategy.java

@Configuration
public class H2PhysicalNamingStrategy implements PhysicalNamingStrategy {
 private static final String TABLE_SUFFIX = "_";
 ...
 @Override
    public Identifier toPhysicalTableName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return Identifier.toIdentifier(identifier.getText() + TABLE_SUFFIX);
    }
 ...
}

但仍然是同样的错误:

...
Hibernate: drop table if exists pokemon_ cascade 
Hibernate: drop table if exists refresh_token_ cascade 
Hibernate: drop table if exists review_ cascade 
Hibernate: drop table if exists role_ cascade 
Hibernate: drop table if exists user_ cascade 
Hibernate: drop table if exists user_roles_ cascade 
Hibernate: create table pokemon_ (id bigint generated by default as identity, name varchar(255), type varchar(255), primary key (id))
Hibernate: create table refresh_token_ (id bigint generated by default as identity, user_id bigint, token varchar(255), primary key (id))
Hibernate: create table review_ (stars integer, id bigint generated by default as identity, pokemon_id bigint, title varchar(255), content clob, primary key (id))
2024-01-07T13:36:53.866+01:00 ERROR 3851 --- [           main] j.LocalContainerEntityManagerFactoryBean : Failed to initialize JPA EntityManagerFactory: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Error creating SQL 'create' commands for table 'role_' [Cannot invoke "org.hibernate.mapping.Column.isUnique()" because the return value of "org.hibernate.mapping.Table.getColumn(org.hibernate.mapping.Column)" is null]

编辑2: 这是完整的源代码: github

java spring-boot hibernate jpa h2
1个回答
0
投票

试试这个

@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class MyRepositoryTests
{
   ...
}

与 application.properties

...
spring.datasource.url=jdbc:h2:mem:;NON_KEYWORDS=USER
...

使用 Replace.NONE,您可以告诉测试框架不要替换 application.properties 中的 URL。您可以附加更多用逗号分隔的关键字(例如 NON_KEYWORDS=USER,ORDER)

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