001package io.ebean.bean; 002 003import io.ebean.DB; 004import io.ebean.Database; 005import io.ebean.ValuePair; 006 007import javax.persistence.EntityNotFoundException; 008import javax.persistence.PersistenceException; 009import java.io.Serializable; 010import java.math.BigDecimal; 011import java.net.URL; 012import java.util.Arrays; 013import java.util.LinkedHashMap; 014import java.util.LinkedHashSet; 015import java.util.Map; 016import java.util.Set; 017import java.util.concurrent.locks.Lock; 018import java.util.concurrent.locks.ReentrantLock; 019 020/** 021 * This is the object added to every entity bean using byte code enhancement. 022 * <p> 023 * This provides the mechanisms to support deferred fetching of reference beans 024 * and oldValues generation for concurrency checking. 025 * </p> 026 */ 027public final class EntityBeanIntercept implements Serializable { 028 029 private static final long serialVersionUID = -3664031775464862649L; 030 031 private static final int STATE_NEW = 0; 032 private static final int STATE_REFERENCE = 1; 033 private static final int STATE_LOADED = 2; 034 035 private transient final ReentrantLock lock = new ReentrantLock(); 036 037 private transient NodeUsageCollector nodeUsageCollector; 038 039 private transient PersistenceContext persistenceContext; 040 041 private transient BeanLoader beanLoader; 042 043 private transient PreGetterCallback preGetterCallback; 044 045 private String ebeanServerName; 046 047 private boolean deletedFromCollection; 048 049 /** 050 * The actual entity bean that 'owns' this intercept. 051 */ 052 private final EntityBean owner; 053 054 private EntityBean embeddedOwner; 055 private int embeddedOwnerIndex; 056 057 /** 058 * One of NEW, REF, UPD. 059 */ 060 private int state; 061 062 private boolean forceUpdate; 063 064 private boolean readOnly; 065 066 private boolean dirty; 067 068 /** 069 * Flag set to disable lazy loading - typically for SQL "report" type entity beans. 070 */ 071 private boolean disableLazyLoad; 072 073 /** 074 * Flag set when lazy loading failed due to the underlying bean being deleted in the DB. 075 */ 076 private boolean lazyLoadFailure; 077 078 /** 079 * Used when a bean is partially filled. 080 */ 081 private static final byte FLAG_LOADED_PROP = 1; 082 083 /** 084 * Set of changed properties. 085 */ 086 private static final byte FLAG_CHANGED_PROP = 2; 087 088 /** 089 * Flags indicating if a property is a dirty embedded bean. Used to distinguish 090 * between an embedded bean being completely overwritten and one of its 091 * embedded properties being made dirty. 092 */ 093 private static final byte FLAG_EMBEDDED_DIRTY = 4; 094 095 /** 096 * Flags indicating if a property is a dirty embedded bean. Used to distinguish 097 * between an embedded bean being completely overwritten and one of its 098 * embedded properties being made dirty. 099 */ 100 private static final byte FLAG_ORIG_VALUE_SET = 8; 101 102 private final byte[] flags; 103 104 private boolean fullyLoadedBean; 105 private boolean loadedFromCache; 106 private Object[] origValues; 107 private Exception[] loadErrors; 108 private int lazyLoadProperty = -1; 109 private Object ownerId; 110 private int sortOrder; 111 112 /** 113 * Create a intercept with a given entity. 114 */ 115 public EntityBeanIntercept(Object ownerBean) { 116 this.owner = (EntityBean) ownerBean; 117 this.flags = new byte[owner._ebean_getPropertyNames().length]; 118 } 119 120 /** 121 * EXPERIMENTAL - Constructor only for use by serialization frameworks. 122 */ 123 public EntityBeanIntercept() { 124 this.owner = null; 125 this.flags = null; 126 } 127 128 /** 129 * Return the 'owning' entity bean. 130 */ 131 public EntityBean getOwner() { 132 return owner; 133 } 134 135 /** 136 * Return the persistenceContext. 137 */ 138 public PersistenceContext getPersistenceContext() { 139 return persistenceContext; 140 } 141 142 /** 143 * Set the persistenceContext. 144 */ 145 public void setPersistenceContext(PersistenceContext persistenceContext) { 146 this.persistenceContext = persistenceContext; 147 } 148 149 /** 150 * Turn on profile collection. 151 */ 152 public void setNodeUsageCollector(NodeUsageCollector usageCollector) { 153 this.nodeUsageCollector = usageCollector; 154 } 155 156 /** 157 * Return the ownerId (IdClass). 158 */ 159 public Object getOwnerId() { 160 return ownerId; 161 } 162 163 /** 164 * Set the ownerId (IdClass). 165 */ 166 public void setOwnerId(Object ownerId) { 167 this.ownerId = ownerId; 168 } 169 170 /** 171 * Return the owning bean for an embedded bean. 172 */ 173 public Object getEmbeddedOwner() { 174 return embeddedOwner; 175 } 176 177 /** 178 * Return the property index (for the parent) of this embedded bean. 179 */ 180 public int getEmbeddedOwnerIndex() { 181 return embeddedOwnerIndex; 182 } 183 184 /** 185 * Clear the getter callback. 186 */ 187 public void clearGetterCallback() { 188 this.preGetterCallback = null; 189 } 190 191 /** 192 * Register the callback to be triggered when getter is called. 193 * This is used primarily to automatically flush the JDBC batch. 194 */ 195 public void registerGetterCallback(PreGetterCallback getterCallback) { 196 this.preGetterCallback = getterCallback; 197 } 198 199 /** 200 * Set the embedded beans owning bean. 201 */ 202 public void setEmbeddedOwner(EntityBean parentBean, int embeddedOwnerIndex) { 203 this.embeddedOwner = parentBean; 204 this.embeddedOwnerIndex = embeddedOwnerIndex; 205 } 206 207 /** 208 * Set the BeanLoader with PersistenceContext. 209 */ 210 public void setBeanLoader(BeanLoader beanLoader, PersistenceContext ctx) { 211 this.beanLoader = beanLoader; 212 this.persistenceContext = ctx; 213 this.ebeanServerName = beanLoader.getName(); 214 } 215 216 /** 217 * Set the BeanLoader. 218 */ 219 public void setBeanLoader(BeanLoader beanLoader) { 220 this.beanLoader = beanLoader; 221 this.ebeanServerName = beanLoader.getName(); 222 } 223 224 public boolean isFullyLoadedBean() { 225 return fullyLoadedBean; 226 } 227 228 public void setFullyLoadedBean(boolean fullyLoadedBean) { 229 this.fullyLoadedBean = fullyLoadedBean; 230 } 231 232 /** 233 * Check each property to see if the bean is partially loaded. 234 */ 235 public boolean isPartial() { 236 for (byte flag : flags) { 237 if ((flag & FLAG_LOADED_PROP) == 0) { 238 return true; 239 } 240 } 241 return false; 242 } 243 244 /** 245 * Return true if this bean has been directly modified (it has oldValues) or 246 * if any embedded beans are either new or dirty (and hence need saving). 247 */ 248 public boolean isDirty() { 249 return dirty; 250 } 251 252 /** 253 * Called by an embedded bean onto its owner. 254 */ 255 public void setEmbeddedDirty(int embeddedProperty) { 256 this.dirty = true; 257 setEmbeddedPropertyDirty(embeddedProperty); 258 } 259 260 public void setDirty(boolean dirty) { 261 this.dirty = dirty; 262 } 263 264 /** 265 * Return true if this entity bean is new and not yet saved. 266 */ 267 public boolean isNew() { 268 return state == STATE_NEW; 269 } 270 271 /** 272 * Return true if the entity bean is new or dirty (and should be saved). 273 */ 274 public boolean isNewOrDirty() { 275 return isNew() || isDirty(); 276 } 277 278 /** 279 * Return true if only the Id property has been loaded. 280 */ 281 public boolean hasIdOnly(int idIndex) { 282 for (int i = 0; i < flags.length; i++) { 283 if (i == idIndex) { 284 if ((flags[i] & FLAG_LOADED_PROP) == 0) return false; 285 } else if ((flags[i] & FLAG_LOADED_PROP) != 0) { 286 return false; 287 } 288 } 289 return true; 290 } 291 292 /** 293 * Return true if the entity is a reference. 294 */ 295 public boolean isReference() { 296 return state == STATE_REFERENCE; 297 } 298 299 /** 300 * Set this as a reference object. 301 */ 302 public void setReference(int idPos) { 303 state = STATE_REFERENCE; 304 if (idPos > -1) { 305 // For cases where properties are set on constructor 306 // set every non Id property to unloaded (for lazy loading) 307 for (int i = 0; i < flags.length; i++) { 308 if (i != idPos) { 309 flags[i] &= ~FLAG_LOADED_PROP; 310 } 311 } 312 } 313 } 314 315 /** 316 * Set true when the bean has been loaded from L2 bean cache. 317 * The effect of this is that we should skip the cache if there 318 * is subsequent lazy loading (bean cache partially populated). 319 */ 320 public void setLoadedFromCache(boolean loadedFromCache) { 321 this.loadedFromCache = loadedFromCache; 322 } 323 324 /** 325 * Return true if this bean was loaded from L2 bean cache. 326 */ 327 public boolean isLoadedFromCache() { 328 return loadedFromCache; 329 } 330 331 /** 332 * Return true if the bean should be treated as readOnly. If a setter method 333 * is called when it is readOnly an Exception is thrown. 334 */ 335 public boolean isReadOnly() { 336 return readOnly; 337 } 338 339 /** 340 * Set the readOnly status. If readOnly then calls to setter methods through 341 * an exception. 342 */ 343 public void setReadOnly(boolean readOnly) { 344 this.readOnly = readOnly; 345 } 346 347 /** 348 * Set the bean to be updated when persisted (for merge). 349 */ 350 public void setForceUpdate(boolean forceUpdate) { 351 this.forceUpdate = forceUpdate; 352 } 353 354 /** 355 * Return true if the entity should be updated. 356 */ 357 public boolean isUpdate() { 358 return forceUpdate || state == STATE_LOADED; 359 } 360 361 /** 362 * Return true if the entity has been loaded. 363 */ 364 public boolean isLoaded() { 365 return state == STATE_LOADED; 366 } 367 368 /** 369 * Set the bean into NEW state. 370 */ 371 public void setNew() { 372 this.state = STATE_NEW; 373 } 374 375 /** 376 * Set the loaded state to true. 377 * <p> 378 * Calls to setter methods after the bean is loaded can result in 379 * 'Old Values' being created. 380 * <p> 381 * Worth noting that this is also set after a insert/update. By doing so it 382 * 'resets' the bean for making further changes and saving again. 383 */ 384 public void setLoaded() { 385 this.state = STATE_LOADED; 386 this.owner._ebean_setEmbeddedLoaded(); 387 this.lazyLoadProperty = -1; 388 this.origValues = null; 389 for (int i = 0; i < flags.length; i++) { 390 flags[i] &= ~(FLAG_CHANGED_PROP + FLAG_ORIG_VALUE_SET); 391 } 392 this.dirty = false; 393 } 394 395 /** 396 * When finished loading for lazy or refresh on an already partially populated bean. 397 */ 398 public void setLoadedLazy() { 399 this.state = STATE_LOADED; 400 this.lazyLoadProperty = -1; 401 } 402 403 /** 404 * Set lazy load failure flag. 405 */ 406 public void setLazyLoadFailure(Object ownerId) { 407 this.lazyLoadFailure = true; 408 this.ownerId = ownerId; 409 } 410 411 /** 412 * Return true if the bean is marked as having failed lazy loading. 413 */ 414 public boolean isLazyLoadFailure() { 415 return lazyLoadFailure; 416 } 417 418 /** 419 * Return true if lazy loading is disabled. 420 */ 421 public boolean isDisableLazyLoad() { 422 return disableLazyLoad; 423 } 424 425 /** 426 * Set true to turn off lazy loading. 427 */ 428 public void setDisableLazyLoad(boolean disableLazyLoad) { 429 this.disableLazyLoad = disableLazyLoad; 430 } 431 432 /** 433 * Set the loaded status for the embedded bean. 434 */ 435 public void setEmbeddedLoaded(Object embeddedBean) { 436 if (embeddedBean instanceof EntityBean) { 437 EntityBean eb = (EntityBean) embeddedBean; 438 eb._ebean_getIntercept().setLoaded(); 439 } 440 } 441 442 /** 443 * Return true if the embedded bean is new or dirty and hence needs saving. 444 */ 445 public boolean isEmbeddedNewOrDirty(Object embeddedBean) { 446 if (embeddedBean == null) { 447 // if it was previously set then the owning bean would 448 // have oldValues containing the previous embedded bean 449 return false; 450 } 451 if (embeddedBean instanceof EntityBean) { 452 return ((EntityBean) embeddedBean)._ebean_getIntercept().isNewOrDirty(); 453 } else { 454 // non-enhanced so must assume it is new and needs to be saved 455 return true; 456 } 457 } 458 459 /** 460 * Return the original value that was changed via an update. 461 */ 462 public Object getOrigValue(int propertyIndex) { 463 if (origValues == null) { 464 return null; 465 } 466 return origValues[propertyIndex]; 467 } 468 469 /** 470 * Finds the index position of a given property. Returns -1 if the 471 * property can not be found. 472 */ 473 public int findProperty(String propertyName) { 474 String[] names = owner._ebean_getPropertyNames(); 475 for (int i = 0; i < names.length; i++) { 476 if (names[i].equals(propertyName)) { 477 return i; 478 } 479 } 480 return -1; 481 } 482 483 /** 484 * Return the property name for the given property. 485 */ 486 public String getProperty(int propertyIndex) { 487 if (propertyIndex == -1) { 488 return null; 489 } 490 return owner._ebean_getPropertyName(propertyIndex); 491 } 492 493 /** 494 * Return the number of properties. 495 */ 496 public int getPropertyLength() { 497 return owner._ebean_getPropertyNames().length; 498 } 499 500 /** 501 * Set the loaded state of the property given it's name. 502 */ 503 public void setPropertyLoaded(String propertyName, boolean loaded) { 504 int position = findProperty(propertyName); 505 if (position == -1) { 506 throw new IllegalArgumentException("Property " + propertyName + " not found"); 507 } 508 if (loaded) { 509 flags[position] |= FLAG_LOADED_PROP; 510 } else { 511 flags[position] &= ~FLAG_LOADED_PROP; 512 } 513 } 514 515 /** 516 * Set the property to be treated as unloaded. Used for properties initialised in default constructor. 517 */ 518 public void setPropertyUnloaded(int propertyIndex) { 519 flags[propertyIndex] &= ~FLAG_LOADED_PROP; 520 } 521 522 /** 523 * Set the property to be loaded. 524 */ 525 public void setLoadedProperty(int propertyIndex) { 526 flags[propertyIndex] |= FLAG_LOADED_PROP; 527 } 528 529 /** 530 * Set all properties to be loaded (post insert). 531 */ 532 public void setLoadedPropertyAll() { 533 for (int i = 0; i < flags.length; i++) { 534 flags[i] |= FLAG_LOADED_PROP; 535 } 536 } 537 538 /** 539 * Return true if the property is loaded. 540 */ 541 public boolean isLoadedProperty(int propertyIndex) { 542 return (flags[propertyIndex] & FLAG_LOADED_PROP) != 0; 543 } 544 545 /** 546 * Return true if the property is considered changed. 547 */ 548 public boolean isChangedProperty(int propertyIndex) { 549 return (flags[propertyIndex] & FLAG_CHANGED_PROP) != 0; 550 } 551 552 /** 553 * Return true if the property was changed or if it is embedded and one of its 554 * embedded properties is dirty. 555 */ 556 public boolean isDirtyProperty(int propertyIndex) { 557 return (flags[propertyIndex] & (FLAG_CHANGED_PROP + FLAG_EMBEDDED_DIRTY)) != 0; 558 } 559 560 /** 561 * Explicitly mark a property as having been changed. 562 */ 563 public void markPropertyAsChanged(int propertyIndex) { 564 setChangedProperty(propertyIndex); 565 setDirty(true); 566 } 567 568 public void setChangedProperty(int propertyIndex) { 569 flags[propertyIndex] |= FLAG_CHANGED_PROP; 570 } 571 572 /** 573 * Set that an embedded bean has had one of its properties changed. 574 */ 575 private void setEmbeddedPropertyDirty(int propertyIndex) { 576 flags[propertyIndex] |= FLAG_EMBEDDED_DIRTY; 577 } 578 579 private void setOriginalValue(int propertyIndex, Object value) { 580 if (origValues == null) { 581 origValues = new Object[owner._ebean_getPropertyNames().length]; 582 } 583 if ((flags[propertyIndex] & FLAG_ORIG_VALUE_SET) == 0) { 584 flags[propertyIndex] |= FLAG_ORIG_VALUE_SET; 585 origValues[propertyIndex] = value; 586 } 587 } 588 589 /** 590 * Set old value but force it to be set regardless if it already has a value. 591 */ 592 private void setOriginalValueForce(int propertyIndex, Object value) { 593 if (origValues == null) { 594 origValues = new Object[owner._ebean_getPropertyNames().length]; 595 } 596 origValues[propertyIndex] = value; 597 } 598 599 /** 600 * For forced update on a 'New' bean set all the loaded properties to changed. 601 */ 602 public void setNewBeanForUpdate() { 603 for (int i = 0; i < flags.length; i++) { 604 if ((flags[i] & FLAG_LOADED_PROP) != 0) { 605 flags[i] |= FLAG_CHANGED_PROP; 606 } 607 } 608 setDirty(true); 609 } 610 611 /** 612 * Return the set of property names for a partially loaded bean. 613 */ 614 public Set<String> getLoadedPropertyNames() { 615 if (fullyLoadedBean) { 616 return null; 617 } 618 Set<String> props = new LinkedHashSet<>(); 619 for (int i = 0; i < flags.length; i++) { 620 if ((flags[i] & FLAG_LOADED_PROP) != 0) { 621 props.add(getProperty(i)); 622 } 623 } 624 return props; 625 } 626 627 /** 628 * Return the array of flags indicating the dirty properties. 629 */ 630 public boolean[] getDirtyProperties() { 631 int len = getPropertyLength(); 632 boolean[] dirties = new boolean[len]; 633 for (int i = 0; i < len; i++) { 634 // this, or an embedded property has been changed - recurse 635 dirties[i] = (flags[i] & (FLAG_CHANGED_PROP + FLAG_EMBEDDED_DIRTY)) != 0; 636 } 637 return dirties; 638 } 639 640 /** 641 * Return the set of dirty properties. 642 */ 643 public Set<String> getDirtyPropertyNames() { 644 Set<String> props = new LinkedHashSet<>(); 645 addDirtyPropertyNames(props, null); 646 return props; 647 } 648 649 /** 650 * Recursively add dirty properties. 651 */ 652 public void addDirtyPropertyNames(Set<String> props, String prefix) { 653 int len = getPropertyLength(); 654 for (int i = 0; i < len; i++) { 655 if ((flags[i] & FLAG_CHANGED_PROP) != 0) { 656 // the property has been changed on this bean 657 props.add((prefix == null ? getProperty(i) : prefix + getProperty(i))); 658 } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) { 659 // an embedded property has been changed - recurse 660 EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i); 661 embeddedBean._ebean_getIntercept().addDirtyPropertyNames(props, getProperty(i) + "."); 662 } 663 } 664 } 665 666 /** 667 * Return true if any of the given property names are dirty. 668 */ 669 public boolean hasDirtyProperty(Set<String> propertyNames) { 670 String[] names = owner._ebean_getPropertyNames(); 671 int len = getPropertyLength(); 672 for (int i = 0; i < len; i++) { 673 if ((flags[i] & FLAG_CHANGED_PROP) != 0) { 674 if (propertyNames.contains(names[i])) { 675 return true; 676 } 677 } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) { 678 if (propertyNames.contains(names[i])) { 679 return true; 680 } 681 } 682 } 683 return false; 684 } 685 686 /** 687 * Return a map of dirty properties with their new and old values. 688 */ 689 public Map<String, ValuePair> getDirtyValues() { 690 Map<String, ValuePair> dirtyValues = new LinkedHashMap<>(); 691 addDirtyPropertyValues(dirtyValues, null); 692 return dirtyValues; 693 } 694 695 /** 696 * Recursively add dirty properties. 697 */ 698 public void addDirtyPropertyValues(Map<String, ValuePair> dirtyValues, String prefix) { 699 int len = getPropertyLength(); 700 for (int i = 0; i < len; i++) { 701 if ((flags[i] & FLAG_CHANGED_PROP) != 0) { 702 // the property has been changed on this bean 703 String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i)); 704 Object newVal = owner._ebean_getField(i); 705 Object oldVal = getOrigValue(i); 706 if (notEqual(oldVal, newVal)) { 707 dirtyValues.put(propName, new ValuePair(newVal, oldVal)); 708 } 709 } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) { 710 // an embedded property has been changed - recurse 711 EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i); 712 embeddedBean._ebean_getIntercept().addDirtyPropertyValues(dirtyValues, getProperty(i) + "."); 713 } 714 } 715 } 716 717 /** 718 * Recursively add dirty properties. 719 */ 720 public void addDirtyPropertyValues(BeanDiffVisitor visitor) { 721 int len = getPropertyLength(); 722 for (int i = 0; i < len; i++) { 723 if ((flags[i] & FLAG_CHANGED_PROP) != 0) { 724 // the property has been changed on this bean 725 Object newVal = owner._ebean_getField(i); 726 Object oldVal = getOrigValue(i); 727 if (notEqual(oldVal, newVal)) { 728 visitor.visit(i, newVal, oldVal); 729 } 730 } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) { 731 // an embedded property has been changed - recurse 732 EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i); 733 visitor.visitPush(i); 734 embeddedBean._ebean_getIntercept().addDirtyPropertyValues(visitor); 735 visitor.visitPop(); 736 } 737 } 738 } 739 740 /** 741 * Return a dirty property hash taking into account embedded beans. 742 */ 743 public StringBuilder getDirtyPropertyKey() { 744 StringBuilder sb = new StringBuilder(); 745 addDirtyPropertyKey(sb); 746 return sb; 747 } 748 749 /** 750 * Add and return a dirty property hash. 751 */ 752 private void addDirtyPropertyKey(StringBuilder sb) { 753 if (sortOrder > 0) { 754 sb.append("s,"); 755 } 756 int len = getPropertyLength(); 757 for (int i = 0; i < len; i++) { 758 if ((flags[i] & FLAG_CHANGED_PROP) != 0) { 759 sb.append(i).append(','); 760 } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) { 761 // an embedded property has been changed - recurse 762 EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i); 763 sb.append(i).append('['); 764 embeddedBean._ebean_getIntercept().addDirtyPropertyKey(sb); 765 sb.append(']'); 766 } 767 } 768 } 769 770 /** 771 * Return a loaded property hash. 772 */ 773 public StringBuilder getLoadedPropertyKey() { 774 StringBuilder sb = new StringBuilder(); 775 int len = getPropertyLength(); 776 for (int i = 0; i < len; i++) { 777 if (isLoadedProperty(i)) { 778 sb.append(i).append(','); 779 } 780 } 781 return sb; 782 } 783 784 public boolean[] getLoaded() { 785 boolean[] ret = new boolean[flags.length]; 786 for (int i = 0; i < ret.length; i++) { 787 ret[i] = (flags[i] & FLAG_LOADED_PROP) != 0; 788 } 789 return ret; 790 } 791 792 /** 793 * Return the index of the property that triggered the lazy load. 794 */ 795 public int getLazyLoadPropertyIndex() { 796 return lazyLoadProperty; 797 } 798 799 /** 800 * Return the property that triggered the lazy load. 801 */ 802 public String getLazyLoadProperty() { 803 return getProperty(lazyLoadProperty); 804 } 805 806 /** 807 * Load the bean when it is a reference. 808 */ 809 protected void loadBean(int loadProperty) { 810 lock.lock(); 811 try { 812 if (beanLoader == null) { 813 final Database database = DB.byName(ebeanServerName); 814 if (database == null) { 815 throw new PersistenceException("Database [" + ebeanServerName + "] was not found?"); 816 } 817 // For stand alone reference bean or after deserialisation lazy load 818 // using the ebeanServer. Synchronise only on the bean. 819 loadBeanInternal(loadProperty, database.getPluginApi().beanLoader()); 820 return; 821 } 822 } finally { 823 lock.unlock(); 824 } 825 final Lock lock = beanLoader.lock(); 826 try { 827 // Lazy loading using LoadBeanContext which supports batch loading 828 // Synchronise on the beanLoader (a 'node' of the LoadBeanContext 'tree') 829 loadBeanInternal(loadProperty, beanLoader); 830 } finally { 831 lock.unlock(); 832 } 833 } 834 835 /** 836 * Invoke the lazy loading. This method is synchronised externally. 837 */ 838 private void loadBeanInternal(int loadProperty, BeanLoader loader) { 839 if ((flags[loadProperty] & FLAG_LOADED_PROP) != 0) { 840 // race condition where multiple threads calling preGetter concurrently 841 return; 842 } 843 if (lazyLoadFailure) { 844 // failed when batch lazy loaded by another bean in the batch 845 throw new EntityNotFoundException("(Lazy) loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted"); 846 } 847 if (lazyLoadProperty == -1) { 848 lazyLoadProperty = loadProperty; 849 if (nodeUsageCollector != null) { 850 nodeUsageCollector.setLoadProperty(getProperty(lazyLoadProperty)); 851 } 852 loader.loadBean(this); 853 if (lazyLoadFailure) { 854 // failed when lazy loading this bean 855 throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted."); 856 } 857 // bean should be loaded and intercepting now. setLoaded() has 858 // been called by the lazy loading mechanism 859 } 860 } 861 862 /** 863 * Helper method to check if two objects are equal. 864 */ 865 @SuppressWarnings({"unchecked", "rawtypes"}) 866 protected static boolean notEqual(Object obj1, Object obj2) { 867 if (obj1 == null) { 868 return (obj2 != null); 869 } 870 if (obj2 == null) { 871 return true; 872 } 873 if (obj1 == obj2) { 874 return false; 875 } 876 if (obj1 instanceof BigDecimal) { 877 // Use comparable for BigDecimal as equals 878 // uses scale in comparison... 879 if (obj2 instanceof BigDecimal) { 880 Comparable com1 = (Comparable) obj1; 881 return (com1.compareTo(obj2) != 0); 882 } else { 883 return true; 884 } 885 } 886 if (obj1 instanceof URL) { 887 // use the string format to determine if dirty 888 return !obj1.toString().equals(obj2.toString()); 889 } 890 return !obj1.equals(obj2); 891 } 892 893 /** 894 * Called when a BeanCollection is initialised automatically. 895 */ 896 public void initialisedMany(int propertyIndex) { 897 flags[propertyIndex] |= FLAG_LOADED_PROP; 898 } 899 900 private void preGetterCallback(int propertyIndex) { 901 PreGetterCallback preGetterCallback = this.preGetterCallback; 902 if (preGetterCallback != null) { 903 preGetterCallback.preGetterTrigger(propertyIndex); 904 } 905 } 906 907 /** 908 * Called prior to Id property getter. 909 */ 910 public void preGetId() { 911 preGetterCallback(-1); 912 } 913 914 /** 915 * Method that is called prior to a getter method on the actual entity. 916 */ 917 public void preGetter(int propertyIndex) { 918 preGetterCallback(propertyIndex); 919 if (state == STATE_NEW || disableLazyLoad) { 920 return; 921 } 922 if (!isLoadedProperty(propertyIndex)) { 923 loadBean(propertyIndex); 924 } 925 if (nodeUsageCollector != null) { 926 nodeUsageCollector.addUsed(getProperty(propertyIndex)); 927 } 928 } 929 930 /** 931 * OneToMany and ManyToMany only set loaded state. 932 */ 933 public void preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) { 934 if (state == STATE_NEW) { 935 setLoadedProperty(propertyIndex); 936 } else { 937 if (readOnly) { 938 throw new IllegalStateException("This bean is readOnly"); 939 } 940 setChangedProperty(propertyIndex); 941 } 942 } 943 944 private void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) { 945 if (readOnly) { 946 throw new IllegalStateException("This bean is readOnly"); 947 } 948 setChangedProperty(propertyIndex); 949 if (setDirtyState) { 950 setOriginalValue(propertyIndex, origValue); 951 setDirtyStatus(); 952 } 953 } 954 955 private void setDirtyStatus() { 956 if (!dirty) { 957 dirty = true; 958 if (embeddedOwner != null) { 959 // Cascade dirty state from Embedded bean to parent bean 960 embeddedOwner._ebean_getIntercept().setEmbeddedDirty(embeddedOwnerIndex); 961 } 962 if (nodeUsageCollector != null) { 963 nodeUsageCollector.setModified(); 964 } 965 } 966 } 967 968 /** 969 * Check to see if the values are not equal. If they are not equal then create 970 * the old values for use with ConcurrencyMode.ALL. 971 */ 972 public void preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) { 973 if (state == STATE_NEW) { 974 setLoadedProperty(propertyIndex); 975 } else if (notEqual(oldValue, newValue)) { 976 setChangedPropertyValue(propertyIndex, intercept, oldValue); 977 } 978 } 979 980 981 /** 982 * Check for primitive boolean. 983 */ 984 public void preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) { 985 if (state == STATE_NEW) { 986 setLoadedProperty(propertyIndex); 987 } else if (oldValue != newValue) { 988 setChangedPropertyValue(propertyIndex, intercept, oldValue); 989 } 990 } 991 992 /** 993 * Check for primitive int. 994 */ 995 public void preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) { 996 if (state == STATE_NEW) { 997 setLoadedProperty(propertyIndex); 998 } else if (oldValue != newValue) { 999 setChangedPropertyValue(propertyIndex, intercept, oldValue); 1000 } 1001 } 1002 1003 /** 1004 * long. 1005 */ 1006 public void preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) { 1007 if (state == STATE_NEW) { 1008 setLoadedProperty(propertyIndex); 1009 } else if (oldValue != newValue) { 1010 setChangedPropertyValue(propertyIndex, intercept, oldValue); 1011 } 1012 } 1013 1014 /** 1015 * double. 1016 */ 1017 public void preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) { 1018 if (state == STATE_NEW) { 1019 setLoadedProperty(propertyIndex); 1020 } else if (Double.compare(oldValue, newValue) != 0) { 1021 setChangedPropertyValue(propertyIndex, intercept, oldValue); 1022 } 1023 } 1024 1025 /** 1026 * float. 1027 */ 1028 public void preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) { 1029 if (state == STATE_NEW) { 1030 setLoadedProperty(propertyIndex); 1031 } else if (Float.compare(oldValue, newValue) != 0) { 1032 setChangedPropertyValue(propertyIndex, intercept, oldValue); 1033 } 1034 } 1035 1036 /** 1037 * short. 1038 */ 1039 public void preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) { 1040 if (state == STATE_NEW) { 1041 setLoadedProperty(propertyIndex); 1042 } else if (oldValue != newValue) { 1043 setChangedPropertyValue(propertyIndex, intercept, oldValue); 1044 } 1045 } 1046 1047 /** 1048 * char. 1049 */ 1050 public void preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) { 1051 if (state == STATE_NEW) { 1052 setLoadedProperty(propertyIndex); 1053 } else if (oldValue != newValue) { 1054 setChangedPropertyValue(propertyIndex, intercept, oldValue); 1055 } 1056 } 1057 1058 /** 1059 * byte. 1060 */ 1061 public void preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) { 1062 if (state == STATE_NEW) { 1063 setLoadedProperty(propertyIndex); 1064 } else if (oldValue != newValue) { 1065 setChangedPropertyValue(propertyIndex, intercept, oldValue); 1066 } 1067 } 1068 1069 /** 1070 * char[]. 1071 */ 1072 public void preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) { 1073 if (state == STATE_NEW) { 1074 setLoadedProperty(propertyIndex); 1075 } else if (!Arrays.equals(oldValue, newValue)) { 1076 setChangedPropertyValue(propertyIndex, intercept, oldValue); 1077 } 1078 } 1079 1080 /** 1081 * byte[]. 1082 */ 1083 public void preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) { 1084 if (state == STATE_NEW) { 1085 setLoadedProperty(propertyIndex); 1086 } else if (!Arrays.equals(oldValue, newValue)) { 1087 setChangedPropertyValue(propertyIndex, intercept, oldValue); 1088 } 1089 } 1090 1091 /** 1092 * Explicitly set an old value with force (the old value is forced even it is already set). 1093 */ 1094 public void setOldValue(int propertyIndex, Object oldValue) { 1095 setChangedProperty(propertyIndex); 1096 setOriginalValueForce(propertyIndex, oldValue); 1097 setDirtyStatus(); 1098 } 1099 1100 /** 1101 * Return the sort order value for an order column. 1102 */ 1103 public int getSortOrder() { 1104 return sortOrder; 1105 } 1106 1107 /** 1108 * Set the sort order value for an order column. 1109 */ 1110 public void setSortOrder(int sortOrder) { 1111 this.sortOrder = sortOrder; 1112 } 1113 1114 /** 1115 * Set if the entity was deleted from a BeanCollection. 1116 */ 1117 public void setDeletedFromCollection(final boolean deletedFromCollection) { 1118 this.deletedFromCollection = deletedFromCollection; 1119 } 1120 1121 public boolean isOrphanDelete() { 1122 return deletedFromCollection && !isNew(); 1123 } 1124 1125 /** 1126 * Set the load error that happened on this property. 1127 */ 1128 public void setLoadError(int propertyIndex, Exception t) { 1129 if (loadErrors == null) { 1130 loadErrors = new Exception[owner._ebean_getPropertyNames().length]; 1131 } 1132 loadErrors[propertyIndex] = t; 1133 flags[propertyIndex] |= FLAG_LOADED_PROP; 1134 } 1135 1136 /** 1137 * Returns the loadErrors. 1138 */ 1139 public Map<String, Exception> getLoadErrors() { 1140 if (loadErrors == null) { 1141 return null; 1142 } 1143 Map<String, Exception> ret = null; 1144 int len = getPropertyLength(); 1145 for (int i = 0; i < len; i++) { 1146 Exception loadError = loadErrors[i]; 1147 if (loadError != null) { 1148 if (ret == null) { 1149 ret = new LinkedHashMap<>(); 1150 } 1151 ret.put(getProperty(i), loadError); 1152 } 1153 } 1154 return ret; 1155 } 1156}