Skip to content

JDBC Select

@JdbcSelect defines a JDBC method that executes a SQL select statement. The generated method creates a PreparedStatement, binds Java method parameters, applies statement configuration, executes the query, and maps the ResultSet to the declared Java return type.

The annotation value must contain a valid SQL select statement with named parameters. Parameter binding is defined in parameter binding. Result values are mapped as defined in result mapping. Converter lookup is defined in converter lookup. Result nullness follows the Nullness Contract.

❗️Select methods

@JdbcSelect methods must not return void. The return type must be one of these forms:

  • a single primitive, column Java type, or row Java type
  • JdbcRow<T>
  • List<T>
  • Stream<T>
  • JdbcIterable<T>
  • JdbcResultSet<T>

For multi-row return forms, T may be a primitive, a column Java type, or a row Java type.

No rows and SQL-NULL are separate cases.

For single-value results, @JdbcNoRows only handles the case where the ResultSet contains no row. If a row exists and the selected column value is SQL-NULL, the mapped Java type decides whether null, Optional.empty(), or an exception is produced. Those rules are part of result mapping and the Nullness Contract.

For @JdbcNoRows(RETURN_NULL), Optional<T> is invalid for direct single-value returns. Optional.empty() is already the result for an existing row whose selected column value maps from SQL-NULL, so it cannot also represent “no row” without losing information.

❗️Single value results

A single-value result reads at most one row from the ResultSet. The mapped value may be a primitive, a column Java type, or a row Java type.

For a column Java type, the first result column is used by default. @JdbcName on the JDBC method selects a named result column instead. @JdbcName on a row Java type return is unused and therefore invalid.

@JdbcConverterName on the JDBC method selects a named JDBC-to-Java result converter.

@JdbcNoRows defines what happens when the ResultSet contains no row.

  • @JdbcNoRows(THROW_EXCEPTION) throws JdbcEmptyResultSetException when no row exists.
  • @JdbcNoRows(RETURN_NULL) returns null when no row exists. The direct return type must be unspecified or @Nullable; @NonNull and Optional<T> are invalid.

@JdbcNoMoreRows defines what happens when more than one row exists.

  • @JdbcNoMoreRows(THROW_EXCEPTION) throws JdbcUnexpectedRowException.
  • @JdbcNoMoreRows(IGNORE) ignores additional rows.

The default behaviour is @JdbcNoRows(THROW_EXCEPTION) and @JdbcNoMoreRows(THROW_EXCEPTION).

JdbcRow<T> models the row boundary in the return type. The outer return type models whether a row was found. The type parameter T models the mapped value of the selected column.

Supported outer return forms are:

  • JdbcRow<T>
  • @Nullable JdbcRow<T>
  • Optional<JdbcRow<T>>

@NonNull JdbcRow<T> is invalid.

Supported type-parameter forms are:

  • unspecified T
  • @Nullable T
  • @NonNull T
  • Optional<T>

The no-row semantics are:

  • JdbcRow<T> and @Nullable JdbcRow<T> return null
  • Optional<JdbcRow<T>> returns Optional.empty()

When a row exists, the method returns a JdbcRow<T> that contains the mapped column value.

❗️List results ❗️Stream results ❗️JdbcIterable results ❗️JdbcResultSet results

Multi-row results map every row of the ResultSet. The result element may be a primitive, a column Java type, or a row Java type.

For a column Java type, the first result column is used by default. @JdbcName on the JDBC method selects a named result column instead. @JdbcName on a row Java type return is unused and therefore invalid.

The element type must be unspecified or @NonNull when it is a row Java type. @Nullable row elements and Optional row elements are invalid. Column Java type elements may use Optional<T> to represent SQL-NULL column values.

@JdbcNoRows and @JdbcNoMoreRows are not supported on multi-row result methods. An empty ResultSet produces an empty multi-row result.

The supported multi-row return forms are:

  • List<T> collects all mapped rows and closes JDBC resources before returning.
  • Stream<T> returns a stream over mapped rows and keeps JDBC resources open until the stream is closed.
  • JdbcIterable<T> returns an Iterable<T> with AutoCloseable support.
  • JdbcResultSet<T> returns a result-set-style wrapper over mapped rows.

List<T> materialises the full result before returning. Use it for bounded result sets. For large or unbounded result sets, prefer Stream<T>, JdbcIterable<T>, or JdbcResultSet<T>. Use @JdbcMaxRows or a database-specific SQL limit such as LIMIT, FETCH FIRST, or TOP when the maximum row count is part of the method contract.