001package io.ebean; 002 003import javax.annotation.Nonnull; 004import javax.annotation.Nullable; 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<?> 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 @Nonnull 086 <A, T> List<A> findIds(Query<T> query, Transaction transaction); 087 088 /** 089 * Return a QueryIterator for the query. 090 * <p> 091 * Generally using {@link #findEach(Query, Consumer, Transaction)} or 092 * {@link #findEachWhile(Query, Predicate, Transaction)} is preferred 093 * to findIterate(). The reason is that those methods automatically take care of 094 * closing the queryIterator (and the underlying jdbc statement and resultSet). 095 * <p> 096 * This is similar to findEach in that not all the result beans need to be held 097 * in memory at the same time and as such is good for processing large queries. 098 * 099 * @see Query#findIterate() 100 * @see Query#findEach(Consumer) 101 * @see Query#findEachWhile(Predicate) 102 */ 103 @Nonnull 104 <T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction); 105 106 /** 107 * Execute the query returning the result as a Stream. 108 * <p> 109 * Note that this can support very large queries iterating any number of results. 110 * To do so internally it can use multiple persistence contexts. 111 * <p> 112 * Note that the stream needs to be closed so use with try with resources. 113 * </p> 114 */ 115 @Nonnull 116 <T> Stream<T> findStream(Query<T> query, Transaction transaction); 117 118 /** 119 * Deprecated - migrate to findStream(). 120 * <p> 121 * Execute the query returning the result as a Stream. 122 * <p> 123 * Note that this can support very large queries iterating any number of results. 124 * To do so internally it can use multiple persistence contexts. 125 * <p> 126 * Note that the stream needs to be closed so use with try with resources. 127 */ 128 @Nonnull 129 @Deprecated 130 <T> Stream<T> findLargeStream(Query<T> query, Transaction transaction); 131 132 /** 133 * Execute the query visiting the each bean one at a time. 134 * <p> 135 * Unlike findList() this is suitable for processing a query that will return 136 * a very large resultSet. The reason is that not all the result beans need to be 137 * held in memory at the same time and instead processed one at a time. 138 * </p> 139 * <p> 140 * Internally this query using a PersistenceContext scoped to each bean (and the 141 * beans associated object graph). 142 * </p> 143 * <p> 144 * <pre>{@code 145 * 146 * DB.find(Order.class) 147 * .where().eq("status", Order.Status.NEW) 148 * .order().asc("id") 149 * .findEach((Order order) -> { 150 * 151 * // do something with the order bean 152 * System.out.println(" -- processing order ... " + order); 153 * }); 154 * 155 * }</pre> 156 * 157 * @see Query#findEach(Consumer) 158 * @see Query#findEachWhile(Predicate) 159 */ 160 <T> void findEach(Query<T> query, Consumer<T> consumer, Transaction transaction); 161 162 /** 163 * Execute the query visiting the each bean one at a time. 164 * <p> 165 * Compared to findEach() this provides the ability to stop processing the query 166 * results early by returning false for the Predicate. 167 * </p> 168 * <p> 169 * Unlike findList() this is suitable for processing a query that will return 170 * a very large resultSet. The reason is that not all the result beans need to be 171 * held in memory at the same time and instead processed one at a time. 172 * </p> 173 * <p> 174 * Internally this query using a PersistenceContext scoped to each bean (and the 175 * beans associated object graph). 176 * </p> 177 * <p> 178 * <pre>{@code 179 * 180 * DB.find(Order.class) 181 * .where().eq("status", Order.Status.NEW) 182 * .order().asc("id") 183 * .findEachWhile((Order order) -> { 184 * 185 * // do something with the order bean 186 * System.out.println(" -- processing order ... " + order); 187 * 188 * boolean carryOnProcessing = ... 189 * return carryOnProcessing; 190 * }); 191 * 192 * }</pre> 193 * 194 * @see Query#findEach(Consumer) 195 * @see Query#findEachWhile(Predicate) 196 */ 197 <T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction transaction); 198 199 /** 200 * Return versions of a @History entity bean. 201 * <p> 202 * Generally this query is expected to be a find by id or unique predicates query. 203 * It will execute the query against the history returning the versions of the bean. 204 * </p> 205 */ 206 @Nonnull 207 <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction); 208 209 /** 210 * Execute a query returning a list of beans. 211 * <p> 212 * Generally you are able to use {@link Query#findList()} rather than 213 * explicitly calling this method. You could use this method if you wish to 214 * explicitly control the transaction used for the query. 215 * </p> 216 * <p> 217 * <pre>{@code 218 * 219 * List<Customer> customers = DB.find(Customer.class) 220 * .where().ilike("name", "rob%") 221 * .findList(); 222 * 223 * }</pre> 224 * 225 * @param <T> the type of entity bean to fetch. 226 * @param query the query to execute. 227 * @param transaction the transaction to use (can be null). 228 * @return the list of fetched beans. 229 * @see Query#findList() 230 */ 231 @Nonnull 232 <T> List<T> findList(Query<T> query, Transaction transaction); 233 234 /** 235 * Execute find row count query in a background thread. 236 * <p> 237 * This returns a Future object which can be used to cancel, check the 238 * execution status (isDone etc) and get the value (with or without a 239 * timeout). 240 * </p> 241 * 242 * @param query the query to execute the row count on 243 * @param transaction the transaction (can be null). 244 * @return a Future object for the row count query 245 * @see Query#findFutureCount() 246 */ 247 @Nonnull 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 @Nonnull 264 <T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction); 265 266 /** 267 * Execute find list query in a background thread returning a FutureList object. 268 * <p> 269 * This returns a Future object which can be used to cancel, check the 270 * execution status (isDone etc) and get the value (with or without a timeout). 271 * <p> 272 * This query will execute in it's own PersistenceContext and using its own transaction. 273 * What that means is that it will not share any bean instances with other queries. 274 * 275 * @param query the query to execute in the background 276 * @param transaction the transaction (can be null). 277 * @return a Future object for the list result of the query 278 * @see Query#findFutureList() 279 */ 280 @Nonnull 281 <T> FutureList<T> findFutureList(Query<T> query, Transaction transaction); 282 283 /** 284 * Return a PagedList for this query using firstRow and maxRows. 285 * <p> 286 * The benefit of using this over findList() is that it provides functionality to get the 287 * total row count etc. 288 * </p> 289 * <p> 290 * If maxRows is not set on the query prior to calling findPagedList() then a 291 * PersistenceException is thrown. 292 * </p> 293 * <p> 294 * <pre>{@code 295 * 296 * PagedList<Order> pagedList = DB.find(Order.class) 297 * .setFirstRow(50) 298 * .setMaxRows(20) 299 * .findPagedList(); 300 * 301 * // fetch the total row count in the background 302 * pagedList.loadRowCount(); 303 * 304 * List<Order> orders = pagedList.getList(); 305 * int totalRowCount = pagedList.getTotalRowCount(); 306 * 307 * }</pre> 308 * 309 * @return The PagedList 310 * @see Query#findPagedList() 311 */ 312 @Nonnull 313 <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction); 314 315 /** 316 * Execute the query returning a set of entity beans. 317 * <p> 318 * Generally you are able to use {@link Query#findSet()} rather than 319 * explicitly calling this method. You could use this method if you wish to 320 * explicitly control the transaction used for the query. 321 * </p> 322 * <p> 323 * <pre>{@code 324 * 325 * Set<Customer> customers = DB.find(Customer.class) 326 * .where().ilike("name", "rob%") 327 * .findSet(); 328 * 329 * }</pre> 330 * 331 * @param <T> the type of entity bean to fetch. 332 * @param query the query to execute 333 * @param transaction the transaction to use (can be null). 334 * @return the set of fetched beans. 335 * @see Query#findSet() 336 */ 337 @Nonnull 338 <T> Set<T> findSet(Query<T> query, Transaction transaction); 339 340 /** 341 * Execute the query returning the entity beans in a Map. 342 * <p> 343 * Generally you are able to use {@link Query#findMap()} rather than 344 * explicitly calling this method. You could use this method if you wish to 345 * explicitly control the transaction used for the query. 346 * </p> 347 * 348 * @param <T> the type of entity bean to fetch. 349 * @param query the query to execute. 350 * @param transaction the transaction to use (can be null). 351 * @return the map of fetched beans. 352 * @see Query#findMap() 353 */ 354 @Nonnull 355 <K, T> Map<K, T> findMap(Query<T> query, Transaction transaction); 356 357 /** 358 * Execute the query returning a list of values for a single property. 359 * <p> 360 * <h3>Example 1:</h3> 361 * <pre>{@code 362 * 363 * List<String> names = 364 * DB.find(Customer.class) 365 * .select("name") 366 * .order().asc("name") 367 * .findSingleAttributeList(); 368 * 369 * }</pre> 370 * <h3>Example 2:</h3> 371 * <pre>{@code 372 * 373 * List<String> names = 374 * DB.find(Customer.class) 375 * .setDistinct(true) 376 * .select("name") 377 * .where().eq("status", Customer.Status.NEW) 378 * .order().asc("name") 379 * .setMaxRows(100) 380 * .findSingleAttributeList(); 381 * 382 * }</pre> 383 * 384 * @return the list of values for the selected property 385 * @see Query#findSingleAttributeList() 386 */ 387 @Nonnull 388 <A, T> List<A> findSingleAttributeList(Query<T> query, Transaction transaction); 389 390 /** 391 * Execute the query returning at most one entity bean or null (if no matching 392 * bean is found). 393 * <p> 394 * This will throw a NonUniqueResultException if the query finds more than one result. 395 * </p> 396 * <p> 397 * Generally you are able to use {@link Query#findOne()} rather than 398 * explicitly calling this method. You could use this method if you wish to 399 * explicitly control the transaction used for the query. 400 * </p> 401 * 402 * @param <T> the type of entity bean to fetch. 403 * @param query the query to execute. 404 * @param transaction the transaction to use (can be null). 405 * @return the list of fetched beans. 406 * @throws NonUniqueResultException if more than one result was found 407 * @see Query#findOne() 408 */ 409 @Nullable 410 <T> T findOne(Query<T> query, Transaction transaction); 411 412 /** 413 * Similar to findOne() but returns an Optional (rather than nullable). 414 */ 415 @Nonnull 416 <T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction); 417 418 /** 419 * Execute as a delete query deleting the 'root level' beans that match the predicates 420 * in the query. 421 * <p> 422 * Note that if the query includes joins then the generated delete statement may not be 423 * optimal depending on the database platform. 424 * </p> 425 * 426 * @param query the query used for the delete 427 * @param transaction the transaction to use (can be null) 428 * @param <T> the type of entity bean to fetch. 429 * @return the number of beans/rows that were deleted 430 */ 431 <T> int delete(Query<T> query, Transaction transaction); 432 433 /** 434 * Execute the update query returning the number of rows updated. 435 * <p> 436 * The update query must be created using {@link Database#update(Class)}. 437 * </p> 438 * 439 * @param query the update query to execute 440 * @param transaction the optional transaction to use for the update (can be null) 441 * @param <T> the type of entity bean 442 * @return The number of rows updated 443 */ 444 <T> int update(Query<T> query, Transaction transaction); 445 446 /** 447 * Execute the sql query returning a list of MapBean. 448 * <p> 449 * Generally you are able to use {@link SqlQuery#findList()} rather than 450 * explicitly calling this method. You could use this method if you wish to 451 * explicitly control the transaction used for the query. 452 * </p> 453 * 454 * @param query the query to execute. 455 * @param transaction the transaction to use (can be null). 456 * @return the list of fetched MapBean. 457 * @see SqlQuery#findList() 458 */ 459 @Nonnull 460 List<SqlRow> findList(SqlQuery query, Transaction transaction); 461 462 /** 463 * Execute the SqlQuery iterating a row at a time. 464 * <p> 465 * This streaming type query is useful for large query execution as only 1 row needs to be held in memory. 466 * </p> 467 */ 468 void findEach(SqlQuery query, Consumer<SqlRow> consumer, Transaction transaction); 469 470 /** 471 * Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through. 472 * <p> 473 * Returning false after processing a row stops the iteration through the query results. 474 * </p> 475 * <p> 476 * This streaming type query is useful for large query execution as only 1 row needs to be held in memory. 477 * </p> 478 */ 479 void findEachWhile(SqlQuery query, Predicate<SqlRow> consumer, Transaction transaction); 480 481 /** 482 * Execute the sql query returning a single MapBean or null. 483 * <p> 484 * This will throw a PersistenceException if the query found more than one 485 * result. 486 * </p> 487 * <p> 488 * Generally you are able to use {@link SqlQuery#findOne()} rather than 489 * explicitly calling this method. You could use this method if you wish to 490 * explicitly control the transaction used for the query. 491 * </p> 492 * 493 * @param query the query to execute. 494 * @param transaction the transaction to use (can be null). 495 * @return the fetched MapBean or null if none was found. 496 * @see SqlQuery#findOne() 497 */ 498 @Nullable 499 SqlRow findOne(SqlQuery query, Transaction transaction); 500 501}