001package io.ebean;
002
003import io.avaje.lang.NonNullApi;
004import io.avaje.lang.Nullable;
005import java.io.Serializable;
006import java.math.BigDecimal;
007import java.util.List;
008import java.util.Optional;
009import java.util.function.Consumer;
010import java.util.function.Predicate;
011
012/**
013 * Query object for performing native SQL queries that return SqlRow or directly read
014 * ResultSet using a RowMapper.
015 * <p>
016 * The returned SqlRow objects are similar to a LinkedHashMap with some type
017 * conversion support added.
018 * </p>
019 * <p>
020 * Refer to {@link DtoQuery} for native sql queries returning DTO beans.
021 * </p>
022 * <p>
023 * Refer to {@link Database#findNative(Class, String)} for native sql queries returning entity beans.
024 * </p>
025 *
026 * <pre>{@code
027 *
028 *   // example using named parameters
029 *
030 *   String sql = "select id, name from customer where name like :name and status_code = :status";
031 *
032 *   List<SqlRow> list =
033 *     DB.sqlQuery(sql)
034 *       .setParameter("name", "Acme%")
035 *       .setParameter("status", "ACTIVE")
036 *       .findList();
037 *
038 * }</pre>
039 */
040@NonNullApi
041public interface SqlQuery extends Serializable, CancelableQuery {
042
043  /**
044   * Execute the query returning a list.
045   */
046  List<SqlRow> findList();
047
048  /**
049   * Execute the SqlQuery iterating a row at a time.
050   * <p>
051   * This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
052   * </p>
053   */
054  void findEach(Consumer<SqlRow> consumer);
055
056  /**
057   * Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through.
058   * <p>
059   * Returning false after processing a row stops the iteration through the query results.
060   * </p>
061   * <p>
062   * This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
063   * </p>
064   */
065  void findEachWhile(Predicate<SqlRow> consumer);
066
067  /**
068   * Execute the query returning a single row or null.
069   * <p>
070   * If this query finds 2 or more rows then it will throw a
071   * PersistenceException.
072   * </p>
073   */
074  @Nullable
075  SqlRow findOne();
076
077  /**
078   * Deprecated migrate to use {@link #mapTo(RowMapper)}
079   */
080  @Deprecated
081  <T> T findOne(RowMapper<T> mapper);
082
083  /**
084   * Deprecated migrate to use {@link #mapTo(RowMapper)}
085   */
086  @Deprecated
087  <T> List<T> findList(RowMapper<T> mapper);
088
089  /**
090   * Execute the query reading each row from ResultSet using the RowConsumer.
091   * <p>
092   * This provides a low level option that reads directly from the JDBC ResultSet
093   * and is good for processing very large results where (unlike findList) we don't
094   * hold all the results in memory but instead can process row by row.
095   * </p>
096   *
097   * <pre>{@code
098   *
099   *  String sql = "select id, name, status from customer order by name desc";
100   *
101   *  DB.sqlQuery(sql)
102   *    .findEachRow((resultSet, rowNum) -> {
103   *
104   *      // read directly from ResultSet
105   *
106   *      long id = resultSet.getLong(1);
107   *      String name = resultSet.getString(2);
108   *
109   *      // do something interesting with the data
110   *
111   *    });
112   *
113   * }</pre>
114   *
115   * @param consumer Used to read and process each ResultSet row.
116   */
117  void findEachRow(RowConsumer consumer);
118
119  /**
120   * Execute the query returning an optional row.
121   */
122  Optional<SqlRow> findOneOrEmpty();
123
124  /**
125   * Deprecated - migrate to <code>.mapToScalar(attributeType).findOne()</code>.
126   * <pre>{@code
127   *
128   *    .mapToScalar(BigDecimal.class)
129   *    .findOne();
130   * }
131   */
132  @Deprecated
133  <T> T findSingleAttribute(Class<T> attributeType);
134
135  /**
136   * Deprecated - migrate to <code>.mapToScalar(BigDecimal.class).findOne()</code>.
137   * <pre>{@code
138   *
139   *    .mapToScalar(BigDecimal.class)
140   *    .findOne();
141   * }
142   */
143  @Deprecated
144  BigDecimal findSingleDecimal();
145
146  /**
147   * Deprecated - migrate to <code>.mapToScalar(Long.class).findOne()</code>.
148   * <pre>{@code
149   *
150   *    .mapToScalar(Long.class)
151   *    .findOne();
152   * }
153   */
154  @Deprecated
155  Long findSingleLong();
156
157  /**
158   * Deprecated - migrate to <code>.mapToScalar(Long.class).findList()</code>.
159   * <pre>{@code
160   *
161   *    .mapToScalar(Long.class)
162   *    .findList();
163   * }
164   */
165  @Deprecated
166  <T> List<T> findSingleAttributeList(Class<T> attributeType);
167
168  /**
169   * Set one of more positioned parameters.
170   * <p>
171   * This is a convenient alternative to multiple calls to {@link #setParameter(Object)}.
172   *
173   * <pre>{@code
174   *
175   *   String sql = "select id, name from customer where name like ? and status = ?";
176   *
177   *   List<SqlRow> list =
178   *     DB.sqlQuery(sql)
179   *       .setParameters("Rob", Status.NEW)
180   *       .findList();
181   *
182   *
183   *   // effectively the same as ...
184   *
185   *       .setParameter("Rob")
186   *       .setParameter("Status.NEW)
187   *
188   *   // and ...
189   *
190   *       .setParameter(1, "Rob")
191   *       .setParameter(2, "Status.NEW)
192   *
193   * }</pre>
194   */
195  SqlQuery setParameters(Object... values);
196
197  /**
198   * Deprecated migrate to setParameters(Object... values)
199   */
200  @Deprecated
201  SqlQuery setParams(Object... values);
202
203  /**
204   * Set the next bind parameter by position.
205   * <pre>{@code
206   *
207   *   String sql = "select id, name from customer where name like ? and status = ?";
208   *
209   *   List<SqlRow> list =
210   *     DB.sqlQuery(sql)
211   *       .setParameter("Rob")
212   *       .setParameter("Status.NEW)
213   *       .findList();
214   *
215   *   // the same as ...
216   *
217   *       .setParameters("Rob", Status.NEW)
218   *
219   *   // and ...
220   *
221   *       .setParameter(1, "Rob")
222   *       .setParameter(2, "Status.NEW)
223   *
224   * }</pre>
225   *
226   * <p>
227   * When binding a collection of values into a IN expression we should use
228   * indexed parameters like ?1, ?2, ?3 etc rather than just ?.
229   * </p>
230   *
231   * <pre>{@code
232   *
233   *   String sql = "select c.id, c.name from customer c where c.name in (?1)";
234   *
235   *   List<SqlRow> rows = DB.sqlQuery(sql)
236   *       .setParameter(asList("Rob", "Fiona", "Jack"))
237   *       .findList();
238   *
239   *
240   *   List<SqlRow> rows = DB.sqlQuery(sql)
241   *       .setParameter(1, asList("Rob", "Fiona", "Jack"))
242   *       .findList();
243   * }</pre>
244   *
245   * @param value The value to bind
246   */
247  SqlQuery setParameter(Object value);
248
249  /**
250   * Set a null parameter by position.
251   */
252  SqlQuery setNullParameter(int position, int jdbcType);
253
254  /**
255   * Set a null parameter by name.
256   */
257  SqlQuery setNullParameter(String name, int jdbcType);
258
259  /**
260   * Bind the parameter by its index position (1 based like JDBC).
261   * <p>
262   * When binding a collection of values into a IN expression we should use
263   * indexed parameters like ?1, ?2, ?3 etc rather than just ?.
264   * </p>
265   *
266   * <pre>{@code
267   *
268   *   String sql = "select c.id, c.name from customer c where c.name in (?1)";
269   *
270   *   List<SqlRow> rows = DB.sqlQuery(sql)
271   *       .setParameter(asList("Rob", "Fiona", "Jack"))
272   *       .findList();
273   *
274   *
275   *   List<SqlRow> rows = DB.sqlQuery(sql)
276   *       .setParameter(1, asList("Rob", "Fiona", "Jack"))
277   *       .findList();
278   * }</pre>
279   */
280  SqlQuery setParameter(int position, Object value);
281
282  /**
283   * Bind the named parameter value.
284   */
285  SqlQuery setParameter(String name, Object value);
286
287  /**
288   * Set the index of the first row of the results to return.
289   */
290  SqlQuery setFirstRow(int firstRow);
291
292  /**
293   * Set the maximum number of query results to return.
294   */
295  SqlQuery setMaxRows(int maxRows);
296
297  /**
298   * Set a timeout on this query.
299   * <p>
300   * This will typically result in a call to setQueryTimeout() on a
301   * preparedStatement. If the timeout occurs an exception will be thrown - this
302   * will be a SQLException wrapped up in a PersistenceException.
303   * </p>
304   *
305   * @param secs the query timeout limit in seconds. Zero means there is no limit.
306   */
307  SqlQuery setTimeout(int secs);
308
309  /**
310   * Set a label that can be put on performance metrics that are collected.
311   */
312  SqlQuery setLabel(String label);
313
314  /**
315   * A hint which for JDBC translates to the Statement.fetchSize().
316   * <p>
317   * Gives the JDBC driver a hint as to the number of rows that should be
318   * fetched from the database when more rows are needed for ResultSet.
319   * </p>
320   */
321  SqlQuery setBufferFetchSizeHint(int bufferFetchSizeHint);
322
323  /**
324   * The query result maps to a single scalar value like Long, BigDecimal,
325   * String, UUID, OffsetDateTime etc.
326   * <p>
327   * Any scalar type Ebean is aware of can be used including java time
328   * types like Instant, LocalDate, OffsetDateTime, UUID, Inet, Cdir etc.
329   *
330   * <pre>{@code
331   *
332   *   String sql = " select min(updtime) from o_order_detail " +
333   *                " where unit_price > ? and updtime is not null ";
334   *
335   *   OffsetDateTime minCreated = DB.sqlQuery(sql)
336   *     .setParameter(42)
337   *     .mapToScalar(OffsetDateTime.class)
338   *     .findOne();
339   *
340   * }</pre>
341   *
342   * @param attributeType The type the result is returned as
343   * @return The query to execute via findOne() findList() etc
344   */
345  <T> TypeQuery<T> mapToScalar(Class<T> attributeType);
346
347  /**
348   * Use a RowMapper to map the result to beans.
349   *
350   * @param mapper Maps rows to beans
351   * @param <T>    The type of beans mapped to
352   * @return The query to execute by findOne() findList() etc
353   */
354  <T> TypeQuery<T> mapTo(RowMapper<T> mapper);
355
356  /**
357   * Query mapping to single scalar values.
358   *
359   * @param <T> The type of the scalar values
360   */
361  interface TypeQuery<T> {
362
363    /**
364     * Return the single value.
365     */
366    @Nullable
367    T findOne();
368
369    /**
370     * Return the single value that is optional.
371     */
372    Optional<T> findOneOrEmpty();
373
374    /**
375     * Return the list of values.
376     */
377    List<T> findList();
378
379    /**
380     * Find streaming the result effectively consuming a row at a time.
381     */
382    void findEach(Consumer<T> consumer);
383  }
384}