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