001package com.avaje.ebean; 002 003import com.avaje.ebean.bean.EntityBean; 004import com.avaje.ebean.util.ClassUtil; 005import org.jetbrains.annotations.Nullable; 006 007import javax.persistence.MappedSuperclass; 008import java.util.List; 009import java.util.Map; 010import java.util.Set; 011import java.util.UUID; 012 013/** 014 * A MappedSuperclass base class that provides convenience methods for inserting, updating and 015 * deleting beans. 016 * 017 * <p> 018 * By having your entity beans extend this it provides a 'Active Record' style programming model for 019 * Ebean users. 020 * 021 * <p> 022 * Note that there is a avaje-ebeanorm-mocker project that enables you to use Mockito or similar 023 * tools to still mock out the underlying 'default EbeanServer' for testing purposes. 024 * 025 * <p> 026 * You may choose not use this Model mapped superclass if you don't like the 'Active Record' style 027 * or if you believe it 'pollutes' your entity beans. 028 * 029 * <p> 030 * You can use Dependency Injection like Guice or Spring to construct and wire a EbeanServer instance 031 * and have that same instance used with this Model and Finder. The way that works is that when the 032 * DI container creates the EbeanServer instance it can be registered with the Ebean singleton. In this 033 * way the EbeanServer instance can be injected as per normal Guice / Spring dependency injection and 034 * that same instance also used to support the Model and Finder active record style. 035 * 036 * <p> 037 * If you choose to use the Model mapped superclass you will probably also chose to additionally add 038 * a {@link Find} as a public static field to complete the active record pattern and provide a 039 * relatively nice clean way to write queries. 040 * 041 * <h3>Typical common @MappedSuperclass</h3> 042 * <pre>{@code 043 * 044 * // Typically there is a common base model that has some 045 * // common properties like the ones below 046 * 047 * @MappedSuperclass 048 * public class BaseModel extends Model { 049 * 050 * @Id Long id; 051 * 052 * @Version Long version; 053 * 054 * @CreatedTimestamp Timestamp whenCreated; 055 * 056 * @UpdatedTimestamp Timestamp whenUpdated; 057 * 058 * ... 059 * 060 * }</pre> 061 * 062 * <h3>Extend the Model</h3> 063 * <pre>{@code 064 * 065 * // Extend the mappedSuperclass 066 * 067 * @Entity @Table(name="oto_account") 068 * public class Customer extends BaseModel { 069 * 070 * // Add a static Find 071 * // ... with Long being the type of our @Id property. 072 * // ... Note the {} at the end as Find is an abstract class. 073 * 074 * public static final Find<Long,Account> find = new Find<Long,Account>(){}; 075 * 076 * String name; 077 * ... 078 * } 079 * 080 * }</pre> 081 * 082 * <h3>Modal: save()</h3> 083 * <pre>{@code 084 * 085 * // Active record style ... save(), delete() etc 086 * Customer customer = new Customer(); 087 * customer.setName("AC234"); 088 * 089 * // save() method inherited from Model 090 * customer.save(); 091 * 092 * }</pre> 093 * 094 * <h3>Find byId</h3> 095 * <pre>{@code 096 * 097 * // find byId 098 * Customer customer = Customer.find.byId(42); 099 * 100 * }</pre> 101 * 102 * <h3>Find where</h3> 103 * <pre>{@code 104 * 105 * // find where ... 106 * List<Customer> customers = 107 * Customer.find 108 * .where().gt("startDate", lastMonth) 109 * .findList(); 110 * 111 * }</pre> 112 */ 113@MappedSuperclass 114public abstract class Model { 115 116 /** 117 * Return the underlying 'default' EbeanServer. 118 * 119 * <p> 120 * This provides full access to the API such as explicit transaction demarcation etc. 121 * 122 * <p> 123 * Example: 124 * <pre>{@code 125 * 126 * Transaction transaction = Customer.db().beginTransaction(); 127 * try { 128 * 129 * // turn off cascade persist for this transaction 130 * transaction.setPersistCascade(false); 131 * 132 * // extra control over jdbc batching for this transaction 133 * transaction.setBatchGetGeneratedKeys(false); 134 * transaction.setBatchMode(true); 135 * transaction.setBatchSize(20); 136 * 137 * Customer customer = new Customer(); 138 * customer.setName("Roberto"); 139 * customer.save(); 140 * 141 * Customer otherCustomer = new Customer(); 142 * otherCustomer.setName("Franko"); 143 * otherCustomer.save(); 144 * 145 * transaction.commit(); 146 * 147 * } finally { 148 * transaction.end(); 149 * } 150 * 151 * }</pre> 152 */ 153 public static EbeanServer db() { 154 return Ebean.getDefaultServer(); 155 } 156 157 /** 158 * Return a named EbeanServer that is typically different to the default server. 159 * 160 * <p> 161 * If you are using multiple databases then each database has a name and maps to a single 162 * EbeanServer. You can use this method to get an EbeanServer for another database. 163 * 164 * @param server 165 * The name of the EbeanServer. If this is null then the default EbeanServer is returned. 166 */ 167 public static EbeanServer db(String server) { 168 return Ebean.getServer(server); 169 } 170 171 /** 172 * Marks the entity bean as dirty. 173 * <p> 174 * This is used so that when a bean that is otherwise unmodified is updated the version 175 * property is updated. 176 * <p> 177 * An unmodified bean that is saved or updated is normally skipped and this marks the bean as 178 * dirty so that it is not skipped. 179 * 180 * <pre>{@code 181 * 182 * Customer customer = Customer.find.byId(id); 183 * 184 * // mark the bean as dirty so that a save() or update() will 185 * // increment the version property 186 * customer.markAsDirty(); 187 * customer.save(); 188 * 189 * }</pre> 190 * 191 * @see EbeanServer#markAsDirty(Object) 192 */ 193 public void markAsDirty() { 194 db().markAsDirty(this); 195 } 196 197 /** 198 * Mark the property as unset or 'not loaded'. 199 * <p> 200 * This would be used to specify a property that we did not wish to include in a stateless update. 201 * </p> 202 * <pre>{@code 203 * 204 * // populate an entity bean from JSON or whatever 205 * User user = ...; 206 * 207 * // mark the email property as 'unset' so that it is not 208 * // included in a 'stateless update' 209 * user.markPropertyUnset("email"); 210 * 211 * user.update(); 212 * 213 * }</pre> 214 * 215 * @param propertyName the name of the property on the bean to be marked as 'unset' 216 */ 217 public void markPropertyUnset(String propertyName) { 218 ((EntityBean)this)._ebean_getIntercept().setPropertyLoaded(propertyName, false); 219 } 220 221 /** 222 * Insert or update this entity depending on its state. 223 * 224 * <p> 225 * Ebean will detect if this is a new bean or a previously fetched bean and perform either an 226 * insert or an update based on that. 227 * 228 * @see EbeanServer#save(Object) 229 */ 230 public void save() { 231 db().save(this); 232 } 233 234 /** 235 * Update this entity. 236 * 237 * @see EbeanServer#update(Object) 238 */ 239 public void update() { 240 db().update(this); 241 } 242 243 /** 244 * Insert this entity. 245 * 246 * @see EbeanServer#insert(Object) 247 */ 248 public void insert() { 249 db().insert(this); 250 } 251 252 /** 253 * Delete this bean. 254 * <p> 255 * This will return true if the bean was deleted successfully or JDBC batch is being used. 256 * </p> 257 * <p> 258 * If there is no current transaction one will be created and committed for 259 * you automatically. 260 * </p> 261 * <p> 262 * If the Bean does not have a version property (or loaded version property) and 263 * the bean does not exist then this returns false indicating that nothing was 264 * deleted. Note that, if JDBC batch mode is used then this always returns true. 265 * </p> 266 * 267 * @see EbeanServer#delete(Object) 268 */ 269 public boolean delete() { 270 return db().delete(this); 271 } 272 273 /** 274 * Delete a bean permanently without soft delete. 275 * <p> 276 * This is used when the bean contains a <code>@SoftDelete</code> property and we 277 * want to perform a hard/permanent delete. 278 * </p> 279 * 280 * @see EbeanServer#deletePermanent(Object) 281 */ 282 public boolean deletePermanent() { 283 return db().deletePermanent(this); 284 } 285 286 /** 287 * Perform an update using this entity against the specified server. 288 */ 289 public void update(String server) { 290 db(server).update(this); 291 } 292 293 /** 294 * Perform an insert using this entity against the specified server. 295 */ 296 public void insert(String server) { 297 db(server).insert(this); 298 } 299 300 /** 301 * Perform a delete using this entity against the specified server. 302 */ 303 public boolean delete(String server) { 304 return db(server).delete(this); 305 } 306 307 /** 308 * Refreshes this entity from the database. 309 * 310 * @see EbeanServer#refresh(Object) 311 */ 312 public void refresh() { 313 db().refresh(this); 314 } 315 316 /** 317 * A concrete implementation of Find. 318 * <p> 319 * It should be preferred to use {@link Find} instead of Finder as that can use reflection to determine the class 320 * literal type of the entity bean. 321 * </p> 322 * @param <I> type of the Id property 323 * @param <T> type of the entity bean 324 */ 325 public static class Finder<I, T> extends Find<I, T> { 326 327 /** 328 * Create with the type of the entity bean. 329 * 330 * <pre>{@code 331 * 332 * @Entity 333 * public class Customer extends BaseModel { 334 * 335 * public static final Finder<Long,Customer> find = new Finder<Long,Customer>(Customer.class); 336 * ... 337 * 338 * }</pre> 339 * 340 * <p/> 341 * The preferred approach is to instead use <code>Find</code> as below. This approach is more DRY in that it does 342 * not require the class literal Customer.class to be passed into the constructor. 343 * 344 * <pre>{@code 345 * 346 * @Entity 347 * public class Customer extends BaseModel { 348 * 349 * public static final Find<Long,Customer> find = new Find<Long,Customer>(){}; 350 * ... 351 * 352 * }</pre> 353 */ 354 public Finder(Class<T> type) { 355 super(null, type); 356 } 357 358 /** 359 * Create with the type of the entity bean and specific server name. 360 */ 361 public Finder(String serverName, Class<T> type) { 362 super(serverName, type); 363 } 364 365 } 366 367 /** 368 * Helper object for performing queries. 369 * 370 * <p> 371 * Typically a Find instance is defined as a public static field on an entity bean class to provide a 372 * nice way to write queries. 373 * 374 * <h3>Example use:</h3> 375 * 376 * <pre>{@code 377 * 378 * @Entity 379 * public class Customer extends BaseModel { 380 * 381 * public static final Find<Long,Customer> find = new Find<Long,Customer>(){}; 382 * 383 * ... 384 * 385 * }</pre> 386 * <p/> 387 * This enables you to write code like: 388 * <pre>{@code 389 * 390 * Customer customer = Customer.find.byId(42L); 391 * 392 * List<Customer> customers = 393 * Customer.find 394 * .select("name, dateOfBirth") 395 * .findList(); 396 * 397 * }</pre> 398 * 399 * <h3>Kotlin</h3> 400 * In Kotlin you would typically create Find as a companion object. 401 * <pre>{@code 402 * 403 * // kotlin 404 * companion object : Model.Find<Long, Product>() {} 405 * 406 * }</pre> 407 * @param <I> 408 * The Id type. This is most often a {@link Long} but is also often a {@link UUID} or 409 * {@link String}. 410 * 411 * @param <T> 412 * The entity bean type 413 */ 414 public static abstract class Find<I, T> { 415 416 /** 417 * The entity bean type. 418 */ 419 private final Class<T> type; 420 421 /** 422 * The name of the EbeanServer, null for the default server. 423 */ 424 private final String serverName; 425 426 /** 427 * Creates a finder for entity of type <code>T</code> with ID of type <code>I</code>. 428 * <p/> 429 * Typically you create Find as a public static field on each entity bean as the example below. 430 * 431 * <p/> 432 * Note that Find is an abstract class and hence <code>{}</code> is required. This is done so 433 * that the type (class literal) of the entity bean can be derived from the generics parameter. 434 * 435 * <pre>{@code 436 * 437 * @Entity 438 * public class Customer extends BaseModel { 439 * 440 * // Note the trailing {} as Find is an abstract class. 441 * // We do this so that we can derive the type literal Customer.class 442 * // via reflection 443 * public static final Find<Long,Customer> find = new Find<Long,Customer>(){}; 444 * ... 445 * 446 * }</pre> 447 * <p/> 448 * This enables you to write code like: 449 * <pre>{@code 450 * 451 * Customer customer = Customer.find.byId(42L); 452 * 453 * List<Customer> customers = 454 * Customer.find 455 * .select("name, email, dateOfBirth") 456 * .findList(); 457 * 458 * }</pre> 459 * 460 * <h3>Kotlin</h3> 461 * In Kotlin you would typically create it as a companion object. 462 * 463 * <pre>{@code 464 * 465 * // kotlin 466 * companion object : Model.Find<Long, Product>() {} 467 * 468 * }</pre> 469 */ 470 @SuppressWarnings("unchecked") 471 public Find() { 472 this.serverName = null; 473 this.type = (Class<T>)ClassUtil.getSecondArgumentType(getClass()); 474 } 475 476 /** 477 * Construct passing the class literal type of the entity type. 478 */ 479 protected Find(String serverName, Class<T> type) { 480 this.serverName = serverName; 481 this.type = type; 482 } 483 484 /** 485 * Return the underlying 'default' EbeanServer. 486 * 487 * <p> 488 * This provides full access to the API such as explicit transaction demarcation etc. 489 * 490 */ 491 public EbeanServer db() { 492 return Ebean.getServer(serverName); 493 } 494 495 /** 496 * Return typically a different EbeanServer to the default. 497 * <p> 498 * This is equivilent to {@link Ebean#getServer(String)} 499 * 500 * @param server 501 * The name of the EbeanServer. If this is null then the default EbeanServer is 502 * returned. 503 */ 504 public EbeanServer db(String server) { 505 return Ebean.getServer(server); 506 } 507 508 /** 509 * Creates a Finder for the named EbeanServer. 510 * 511 * <p> 512 * Create and return a new Finder for a different server. 513 */ 514 public Finder<I, T> on(String server) { 515 return new Finder<I, T>(server, type); 516 } 517 518 /** 519 * Delete a bean by Id. 520 * <p> 521 * Equivalent to {@link EbeanServer#delete(Class, Object)} 522 */ 523 public void deleteById(I id) { 524 db().delete(type, id); 525 } 526 527 /** 528 * Retrieves all entities of the given type. 529 * 530 * <p> 531 * This is the same as (synonym for) {@link #findList()} 532 */ 533 public List<T> all() { 534 return findList(); 535 } 536 537 /** 538 * Retrieves an entity by ID. 539 * 540 * <p> 541 * Equivalent to {@link EbeanServer#find(Class, Object)} 542 */ 543 @Nullable 544 public T byId(I id) { 545 return db().find(type, id); 546 } 547 548 /** 549 * Creates an entity reference for this ID. 550 * 551 * <p> 552 * Equivalent to {@link EbeanServer#getReference(Class, Object)} 553 */ 554 public T ref(I id) { 555 return db().getReference(type, id); 556 } 557 558 /** 559 * Creates a filter for sorting and filtering lists of entities locally without going back to 560 * the database. 561 * <p> 562 * Equivalent to {@link EbeanServer#filter(Class)} 563 */ 564 public Filter<T> filter() { 565 return db().filter(type); 566 } 567 568 /** 569 * Creates a query. 570 * <p> 571 * Equivalent to {@link EbeanServer#find(Class)} 572 */ 573 public Query<T> query() { 574 return db().find(type); 575 } 576 577 /** 578 * Creates a query applying the path properties to set the select and fetch clauses. 579 * <p> 580 * Equivalent to {@link Query#apply(FetchPath)} 581 */ 582 public Query<T> apply(FetchPath fetchPath) { 583 return db().find(type).apply(fetchPath); 584 } 585 586 /** 587 * Returns the next identity value. 588 * 589 * @see EbeanServer#nextId(Class) 590 */ 591 @SuppressWarnings("unchecked") 592 public I nextId() { 593 return (I) db().nextId(type); 594 } 595 596 /** 597 * Executes a query and returns the results as a list of IDs. 598 * <p> 599 * Equivalent to {@link Query#findIds()} 600 */ 601 public List<Object> findIds() { 602 return query().findIds(); 603 } 604 605 /** 606 * Execute the query consuming each bean one at a time. 607 * <p> 608 * This is generally used to process large queries where unlike findList 609 * you do not want to hold all the results in memory at once but instead 610 * process them one at a time (requiring far less memory). 611 * </p> 612 * Equivalent to {@link Query#findEach(QueryEachConsumer)} 613 */ 614 public void findEach(QueryEachConsumer<T> consumer) { 615 query().findEach(consumer); 616 } 617 618 /** 619 * Execute the query consuming each bean one at a time. 620 * <p> 621 * Equivalent to {@link Query#findEachWhile(QueryEachWhileConsumer)} 622 * <p> 623 * This is similar to #findEach except that you return boolean 624 * true to continue processing beans and return false to stop 625 * processing early. 626 * </p> 627 * <p> 628 * This is generally used to process large queries where unlike findList 629 * you do not want to hold all the results in memory at once but instead 630 * process them one at a time (requiring far less memory). 631 * </p> 632 * Equivalent to {@link Query#findEachWhile(QueryEachWhileConsumer)} 633 */ 634 public void findEachWhile(QueryEachWhileConsumer<T> consumer) { 635 query().findEachWhile(consumer); 636 } 637 638 /** 639 * Retrieves all entities of the given type. 640 * <p> 641 * The same as {@link #all()} 642 * <p> 643 * Equivalent to {@link Query#findList()} 644 */ 645 public List<T> findList() { 646 return query().findList(); 647 } 648 649 /** 650 * Returns all the entities of the given type as a set. 651 * <p> 652 * Equivalent to {@link Query#findSet()} 653 */ 654 public Set<T> findSet() { 655 return query().findSet(); 656 } 657 658 /** 659 * Retrieves all entities of the given type as a map of objects. 660 * <p> 661 * Equivalent to {@link Query#findMap()} 662 */ 663 public Map<?, T> findMap() { 664 return query().findMap(); 665 } 666 667 /** 668 * Executes the query and returns the results as a map of the objects specifying the map key 669 * property. 670 * <p> 671 * Equivalent to {@link Query#findMap(String, Class)} 672 */ 673 public <K> Map<K, T> findMap(String keyProperty, Class<K> keyType) { 674 return query().findMap(keyProperty, keyType); 675 } 676 677 /** 678 * Executes a find row count query in a background thread. 679 * <p> 680 * Equivalent to {@link Query#findFutureRowCount()} 681 */ 682 public FutureRowCount<T> findFutureRowCount() { 683 return query().findFutureRowCount(); 684 } 685 686 /** 687 * Returns the total number of entities for this type. * 688 * <p> 689 * Equivalent to {@link Query#findRowCount()} 690 */ 691 public int findRowCount() { 692 return query().findRowCount(); 693 } 694 695 /** 696 * Returns the <code>ExpressionFactory</code> used by this query. 697 */ 698 public ExpressionFactory getExpressionFactory() { 699 return query().getExpressionFactory(); 700 } 701 702 /** 703 * Explicitly sets a comma delimited list of the properties to fetch on the 'main' entity bean, 704 * to load a partial object. 705 * <p> 706 * Equivalent to {@link Query#select(String)} 707 */ 708 public Query<T> select(String fetchProperties) { 709 return query().select(fetchProperties); 710 } 711 712 /** 713 * Specifies a path to load including all its properties. 714 * <p> 715 * Equivalent to {@link Query#fetch(String)} 716 */ 717 public Query<T> fetch(String path) { 718 return query().fetch(path); 719 } 720 721 /** 722 * Additionally specifies a <code>FetchConfig</code> to specify a 'query join' and/or define the 723 * lazy loading query. 724 * <p> 725 * Equivalent to {@link Query#fetch(String, FetchConfig)} 726 */ 727 public Query<T> fetch(String path, FetchConfig joinConfig) { 728 return query().fetch(path, joinConfig); 729 } 730 731 /** 732 * Specifies a path to fetch with a specific list properties to include, to load a partial 733 * object. 734 * <p> 735 * Equivalent to {@link Query#fetch(String, String)} 736 */ 737 public Query<T> fetch(String path, String fetchProperties) { 738 return query().fetch(path, fetchProperties); 739 } 740 741 /** 742 * Additionally specifies a <code>FetchConfig</code> to use a separate query or lazy loading to 743 * load this path. 744 * <p> 745 * Equivalent to {@link Query#fetch(String, String, FetchConfig)} 746 */ 747 public Query<T> fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig) { 748 return query().fetch(assocProperty, fetchProperties, fetchConfig); 749 } 750 751 /** 752 * Adds expressions to the <code>where</code> clause with the ability to chain on the 753 * <code>ExpressionList</code>. 754 * <p> 755 * Equivalent to {@link Query#where()} 756 */ 757 public ExpressionList<T> where() { 758 return query().where(); 759 } 760 761 /** 762 * Returns the <code>order by</code> clause so that you can append an ascending or descending 763 * property to the <code>order by</code> clause. 764 * <p> 765 * This is exactly the same as {@link #orderBy}. 766 * <p> 767 * Equivalent to {@link Query#order()} 768 */ 769 public OrderBy<T> order() { 770 return query().order(); 771 } 772 773 /** 774 * Sets the <code>order by</code> clause, replacing the existing <code>order by</code> clause if 775 * there is one. 776 * <p> 777 * This is exactly the same as {@link #orderBy(String)}. 778 */ 779 public Query<T> order(String orderByClause) { 780 return query().order(orderByClause); 781 } 782 783 /** 784 * Returns the <code>order by</code> clause so that you can append an ascending or descending 785 * property to the <code>order by</code> clause. 786 * <p> 787 * This is exactly the same as {@link #order}. 788 * <p> 789 * Equivalent to {@link Query#orderBy()} 790 */ 791 public OrderBy<T> orderBy() { 792 return query().orderBy(); 793 } 794 795 /** 796 * Set the <code>order by</code> clause replacing the existing <code>order by</code> clause if 797 * there is one. 798 * <p> 799 * This is exactly the same as {@link #order(String)}. 800 */ 801 public Query<T> orderBy(String orderByClause) { 802 return query().orderBy(orderByClause); 803 } 804 805 /** 806 * Sets the first row to return for this query. 807 * <p> 808 * Equivalent to {@link Query#setFirstRow(int)} 809 */ 810 public Query<T> setFirstRow(int firstRow) { 811 return query().setFirstRow(firstRow); 812 } 813 814 /** 815 * Sets the maximum number of rows to return in the query. 816 * <p> 817 * Equivalent to {@link Query#setMaxRows(int)} 818 */ 819 public Query<T> setMaxRows(int maxRows) { 820 return query().setMaxRows(maxRows); 821 } 822 823 /** 824 * Sets the ID value to query. 825 * 826 * <p> 827 * Use this to perform a find byId query but with additional control over the query such as 828 * using select and fetch to control what parts of the object graph are returned. 829 * <p> 830 * Equivalent to {@link Query#setId(Object)} 831 */ 832 public Query<T> setId(Object id) { 833 return query().setId(id); 834 } 835 836 /** 837 * Create and return a new query based on the <code>RawSql</code>. 838 * <p> 839 * Equivalent to {@link Query#setRawSql(RawSql)} 840 */ 841 public Query<T> setRawSql(RawSql rawSql) { 842 return query().setRawSql(rawSql); 843 } 844 845 /** 846 * Create a query with explicit 'AutoTune' use. 847 */ 848 public Query<T> setAutoTune(boolean autoTune) { 849 return query().setAutoTune(autoTune); 850 } 851 852 /** 853 * Create a query with the select with "for update" specified. 854 * 855 * <p> 856 * This will typically create row level database locks on the selected rows. 857 */ 858 public Query<T> setForUpdate(boolean forUpdate) { 859 return query().setForUpdate(forUpdate); 860 } 861 862 /** 863 * Create a query specifying whether the returned beans will be read-only. 864 */ 865 public Query<T> setReadOnly(boolean readOnly) { 866 return query().setReadOnly(readOnly); 867 } 868 869 /** 870 * Create a query specifying if the beans should be loaded into the L2 cache. 871 */ 872 public Query<T> setLoadBeanCache(boolean loadBeanCache) { 873 return query().setLoadBeanCache(loadBeanCache); 874 } 875 876 /** 877 * Create a query specifying if the L2 bean cache should be used. 878 */ 879 public Query<T> setUseCache(boolean useBeanCache) { 880 return query().setUseCache(useBeanCache); 881 } 882 883 /** 884 * Create a query specifying if the L2 query cache should be used. 885 */ 886 public Query<T> setUseQueryCache(boolean useQueryCache) { 887 return query().setUseQueryCache(useQueryCache); 888 } 889 890 } 891}