我正在尝试进行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
试试这个
@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)