Spring JpaRepository自定义实现测试遇H2 Schema未找到问题
自定义JPA Repository实现类的测试问题
我通过@PersistenceContext注入EntityManager实现了自定义JPA Repository扩展类,因为要动态构建查询,没法使用仓库接口上的标准@Query注解。但用@DataJpaTest测试时,H2数据库报错找不到my_database schema;代码库中其他标准JpaRepository用同样方式测试都正常。我尝试在测试类中@Autowired EntityManager,再通过setter注入给new实例化的CustomFooDaoImpl,但仍出现H2提示找不到my_database Schema的异常。
相关代码
配置代码
public interface FooDao extends JpaRepository<Foo, Long>, CustomFooDao {} public interface CustomFooDao { List<Foo> query(); } public class CustomFooDaoImpl implements CustomFooDao { @PersistenceContext private EntityManager entityManager; public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } public List<Foo> query() { return entityManager.createNativeQuery(someFancyQueryLiteral).getResultList(); } }
测试代码
@DataJpaTest class CustomFooImplTest { @Autowired private EntityManager entityManager; private CustomFooDaoImpl customFooDaoImpl; @BeforeEach void setUp() { customFooDaoImpl = new CustomFooDaoImpl(); customFooDaoImpl.setEntityManager(entityManager); } }
异常信息
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Schema "my_database" not found; ...
问题原因
你的原生SQL中硬编码了my_database这个schema名称,而H2测试数据库默认不会创建该schema,也不会自动切换到该schema下执行SQL。标准JpaRepository的方法使用JPQL,会自动适配测试环境的数据源,因此不会出现这个问题。
解决方法
方案1:测试配置中指定默认schema并自动创建
在src/test/resources/application.properties中添加H2配置,启动时自动创建并切换到目标schema:
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS my_database spring.jpa.properties.hibernate.default_schema=my_database
方案2:测试类中手动创建schema
如果不想修改全局配置,可以在测试启动前手动执行创建schema的SQL:
@DataJpaTest class CustomFooImplTest { @Autowired private EntityManager entityManager; private CustomFooDaoImpl customFooDaoImpl; @BeforeAll static void initSchema(@Autowired DataSource dataSource) { try (Connection conn = dataSource.getConnection()) { Statement stmt = conn.createStatement(); stmt.execute("CREATE SCHEMA IF NOT EXISTS my_database"); stmt.close(); } catch (SQLException e) { throw new RuntimeException(e); } } @BeforeEach void setUp() { customFooDaoImpl = new CustomFooDaoImpl(); customFooDaoImpl.setEntityManager(entityManager); } // 测试方法编写... }
方案3:改造DAO,避免硬编码schema
将schema名称抽为配置项,适配不同环境:
- 在生产环境配置(
src/main/resources/application.properties):
app.db.schema=my_database
- 在测试环境配置(
src/test/resources/application.properties):
app.db.schema=PUBLIC
- 修改自定义DAO实现:
public class CustomFooDaoImpl implements CustomFooDao { @PersistenceContext private EntityManager entityManager; @Value("${app.db.schema}") private String dbSchema; public List<Foo> query() { String someFancyQueryLiteral = String.format("SELECT * FROM %s.foo WHERE ...", dbSchema); // 注意:如果要返回实体类,需要指定实体类型 return entityManager.createNativeQuery(someFancyQueryLiteral, Foo.class).getResultList(); } }
方案4:直接注入完整的FooDao(推荐)
Spring Data JPA会自动代理实现FooDao接口,包含自定义扩展部分,无需手动实例化CustomFooDaoImpl:
@DataJpaTest class CustomFooImplTest { @Autowired private FooDao fooDao; @Test void testQuery() { List<Foo> result = fooDao.query(); // 编写断言逻辑 } }
结合方案1或3的schema配置,即可正常测试。
内容的提问来源于stack exchange,提问作者Daniel Pop
相关产品推荐
相关产品推荐

