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 * Bind the parameter by its index position (1 based like JDBC). 251 * <p> 252 * When binding a collection of values into a IN expression we should use 253 * indexed parameters like ?1, ?2, ?3 etc rather than just ?. 254 * </p> 255 * 256 * <pre>{@code 257 * 258 * String sql = "select c.id, c.name from customer c where c.name in (?1)"; 259 * 260 * List<SqlRow> rows = DB.sqlQuery(sql) 261 * .setParameter(asList("Rob", "Fiona", "Jack")) 262 * .findList(); 263 * 264 * 265 * List<SqlRow> rows = DB.sqlQuery(sql) 266 * .setParameter(1, asList("Rob", "Fiona", "Jack")) 267 * .findList(); 268 * }</pre> 269 */ 270 SqlQuery setParameter(int position, Object value); 271 272 /** 273 * Bind the named parameter value. 274 */ 275 SqlQuery setParameter(String name, Object value); 276 277 /** 278 * Set the index of the first row of the results to return. 279 */ 280 SqlQuery setFirstRow(int firstRow); 281 282 /** 283 * Set the maximum number of query results to return. 284 */ 285 SqlQuery setMaxRows(int maxRows); 286 287 /** 288 * Set a timeout on this query. 289 * <p> 290 * This will typically result in a call to setQueryTimeout() on a 291 * preparedStatement. If the timeout occurs an exception will be thrown - this 292 * will be a SQLException wrapped up in a PersistenceException. 293 * </p> 294 * 295 * @param secs the query timeout limit in seconds. Zero means there is no limit. 296 */ 297 SqlQuery setTimeout(int secs); 298 299 /** 300 * Set a label that can be put on performance metrics that are collected. 301 */ 302 SqlQuery setLabel(String label); 303 304 /** 305 * A hint which for JDBC translates to the Statement.fetchSize(). 306 * <p> 307 * Gives the JDBC driver a hint as to the number of rows that should be 308 * fetched from the database when more rows are needed for ResultSet. 309 * </p> 310 */ 311 SqlQuery setBufferFetchSizeHint(int bufferFetchSizeHint); 312 313 /** 314 * The query result maps to a single scalar value like Long, BigDecimal, 315 * String, UUID, OffsetDateTime etc. 316 * <p> 317 * Any scalar type Ebean is aware of can be used including java time 318 * types like Instant, LocalDate, OffsetDateTime, UUID, Inet, Cdir etc. 319 * 320 * <pre>{@code 321 * 322 * String sql = " select min(updtime) from o_order_detail " + 323 * " where unit_price > ? and updtime is not null "; 324 * 325 * OffsetDateTime minCreated = DB.sqlQuery(sql) 326 * .setParameter(42) 327 * .mapToScalar(OffsetDateTime.class) 328 * .findOne(); 329 * 330 * }</pre> 331 * 332 * @param attributeType The type the result is returned as 333 * @return The query to execute via findOne() findList() etc 334 */ 335 <T> TypeQuery<T> mapToScalar(Class<T> attributeType); 336 337 /** 338 * Use a RowMapper to map the result to beans. 339 * 340 * @param mapper Maps rows to beans 341 * @param <T> The type of beans mapped to 342 * @return The query to execute by findOne() findList() etc 343 */ 344 <T> TypeQuery<T> mapTo(RowMapper<T> mapper); 345 346 /** 347 * Query mapping to single scalar values. 348 * 349 * @param <T> The type of the scalar values 350 */ 351 interface TypeQuery<T> { 352 353 /** 354 * Return the single value. 355 */ 356 @Nullable 357 T findOne(); 358 359 /** 360 * Return the single value that is optional. 361 */ 362 Optional<T> findOneOrEmpty(); 363 364 /** 365 * Return the list of values. 366 */ 367 List<T> findList(); 368 369 /** 370 * Find streaming the result effectively consuming a row at a time. 371 */ 372 void findEach(Consumer<T> consumer); 373 } 374}