001package io.ebean; 002 003import io.avaje.lang.Nullable; 004 005import javax.persistence.NonUniqueResultException; 006import java.time.Clock; 007import java.util.List; 008import java.util.Map; 009import java.util.Optional; 010import java.util.Set; 011import java.util.function.Consumer; 012import java.util.function.Predicate; 013import java.util.stream.Stream; 014 015/** 016 * The extended API for Database. 017 * <p> 018 * This provides the finder methods that take an explicit transaction rather than obtaining 019 * the transaction from the usual mechanism (which is ThreadLocal based). 020 * </p> 021 * <p> 022 * In general we only want to use this ExtendedServer API when we want to avoid / bypass 023 * the use of the mechanism to get the current transaction and instead explicitly supply 024 * the transaction to use. 025 * </p> 026 * <p> 027 * Note that in all cases the transaction supplied can be null and in this case the Database 028 * will use the normal mechanism to obtain the transaction to use. 029 * </p> 030 */ 031public interface ExtendedServer { 032 033 /** 034 * Return the NOW time from the Clock. 035 */ 036 long clockNow(); 037 038 /** 039 * Set the Clock to use for <code>@WhenCreated</code> and <code>@WhenModified</code>. 040 * <p> 041 * Note that we only expect to change the Clock for testing purposes. 042 * </p> 043 */ 044 void setClock(Clock clock); 045 046 /** 047 * Execute the query returning true if a row is found. 048 * <p> 049 * The query is executed using max rows of 1 and will only select the id property. 050 * This method is really just a convenient way to optimise a query to perform a 051 * 'does a row exist in the db' check. 052 * </p> 053 * 054 * <h2>Example:</h2> 055 * <pre>{@code 056 * 057 * boolean userExists = query().where().eq("email", "[email protected]").exists(); 058 * 059 * }</pre> 060 * 061 * <h2>Example using a query bean:</h2> 062 * <pre>{@code 063 * 064 * boolean userExists = new QContact().email.equalTo("[email protected]").exists(); 065 * 066 * }</pre> 067 * 068 * @return True if the query finds a matching row in the database 069 */ 070 <T> boolean exists(Query<T> ormQuery, Transaction transaction); 071 072 /** 073 * Return the number of 'top level' or 'root' entities this query should return. 074 * 075 * @see Query#findCount() 076 * @see Query#findFutureCount() 077 */ 078 <T> int findCount(Query<T> query, Transaction transaction); 079 080 /** 081 * Return the Id values of the query as a List. 082 * 083 * @see Query#findIds() 084 */ 085 <A, T> List<A> findIds(Query<T> query, Transaction transaction); 086 087 /** 088 * Return a QueryIterator for the query. 089 * <p> 090 * Generally using {@link #findEach(Query, Consumer, Transaction)} or 091 * {@link #findEachWhile(Query, Predicate, Transaction)} is preferred 092 * to findIterate(). The reason is that those methods automatically take care of 093 * closing the queryIterator (and the underlying jdbc statement and resultSet). 094 * <p> 095 * This is similar to findEach in that not all the result beans need to be held 096 * in memory at the same time and as such is good for processing large queries. 097 * 098 * @see Query#findIterate() 099 * @see Query#findEach(Consumer) 100 * @see Query#findEachWhile(Predicate) 101 */ 102 <T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction); 103 104 /** 105 * Execute the query returning the result as a Stream. 106 * <p> 107 * Note that this can support very large queries iterating any number of results. 108 * To do so internally it can use multiple persistence contexts. 109 * <p> 110 * Note that the stream needs to be closed so use with try with resources. 111 * </p> 112 */ 113 <T> Stream<T> findStream(Query<T> query, Transaction transaction); 114 115 /** 116 * Deprecated - migrate to findStream(). 117 * <p> 118 * Execute the query returning the result as a Stream. 119 * <p> 120 * Note that this can support very large queries iterating any number of results. 121 * To do so internally it can use multiple persistence contexts. 122 * <p> 123 * Note that the stream needs to be closed so use with try with resources. 124 */ 125 @Deprecated 126 <T> Stream<T> findLargeStream(Query<T> query, Transaction transaction); 127 128 /** 129 * Execute the query visiting the each bean one at a time. 130 * <p> 131 * Unlike findList() this is suitable for processing a query that will return 132 * a very large resultSet. The reason is that not all the result beans need to be 133 * held in memory at the same time and instead processed one at a time. 134 * </p> 135 * <p> 136 * Internally this query using a PersistenceContext scoped to each bean (and the 137 * beans associated object graph). 138 * </p> 139 * <p> 140 * <pre>{@code 141 * 142 * DB.find(Order.class) 143 * .where().eq("status", Order.Status.NEW) 144 * .order().asc("id") 145 * .findEach((Order order) -> { 146 * 147 * // do something with the order bean 148 * System.out.println(" -- processing order ... " + order); 149 * }); 150 * 151 * }</pre> 152 * 153 * @see Query#findEach(Consumer) 154 * @see Query#findEachWhile(Predicate) 155 */ 156 <T> void findEach(Query<T> query, Consumer<T> consumer, Transaction transaction); 157 158 /** 159 * Execute findEach with batch consumer. 160 * 161 * @see Query#findEach(int, Consumer) 162 */ 163 <T> void findEach(Query<T> query, int batch, Consumer<List<T>> consumer, Transaction t); 164 165 /** 166 * Execute the query visiting the each bean one at a time. 167 * <p> 168 * Compared to findEach() this provides the ability to stop processing the query 169 * results early by returning false for the Predicate. 170 * </p> 171 * <p> 172 * Unlike findList() this is suitable for processing a query that will return 173 * a very large resultSet. The reason is that not all the result beans need to be 174 * held in memory at the same time and instead processed one at a time. 175 * </p> 176 * <p> 177 * Internally this query using a PersistenceContext scoped to each bean (and the 178 * beans associated object graph). 179 * </p> 180 * <p> 181 * <pre>{@code 182 * 183 * DB.find(Order.class) 184 * .where().eq("status", Order.Status.NEW) 185 * .order().asc("id") 186 * .findEachWhile((Order order) -> { 187 * 188 * // do something with the order bean 189 * System.out.println(" -- processing order ... " + order); 190 * 191 * boolean carryOnProcessing = ... 192 * return carryOnProcessing; 193 * }); 194 * 195 * }</pre> 196 * 197 * @see Query#findEach(Consumer) 198 * @see Query#findEachWhile(Predicate) 199 */ 200 <T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction transaction); 201 202 /** 203 * Return versions of a @History entity bean. 204 * <p> 205 * Generally this query is expected to be a find by id or unique predicates query. 206 * It will execute the query against the history returning the versions of the bean. 207 * </p> 208 */ 209 <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction); 210 211 /** 212 * Execute a query returning a list of beans. 213 * <p> 214 * Generally you are able to use {@link Query#findList()} rather than 215 * explicitly calling this method. You could use this method if you wish to 216 * explicitly control the transaction used for the query. 217 * </p> 218 * <p> 219 * <pre>{@code 220 * 221 * List<Customer> customers = DB.find(Customer.class) 222 * .where().ilike("name", "rob%") 223 * .findList(); 224 * 225 * }</pre> 226 * 227 * @param <T> the type of entity bean to fetch. 228 * @param query the query to execute. 229 * @param transaction the transaction to use (can be null). 230 * @return the list of fetched beans. 231 * @see Query#findList() 232 */ 233 <T> List<T> findList(Query<T> query, Transaction transaction); 234 235 /** 236 * Execute find row count query in a background thread. 237 * <p> 238 * This returns a Future object which can be used to cancel, check the 239 * execution status (isDone etc) and get the value (with or without a 240 * timeout). 241 * </p> 242 * 243 * @param query the query to execute the row count on 244 * @param transaction the transaction (can be null). 245 * @return a Future object for the row count query 246 * @see Query#findFutureCount() 247 */ 248 <T> FutureRowCount<T> findFutureCount(Query<T> query, Transaction transaction); 249 250 /** 251 * Execute find Id's query in a background thread. 252 * <p> 253 * This returns a Future object which can be used to cancel, check the 254 * execution status (isDone etc) and get the value (with or without a 255 * timeout). 256 * </p> 257 * 258 * @param query the query to execute the fetch Id's on 259 * @param transaction the transaction (can be null). 260 * @return a Future object for the list of Id's 261 * @see Query#findFutureIds() 262 */ 263 <T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction); 264 265 /** 266 * Execute find list query in a background thread returning a FutureList object. 267 * <p> 268 * This returns a Future object which can be used to cancel, check the 269 * execution status (isDone etc) and get the value (with or without a timeout). 270 * <p> 271 * This query will execute in it's own PersistenceContext and using its own transaction. 272 * What that means is that it will not share any bean instances with other queries. 273 * 274 * @param query the query to execute in the background 275 * @param transaction the transaction (can be null). 276 * @return a Future object for the list result of the query 277 * @see Query#findFutureList() 278 */ 279 <T> FutureList<T> findFutureList(Query<T> query, Transaction transaction); 280 281 /** 282 * Return a PagedList for this query using firstRow and maxRows. 283 * <p> 284 * The benefit of using this over findList() is that it provides functionality to get the 285 * total row count etc. 286 * </p> 287 * <p> 288 * If maxRows is not set on the query prior to calling findPagedList() then a 289 * PersistenceException is thrown. 290 * </p> 291 * <p> 292 * <pre>{@code 293 * 294 * PagedList<Order> pagedList = DB.find(Order.class) 295 * .setFirstRow(50) 296 * .setMaxRows(20) 297 * .findPagedList(); 298 * 299 * // fetch the total row count in the background 300 * pagedList.loadRowCount(); 301 * 302 * List<Order> orders = pagedList.getList(); 303 * int totalRowCount = pagedList.getTotalRowCount(); 304 * 305 * }</pre> 306 * 307 * @return The PagedList 308 * @see Query#findPagedList() 309 */ 310 <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction); 311 312 /** 313 * Execute the query returning a set of entity beans. 314 * <p> 315 * Generally you are able to use {@link Query#findSet()} rather than 316 * explicitly calling this method. You could use this method if you wish to 317 * explicitly control the transaction used for the query. 318 * </p> 319 * <p> 320 * <pre>{@code 321 * 322 * Set<Customer> customers = DB.find(Customer.class) 323 * .where().ilike("name", "rob%") 324 * .findSet(); 325 * 326 * }</pre> 327 * 328 * @param <T> the type of entity bean to fetch. 329 * @param query the query to execute 330 * @param transaction the transaction to use (can be null). 331 * @return the set of fetched beans. 332 * @see Query#findSet() 333 */ 334 <T> Set<T> findSet(Query<T> query, Transaction transaction); 335 336 /** 337 * Execute the query returning the entity beans in a Map. 338 * <p> 339 * Generally you are able to use {@link Query#findMap()} rather than 340 * explicitly calling this method. You could use this method if you wish to 341 * explicitly control the transaction used for the query. 342 * </p> 343 * 344 * @param <T> the type of entity bean to fetch. 345 * @param query the query to execute. 346 * @param transaction the transaction to use (can be null). 347 * @return the map of fetched beans. 348 * @see Query#findMap() 349 */ 350 <K, T> Map<K, T> findMap(Query<T> query, Transaction transaction); 351 352 /** 353 * Execute the query returning a list of values for a single property. 354 * <p> 355 * <h3>Example 1:</h3> 356 * <pre>{@code 357 * 358 * List<String> names = 359 * DB.find(Customer.class) 360 * .select("name") 361 * .order().asc("name") 362 * .findSingleAttributeList(); 363 * 364 * }</pre> 365 * <h3>Example 2:</h3> 366 * <pre>{@code 367 * 368 * List<String> names = 369 * DB.find(Customer.class) 370 * .setDistinct(true) 371 * .select("name") 372 * .where().eq("status", Customer.Status.NEW) 373 * .order().asc("name") 374 * .setMaxRows(100) 375 * .findSingleAttributeList(); 376 * 377 * }</pre> 378 * 379 * @return the list of values for the selected property 380 * @see Query#findSingleAttributeList() 381 */ 382 <A, T> List<A> findSingleAttributeList(Query<T> query, Transaction transaction); 383 384 /** 385 * Execute the query returning at most one entity bean or null (if no matching 386 * bean is found). 387 * <p> 388 * This will throw a NonUniqueResultException if the query finds more than one result. 389 * </p> 390 * <p> 391 * Generally you are able to use {@link Query#findOne()} rather than 392 * explicitly calling this method. You could use this method if you wish to 393 * explicitly control the transaction used for the query. 394 * </p> 395 * 396 * @param <T> the type of entity bean to fetch. 397 * @param query the query to execute. 398 * @param transaction the transaction to use (can be null). 399 * @return the list of fetched beans. 400 * @throws NonUniqueResultException if more than one result was found 401 * @see Query#findOne() 402 */ 403 @Nullable 404 <T> T findOne(Query<T> query, Transaction transaction); 405 406 /** 407 * Similar to findOne() but returns an Optional (rather than nullable). 408 */ 409 <T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction); 410 411 /** 412 * Execute as a delete query deleting the 'root level' beans that match the predicates 413 * in the query. 414 * <p> 415 * Note that if the query includes joins then the generated delete statement may not be 416 * optimal depending on the database platform. 417 * </p> 418 * 419 * @param query the query used for the delete 420 * @param transaction the transaction to use (can be null) 421 * @param <T> the type of entity bean to fetch. 422 * @return the number of beans/rows that were deleted 423 */ 424 <T> int delete(Query<T> query, Transaction transaction); 425 426 /** 427 * Execute the update query returning the number of rows updated. 428 * <p> 429 * The update query must be created using {@link Database#update(Class)}. 430 * </p> 431 * 432 * @param query the update query to execute 433 * @param transaction the optional transaction to use for the update (can be null) 434 * @param <T> the type of entity bean 435 * @return The number of rows updated 436 */ 437 <T> int update(Query<T> query, Transaction transaction); 438 439 /** 440 * Execute the sql query returning a list of MapBean. 441 * <p> 442 * Generally you are able to use {@link SqlQuery#findList()} rather than 443 * explicitly calling this method. You could use this method if you wish to 444 * explicitly control the transaction used for the query. 445 * </p> 446 * 447 * @param query the query to execute. 448 * @param transaction the transaction to use (can be null). 449 * @return the list of fetched MapBean. 450 * @see SqlQuery#findList() 451 */ 452 List<SqlRow> findList(SqlQuery query, Transaction transaction); 453 454 /** 455 * Execute the SqlQuery iterating a row at a time. 456 * <p> 457 * This streaming type query is useful for large query execution as only 1 row needs to be held in memory. 458 * </p> 459 */ 460 void findEach(SqlQuery query, Consumer<SqlRow> consumer, Transaction transaction); 461 462 /** 463 * Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through. 464 * <p> 465 * Returning false after processing a row stops the iteration through the query results. 466 * </p> 467 * <p> 468 * This streaming type query is useful for large query execution as only 1 row needs to be held in memory. 469 * </p> 470 */ 471 void findEachWhile(SqlQuery query, Predicate<SqlRow> consumer, Transaction transaction); 472 473 /** 474 * Execute the sql query returning a single MapBean or null. 475 * <p> 476 * This will throw a PersistenceException if the query found more than one 477 * result. 478 * </p> 479 * <p> 480 * Generally you are able to use {@link SqlQuery#findOne()} rather than 481 * explicitly calling this method. You could use this method if you wish to 482 * explicitly control the transaction used for the query. 483 * </p> 484 * 485 * @param query the query to execute. 486 * @param transaction the transaction to use (can be null). 487 * @return the fetched MapBean or null if none was found. 488 * @see SqlQuery#findOne() 489 */ 490 @Nullable 491 SqlRow findOne(SqlQuery query, Transaction transaction); 492 493}