Interfaces (and implementations) like JdbcOperations contain several methods, historically, that operate on an Object[]. With the introduction of varargs most of those methods now have a varargs counterpart. As Java 8 is the baseline it might be considered to deprecate the methods using an Object[] in favor of the varargs one.

This would allow for some API cleanup in for instance Spring 6.

This might apply to other templates as well (messaging, jms, ...).

Comment From: jhoeller

It's indeed primarily JdbcOperations and JdbcTemplate affected here, I couldn't find any other such cases. Anyway, I'll do this for 5.3 indeed, goes nicely together with #18474 which adds yet another few new methods to JdbcOperations (among them a varargs one but no Object[] equivalent with inverted argument signature).

I'll retain the Object[] args, int[] argTypes variants for the time being as an alternative to the varargs methods, even if they're probably better off as a PreparedStatementSetter lambda these days.

Comment From: lukaseder

I've stumbled upon this change due to the deprecation that has been introduced recently.

From an API design perspective, I'm not convinced things have been changed for the better. Of course, the benefit is that varargs can be used, in some cases, for bind variables, which is more convenient than an explicit Object[]. But now, the order of parameters changes depending on the use case, e.g.

  • <T> T query(String sql, Object[] args, int[] argTypes, ResultSetExtractor<T> rse)
  • <T> T query(String sql, ResultSetExtractor<T> rse, @Nullable Object... args)

Not just the order depends on what is being passed, but the two "highly connected" arguments (SQL string and binds) are now separated by a potentially large code block.

Maybe, a better way to achieve what seems to have been the goal here is to create an intermediate API builder style "step" to cleanly separate creation and execution of the query? That would also drastically reduce the API surface, because a lot of overloads would have to be implemented only once:

  • Query prepare(String sql, Object[] args, int[] argTypes)
  • Query prepare(String sql, @Nullable Object... args)

And then:

interface Query {
  <T> T query(ResultSetExtractor<T> rse);
  <T> Stream<T> queryForStream(RowMapper<T> rm);
  // ...
}