Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Spring 测试因@Scheduled Task:JdbcSQLSyntaxErrorException表“而失败;用户“U账户”创建“U事件”;找不到 总结&第一个问题_Spring_Hibernate_Spring Boot_Spring Data Jpa_Spring Test - Fatal编程技术网

Spring 测试因@Scheduled Task:JdbcSQLSyntaxErrorException表“而失败;用户“U账户”创建“U事件”;找不到 总结&第一个问题

Spring 测试因@Scheduled Task:JdbcSQLSyntaxErrorException表“而失败;用户“U账户”创建“U事件”;找不到 总结&第一个问题,spring,hibernate,spring-boot,spring-data-jpa,spring-test,Spring,Hibernate,Spring Boot,Spring Data Jpa,Spring Test,我正在尝试测试我的用户注册机制。通过我的REST API创建新用户帐户时,数据库中会存储一个UserAccountCreatedEvent。计划任务每5秒检查一次数据库,查看是否有新的UserAccountCreatedEvents,如果有,则向注册用户发送电子邮件。在运行测试时,我遇到了一个问题,即找不到UserAccountCreatedEvent的表(请参见下面的异常)。我曾经在服务方法中以阻塞方式发送电子邮件,但最近我切换到了这种异步方法。我所有的测试对于阻塞方法都非常有效,对于异步方法

我正在尝试测试我的用户注册机制。通过我的REST API创建新用户帐户时,数据库中会存储一个
UserAccountCreatedEvent
。计划任务每5秒检查一次数据库,查看是否有新的
UserAccountCreatedEvent
s,如果有,则向注册用户发送电子邮件。在运行测试时,我遇到了一个问题,即找不到
UserAccountCreatedEvent
的表(请参见下面的异常)。我曾经在服务方法中以阻塞方式发送电子邮件,但最近我切换到了这种异步方法。我所有的测试对于阻塞方法都非常有效,对于异步方法,我唯一改变的就是在测试中包含
waitibility

2019-04-23 11:24:51.605 ERROR 7968 --- [taskScheduler-1] o.s.s.s.TaskUtils$LoggingErrorHandler    : Unexpected error occurred in scheduled task.

org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [select useraccoun0_.id as id1_0_, useraccoun0_.completed_at as complete2_0_, useraccoun0_.created_at as created_3_0_, useraccoun0_.in_process_since as in_proce4_0_, useraccoun0_.status as status5_0_, useraccoun0_.user_id as user_id1_35_ from user_account_created_event useraccoun0_ where useraccoun0_.status=? order by useraccoun0_.created_at asc limit ?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement

Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException:
Table "USER_ACCOUNT_CREATED_EVENT" not found; SQL statement:
select useraccoun0_.id as id1_0_, useraccoun0_.completed_at as complete2_0_, useraccoun0_.created_at as created_3_0_, useraccoun0_.in_process_since as in_proce4_0_, useraccoun0_.status as status5_0_, useraccoun0_.user_id as user_id1_35_ from user_account_created_event useraccoun0_ where useraccoun0_.status=? order by useraccoun0_.created_at asc limit ? [42102-199]


第二个问题 似乎这还不够,在调试模式下运行测试时,测试的行为完全不同。当我在方法中设置断点时,该方法调用的断点被注释为
@Scheduled
,它会被调用多次,尽管
@Scheduled
配置了5000ms的
fixedDelayString
(固定延迟)。多亏了日志记录,我甚至可以看到发送了几封邮件。不过,我的测试SMTP服务器(
GreenMail
)没有收到任何电子邮件。这怎么可能呢?我特意将事务隔离设置为
isolation.SERIALIZABLE
,这样就不可能(就我所理解的事务隔离而言)两个计划的方法从数据库访问同一事件


第三个问题 最重要的是,当我重新运行失败的测试时,它们是有效的。但是,控制台上存在不同的异常(请参见下文)。但是,应用程序启动,测试成功完成。根据我是否运行所有测试、是否只运行类、是否只运行方法以及是否重新运行失败的测试,会有不同的测试结果。我不明白这种不确定的行为怎么可能发生

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Failed to scan classpath for unlisted entity classes

Caused by: java.nio.channels.ClosedByInterruptException: null


我的代码 测试类(
UserRegistrationTest
) 计划方法(
RegistrationTask
)调用的代码
UserAccountCreatedEvent

application.yml
我不熟悉Spring的日程安排,因此非常感谢您的帮助

@ActiveProfiles("test")
@AutoConfigureMockMvc
@RunWith(SpringRunner.class)
@SpringBootTest
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class UserRegistrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private Routes routes;

    @Autowired
    private TestConfig testConfig;

    @Resource(name = "validCustomerDTO")
    private CustomerDTO validCustomerDTO;

    @Resource(name = "validVendorDTO")
    private VendorRegistrationDTO validVendorRegistrationDTO;

    @Value("${schedule.sendRegistrationConfirmationEmailTaskDelay}")
    private Short registrationConfirmationEmailSenderTaskDelay;

    private GreenMail smtpServer;

    // Setup & tear down

    @Before
    public void setUp() {
        smtpServer = testConfig.getMailServer();
        smtpServer.start();
    }

    @After
    public void tearDown() {
        smtpServer.stop();
    }

    // Tests

    @Test
    public void testCreateCustomerAccount() throws Exception {
        mockMvc.perform(
            post(routes.getCustomerPath())
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .content(objectMapper.writeValueAsString(validCustomerDTO)))
            .andExpect(status().isCreated());

        // When run normally, I get a timeout from the next line
        await().atMost(registrationConfirmationEmailSenderTaskDelay + 10000, MILLISECONDS).until(smtpServerReceivedOneEmail());

        // Verify correct registration confirmation email was sent
        MimeMessage[] receivedMessages = smtpServer.getReceivedMessages();
        assertThat(receivedMessages).hasSize(1);

        // other checks
        // ...
    }

    @Test
    public void testCreateVendorAccount() throws Exception {
        mockMvc.perform(
            post(routes.getVendorPath())
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .content(objectMapper.writeValueAsString(validVendorRegistrationDTO)))
            .andExpect(status().isCreated());

        // When run normally, I get a timeout from the next line
        await().atMost(registrationConfirmationEmailSenderTaskDelay + 10000, MILLISECONDS).until(smtpServerReceivedOneEmail());

        // Verify correct registration confirmation email was sent
        MimeMessage[] receivedMessages = smtpServer.getReceivedMessages();
        assertThat(receivedMessages).hasSize(1);

        // other checks
        // ...
    }

    // Helper methods

    private Callable<Boolean> smtpServerReceivedOneEmail() {
        return () -> smtpServer.getReceivedMessages().length == 1;
    }

    // Test configuration

    @TestConfiguration
    static class TestConfig {

        private static final int PORT = 3025;
        private static final String HOST = "localhost";
        private static final String PROTOCOL = "smtp";

        GreenMail getMailServer() {
            return new GreenMail(new ServerSetup(PORT, HOST, PROTOCOL));
        }

        @Bean
        public JavaMailSender javaMailSender() {
            JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
            javaMailSender.setHost(HOST);
            javaMailSender.setPort(PORT);
            javaMailSender.setProtocol(PROTOCOL);
            javaMailSender.setDefaultEncoding("UTF-8");
            return javaMailSender;
        }
    }
@Component
public class BusinessTaskScheduler {

    private final RegistrationTask registrationTask;

    @Autowired
    public BusinessTaskScheduler(RegistrationTask registrationTask) {
        this.registrationTask = registrationTask;
    }

    @Scheduled(fixedDelayString = "${schedule.sendRegistrationConfirmationEmailTaskDelay}")
    public void sendRegistrationConfirmationEmail() {
        registrationTask.sendRegistrationConfirmationEmail();
    }
}
@Component
@Transactional(isolation = Isolation.SERIALIZABLE)
public class RegistrationTask {

    private final EmailHelper emailHelper;
    private final EventService eventService;
    private final UserRegistrationService userRegistrationService;

    @Autowired
    public RegistrationTask(EmailHelper emailHelper, EventService eventService, UserRegistrationService userRegistrationService) {
        this.emailHelper = emailHelper;
        this.eventService = eventService;
        this.userRegistrationService = userRegistrationService;
    }

    public void sendRegistrationConfirmationEmail() {
        Optional<UserAccountCreatedEvent> optionalEvent = eventService.getOldestUncompletedUserAccountCreatedEvent();
        if (optionalEvent.isPresent()) {
            UserAccountCreatedEvent event = optionalEvent.get();
            User user = event.getUser();
            RegistrationVerificationToken token = userRegistrationService.createRegistrationVerificationTokenForUser(user);
            emailHelper.sendRegistrationConfirmationEmail(token);
            eventService.completeEvent(event);
        }
    }
}
@Service
@Transactional(isolation = Isolation.SERIALIZABLE)
public class EventServiceImpl implements EventService {

    private final ApplicationEventDAO applicationEventDAO;
    private final UserAccountCreatedEventDAO userAccountCreatedEventDAO;

    @Autowired
    public EventServiceImpl(ApplicationEventDAO applicationEventDAO, UserAccountCreatedEventDAO userAccountCreatedEventDAO) {
        this.applicationEventDAO = applicationEventDAO;
        this.userAccountCreatedEventDAO = userAccountCreatedEventDAO;
    }

    @Override
    public void completeEvent(ApplicationEvent event) {
        if (!event.getStatus().equals(COMPLETED) && Objects.isNull(event.getCompletedAt())) {
            event.setStatus(COMPLETED);
            event.setCompletedAt(LocalDateTime.now());
            applicationEventDAO.save(event);
        }
    }

    @Override
    public Optional<UserAccountCreatedEvent> getOldestUncompletedUserAccountCreatedEvent() {
        Optional<UserAccountCreatedEvent> optionalEvent = userAccountCreatedEventDAO.findFirstByStatusOrderByCreatedAtAsc(NEW);
        if (optionalEvent.isPresent()) {
            UserAccountCreatedEvent event = optionalEvent.get();
            setEventInProcess(event);
            return Optional.of(userAccountCreatedEventDAO.save(event));
        }
        return Optional.empty();
    }

    @Override
    public void publishEvent(ApplicationEvent event) {
        applicationEventDAO.save(event);
    }

    // Helper methods

    private void setEventInProcess(ApplicationEvent event) {
        event.setStatus(Status.IN_PROCESS);
        event.setInProcessSince(LocalDateTime.now());
    }
}
schedule:
  sendRegistrationConfirmationEmailTaskDelay: 5000 # delay between tasks in milliseconds