Affects: Spring Framework 5.3.3


When using SimpleJdbcInsert without usingColumns(), it tries to determine the columns from database metadata. When there is any exception, a warning is logged, but otherwise the exception is ignored:

https://github.com/spring-projects/spring-framework/blob/91509805b759a108d7eca0b6b3041c434c61d837/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/GenericCallMetaDataProvider.java#L415-L419

This is fine as long as the exception occurs before any metadata could be read, because later this will cause an exception when generating the SQL string:

https://github.com/spring-projects/spring-framework/blob/91509805b759a108d7eca0b6b3041c434c61d837/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java#L305-L306

However, when an SQLException occurs in columns.next() ...

https://github.com/spring-projects/spring-framework/blob/91509805b759a108d7eca0b6b3041c434c61d837/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/GenericCallMetaDataProvider.java#L390

... after the first column metadata has already been read and added to this.callParameterMetaData, it continues with an incomplete list of columns and only inserts these, ignoring any other columns.

We have experienced this problem in our system very sporadically, and it's especially dangerous, because SimpleJdbcInsert.execute() (as long as there are no NOT NULL constraints for the columns) returns seemingly successfully, so the transactions are committed with incomplete/corrupted data.

It seems like we can work around the problem by calling usingColumns() for all uses of SimpleJdbcInsert.

Comment From: sbrannen

SimpleJdbcTemplate no longer exists in Spring Framework 5.3. So I assume you meant to refer to SimpleJdbcInsert. I've edited this issue's title and description accordingly.

In light of that, did you perhaps mean to provide a link to GenericTableMetaDataProvider instead of GenericCallMetaDataProvider?

Or are you using SimpleJdbcCall instead of SimpleJdbcInsert?

Comment From: sbrannen

Or are you using SimpleJdbcCall instead of SimpleJdbcInsert?

In any case, it looks like GenericCallMetaDataProvider.processProcedureColumns(DatabaseMetaData, String, String, String) and GenericTableMetaDataProvider.processTableColumns(DatabaseMetaData, TableMetaData) have similar constructs that can result in partial metadata being recorded.

So I'll investigate both.

Comment From: j8zdev

Sorry for the confusion. We're actually using SimpleJdbcInsert and I accidently linked to a similar but wrong line in GenericCallMetaDataProvider instead of GenericTableMetaDataProvider. But as you noticed, probably both are affected.

Thanks for investigating!

Comment From: sbrannen

The fix has been merged into master (5.3.x) and 5.2.x.

Feel free to try it out in the latest 5.3.4 and 5.2.13 snapshots.