mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Experimental Feature - Freeze and detach beans (and attach)
- Frozen beans as ReadOnly + Unmodifiable collections - Add Transaction.freezeAndDetach() returning FrozenBeans - Add Transaction.attach( FrozenBeans ) - InterceptReadOnly now supports lazy loading - Query.setReadOnly(true) now uses InterceptReadOnly [when it used to ALSO require Query.setDisableLazyLoading(true)} - Add EntityBeanIntercept.errorOnLazyLoad (such that lazy loading on frozen bean is an error) - BeanList, BeanSet, BeanMap are now freezable - ModifyAwareList Set and Map are now freezable
This commit is contained in:
@@ -16,4 +16,9 @@ public interface ModifyAwareType {
|
||||
*/
|
||||
void setMarkedDirty(boolean markedDirty);
|
||||
|
||||
/**
|
||||
* Return a unmodifiable version of this type.
|
||||
*/
|
||||
Object freeze();
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebean;
|
||||
|
||||
import io.ebean.annotation.DocStoreMode;
|
||||
import io.ebean.annotation.PersistBatch;
|
||||
import io.ebean.bean.FrozenBeans;
|
||||
import io.ebean.config.DatabaseConfig;
|
||||
import io.ebean.config.DocStoreConfig;
|
||||
|
||||
@@ -54,6 +55,24 @@ public interface Transaction extends AutoCloseable {
|
||||
*/
|
||||
int SERIALIZABLE = java.sql.Connection.TRANSACTION_SERIALIZABLE;
|
||||
|
||||
|
||||
/**
|
||||
* Experimental Feature - Freeze the beans in the persistence context and detach them.
|
||||
*/
|
||||
@Deprecated(since = "Experimental Feature")
|
||||
default FrozenBeans freezeAndDetach() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Experimental Feature - Attach frozen beans to the persistence context so that they can be
|
||||
* used with ORM queries (with the persistence context acting like a cache).
|
||||
*/
|
||||
@Deprecated(since = "Experimental Feature")
|
||||
default void attach(FrozenBeans frozenBeans) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a TransactionCallback with this transaction.
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,11 @@ public interface BeanCollection<E> extends Serializable, ToStringAware {
|
||||
ALL
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a unmodifiable collection of the underlying entities.
|
||||
*/
|
||||
Object freeze();
|
||||
|
||||
/**
|
||||
* Set the disableLazyLoad state.
|
||||
*/
|
||||
|
||||
@@ -552,4 +552,16 @@ public interface EntityBeanIntercept extends Serializable {
|
||||
* Update the 'next' mutable info returning the content that was obtained via dirty detection.
|
||||
*/
|
||||
String mutableNext(int propertyIndex);
|
||||
|
||||
/**
|
||||
* Set that lazy loading if invoked throws a PersistenceException.
|
||||
*/
|
||||
void errorOnLazyLoad(boolean lazyLoadAsError);
|
||||
|
||||
/**
|
||||
* Freeze the intercept so that it is readOnly.
|
||||
* Lazy loading and mutation of the bean is not allowed.
|
||||
*/
|
||||
void freeze();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package io.ebean.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Holds entity beans that are frozen.
|
||||
*/
|
||||
public interface FrozenBeans extends Serializable {
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
package io.ebean.bean;
|
||||
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Database;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import jakarta.persistence.PersistenceException;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Base EntityBeanIntercept that supports partial loaded state and lazy loading.
|
||||
*/
|
||||
abstract class InterceptBase implements EntityBeanIntercept {
|
||||
|
||||
/**
|
||||
* Flag for loaded property.
|
||||
*/
|
||||
static final byte FLAG_LOADED_PROP = 1;
|
||||
|
||||
/**
|
||||
* The actual entity bean that 'owns' this intercept.
|
||||
*/
|
||||
protected final EntityBean owner;
|
||||
protected final byte[] flags;
|
||||
protected boolean readOnly;
|
||||
protected boolean disableLazyLoad;
|
||||
protected boolean errorOnLazyLoad;
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private String ebeanServerName;
|
||||
private transient BeanLoader beanLoader;
|
||||
private transient PersistenceContext persistenceContext;
|
||||
|
||||
/**
|
||||
* Flag set when lazy loading failed due to the underlying bean being deleted in the DB.
|
||||
*/
|
||||
protected boolean lazyLoadFailure;
|
||||
protected boolean fullyLoadedBean;
|
||||
protected int lazyLoadProperty = -1;
|
||||
protected Object ownerId;
|
||||
|
||||
/**
|
||||
* Create with a given entity.
|
||||
*/
|
||||
InterceptBase(Object ownerBean) {
|
||||
this.owner = (EntityBean) ownerBean;
|
||||
this.flags = new byte[owner._ebean_getPropertyNames().length];
|
||||
}
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL - Constructor only for use by serialization frameworks.
|
||||
*/
|
||||
InterceptBase() {
|
||||
this.owner = null;
|
||||
this.flags = null;
|
||||
}
|
||||
|
||||
public void freeze() {
|
||||
this.errorOnLazyLoad = true;
|
||||
this.readOnly = true;
|
||||
this.beanLoader = null;
|
||||
this.persistenceContext = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final EntityBean owner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object ownerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setOwnerId(Object ownerId) {
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setBeanLoader(BeanLoader beanLoader) {
|
||||
this.beanLoader = beanLoader;
|
||||
this.ebeanServerName = beanLoader.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setBeanLoader(BeanLoader beanLoader, PersistenceContext ctx) {
|
||||
this.beanLoader = beanLoader;
|
||||
this.persistenceContext = ctx;
|
||||
this.ebeanServerName = beanLoader.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final PersistenceContext persistenceContext() {
|
||||
return persistenceContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setPersistenceContext(PersistenceContext persistenceContext) {
|
||||
this.persistenceContext = persistenceContext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final boolean isFullyLoadedBean() {
|
||||
return fullyLoadedBean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setFullyLoadedBean(boolean fullyLoadedBean) {
|
||||
this.fullyLoadedBean = fullyLoadedBean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isPartial() {
|
||||
for (byte flag : flags) {
|
||||
if ((flag & FLAG_LOADED_PROP) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean hasIdOnly(int idIndex) {
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
if (i == idIndex) {
|
||||
if ((flags[i] & FLAG_LOADED_PROP) == 0) return false;
|
||||
} else if ((flags[i] & FLAG_LOADED_PROP) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setLazyLoadFailure(Object ownerId) {
|
||||
this.lazyLoadFailure = true;
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isLazyLoadFailure() {
|
||||
return lazyLoadFailure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isDisableLazyLoad() {
|
||||
return disableLazyLoad;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setDisableLazyLoad(boolean disableLazyLoad) {
|
||||
this.disableLazyLoad = disableLazyLoad;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void errorOnLazyLoad(boolean errorOnLazyLoad) {
|
||||
this.errorOnLazyLoad = errorOnLazyLoad;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setEmbeddedLoaded(Object embeddedBean) {
|
||||
if (embeddedBean instanceof EntityBean) {
|
||||
((EntityBean) embeddedBean)._ebean_getIntercept().setLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int findProperty(String propertyName) {
|
||||
final String[] names = owner._ebean_getPropertyNames();
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
if (names[i].equals(propertyName)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String property(int propertyIndex) {
|
||||
if (propertyIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
return owner._ebean_getPropertyName(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int propertyLength() {
|
||||
return flags.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setPropertyLoaded(String propertyName, boolean loaded) {
|
||||
final int position = findProperty(propertyName);
|
||||
if (position == -1) {
|
||||
throw new IllegalArgumentException("Property " + propertyName + " not found");
|
||||
}
|
||||
if (loaded) {
|
||||
flags[position] |= FLAG_LOADED_PROP;
|
||||
} else {
|
||||
flags[position] &= ~FLAG_LOADED_PROP;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setPropertyUnloaded(int propertyIndex) {
|
||||
flags[propertyIndex] &= ~FLAG_LOADED_PROP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void initialisedMany(int propertyIndex) {
|
||||
flags[propertyIndex] |= FLAG_LOADED_PROP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setLoadedProperty(int propertyIndex) {
|
||||
flags[propertyIndex] |= FLAG_LOADED_PROP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setLoadedPropertyAll() {
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
flags[i] |= FLAG_LOADED_PROP;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isLoadedProperty(int propertyIndex) {
|
||||
return (flags[propertyIndex] & FLAG_LOADED_PROP) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Set<String> loadedPropertyNames() {
|
||||
if (fullyLoadedBean) {
|
||||
return null;
|
||||
}
|
||||
final Set<String> props = new LinkedHashSet<>();
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
if ((flags[i] & FLAG_LOADED_PROP) != 0) {
|
||||
props.add(property(i));
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final StringBuilder loadedPropertyKey() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final int len = propertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (isLoadedProperty(i)) {
|
||||
sb.append(i).append(',');
|
||||
}
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean[] loaded() {
|
||||
final boolean[] ret = new boolean[flags.length];
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
ret[i] = (flags[i] & FLAG_LOADED_PROP) != 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int lazyLoadPropertyIndex() {
|
||||
return lazyLoadProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String lazyLoadProperty() {
|
||||
return property(lazyLoadProperty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void loadBean(int loadProperty) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (errorOnLazyLoad) {
|
||||
throw new PersistenceException("Lazy loading not allowed on this bean");
|
||||
}
|
||||
if (beanLoader == null) {
|
||||
final Database database = DB.byName(ebeanServerName);
|
||||
if (database == null) {
|
||||
throw new PersistenceException(ebeanServerName == null ? "No registered default server" : "Database [" + ebeanServerName + "] is not registered");
|
||||
}
|
||||
// For stand alone reference bean or after deserialisation lazy load
|
||||
// using the ebeanServer. Synchronise only on the bean.
|
||||
loadBeanInternal(loadProperty, database.pluginApi().beanLoader());
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
final Lock lock = beanLoader.lock();
|
||||
try {
|
||||
// Lazy loading using LoadBeanContext which supports batch loading
|
||||
// Synchronise on the beanLoader (a 'node' of the LoadBeanContext 'tree')
|
||||
loadBeanInternal(loadProperty, beanLoader);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void loadBeanInternal(int loadProperty, BeanLoader loader) {
|
||||
if ((flags[loadProperty] & FLAG_LOADED_PROP) != 0) {
|
||||
// race condition where multiple threads calling preGetter concurrently
|
||||
return;
|
||||
}
|
||||
if (lazyLoadFailure) {
|
||||
// failed when batch lazy loaded by another bean in the batch
|
||||
throw new EntityNotFoundException("(Lazy) loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted. BeanLoader: " + beanLoader);
|
||||
}
|
||||
if (lazyLoadProperty == -1) {
|
||||
lazyLoadProperty = loadProperty;
|
||||
loader.loadBean(this);
|
||||
if (lazyLoadFailure) {
|
||||
// failed when lazy loading this bean
|
||||
throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted. BeanLoader: " + beanLoader);
|
||||
}
|
||||
// bean should be loaded and intercepting now. setLoaded() has
|
||||
// been called by the lazy loading mechanism
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,55 +10,110 @@ import java.util.Set;
|
||||
* EntityBeanIntercept optimised for read only use.
|
||||
* <p>
|
||||
* For the read only use this intercept doesn't need to hold any state that is normally
|
||||
* required for updates such as per property changed, loaded, dirty state, original values
|
||||
* required for updates such as per property changed, dirty state, original values
|
||||
* bean state etc.
|
||||
*/
|
||||
public class InterceptReadOnly implements EntityBeanIntercept {
|
||||
|
||||
private final EntityBean owner;
|
||||
public final class InterceptReadOnly extends InterceptBase {
|
||||
|
||||
/**
|
||||
* Create with a given entity.
|
||||
*/
|
||||
public InterceptReadOnly(Object ownerBean) {
|
||||
this.owner = (EntityBean) ownerBean;
|
||||
super(ownerBean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InterceptReadOnly{" + owner + '}';
|
||||
public void preGetId() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean owner() {
|
||||
return owner;
|
||||
public void preGetter(int propertyIndex) {
|
||||
if (disableLazyLoad) {
|
||||
return;
|
||||
}
|
||||
if (!isLoadedProperty(propertyIndex)) {
|
||||
loadBean(propertyIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void preSetter(int propertyIndex) {
|
||||
if (readOnly) {
|
||||
throw new IllegalStateException("This bean is readOnly");
|
||||
}
|
||||
setLoadedProperty(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistenceContext persistenceContext() {
|
||||
return null;
|
||||
public void preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
|
||||
preSetter(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPersistenceContext(PersistenceContext persistenceContext) {
|
||||
|
||||
public void preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) {
|
||||
preSetter(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) {
|
||||
preSetter(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) {
|
||||
preSetter(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) {
|
||||
preSetter(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) {
|
||||
preSetter(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) {
|
||||
preSetter(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) {
|
||||
preSetter(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) {
|
||||
preSetter(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) {
|
||||
preSetter(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) {
|
||||
preSetter(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) {
|
||||
preSetter(propertyIndex);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// The following method have "Do Nothing" behaviour as not interested in mutation state
|
||||
// ------------------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void setNodeUsageCollector(NodeUsageCollector usageCollector) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object ownerId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwnerId(Object ownerId) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object embeddedOwner() {
|
||||
return null;
|
||||
@@ -84,31 +139,6 @@ public class InterceptReadOnly implements EntityBeanIntercept {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanLoader(BeanLoader beanLoader, PersistenceContext ctx) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanLoader(BeanLoader beanLoader) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullyLoadedBean() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFullyLoadedBean(boolean fullyLoadedBean) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartial() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return false;
|
||||
@@ -134,11 +164,6 @@ public class InterceptReadOnly implements EntityBeanIntercept {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIdOnly(int idIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReference() {
|
||||
return false;
|
||||
@@ -159,16 +184,6 @@ public class InterceptReadOnly implements EntityBeanIntercept {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForceUpdate(boolean forceUpdate) {
|
||||
|
||||
@@ -199,31 +214,6 @@ public class InterceptReadOnly implements EntityBeanIntercept {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLazyLoadFailure(Object ownerId) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLazyLoadFailure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisableLazyLoad() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisableLazyLoad(boolean disableLazyLoad) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmbeddedLoaded(Object embeddedBean) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmbeddedNewOrDirty(Object embeddedBean) {
|
||||
return false;
|
||||
@@ -234,45 +224,6 @@ public class InterceptReadOnly implements EntityBeanIntercept {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findProperty(String propertyName) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String property(int propertyIndex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int propertyLength() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPropertyLoaded(String propertyName, boolean loaded) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPropertyUnloaded(int propertyIndex) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoadedProperty(int propertyIndex) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoadedPropertyAll() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoadedProperty(int propertyIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChangedProperty(int propertyIndex) {
|
||||
@@ -319,11 +270,6 @@ public class InterceptReadOnly implements EntityBeanIntercept {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> loadedPropertyNames() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean[] dirtyProperties() {
|
||||
return new boolean[0];
|
||||
@@ -369,61 +315,11 @@ public class InterceptReadOnly implements EntityBeanIntercept {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBuilder loadedPropertyKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean[] loaded() {
|
||||
return new boolean[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lazyLoadPropertyIndex() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String lazyLoadProperty() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadBean(int loadProperty) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadBeanInternal(int loadProperty, BeanLoader loader) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialisedMany(int propertyIndex) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preGetterCallback(int propertyIndex) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preGetId() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preGetter(int propertyIndex) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) {
|
||||
|
||||
@@ -434,61 +330,6 @@ public class InterceptReadOnly implements EntityBeanIntercept {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOldValue(int propertyIndex, Object oldValue) {
|
||||
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package io.ebean.bean;
|
||||
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Database;
|
||||
import io.ebean.ValuePair;
|
||||
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import jakarta.persistence.PersistenceException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -13,8 +9,6 @@ import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* This is the object added to every entity bean using byte code enhancement.
|
||||
@@ -22,7 +16,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
* This provides the mechanisms to support deferred fetching of reference beans
|
||||
* and oldValues generation for concurrency checking.
|
||||
*/
|
||||
public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
public final class InterceptReadWrite extends InterceptBase {
|
||||
|
||||
private static final long serialVersionUID = -3664031775464862649L;
|
||||
|
||||
@@ -30,10 +24,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
private static final int STATE_REFERENCE = 1;
|
||||
private static final int STATE_LOADED = 2;
|
||||
|
||||
/**
|
||||
* Used when a bean is partially loaded.
|
||||
*/
|
||||
private static final byte FLAG_LOADED_PROP = 1;
|
||||
private static final byte FLAG_CHANGED_PROP = 2;
|
||||
private static final byte FLAG_CHANGEDLOADED_PROP = 3;
|
||||
/**
|
||||
@@ -47,43 +37,21 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
*/
|
||||
private static final byte FLAG_MUTABLE_HASH_SET = 16;
|
||||
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private transient NodeUsageCollector nodeUsageCollector;
|
||||
private transient PersistenceContext persistenceContext;
|
||||
private transient BeanLoader beanLoader;
|
||||
private transient PreGetterCallback preGetterCallback;
|
||||
|
||||
private String ebeanServerName;
|
||||
private boolean deletedFromCollection;
|
||||
|
||||
/**
|
||||
* The actual entity bean that 'owns' this intercept.
|
||||
*/
|
||||
private final EntityBean owner;
|
||||
private EntityBean embeddedOwner;
|
||||
private int embeddedOwnerIndex;
|
||||
/**
|
||||
* One of NEW, REF, UPD.
|
||||
* One of NEW, REF, LOADED.
|
||||
*/
|
||||
private int state;
|
||||
private boolean deletedFromCollection;
|
||||
private boolean forceUpdate;
|
||||
private boolean readOnly;
|
||||
private boolean dirty;
|
||||
/**
|
||||
* Flag set to disable lazy loading.
|
||||
*/
|
||||
private boolean disableLazyLoad;
|
||||
/**
|
||||
* Flag set when lazy loading failed due to the underlying bean being deleted in the DB.
|
||||
*/
|
||||
private boolean lazyLoadFailure;
|
||||
private boolean fullyLoadedBean;
|
||||
private boolean loadedFromCache;
|
||||
private final byte[] flags;
|
||||
private Object[] origValues;
|
||||
private Exception[] loadErrors;
|
||||
private int lazyLoadProperty = -1;
|
||||
private Object ownerId;
|
||||
private int sortOrder;
|
||||
|
||||
/**
|
||||
@@ -101,49 +69,14 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
* Create with a given entity.
|
||||
*/
|
||||
public InterceptReadWrite(Object ownerBean) {
|
||||
this.owner = (EntityBean) ownerBean;
|
||||
this.flags = new byte[owner._ebean_getPropertyNames().length];
|
||||
super(ownerBean);
|
||||
}
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL - Constructor only for use by serialization frameworks.
|
||||
*/
|
||||
public InterceptReadWrite() {
|
||||
this.owner = null;
|
||||
this.flags = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InterceptReadWrite@" + hashCode() + "{state=" + state +
|
||||
(dirty ? " dirty;" : "") +
|
||||
(forceUpdate ? " forceUpdate;" : "") +
|
||||
(readOnly ? " readOnly;" : "") +
|
||||
(disableLazyLoad ? " disableLazyLoad;" : "") +
|
||||
(lazyLoadFailure ? " lazyLoadFailure;" : "") +
|
||||
(fullyLoadedBean ? " fullyLoadedBean;" : "") +
|
||||
(loadedFromCache ? " loadedFromCache;" : "") +
|
||||
", pc=" + System.identityHashCode(persistenceContext) +
|
||||
", flags=" + Arrays.toString(flags) +
|
||||
(lazyLoadProperty > -1 ? (", lazyLoadProperty=" + lazyLoadProperty) : "") +
|
||||
", loader=" + beanLoader +
|
||||
(ownerId != null ? (", ownerId=" + ownerId) : "") +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean owner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistenceContext persistenceContext() {
|
||||
return persistenceContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPersistenceContext(PersistenceContext persistenceContext) {
|
||||
this.persistenceContext = persistenceContext;
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -151,16 +84,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
this.nodeUsageCollector = usageCollector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object ownerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwnerId(Object ownerId) {
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object embeddedOwner() {
|
||||
return embeddedOwner;
|
||||
@@ -187,39 +110,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
this.embeddedOwnerIndex = embeddedOwnerIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanLoader(BeanLoader beanLoader, PersistenceContext ctx) {
|
||||
this.beanLoader = beanLoader;
|
||||
this.persistenceContext = ctx;
|
||||
this.ebeanServerName = beanLoader.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanLoader(BeanLoader beanLoader) {
|
||||
this.beanLoader = beanLoader;
|
||||
this.ebeanServerName = beanLoader.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullyLoadedBean() {
|
||||
return fullyLoadedBean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFullyLoadedBean(boolean fullyLoadedBean) {
|
||||
this.fullyLoadedBean = fullyLoadedBean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartial() {
|
||||
for (byte flag : flags) {
|
||||
if ((flag & FLAG_LOADED_PROP) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
if (dirty) {
|
||||
@@ -257,18 +147,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
return isNew() || isDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIdOnly(int idIndex) {
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
if (i == idIndex) {
|
||||
if ((flags[i] & FLAG_LOADED_PROP) == 0) return false;
|
||||
} else if ((flags[i] & FLAG_LOADED_PROP) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReference() {
|
||||
return state == STATE_REFERENCE;
|
||||
@@ -298,16 +176,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
return loadedFromCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForceUpdate(boolean forceUpdate) {
|
||||
this.forceUpdate = forceUpdate;
|
||||
@@ -356,34 +224,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
this.lazyLoadProperty = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLazyLoadFailure(Object ownerId) {
|
||||
this.lazyLoadFailure = true;
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLazyLoadFailure() {
|
||||
return lazyLoadFailure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisableLazyLoad() {
|
||||
return disableLazyLoad;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisableLazyLoad(boolean disableLazyLoad) {
|
||||
this.disableLazyLoad = disableLazyLoad;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmbeddedLoaded(Object embeddedBean) {
|
||||
if (embeddedBean instanceof EntityBean) {
|
||||
((EntityBean) embeddedBean)._ebean_getIntercept().setLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmbeddedNewOrDirty(Object embeddedBean) {
|
||||
if (embeddedBean == null) {
|
||||
@@ -411,65 +251,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
return origValues[propertyIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int findProperty(String propertyName) {
|
||||
final String[] names = owner._ebean_getPropertyNames();
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
if (names[i].equals(propertyName)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String property(int propertyIndex) {
|
||||
if (propertyIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
return owner._ebean_getPropertyName(propertyIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int propertyLength() {
|
||||
return flags.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPropertyLoaded(String propertyName, boolean loaded) {
|
||||
final int position = findProperty(propertyName);
|
||||
if (position == -1) {
|
||||
throw new IllegalArgumentException("Property " + propertyName + " not found");
|
||||
}
|
||||
if (loaded) {
|
||||
flags[position] |= FLAG_LOADED_PROP;
|
||||
} else {
|
||||
flags[position] &= ~FLAG_LOADED_PROP;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPropertyUnloaded(int propertyIndex) {
|
||||
flags[propertyIndex] &= ~FLAG_LOADED_PROP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoadedProperty(int propertyIndex) {
|
||||
flags[propertyIndex] |= FLAG_LOADED_PROP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoadedPropertyAll() {
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
flags[i] |= FLAG_LOADED_PROP;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoadedProperty(int propertyIndex) {
|
||||
return (flags[propertyIndex] & FLAG_LOADED_PROP) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChangedProperty(int propertyIndex) {
|
||||
return (flags[propertyIndex] & FLAG_CHANGED_PROP) != 0;
|
||||
@@ -530,20 +311,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> loadedPropertyNames() {
|
||||
if (fullyLoadedBean) {
|
||||
return null;
|
||||
}
|
||||
final Set<String> props = new LinkedHashSet<>();
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
if ((flags[i] & FLAG_LOADED_PROP) != 0) {
|
||||
props.add(property(i));
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean[] dirtyProperties() {
|
||||
final int len = propertyLength();
|
||||
@@ -668,86 +435,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBuilder loadedPropertyKey() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final int len = propertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (isLoadedProperty(i)) {
|
||||
sb.append(i).append(',');
|
||||
}
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean[] loaded() {
|
||||
final boolean[] ret = new boolean[flags.length];
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
ret[i] = (flags[i] & FLAG_LOADED_PROP) != 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lazyLoadPropertyIndex() {
|
||||
return lazyLoadProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String lazyLoadProperty() {
|
||||
return property(lazyLoadProperty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadBean(int loadProperty) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (beanLoader == null) {
|
||||
final Database database = DB.byName(ebeanServerName);
|
||||
if (database == null) {
|
||||
throw new PersistenceException(ebeanServerName == null ? "No registered default server" : "Database [" + ebeanServerName + "] is not registered");
|
||||
}
|
||||
// For stand alone reference bean or after deserialisation lazy load
|
||||
// using the ebeanServer. Synchronise only on the bean.
|
||||
loadBeanInternal(loadProperty, database.pluginApi().beanLoader());
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
final Lock lock = beanLoader.lock();
|
||||
try {
|
||||
// Lazy loading using LoadBeanContext which supports batch loading
|
||||
// Synchronise on the beanLoader (a 'node' of the LoadBeanContext 'tree')
|
||||
loadBeanInternal(loadProperty, beanLoader);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadBeanInternal(int loadProperty, BeanLoader loader) {
|
||||
if ((flags[loadProperty] & FLAG_LOADED_PROP) != 0) {
|
||||
// race condition where multiple threads calling preGetter concurrently
|
||||
return;
|
||||
}
|
||||
if (lazyLoadFailure) {
|
||||
// failed when batch lazy loaded by another bean in the batch
|
||||
throw new EntityNotFoundException("(Lazy) loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted. BeanLoader: " + beanLoader);
|
||||
}
|
||||
if (lazyLoadProperty == -1) {
|
||||
lazyLoadProperty = loadProperty;
|
||||
loader.loadBean(this);
|
||||
if (lazyLoadFailure) {
|
||||
// failed when lazy loading this bean
|
||||
throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted. BeanLoader: " + beanLoader);
|
||||
}
|
||||
// bean should be loaded and intercepting now. setLoaded() has
|
||||
// been called by the lazy loading mechanism
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to check if two objects are equal.
|
||||
*/
|
||||
@@ -808,11 +495,6 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialisedMany(int propertyIndex) {
|
||||
flags[propertyIndex] |= FLAG_LOADED_PROP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preGetterCallback(int propertyIndex) {
|
||||
PreGetterCallback preGetterCallback = this.preGetterCallback;
|
||||
@@ -1076,4 +758,16 @@ public final class InterceptReadWrite implements EntityBeanIntercept {
|
||||
final MutableValueNext next = mutableNext[propertyIndex];
|
||||
return next != null ? next.content() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void freeze() {
|
||||
super.freeze();
|
||||
// not allowed to be used for updates
|
||||
this.dirty = false;
|
||||
this.forceUpdate = false;
|
||||
this.deletedFromCollection = false;
|
||||
this.origValues = null;
|
||||
this.nodeUsageCollector = null;
|
||||
this.embeddedOwner = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
|
||||
builder.addCollection(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object freeze() {
|
||||
return list == null ? Collections.emptyList() : Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(EntityBean ownerBean, String propertyName) {
|
||||
this.ownerBean = ownerBean;
|
||||
|
||||
@@ -37,6 +37,11 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
|
||||
super(ebeanServer, ownerBean, propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object freeze() {
|
||||
return map == null ? Collections.emptyMap() : Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(ToStringBuilder builder) {
|
||||
if (map == null || map.isEmpty()) {
|
||||
|
||||
@@ -3,10 +3,7 @@ package io.ebean.common;
|
||||
import io.ebean.bean.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Set capable of lazy loading and modification aware.
|
||||
@@ -43,6 +40,11 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
|
||||
builder.addCollection(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object freeze() {
|
||||
return set == null ? Collections.emptySet() : Collections.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(EntityBean ownerBean, String propertyName) {
|
||||
this.ownerBean = ownerBean;
|
||||
|
||||
Reference in New Issue
Block a user