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", "rob@foo.com").exists(); 058 * 059 * }</pre> 060 * 061 * <h2>Example using a query bean:</h2> 062 * <pre>{@code 063 * 064 * boolean userExists = new QContact().email.equalTo("rob@foo.com").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 findEach with batch consumer. 164 * 165 * @see Query#findEach(int, Consumer) 166 */ 167 <T> void findEach(Query<T> query, int batch, Consumer<List<T>> consumer, Transaction t); 168 169 /** 170 * Execute the query visiting the each bean one at a time. 171 * <p> 172 * Compared to findEach() this provides the ability to stop processing the query 173 * results early by returning false for the Predicate. 174 * </p> 175 * <p> 176 * Unlike findList() this is suitable for processing a query that will return 177 * a very large resultSet. The reason is that not all the result beans need to be 178 * held in memory at the same time and instead processed one at a time. 179 * </p> 180 * <p> 181 * Internally this query using a PersistenceContext scoped to each bean (and the 182 * beans associated object graph). 183 * </p> 184 * <p> 185 * <pre>{@code 186 * 187 * DB.find(Order.class) 188 * .where().eq("status", Order.Status.NEW) 189 * .order().asc("id") 190 * .findEachWhile((Order order) -> { 191 * 192 * // do something with the order bean 193 * System.out.println(" -- processing order ... " + order); 194 * 195 * boolean carryOnProcessing = ... 196 * return carryOnProcessing; 197 * }); 198 * 199 * }</pre> 200 * 201 * @see Query#findEach(Consumer) 202 * @see Query#findEachWhile(Predicate) 203 */ 204 <T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction transaction); 205 206 /** 207 * Return versions of a @History entity bean. 208 * <p> 209 * Generally this query is expected to be a find by id or unique predicates query. 210 * It will execute the query against the history returning the versions of the bean. 211 * </p> 212 */ 213 @Nonnull 214 <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction); 215 216 /** 217 * Execute a query returning a list of beans. 218 * <p> 219 * Generally you are able to use {@link Query#findList()} rather than 220 * explicitly calling this method. You could use this method if you wish to 221 * explicitly control the transaction used for the query. 222 * </p> 223 * <p> 224 * <pre>{@code 225 * 226 * List<Customer> customers = DB.find(Customer.class) 227 * .where().ilike("name", "rob%") 228 * .findList(); 229 * 230 * }</pre> 231 * 232 * @param <T> the type of entity bean to fetch. 233 * @param query the query to execute. 234 * @param transaction the transaction to use (can be null). 235 * @return the list of fetched beans. 236 * @see Query#findList() 237 */ 238 @Nonnull 239 <T> List<T> findList(Query<T> query, Transaction transaction); 240 241 /** 242 * Execute find row count query in a background thread. 243 * <p> 244 * This returns a Future object which can be used to cancel, check the 245 * execution status (isDone etc) and get the value (with or without a 246 * timeout). 247 * </p> 248 * 249 * @param query the query to execute the row count on 250 * @param transaction the transaction (can be null). 251 * @return a Future object for the row count query 252 * @see Query#findFutureCount() 253 */ 254 @Nonnull 255 <T> FutureRowCount<T> findFutureCount(Query<T> query, Transaction transaction); 256 257 /** 258 * Execute find Id's query in a background thread. 259 * <p> 260 * This returns a Future object which can be used to cancel, check the 261 * execution status (isDone etc) and get the value (with or without a 262 * timeout). 263 * </p> 264 * 265 * @param query the query to execute the fetch Id's on 266 * @param transaction the transaction (can be null). 267 * @return a Future object for the list of Id's 268 * @see Query#findFutureIds() 269 */ 270 @Nonnull 271 <T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction); 272 273 /** 274 * Execute find list query in a background thread returning a FutureList object. 275 * <p> 276 * This returns a Future object which can be used to cancel, check the 277 * execution status (isDone etc) and get the value (with or without a timeout). 278 * <p> 279 * This query will execute in it's own PersistenceContext and using its own transaction. 280 * What that means is that it will not share any bean instances with other queries. 281 * 282 * @param query the query to execute in the background 283 * @param transaction the transaction (can be null). 284 * @return a Future object for the list result of the query 285 * @see Query#findFutureList() 286 */ 287 @Nonnull 288 <T> FutureList<T> findFutureList(Query<T> query, Transaction transaction); 289 290 /** 291 * Return a PagedList for this query using firstRow and maxRows. 292 * <p> 293 * The benefit of using this over findList() is that it provides functionality to get the 294 * total row count etc. 295 * </p> 296 * <p> 297 * If maxRows is not set on the query prior to calling findPagedList() then a 298 * PersistenceException is thrown. 299 * </p> 300 * <p> 301 * <pre>{@code 302 * 303 * PagedList<Order> pagedList = DB.find(Order.class) 304 * .setFirstRow(50) 305 * .setMaxRows(20) 306 * .findPagedList(); 307 * 308 * // fetch the total row count in the background 309 * pagedList.loadRowCount(); 310 * 311 * List<Order> orders = pagedList.getList(); 312 * int totalRowCount = pagedList.getTotalRowCount(); 313 * 314 * }</pre> 315 * 316 * @return The PagedList 317 * @see Query#findPagedList() 318 */ 319 @Nonnull 320 <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction); 321 322 /** 323 * Execute the query returning a set of entity beans. 324 * <p> 325 * Generally you are able to use {@link Query#findSet()} rather than 326 * explicitly calling this method. You could use this method if you wish to 327 * explicitly control the transaction used for the query. 328 * </p> 329 * <p> 330 * <pre>{@code 331 * 332 * Set<Customer> customers = DB.find(Customer.class) 333 * .where().ilike("name", "rob%") 334 * .findSet(); 335 * 336 * }</pre> 337 * 338 * @param <T> the type of entity bean to fetch. 339 * @param query the query to execute 340 * @param transaction the transaction to use (can be null). 341 * @return the set of fetched beans. 342 * @see Query#findSet() 343 */ 344 @Nonnull 345 <T> Set<T> findSet(Query<T> query, Transaction transaction); 346 347 /** 348 * Execute the query returning the entity beans in a Map. 349 * <p> 350 * Generally you are able to use {@link Query#findMap()} rather than 351 * explicitly calling this method. You could use this method if you wish to 352 * explicitly control the transaction used for the query. 353 * </p> 354 * 355 * @param <T> the type of entity bean to fetch. 356 * @param query the query to execute. 357 * @param transaction the transaction to use (can be null). 358 * @return the map of fetched beans. 359 * @see Query#findMap() 360 */ 361 @Nonnull 362 <K, T> Map<K, T> findMap(Query<T> query, Transaction transaction); 363 364 /** 365 * Execute the query returning a list of values for a single property. 366 * <p> 367 * <h3>Example 1:</h3> 368 * <pre>{@code 369 * 370 * List<String> names = 371 * DB.find(Customer.class) 372 * .select("name") 373 * .order().asc("name") 374 * .findSingleAttributeList(); 375 * 376 * }</pre> 377 * <h3>Example 2:</h3> 378 * <pre>{@code 379 * 380 * List<String> names = 381 * DB.find(Customer.class) 382 * .setDistinct(true) 383 * .select("name") 384 * .where().eq("status", Customer.Status.NEW) 385 * .order().asc("name") 386 * .setMaxRows(100) 387 * .findSingleAttributeList(); 388 * 389 * }</pre> 390 * 391 * @return the list of values for the selected property 392 * @see Query#findSingleAttributeList() 393 */ 394 @Nonnull 395 <A, T> List<A> findSingleAttributeList(Query<T> query, Transaction transaction); 396 397 /** 398 * Execute the query returning at most one entity bean or null (if no matching 399 * bean is found). 400 * <p> 401 * This will throw a NonUniqueResultException if the query finds more than one result. 402 * </p> 403 * <p> 404 * Generally you are able to use {@link Query#findOne()} rather than 405 * explicitly calling this method. You could use this method if you wish to 406 * explicitly control the transaction used for the query. 407 * </p> 408 * 409 * @param <T> the type of entity bean to fetch. 410 * @param query the query to execute. 411 * @param transaction the transaction to use (can be null). 412 * @return the list of fetched beans. 413 * @throws NonUniqueResultException if more than one result was found 414 * @see Query#findOne() 415 */ 416 @Nullable 417 <T> T findOne(Query<T> query, Transaction transaction); 418 419 /** 420 * Similar to findOne() but returns an Optional (rather than nullable). 421 */ 422 @Nonnull 423 <T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction); 424 425 /** 426 * Execute as a delete query deleting the 'root level' beans that match the predicates 427 * in the query. 428 * <p> 429 * Note that if the query includes joins then the generated delete statement may not be 430 * optimal depending on the database platform. 431 * </p> 432 * 433 * @param query the query used for the delete 434 * @param transaction the transaction to use (can be null) 435 * @param <T> the type of entity bean to fetch. 436 * @return the number of beans/rows that were deleted 437 */ 438 <T> int delete(Query<T> query, Transaction transaction); 439 440 /** 441 * Execute the update query returning the number of rows updated. 442 * <p> 443 * The update query must be created using {@link Database#update(Class)}. 444 * </p> 445 * 446 * @param query the update query to execute 447 * @param transaction the optional transaction to use for the update (can be null) 448 * @param <T> the type of entity bean 449 * @return The number of rows updated 450 */ 451 <T> int update(Query<T> query, Transaction transaction); 452 453 /** 454 * Execute the sql query returning a list of MapBean. 455 * <p> 456 * Generally you are able to use {@link SqlQuery#findList()} rather than 457 * explicitly calling this method. You could use this method if you wish to 458 * explicitly control the transaction used for the query. 459 * </p> 460 * 461 * @param query the query to execute. 462 * @param transaction the transaction to use (can be null). 463 * @return the list of fetched MapBean. 464 * @see SqlQuery#findList() 465 */ 466 @Nonnull 467 List<SqlRow> findList(SqlQuery query, Transaction transaction); 468 469 /** 470 * Execute the SqlQuery iterating a row at a time. 471 * <p> 472 * This streaming type query is useful for large query execution as only 1 row needs to be held in memory. 473 * </p> 474 */ 475 void findEach(SqlQuery query, Consumer<SqlRow> consumer, Transaction transaction); 476 477 /** 478 * Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through. 479 * <p> 480 * Returning false after processing a row stops the iteration through the query results. 481 * </p> 482 * <p> 483 * This streaming type query is useful for large query execution as only 1 row needs to be held in memory. 484 * </p> 485 */ 486 void findEachWhile(SqlQuery query, Predicate<SqlRow> consumer, Transaction transaction); 487 488 /** 489 * Execute the sql query returning a single MapBean or null. 490 * <p> 491 * This will throw a PersistenceException if the query found more than one 492 * result. 493 * </p> 494 * <p> 495 * Generally you are able to use {@link SqlQuery#findOne()} rather than 496 * explicitly calling this method. You could use this method if you wish to 497 * explicitly control the transaction used for the query. 498 * </p> 499 * 500 * @param query the query to execute. 501 * @param transaction the transaction to use (can be null). 502 * @return the fetched MapBean or null if none was found. 503 * @see SqlQuery#findOne() 504 */ 505 @Nullable 506 SqlRow findOne(SqlQuery query, Transaction transaction); 507 508}