简体   繁体   English

Spring JDBCTemplate 与 Hikari 数据源批量更新行为异步

[英]Spring JDBCTemplate with Hikari datasource batch update behaving asynchronously

I'm working on a webapp where I display some information that the user can act on.我正在开发一个 web 应用程序,我在其中显示一些用户可以采取行动的信息。 After taking action the list should update and reflect those changes in the webpage.采取行动后,列表应更新并在网页中反映这些更改。 I've got all the individual pieces working, but connecting them together is causing issues.我已经让所有单独的部分都在工作,但是将它们连接在一起会导致问题。

Here's essentially the retrieval code:这里基本上是检索代码:

public List<Entry> findEntriesForIdByStatus(Long id, Status status) {

    MapSqlParameterSource paramSource = new MapSqlParameterSource();

    paramSource.addValue("id", id);

    if (null == status) {
        return template.query(FIND_ALL_ENTRIES_QUERY, paramSource, entryResultSetExtractor);
    }

    paramSource.addValue("status", status.getCode());

    List<Entry> entryWithStatus = springJdbcTemplate.query(FIND_ENTRIES_FOR_STATUS_QUERY, 
            paramSource,
            entryResultSetExtractor);

    return equipmentWithStatus;
}

And the update code:和更新代码:

@Transactional("myTransactionManager")
public void expire(CustomObject customObj) {
    Timestamp expirationTime = Timestamp.valueOf(dateTimeFactory.now());

    Long id = customObj.getId();
    List<Entry> entryList = customObj.getEntryList();

    SqlParameterSource[] params = new MapSqlParameterSource[entryList.size()];
    for (int i = 0; i < entryList.size(); i++) {
        Entry entry = entryList.get(i);
        MapSqlParameterSource paramSource = new MapSqlParameterSource("id", id)
                .addValue("fieldA", entry.getFieldA())
                .addValue("fieldB", entry.getFieldB())
                .addValue("expirationTime", expirationTime);
        params[i] = paramSource;
    }

    springJdbcTemplate.batchUpdate(EXPIRE_QUERY, params);
}

The template is is a SpringJDBCNamedParameterTemplate that's being managed by Spring Boot and the datasource is an instance of com.zaxxer.hikari.HikariDataSource.模板是由 Spring Boot 管理的 SpringJDBCNamedParameterTemplate,数据源是 com.zaxxer.hikari.HikariDataSource 的实例。

The retrieval method is called upon page load and returning the correct data.在页面加载并返回正确数据时调用检索方法。 When running the expire method, the correct records are expired, and both methods execute successfully.运行 expire 方法时,正确的记录过期,两个方法都执行成功。 Initially I was chaining them on the front end using an excludeMap (rxjs/angular 7), but it was being inconsistent.最初我使用 excludeMap (rxjs/angular 7) 将它们链接在前端,但它不一致。 Sometimes the retrieve after expire would return as though the expire hadn't happened, and sometimes it would.有时,过期后的检索会返回,就好像过期没有发生一样,有时会。 However, regardless of outcome, if I refreshed the page, the results would be empty, as expected.但是,无论结果如何,如果我刷新页面,结果将是空的,正如预期的那样。 I switched it to chaining the calls on the back end to see if it would help at all, and it started returning the results as though the expire hadn't happened all the time, but upon page refresh, it worked as expected.我将其切换为在后端链接调用以查看它是否有帮助,并且它开始返回结果,就好像过期并没有一直发生一样,但是在页面刷新时,它按预期工作。

Here's the chaining on the backend:这是后端的链接:

public @ResponseBody List<Entry> expireEntries(@RequestBody CustomObj customObj) {
    entryService.expire(customObj, WebUtil.getCurrentUser());
    System.out.println(LocalDateTime.now().toString() + ": about to retrieve");
    List<Entry> entries = entryService.findEntriesByStatus(customObj.getId(), Status.NA);
    System.out.println(LocalDateTime.now().toString() + ": it's done retrieving");
    return equips;
}

The services pretty much just call the DAOs without much in between.这些服务几乎只是调用 DAO,中间没有太多。 The logging output is similar to below:日志记录 output 类似于以下内容:

didn't work没用

2019-09-19T13:33:50.998: about to delete 2019-09-19T13:33:50.998:即将删除
2019-09-19T13:33:51.050: It's done deleting 2019-09-19T13:33:51.050:删除完成
2019-09-19T13:33:51.246: about to retrieve 2019-09-19T13:33:51.246:即将检索
13:33:51.465 [http-nio-8080-exec-3] INFO EntryDAO - query for parent 27 with status=NA returned 364 and took 219 milliseconds 13:33:51.465 [http-nio-8080-exec-3] INFO EntryDAO - 查询父级 27,状态 = NA 返回 364,耗时 219 毫秒
13:33:51.466 [http-nio-8080-exec-3] INFO EntryService - query for parent 27 and status NA returned 364 13:33:51.466 [http-nio-8080-exec-3] INFO EntryService - 查询父级 27 和状态 NA 返回 364
2019-09-19T13:33:51.466: it's done retrieving 2019-09-19T13:33:51.466:已完成检索

worked工作过

2019-09-19T13:38:13.752: about to delete 2019-09-19T13:38:13.752:即将删除
2019-09-19T13:38:13.798: It's done deleting 2019-09-19T13:38:13.798:删除完成
2019-09-19T13:38:14.112: about to retrieve 2019-09-19T13:38:14.112:即将检索
13:38:14.120 [http-nio-8080-exec-5] INFO EntryDAO - query for parent 27 with status=NA returned 0 and took 8 milliseconds 13:38:14.120 [http-nio-8080-exec-5] INFO EntryDAO - 查询父级 27,状态 = NA 返回 0,耗时 8 毫秒
13:38:14.120 [http-nio-8080-exec-5] INFO EntryService - query for parent 27 and status NA returned 0 13:38:14.120 [http-nio-8080-exec-5] INFO EntryService - 查询父级 27 和状态 NA 返回 0
2019-09-19T13:38:14.120: it's done retrieving 2019-09-19T13:38:14.120:已完成检索

These are from when the chaining was on the front end, but after moving to the backend, it's the same, just with "It's done deleting" and "about to retrieve" closer in time, generally within a millisecond or two.这些是从前端链接的时候开始的,但是移动到后端之后,它是相同的,只是“删除完成”和“即将检索”的时间更接近,通常在一两毫秒内。 Adding a 2 second sleep between the update and the retrieval seems to fix the issue.在更新和检索之间添加 2 秒睡眠似乎可以解决问题。 I tried locking the rows for update and making the methods transactional with Spring's @Transactional annotation, but neither seemed to help.我尝试锁定行以进行更新并使用 Spring 的 @Transactional 注释使方法具有事务性,但似乎都没有帮助。

Near as I can tell, it seems like the batch update is getting triggered, but the Java method returns before the update has actually finished in the database.据我所知,似乎正在触发批量更新,但 Java 方法在数据库中实际完成更新之前返回。 So when the retrieve query executes, it gets the pre-update data.因此,当检索查询执行时,它会获取更新前的数据。 Is anyone able to confirm that this is the behavior, and if it is, is there a way to fix it without just hacking it with a Thread.sleep?有没有人能够确认这是这种行为,如果是,有没有办法修复它而不仅仅是用 Thread.sleep 破解它?

EDIT: Here's the bean definition for the transaction manager.编辑:这是事务管理器的 bean 定义。

@Bean(name = "myDbProperties")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.mydb")
public DataSourceProperties dbProperties() {
    return new DataSourceProperties();
}

@Primary
@Bean(name = "myDataSource")
@ConfigurationProperties(prefix = "spring.datasource.mydb.configuration")
public DataSource dataSource(@Qualifier("myDbProperties") DataSourceProperties dbProperties) {
    return dbProperties.initializeDataSourceBuilder().build();
}

@Bean
@Primary
@Qualifier("myTransactionManager")
public PlatformTransactionManager loadauthTransactionManager(
        @Qualifier("myDataSource") DataSource datasource) {
    return new DataSourceTransactionManager(datasource);
}

So turns out this was a case of overthinking and the solution was actually quite simple.事实证明,这是一个过度思考的案例,解决方案实际上非常简单。 The read query used sysdate between create and delete .读取查询sysdate between create and delete Turns out between is inclusive and since the queries were happening within whatever sysdate's minimum time increment results returned because sysdate = delete.结果是介于两者之间,并且因为查询发生在由于 sysdate = delete 而返回的任何 sysdate 的最小时间增量结果内。 Switching it to where systimestamp >= create and systimestamp < delete fixed the issue.将其切换到where systimestamp >= create and systimestamp < delete解决了这个问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM