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