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