具有本机查询的存储库在测试环境中失败 - postgres、jpa、spring

Repositories with native queries fail in test environment - postgres, jpa, spring

我已经使用测试容器为 spring 引导项目设置了集成测试(使用 postgresql 设置了 docker 实例)。如果我正在测试的存储库不使用本机查询,则测试效果很好。但是,每当存储库包含本机查询时,我都会收到以下错误:ERROR: relation "my_table_here" does not exist。如何让我的测试配置工作以允许本机查询?

下面是我的测试设置:

@RunWith(SpringRunner.class)
public class TestPostgresql {

    @ClassRule
    public static PostgreSQLContainer postgreSQLContainer = PostgresDbContainer.getInstance();

    /**
     * ************ REPOSITORIES ************
     */
    @Autowired
    NativeQueryRepository nativeQueryRepository;

    @TestConfiguration
    @EnableJpaAuditing
    @EnableJpaRepositories(
            basePackageClasses = {
                    NativeQueryRepository.class
            })
    @ComponentScan(
            basePackages = {
                    "com.company.project.package.repository"
            }
    )
    static class PostgresConfiguration {

        /**
         * ************ DATABASE SETUP ************
         */
        @Bean
        public DataSource dataSource() {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setUrl(postgreSQLContainer.getJdbcUrl());
            dataSource.setUsername(postgreSQLContainer.getUsername());
            dataSource.setPassword(postgreSQLContainer.getPassword());
            return dataSource;
        }

        @Bean
        public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
            HibernateJpaVendorAdapter vendorAdapter = new JpaVendorAdapter();
            vendorAdapter.setDatabase(Database.POSTGRESQL);
            vendorAdapter.setGenerateDdl(true);

            LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
            factory.setJpaVendorAdapter(vendorAdapter);
            factory.setPackagesToScan("com.company.project");
            factory.setDataSource(dataSource());
            return factory;
        }

        @Bean
        public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
            JpaTransactionManager txManager = new JpaTransactionManager();
            txManager.setEntityManagerFactory(entityManagerFactory);
            return txManager;
        }
    }
}

编辑:我认为这与命名策略有关?

这里有一个关于如何在存储库中使用 nativeQuery 的示例

@Repository
public interface NativeQueryRepository extends JpaRepository<NativeEvent, Long> {

    @Modifying
    @Transactional
    @Query(value = "UPDATE native_event SET state = :state " +
                    "WHERE secondary_id = :secondaryId", nativeQuery = true)
    void updateState(
            @Param("state") String state,
            @Param("secondaryId") String secondaryId);

}

我还尝试通过添加注释更新 TestPostgresql 内静态 class 的 testProperties:

@TestPropertySource(properties = {
            "spring.jpa.hibernate.naming-strategy=org.springframework.boot.orm.jpa.SpringNamingStrategy"
    })

但是,收到的错误没有任何变化。

编辑:添加 NativeEvent:

@Entity
@Table(
        name = "NativeEvent",
        indexes = {
                @Index(name = "idx_native_event_secondary_id", columnList = "secondaryId")
        }
)
@EntityListeners(AuditingEntityListener.class)
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class NativeEvent implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name="secondaryId", nullable=false)
    private String secondaryId;

    @Column(name="state")
    private String state;
}

您明确指定您的 table 名称,如下所示:

@Table(name = "NativeEvent")

但在您的本机查询中您有不同的名称 table:

 @Query(value = "UPDATE native_event ...)

从您的 @Table 注释中删除 name 属性(假设您的命名策略会产生类似 native_event 的名称)或更改 table 本机查询中的名称为 nativeeventnativeEvent 所以在这种情况下只需删除下划线。

有点相关post

您正在进行手动配置,而不是使用运行时配置。因此,命名策略的处理方式不同。相反,您应该重用相同的配置而不是编写自己的配置。

使用 @SpringBootTest@DataJpaTest,并且仅 re-configure DataSource

ApplicationContextInitializer 做一些事情,将 JDBC 属性放入 ApplicationContext

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(initializers = {TestPostgresql.JdbcInitializer.class})
public class TestPostgresql {

    @ClassRule
    public static PostgreSQLContainer postgreSQLContainer = PostgresDbContainer.getInstance();

    /**
     * ************ REPOSITORIES ************
     */
    @Autowired
    NativeQueryRepository nativeQueryRepository;

    static class JdbcInitializer
      implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
            TestPropertyValues.of(
              "spring.datasource.url=" + postgreSQLContainer.getJdbcUrl(),
              "spring.datasource.username=" + postgreSQLContainer.getUsername(),
              "spring.datasource.password=" + postgreSQLContainer.getPassword()
            ).applyTo(configurableApplicationContext.getEnvironment());
        }
    }    
}

这将在您的测试中重用运行时的配置。除了 @SpringBootTest,您还应该能够使用 @DataJpaTest(NativeQueryRepository.class) 来仅对 JPA 进行切片测试。