001package io.ebean; 002 003import javax.annotation.Nonnull; 004import javax.annotation.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 */ 040public interface SqlQuery extends Serializable { 041 042 /** 043 * Execute the query returning a list. 044 */ 045 @Nonnull 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 @Nonnull 123 Optional<SqlRow> findOneOrEmpty(); 124 125 /** 126 * Deprecated - migrate to <code>.mapToScalar(attributeType).findOne()</code>. 127 * <pre>{@code 128 * 129 * .mapToScalar(BigDecimal.class) 130 * .findOne(); 131 * } 132 */ 133 @Deprecated 134 <T> T findSingleAttribute(Class<T> attributeType); 135 136 /** 137 * Deprecated - migrate to <code>.mapToScalar(BigDecimal.class).findOne()</code>. 138 * <pre>{@code 139 * 140 * .mapToScalar(BigDecimal.class) 141 * .findOne(); 142 * } 143 */ 144 @Deprecated 145 BigDecimal findSingleDecimal(); 146 147 /** 148 * Deprecated - migrate to <code>.mapToScalar(Long.class).findOne()</code>. 149 * <pre>{@code 150 * 151 * .mapToScalar(Long.class) 152 * .findOne(); 153 * } 154 */ 155 @Deprecated 156 Long findSingleLong(); 157 158 /** 159 * Deprecated - migrate to <code>.mapToScalar(Long.class).findList()</code>. 160 * <pre>{@code 161 * 162 * .mapToScalar(Long.class) 163 * .findList(); 164 * } 165 */ 166 @Deprecated 167 <T> List<T> findSingleAttributeList(Class<T> attributeType); 168 169 /** 170 * Set one of more positioned parameters. 171 * <p> 172 * This is a convenient alternative to multiple calls to {@link #setParameter(Object)}. 173 * 174 * <pre>{@code 175 * 176 * String sql = "select id, name from customer where name like ? and status = ?"; 177 * 178 * List<SqlRow> list = 179 * DB.sqlQuery(sql) 180 * .setParameters("Rob", Status.NEW) 181 * .findList(); 182 * 183 * 184 * // effectively the same as ... 185 * 186 * .setParameter("Rob") 187 * .setParameter("Status.NEW) 188 * 189 * // and ... 190 * 191 * .setParameter(1, "Rob") 192 * .setParameter(2, "Status.NEW) 193 * 194 * }</pre> 195 */ 196 SqlQuery setParameters(Object... values); 197 198 /** 199 * Deprecated migrate to setParameters(Object... values) 200 */ 201 @Deprecated 202 SqlQuery setParams(Object... values); 203 204 /** 205 * Set the next bind parameter by position. 206 * <pre>{@code 207 * 208 * String sql = "select id, name from customer where name like ? and status = ?"; 209 * 210 * List<SqlRow> list = 211 * DB.sqlQuery(sql) 212 * .setParameter("Rob") 213 * .setParameter("Status.NEW) 214 * .findList(); 215 * 216 * // the same as ... 217 * 218 * .setParameters("Rob", Status.NEW) 219 * 220 * // and ... 221 * 222 * .setParameter(1, "Rob") 223 * .setParameter(2, "Status.NEW) 224 * 225 * }</pre> 226 * 227 * <p> 228 * When binding a collection of values into a IN expression we should use 229 * indexed parameters like ?1, ?2, ?3 etc rather than just ?. 230 * </p> 231 * 232 * <pre>{@code 233 * 234 * String sql = "select c.id, c.name from customer c where c.name in (?1)"; 235 * 236 * List<SqlRow> rows = DB.sqlQuery(sql) 237 * .setParameter(asList("Rob", "Fiona", "Jack")) 238 * .findList(); 239 * 240 * 241 * List<SqlRow> rows = DB.sqlQuery(sql) 242 * .setParameter(1, asList("Rob", "Fiona", "Jack")) 243 * .findList(); 244 * }</pre> 245 * 246 * @param value The value to bind 247 */ 248 SqlQuery setParameter(Object value); 249 250 /** 251 * Bind the parameter by its index position (1 based like JDBC). 252 * <p> 253 * When binding a collection of values into a IN expression we should use 254 * indexed parameters like ?1, ?2, ?3 etc rather than just ?. 255 * </p> 256 * 257 * <pre>{@code 258 * 259 * String sql = "select c.id, c.name from customer c where c.name in (?1)"; 260 * 261 * List<SqlRow> rows = DB.sqlQuery(sql) 262 * .setParameter(asList("Rob", "Fiona", "Jack")) 263 * .findList(); 264 * 265 * 266 * List<SqlRow> rows = DB.sqlQuery(sql) 267 * .setParameter(1, asList("Rob", "Fiona", "Jack")) 268 * .findList(); 269 * }</pre> 270 */ 271 SqlQuery setParameter(int position, Object value); 272 273 /** 274 * Bind the named parameter value. 275 */ 276 SqlQuery setParameter(String name, Object value); 277 278 /** 279 * Set the index of the first row of the results to return. 280 */ 281 SqlQuery setFirstRow(int firstRow); 282 283 /** 284 * Set the maximum number of query results to return. 285 */ 286 SqlQuery setMaxRows(int maxRows); 287 288 /** 289 * Set a timeout on this query. 290 * <p> 291 * This will typically result in a call to setQueryTimeout() on a 292 * preparedStatement. If the timeout occurs an exception will be thrown - this 293 * will be a SQLException wrapped up in a PersistenceException. 294 * </p> 295 * 296 * @param secs the query timeout limit in seconds. Zero means there is no limit. 297 */ 298 SqlQuery setTimeout(int secs); 299 300 /** 301 * Set a label that can be put on performance metrics that are collected. 302 */ 303 SqlQuery setLabel(String label); 304 305 /** 306 * A hint which for JDBC translates to the Statement.fetchSize(). 307 * <p> 308 * Gives the JDBC driver a hint as to the number of rows that should be 309 * fetched from the database when more rows are needed for ResultSet. 310 * </p> 311 */ 312 SqlQuery setBufferFetchSizeHint(int bufferFetchSizeHint); 313 314 /** 315 * The query result maps to a single scalar value like Long, BigDecimal, 316 * String, UUID, OffsetDateTime etc. 317 * <p> 318 * Any scalar type Ebean is aware of can be used including java time 319 * types like Instant, LocalDate, OffsetDateTime, UUID, Inet, Cdir etc. 320 * 321 * <pre>{@code 322 * 323 * String sql = " select min(updtime) from o_order_detail " + 324 * " where unit_price > ? and updtime is not null "; 325 * 326 * OffsetDateTime minCreated = DB.sqlQuery(sql) 327 * .setParameter(42) 328 * .mapToScalar(OffsetDateTime.class) 329 * .findOne(); 330 * 331 * }</pre> 332 * 333 * @param attributeType The type the result is returned as 334 * @return The query to execute via findOne() findList() etc 335 */ 336 <T> TypeQuery<T> mapToScalar(Class<T> attributeType); 337 338 /** 339 * Use a RowMapper to map the result to beans. 340 * 341 * @param mapper Maps rows to beans 342 * @param <T> The type of beans mapped to 343 * @return The query to execute by findOne() findList() etc 344 */ 345 <T> TypeQuery<T> mapTo(RowMapper<T> mapper); 346 347 /** 348 * Query mapping to single scalar values. 349 * 350 * @param <T> The type of the scalar values 351 */ 352 interface TypeQuery<T> { 353 354 /** 355 * Return the single value. 356 */ 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}