Search code examples
neo4jmybatisspring-mybatis

MyBatis mapper never returns when querying for inexisting label in Neo4j


I have this very simple Spring Boot 2 application with MyBatis Spring Boot starter:

@Slf4j
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Autowired
    UserMapper userMapper;

    @Bean
    public CommandLineRunner commandLineRunner() {
        return (args -> {
            final List<User> users = userMapper.getUsers();
            log.info("users: {}", users);
        });
    }

    @Bean
    @ConfigurationProperties(prefix="app.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

}

It works well when I have a User in Neo4j database. But if they aren't any, the call to getUsers never returns, no timeout, nothing happens like the app is stuck forever. Any ideas why this would happen?

EDIT: Apparently it is looping in the DefaultResultSetHandler class of MyBatis.

  private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
    ResultSet rs = stmt.getResultSet();
    while (rs == null) {
      // move forward to get the first resultset in case the driver
      // doesn't return the resultset as the first result (HSQLDB 2.1)
      if (stmt.getMoreResults()) {
        rs = stmt.getResultSet();
      } else {
        if (stmt.getUpdateCount() == -1) {
          // no more results. Must be no resultset
          break;
        }
      }
    }
    return rs != null ? new ResultSetWrapper(rs, configuration) : null;
  }

The ResultSet is null because there is no User. But stmt.getMoreResults() returns true from within Neo4jInvocationHandler:

@Override public boolean getMoreResults() throws SQLException {
        this.checkClosed();
        return !(this.currentResultSet == null && this.currentUpdateCount == -1);
    }

the currentUpdateCount is 0 because in BoltNeo4jPreparedStatement:

@Override public boolean execute() throws SQLException {
        StatementResult result = executeInternal();

        boolean hasResultSet = hasResultSet(result);
        if (hasResultSet) {
            this.currentResultSet = BoltNeo4jResultSet.newInstance(this.hasDebug(), this, result, this.resultSetParams);
            this.currentUpdateCount = -1;
        } else {
            this.currentResultSet = null;
            try {
                SummaryCounters stats = result.consume().counters();
                this.currentUpdateCount = stats.nodesCreated() + stats.nodesDeleted() + stats.relationshipsCreated() + stats.relationshipsDeleted();
            } catch (Exception e) {
                throw new SQLException(e);
            }
        }
        return hasResultSet;
    }

I think the getMoreResults implementation needs to be updated.
Maybe in MyBatis PreparedStatementHandler they should check the returned value of the execute() method.


Solution

  • This issue has now been fixed with neo4j-jdbc 3.3.1. See https://github.com/neo4j-contrib/neo4j-jdbc/issues/137.