Skip to content

Error retrieving database id results in invalid bound statement when statements are executed #3040

Closed
@p91paul

Description

@p91paul

Here's what happened to me:

  1. During application startup, connecting to database fails
  2. SqlSessionFactoryBean configures mappers and tries to determine the databaseId to parse mappers xml https://github.com/mybatis/spring/blob/d081a644e12eb036652e010037fd1bf84e398d7c/src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java#L664
  3. Finding databaseId fails: in case of failure, VendorDatabaseIdProvider returns null, not a SqlException
  4. VendorDatabaseIdProvider logs the error, then SqlSessionFactoryBean continues and does not bind all statements with a databaseId="xxx" attribute
  5. The application starts up
  6. Database connection is resumed
  7. Several hours, or days, later, the application runs the rarely-used database-specific statement that could not be bound (ok this is oddly specific, but very important: this specific fault happened several times in production, but it was very difficult debugging it because the revealing log from VendorDatabaseIdProvider was much earlier than the runtime bug)
  8. The statement cannot be executed due to org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):

Proposed solutions:

  • When databaseId is set on a statement, resolve the correct statement when the query is executed instead of during application startup, using the currently live connection instead of the possibly different one used during startup. This would defer binding and avoid situations where the application starts up despite the database being down but then is unable to execute queries.
  • If VendorDatabaseIdProvider returns null, fail (or fix VendorDatabaseIdProvider to not return null on exception)
  • Probably the safest and easiest: if all candidates for binding a statement declared in a mapper interface require a specific databaseId, but no databaseId is available, fail immediately instead of silently not binding the statement

Current workaround: instead of the default VendorDatabaseIdProvider, we decided to inject a subclass where we override getDatabaseId; if super.getDatabaseId() returns null, we throw an exception. This way the application fails and our application orchestrator restarts the application (until the database becomes available again).

Steps to reproduce in attached demo project:

  1. Run the application with the spring boot "h2" profile -> "X" is logged by the DemoCommand
  2. Run the application with the spring boot "oracle" profile -> Invalid bound statement is thrown (the jdbc url for oracle is willingly bogus, to simulate a connection failure)

demo-mybatis-invalidbound.zip

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions