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}