diff --git a/.gitignore b/.gitignore
index ce95643d4..d0ae91982 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,10 @@ target/
logs/
log/
/mydb.db
+
+
+# Intellij project files
+*.iml
+*.ipr
+*.iws
+.idea/
diff --git a/pom.xml b/pom.xml
index b5ea3af0c..67ed32e21 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
org.avaje.ebeanormavaje-ebeanorm
- 3.3.3-SNAPSHOT
+ 4.0.1-RC1-SNAPSHOTjaravaje-ebeanorm
@@ -94,7 +94,7 @@
org.avaje.ebeanormavaje-ebeanorm-agent
- 3.2.2
+ 4.0.1-RC3-SNAPSHOTtest
@@ -170,7 +170,7 @@
org.avaje.ebeanormavaje-ebeanorm-mavenenhancer
- 3.3.2
+ 4.0.1-RC3-SNAPSHOTmain
diff --git a/src/main/java/com/avaje/ebean/BeanState.java b/src/main/java/com/avaje/ebean/BeanState.java
index fc4e3c371..b232ff130 100644
--- a/src/main/java/com/avaje/ebean/BeanState.java
+++ b/src/main/java/com/avaje/ebean/BeanState.java
@@ -1,6 +1,7 @@
package com.avaje.ebean;
import java.beans.PropertyChangeListener;
+import java.util.Map;
import java.util.Set;
/**
@@ -46,6 +47,11 @@ public interface BeanState {
*/
public Set getChangedProps();
+ /**
+ * Return a map of the updated properties and their new and old values.
+ */
+ public Map getDirtyValues();
+
/**
* Return true if the bean is readOnly.
*
@@ -69,16 +75,6 @@ public interface BeanState {
*/
public void removePropertyChangeListener(PropertyChangeListener listener);
- /**
- * Advanced - Used to programmatically build a reference object.
- *
- * You can create a new EntityBean (
- * {@link EbeanServer#createEntityBean(Class)}, set its Id property and then
- * call this setReference() method.
- *
- */
- public void setReference();
-
/**
* Advanced - Used to programmatically build a partially or fully loaded
* entity bean. First create an entity bean via
@@ -90,5 +86,5 @@ public interface BeanState {
* the properties that where loaded or null for a fully loaded entity
* bean.
*/
- public void setLoaded(Set loadedProperties);
+ public void setLoaded();
}
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebean/Ebean.java b/src/main/java/com/avaje/ebean/Ebean.java
index b8f6a86f5..e24f760bf 100644
--- a/src/main/java/com/avaje/ebean/Ebean.java
+++ b/src/main/java/com/avaje/ebean/Ebean.java
@@ -453,6 +453,14 @@ public final class Ebean {
serverMgr.getPrimaryServer().save(bean);
}
+ /**
+ * Insert the bean. This is useful when you set the Id property on a bean and
+ * want to explicitly insert it.
+ */
+ public static void insert(Object bean) {
+ serverMgr.getPrimaryServer().insert(bean);
+ }
+
/**
* Force an update using the bean updating the non-null properties.
*
@@ -496,29 +504,6 @@ public final class Ebean {
serverMgr.getPrimaryServer().update(bean);
}
- /**
- * Force an update using the bean explicitly stating the properties to update.
- *
- * If you don't specify explicit properties to use in the update then the
- * non-null properties are included in the update.
- *
- *
- * For updates against beans that have not been fetched (say built from JSON
- * or XML) this will treat deleteMissingChildren=true and will delete any
- * 'missing children'. Refer to
- * {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
- *
- *
- * @param bean
- * The bean holding the values to be included in the update.
- * @param updateProps
- * the explicit set of properties to include in the update (can be
- * null).
- */
- public static void update(Object bean, Set updateProps) {
- serverMgr.getPrimaryServer().update(bean, updateProps);
- }
-
/**
* Save all the beans from an Iterator.
*/
diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java
index e6567b8bb..b2fb352f4 100644
--- a/src/main/java/com/avaje/ebean/EbeanServer.java
+++ b/src/main/java/com/avaje/ebean/EbeanServer.java
@@ -856,71 +856,6 @@ public interface EbeanServer {
*/
public void update(Object bean, Transaction t);
- /**
- * Force an update using the bean explicitly stating the properties to update.
- *
- * You can use this method to FORCE an update to occur (even on a bean that
- * has not been fetched but say built from JSON or XML). When
- * {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
- * insert or an update based on the state of the bean. Using this method will
- * force an update to occur.
- *
- *
- * It is expected that this method is most useful in stateless REST services
- * or web applications where you have the values you wish to update but no
- * existing bean.
- *
- *
- * For updates against beans that have not been fetched (say built from JSON
- * or XML) this will treat deleteMissingChildren=true and will delete any
- * 'missing children'. Refer to
- * {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
- *
- *
- *
- *
- * Customer c = new Customer();
- * c.setId(7);
- * c.setName("ModifiedNameNoOCC");
- *
- * // generally you should set the version property
- * // so that Optimistic Concurrency Checking is used.
- * // If a version property is not set then no Optimistic
- * // Concurrency Checking occurs for the update
- * // c.setLastUpdate(lastUpdateTime);
- *
- * // by default the Non-null properties
- * // are included in the update
- * ebeanServer.update(c);
- *
- *
- */
- public void update(Object bean, Set updateProps);
-
- /**
- * Force an update of the specified properties of the bean with an explicit
- * transaction.
- *
- * You can use this method to FORCE an update to occur (even on a bean that
- * has not been fetched but say built from JSON or XML). When
- * {@link EbeanServer#save(Object)} is used Ebean determines whether to use an
- * insert or an update based on the state of the bean. Using this method will
- * force an update to occur.
- *
- *
- * It is expected that this method is most useful in stateless REST services
- * or web applications where you have the values you wish to update but no
- * existing bean.
- *
- *
- * For updates against beans that have not been fetched (say built from JSON
- * or XML) this will treat deleteMissingChildren=true and will delete any
- * 'missing children'. Refer to
- * {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}.
- *
- */
- public void update(Object bean, Set updateProps, Transaction t);
-
/**
* Force an update additionally specifying whether to 'deleteMissingChildren'
* when the update cascades to a OneToMany or ManyToMany.
@@ -945,21 +880,14 @@ public interface EbeanServer {
*
* @param bean
* the bean to update
- * @param updateProps
- * optionally you can specify the properties to update (can be null).
* @param t
* optionally you can specify the transaction to use (can be null).
* @param deleteMissingChildren
* specify false if you do not want 'missing children' of a OneToMany
* or ManyToMany to be automatically deleted.
- * @param updateNullProperties
- * specify true if by default you want properties with null values to
- * be included in the update and false if those properties should be
- * treated as 'unloaded' and excluded from the update. This only
- * takes effect if the updateProps is null.
+
*/
- public void update(Object bean, Set updateProps, Transaction t,
- boolean deleteMissingChildren, boolean updateNullProperties);
+ public void update(Object bean, Transaction t, boolean deleteMissingChildren);
/**
* Force the bean to be saved with an explicit insert.
diff --git a/src/main/java/com/avaje/ebean/ExpressionList.java b/src/main/java/com/avaje/ebean/ExpressionList.java
index 98d912e48..bd0ae473b 100644
--- a/src/main/java/com/avaje/ebean/ExpressionList.java
+++ b/src/main/java/com/avaje/ebean/ExpressionList.java
@@ -244,14 +244,6 @@ public interface ExpressionList extends Serializable {
*/
public Query setMaxRows(int maxRows);
- /**
- * Set the number of rows after which the fetching should continue in a
- * background thread.
- *
- * @see Query#setBackgroundFetchAfter(int)
- */
- public Query setBackgroundFetchAfter(int backgroundFetchAfter);
-
/**
* Set the name of the property which values become the key of a map.
*
@@ -259,15 +251,6 @@ public interface ExpressionList extends Serializable {
*/
public Query setMapKey(String mapKey);
- /**
- * Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}.
- * Set a QueryListener for bean by bean processing.
- *
- * @see Query#setListener(QueryListener)
- * @deprecated Migrate to {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
- */
- public Query setListener(QueryListener queryListener);
-
/**
* Set to true to use the query for executing this query.
*
diff --git a/src/main/java/com/avaje/ebean/Query.java b/src/main/java/com/avaje/ebean/Query.java
index fa7df252b..57a86f241 100644
--- a/src/main/java/com/avaje/ebean/Query.java
+++ b/src/main/java/com/avaje/ebean/Query.java
@@ -663,36 +663,6 @@ public interface Query extends Serializable {
*/
public Query setParameter(int position, Object value);
- /**
- * Please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
- *
- * Set a listener to process the query on a row by row basis.
- *
- *
- * Use this when you want to process a large query and do not want to hold the
- * entire query result in memory.
- *
- *
- * It this case the rows are not loaded into the persistence context and
- * instead are processed by the query listener.
- *
- *
- *
- * QueryListener<Order> listener = ...;
- *
- * Query<Order> query = Ebean.createQuery(Order.class);
- *
- * // set the listener that will process each order one at a time
- * query.setListener(listener);
- *
- * // execute the query. Note that the returned
- * // list (emptyList) will be empty ...
- * List<Order> emtyList = query.findList();
- *
- * @deprecated Deprecated in favor of {@link #findIterate()} and {@link #findVisit(QueryResultVisitor)}
- */
- public Query setListener(QueryListener queryListener);
-
/**
* Set the Id value to query. This is used with findUnique().
*
@@ -966,13 +936,6 @@ public interface Query extends Serializable {
*/
public Query setMaxRows(int maxRows);
- /**
- * Set the rows after which fetching should continue in a background thread.
- *
- * @param backgroundFetchAfter
- */
- public Query setBackgroundFetchAfter(int backgroundFetchAfter);
-
/**
* Set the property to use as keys for a map.
*
diff --git a/src/main/java/com/avaje/ebean/QueryListener.java b/src/main/java/com/avaje/ebean/QueryListener.java
deleted file mode 100644
index 959a3d09c..000000000
--- a/src/main/java/com/avaje/ebean/QueryListener.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.avaje.ebean;
-
-/**
- * Deprecated, please migrate to using {@link #findIterate()} or {@link #findVisit(QueryResultVisitor)}
- *
- * Provides a mechanism for processing a query one bean at a time.
- *
- *
- * This is useful when the query will return a large number of results and you
- * want to process the beans one at a time rather than whole all of the beans in
- * memory at once.
- *
- *
- *
- * QueryListener<Order> listener = ...;
- *
- * Query<Order> query = Ebean.createQuery(Order.class);
- *
- * // set the listener that will process each order one at a time
- * query.setListener(listener);
- *
- * // execute the query. Note that the returned
- * // list will be empty ... so don't bother assigning it
- * query.findList();
- *
- *
- * @param
- * the type of entity bean
- * @deprecated Please migrate to using {@link #findIterate()} or
- * {@link #findVisit(QueryResultVisitor)}
- */
-public interface QueryListener {
-
- /**
- * Process the bean that has just been read.
- *
- * This bean will not be added to the List Set or Map and nor will it be put
- * into the PersistenceContext. This is what makes this a good way to process
- * a large result set (which could normally use a lot of memory).
- *
- */
- public void process(T bean);
-}
diff --git a/src/main/java/com/avaje/ebean/ValuePair.java b/src/main/java/com/avaje/ebean/ValuePair.java
index e1a89caad..fe11b7799 100644
--- a/src/main/java/com/avaje/ebean/ValuePair.java
+++ b/src/main/java/com/avaje/ebean/ValuePair.java
@@ -5,30 +5,46 @@ package com.avaje.ebean;
*/
public class ValuePair {
- final Object value1;
+ private final Object newValue;
- final Object value2;
+ private final Object oldValue;
- public ValuePair(Object value1, Object value2) {
- this.value1 = value1;
- this.value2 = value2;
+ public ValuePair(Object newValue, Object oldValue) {
+ this.newValue = newValue;
+ this.oldValue = oldValue;
}
/**
- * Return the first value.
+ * Return the new value.
*/
+ public Object getNewValue() {
+ return newValue;
+ }
+
+ /**
+ * Return the old value.
+ */
+ public Object getOldValue() {
+ return oldValue;
+ }
+
+ /**
+ * Return the new value.
+ */
+ @Deprecated
public Object getValue1() {
- return value1;
+ return newValue;
}
/**
- * Return the second value.
+ * Return the old value.
*/
+ @Deprecated
public Object getValue2() {
- return value2;
+ return oldValue;
}
public String toString() {
- return value1 + "," + value2;
+ return newValue + "," + oldValue;
}
}
diff --git a/src/main/java/com/avaje/ebean/annotation/ConcurrencyMode.java b/src/main/java/com/avaje/ebean/annotation/ConcurrencyMode.java
index 59730225c..06d5cd7c5 100644
--- a/src/main/java/com/avaje/ebean/annotation/ConcurrencyMode.java
+++ b/src/main/java/com/avaje/ebean/annotation/ConcurrencyMode.java
@@ -13,10 +13,5 @@ public enum ConcurrencyMode {
/**
* Use a version column.
*/
- VERSION,
-
- /**
- * Use all the columns (except Lobs).
- */
- ALL
+ VERSION
}
diff --git a/src/main/java/com/avaje/ebean/bean/BeanCollection.java b/src/main/java/com/avaje/ebean/bean/BeanCollection.java
index 2b7e83342..ededeabc4 100644
--- a/src/main/java/com/avaje/ebean/bean/BeanCollection.java
+++ b/src/main/java/com/avaje/ebean/bean/BeanCollection.java
@@ -3,11 +3,8 @@ package com.avaje.ebean.bean;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
import com.avaje.ebean.ExpressionList;
-import com.avaje.ebean.Query;
/**
* Lazy loading capable Maps, Lists and Sets.
@@ -34,10 +31,16 @@ public interface BeanCollection extends Serializable {
ALL
}
+ /**
+ * Return true if the collection is empty and untouched. Used to detect if a
+ * collection was 'cleared' deliberately or just un-initialised.
+ */
+ public boolean isEmptyAndUntouched();
+
/**
* Return the bean that owns this collection.
*/
- public Object getOwnerBean();
+ public EntityBean getOwnerBean();
/**
* Return the bean property name this collection represents.
@@ -75,30 +78,6 @@ public interface BeanCollection extends Serializable {
*/
public void setFilterMany(ExpressionList> filterMany);
- /**
- * Set when this collection is being loaded via a background thread.
- *
- * Refer to {@link Query#setBackgroundFetchAfter(int)}
- *
- */
- public void setBackgroundFetch(Future future);
-
- /**
- * Wait for the fetch to complete with a given timeout.
- *
- * Refer to {@link Query#setBackgroundFetchAfter(int)}
- *
- */
- public void backgroundFetchWait(long wait, TimeUnit timeUnit);
-
- /**
- * Wait for the fetch to complete.
- *
- * Refer to {@link Query#setBackgroundFetchAfter(int)}
- *
- */
- public void backgroundFetchWait();
-
/**
* Set a listener to be notified when the BeanCollection is first touched.
*/
@@ -174,18 +153,6 @@ public interface BeanCollection extends Serializable {
*/
public void setHasMoreRows(boolean hasMoreRows);
- /**
- * Returns true if the fetch has finished. False if the fetch is continuing in
- * a background thread.
- */
- public boolean isFinishedFetch();
-
- /**
- * Set to true when a fetch has finished. Used when a fetch continues in the
- * background.
- */
- public void setFinishedFetch(boolean finishedFetch);
-
/**
* return true if there are real rows held. Return false is this is using
* Deferred fetch to lazy load the rows and the rows have not yet been
diff --git a/src/main/java/com/avaje/ebean/bean/BeanCollectionAdd.java b/src/main/java/com/avaje/ebean/bean/BeanCollectionAdd.java
index 8bc754166..1df602fc8 100644
--- a/src/main/java/com/avaje/ebean/bean/BeanCollectionAdd.java
+++ b/src/main/java/com/avaje/ebean/bean/BeanCollectionAdd.java
@@ -12,5 +12,5 @@ public interface BeanCollectionAdd {
/**
* Add a loaded bean to the collection.
*/
- public void addBean(Object bean);
+ public void addBean(EntityBean bean);
}
diff --git a/src/main/java/com/avaje/ebean/bean/EntityBean.java b/src/main/java/com/avaje/ebean/bean/EntityBean.java
index 701691126..ea39333c8 100644
--- a/src/main/java/com/avaje/ebean/bean/EntityBean.java
+++ b/src/main/java/com/avaje/ebean/bean/EntityBean.java
@@ -14,6 +14,10 @@ import java.io.Serializable;
*/
public interface EntityBean extends Serializable {
+ public String[] _ebean_getPropertyNames();
+
+ public String _ebean_getPropertyName(int pos);
+
/**
* Return the enhancement marker value.
*
@@ -79,31 +83,19 @@ public interface EntityBean extends Serializable {
*/
public Object _ebean_createCopy();
- /**
- * Return the fields in their index order.
- */
- public String[] _ebean_getFieldNames();
-
/**
* Set the value of a field of an entity bean of this type.
*
* Note that using this method bypasses any interception that otherwise occurs
* on entity beans. That means lazy loading and oldValues creation.
*
- *
- * @param fieldIndex
- * the index of the field
- * @param entityBean
- * the entityBean of this type to modify
- * @param value
- * the value to set
*/
- public void _ebean_setField(int fieldIndex, Object entityBean, Object value);
+ public void _ebean_setField(int fieldIndex, Object value);
/**
* Set the field value with interception.
*/
- public void _ebean_setFieldIntercept(int fieldIndex, Object entityBean, Object value);
+ public void _ebean_setFieldIntercept(int fieldIndex, Object value);
/**
* Return the value of a field from an entity bean of this type.
@@ -111,17 +103,12 @@ public interface EntityBean extends Serializable {
* Note that using this method bypasses any interception that otherwise occurs
* on entity beans. That means lazy loading.
*
- *
- * @param fieldIndex
- * the index of the field
- * @param entityBean
- * the entityBean to get the value from
*/
- public Object _ebean_getField(int fieldIndex, Object entityBean);
+ public Object _ebean_getField(int fieldIndex);
/**
* Return the field value with interception.
*/
- public Object _ebean_getFieldIntercept(int fieldIndex, Object entityBean);
+ public Object _ebean_getFieldIntercept(int fieldIndex);
}
diff --git a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java
index ab9fa87c8..a1bc4d89e 100644
--- a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java
+++ b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java
@@ -6,13 +6,16 @@ import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import java.math.BigDecimal;
import java.net.URL;
-import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
import java.util.Set;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceException;
import com.avaje.ebean.Ebean;
+import com.avaje.ebean.ValuePair;
/**
* This is the object added to every entity bean using byte code enhancement.
@@ -23,8 +26,12 @@ import com.avaje.ebean.Ebean;
*/
public final class EntityBeanIntercept implements Serializable {
- private static final long serialVersionUID = -3664031775464862648L;
+ private static final long serialVersionUID = -3664031775464862649L;
+ private static final int STATE_NEW = 0;
+ private static final int STATE_REFERENCE = 1;
+ private static final int STATE_LOADED = 2;
+
private transient NodeUsageCollector nodeUsageCollector;
private transient PropertyChangeSupport pcs;
@@ -32,7 +39,7 @@ public final class EntityBeanIntercept implements Serializable {
private transient PersistenceContext persistenceContext;
private transient BeanLoader beanLoader;
-
+
private int beanLoaderIndex;
private String ebeanServerName;
@@ -45,54 +52,50 @@ public final class EntityBeanIntercept implements Serializable {
/**
* The parent bean by relationship (1-1 or 1-M).
*/
- private Object parentBean;
+ private EntityBean embeddedOwner;
+ private int embeddedOwnerIndex;
/**
- * true if the bean properties have been loaded. false if it is a reference
- * bean (will lazy load etc).
+ * One of NEW, REF, UPD.
*/
- private volatile boolean loaded;
-
+ private int state;
+
+ private boolean readOnly;
+
+ private boolean dirty;
+
/**
- * Flag set to disable lazy loading - typically for SQL "report" type entity
- * beans.
+ * Flag set to disable lazy loading - typically for SQL "report" type entity beans.
*/
private boolean disableLazyLoad;
/**
- * Flag set when lazy loading failed due to the underlying bean being deleted
- * in the DB.
+ * Flag set when lazy loading failed due to the underlying bean being deleted in the DB.
*/
private boolean lazyLoadFailure;
- /**
- * Set true when loaded or reference. Used to bypass interception when created
- * by user code.
- */
- private boolean intercepting;
-
- /**
- * The state of the Bean (DEFAULT,UDPATE,READONLY,SHARED).
- */
- private boolean readOnly;
-
- /**
- * The bean as it was before it was modified. Null if no non-transient setters
- * have been called.
- */
- private Object oldValues;
-
/**
* Used when a bean is partially filled.
*/
- private volatile Set loadedProps;
+ private boolean[] loadedProps;
+
+ private boolean fullyLoadedBean;
/**
* Set of changed properties.
*/
- private HashSet changedProps;
+ private boolean[] changedProps;
+
+ /**
+ * Flags indicating if a property is a dirty embedded bean. Used to distingush
+ * between an embedded bean being completely overwritten and one of its
+ * embedded properties being made dirty.
+ */
+ private boolean[] embeddedDirty;
- private String lazyLoadProperty;
+ private Object[] origValues;
+
+ private int lazyLoadProperty = -1;
/**
* Create a intercept with a given entity.
@@ -100,20 +103,9 @@ public final class EntityBeanIntercept implements Serializable {
* Refer to agent ProxyConstructor.
*
*/
- public EntityBeanIntercept(Object owner) {
- this.owner = (EntityBean) owner;
- }
-
- /**
- * Copy the internal state of the intercept to another intercept.
- */
- public void copyStateTo(EntityBeanIntercept dest) {
- dest.loadedProps = loadedProps;
- dest.ebeanServerName = ebeanServerName;
-
- if (loaded) {
- dest.setLoaded();
- }
+ public EntityBeanIntercept(Object ownerBean) {
+ this.owner = (EntityBean) ownerBean;
+ this.loadedProps = new boolean[owner._ebean_getPropertyNames().length];
}
/**
@@ -123,13 +115,6 @@ public final class EntityBeanIntercept implements Serializable {
return owner;
}
- public String toString() {
- if (!loaded) {
- return "Reference...";
- }
- return "OldValues: " + oldValues;
- }
-
/**
* Return the persistenceContext.
*/
@@ -194,16 +179,17 @@ public final class EntityBeanIntercept implements Serializable {
/**
* Return the parent bean (by relationship).
*/
- public Object getParentBean() {
- return parentBean;
+ public Object getEmbeddedOwner() {
+ return embeddedOwner;
}
/**
* Special case for a OneToOne, Set the parent bean (by relationship). This is
* the owner of a 1-1.
*/
- public void setParentBean(Object parentBean) {
- this.parentBean = parentBean;
+ public void setEmbeddedOwner(EntityBean parentBean, int embeddedOwnerIndex) {
+ this.embeddedOwner = parentBean;
+ this.embeddedOwnerIndex = embeddedOwnerIndex;
}
/**
@@ -234,24 +220,40 @@ public final class EntityBeanIntercept implements Serializable {
this.persistenceContext = ctx;
this.ebeanServerName = beanLoader.getName();
}
+
+ public boolean isFullyLoadedBean() {
+ return fullyLoadedBean;
+ }
+
+ public void setFullyLoadedBean(boolean fullyLoadedBean) {
+ this.fullyLoadedBean = fullyLoadedBean;
+ }
/**
* Return true if this bean has been directly modified (it has oldValues) or
* if any embedded beans are either new or dirty (and hence need saving).
*/
public boolean isDirty() {
- if (oldValues != null) {
- return true;
- }
- // need to check all the embedded beans
- return owner._ebean_isEmbeddedNewOrDirty();
+ return dirty;
+ }
+
+ /**
+ * Called by an embedded bean onto its owner.
+ */
+ public void setEmbeddedDirty(int embeddedProperty) {
+ this.dirty = true;
+ setEmbeddedPropertyDirty(embeddedProperty);
+ }
+
+ public void setDirty(boolean dirty) {
+ this.dirty = dirty;
}
/**
* Return true if this entity bean is new and not yet saved.
*/
public boolean isNew() {
- return !intercepting && !loaded;
+ return state == STATE_NEW;
}
/**
@@ -261,26 +263,41 @@ public final class EntityBeanIntercept implements Serializable {
return isNew() || isDirty();
}
+ /**
+ * Return true if only the Id property has been loaded.
+ */
+ public boolean hasIdOnly(int idIndex) {
+ for (int i = 0; i < loadedProps.length; i++) {
+ if (i == idIndex) {
+ if (!loadedProps[i]) return false;
+ } else if (loadedProps[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* Return true if the entity is a reference.
*/
public boolean isReference() {
- return intercepting && !loaded;
+ return state == STATE_REFERENCE;
}
/**
* Set this as a reference object.
*/
- public void setReference() {
- this.loaded = false;
- this.intercepting = true;
- }
-
- /**
- * Return the old values used for ConcurrencyMode.ALL.
- */
- public Object getOldValues() {
- return oldValues;
+ public void setReference(int idPos) {
+ state = STATE_REFERENCE;
+ if (idPos > -1) {
+ // For cases where properties are set on constructor
+ // set every non Id property to unloaded (for lazy loading)
+ for (int i=0; i< loadedProps.length; i++) {
+ if (i != idPos) {
+ loadedProps[i] = false;
+ }
+ }
+ }
}
/**
@@ -299,33 +316,11 @@ public final class EntityBeanIntercept implements Serializable {
this.readOnly = readOnly;
}
- /**
- * Return true if the bean currently has interception on.
- *
- * With interception on the bean will invoke lazy loading and dirty checking.
- *
- */
- public boolean isIntercepting() {
- return intercepting;
- }
-
- /**
- * Turn interception off or on.
- *
- * This is to support custom serialisation mechanisms that just read all the
- * properties on the bean.
- *
- *
- */
- public void setIntercepting(boolean intercepting) {
- this.intercepting = intercepting;
- }
-
/**
* Return true if the entity has been loaded.
*/
public boolean isLoaded() {
- return loaded;
+ return state == STATE_LOADED;
}
/**
@@ -340,12 +335,12 @@ public final class EntityBeanIntercept implements Serializable {
*
*/
public void setLoaded() {
- this.loaded = true;
- this.oldValues = null;
- this.intercepting = true;
+ this.state = STATE_LOADED;
this.owner._ebean_setEmbeddedLoaded();
- this.lazyLoadProperty = null;
+ this.lazyLoadProperty = -1;
+ this.origValues = null;
this.changedProps = null;
+ this.dirty = false;
}
/**
@@ -353,22 +348,23 @@ public final class EntityBeanIntercept implements Serializable {
* bean.
*/
public void setLoadedLazy() {
- this.loaded = true;
- this.intercepting = true;
- this.lazyLoadProperty = null;
+ this.state = STATE_LOADED;
+ this.lazyLoadProperty = -1;
}
/**
- * Mark this bean as having failed lazy loading due to the underlying row
- * being deleted.
+ * Check if the lazy load succeeded. If not then mark this bean as having
+ * failed lazy loading due to the underlying row being deleted.
*
* We mark the bean this way rather than immediately fail as we might be batch
* lazy loading and this bean might not be used by the client code at all.
* Instead we will fail as soon as the client code tries to use this bean.
*
*/
- public void setLazyLoadFailure() {
- this.lazyLoadFailure = true;
+ public void checkLazyLoadFailure() {
+ if (lazyLoadProperty != -1) {
+ this.lazyLoadFailure = true;
+ }
}
/**
@@ -425,40 +421,240 @@ public final class EntityBeanIntercept implements Serializable {
}
/**
- * Set the property names for a partially loaded bean.
- *
- * @param loadedPropertyNames
- * the names of the loaded properties
+ * Return the original value that was changed via an update.
*/
- public void setLoadedProps(Set loadedPropertyNames) {
- this.loadedProps = loadedPropertyNames;
+ public Object getOrigValue(int propertyIndex) {
+ if (origValues == null) {
+ return null;
+ }
+ return origValues[propertyIndex];
+ }
+
+ /**
+ * Finds the index position of a given property. Returns -1 if the property
+ * can not be found.
+ */
+ public int findProperty(String propertyName) {
+ String[] names = owner._ebean_getPropertyNames();
+ for (int i = 0; i < names.length; i++) {
+ if (names[i].equals(propertyName)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public String getProperty(int propertyIndex) {
+ if (propertyIndex == -1) {
+ return null;
+ }
+ return owner._ebean_getPropertyName(propertyIndex);
+ }
+
+ public int getPropertyLength() {
+ return owner._ebean_getPropertyNames().length;
+ }
+
+ public void setLoadedProperty(int propertyIndex) {
+ loadedProps[propertyIndex] = true;
+ }
+
+ public boolean isLoadedProperty(int propertyIndex) {
+ return loadedProps[propertyIndex];
+ }
+
+ public boolean isChangedProperty(int propertyIndex) {
+ return (changedProps != null && changedProps[propertyIndex]);
}
+ /**
+ * Return true if the property was changed or if it is embedded and one of its
+ * embedded properties is dirty.
+ */
+ public boolean isDirtyProperty(int propertyIndex) {
+ return (changedProps != null && changedProps[propertyIndex]
+ || embeddedDirty != null && embeddedDirty[propertyIndex]);
+ }
+
+ /**
+ * Explicitly mark a property as having been changed.
+ */
+ public void markPropertyAsChanged(int propertyIndex) {
+ setChangedProperty(propertyIndex);
+ setDirty(true);
+ }
+
+ private void setChangedProperty(int propertyIndex) {
+ if (changedProps == null) {
+ changedProps = new boolean[owner._ebean_getPropertyNames().length];
+ }
+ changedProps[propertyIndex] = true;
+ }
+
+ /**
+ * Set that an embedded bean has had one of its properties changed.
+ */
+ private void setEmbeddedPropertyDirty(int propertyIndex) {
+ if (embeddedDirty == null) {
+ embeddedDirty = new boolean[owner._ebean_getPropertyNames().length];
+ }
+ embeddedDirty[propertyIndex] = true;
+ }
+
+ private void setOriginalValue(int propertyIndex, Object value) {
+ if (origValues == null) {
+ origValues = new Object[owner._ebean_getPropertyNames().length];
+ }
+ if (origValues[propertyIndex] == null) {
+ origValues[propertyIndex] = value;
+ }
+ }
+
+ /**
+ * For forced update on a 'New' bean move set all the changedProperties to loaded properties.
+ */
+ public void setNewBeanForUpdate() {
+
+ if (changedProps == null) {
+ changedProps = new boolean[owner._ebean_getPropertyNames().length];
+ }
+
+ for (int i=0; i< loadedProps.length; i++) {
+ if (loadedProps[i]) {
+ changedProps[i] = true;
+ }
+ }
+ setDirty(true);
+ }
+
/**
* Return the set of property names for a partially loaded bean.
*/
- public Set getLoadedProps() {
- return loadedProps;
+ public Set getLoadedPropertyNames() {
+ if (fullyLoadedBean) {
+ return null;
+ }
+ Set props = new LinkedHashSet();
+ for (int i=0; i getDirtyPropertyNames() {
+ Set props = new LinkedHashSet();
+ addDirtyPropertyNames(props, null);
+ return props;
+ }
+
+ /**
+ * Recursively add dirty properties.
+ */
+ public void addDirtyPropertyNames(Set props, String prefix) {
+ int len = getPropertyLength();
+ for (int i = 0; i < len; i++) {
+ if (changedProps != null && changedProps[i]) {
+ // the property has been changed on this bean
+ String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
+ props.add(propName);
+ } else if (embeddedDirty != null && embeddedDirty[i]) {
+ // an embedded property has been changed - recurse
+ EntityBean embeddedBean = (EntityBean)owner._ebean_getField(i);
+ embeddedBean._ebean_getIntercept().addDirtyPropertyNames(props, getProperty(i)+".");
+ }
+ }
+ }
+
+ /**
+ * Return a map of dirty properties with their new and old values.
+ */
+ public Map getDirtyValues() {
+ Map dirtyValues = new LinkedHashMap();
+ addDirtyPropertyValues(dirtyValues, null);
+ return dirtyValues;
+ }
+
+ /**
+ * Recursively add dirty properties.
+ */
+ public void addDirtyPropertyValues(Map dirtyValues, String prefix) {
+ int len = getPropertyLength();
+ for (int i = 0; i < len; i++) {
+ if (changedProps != null && changedProps[i]) {
+ // the property has been changed on this bean
+ String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
+ Object newVal = owner._ebean_getField(i);
+ Object oldVal = getOrigValue(i);
+
+ dirtyValues.put(propName, new ValuePair(newVal, oldVal));
+
+ } else if (embeddedDirty != null && embeddedDirty[i]) {
+ // an embedded property has been changed - recurse
+ EntityBean embeddedBean = (EntityBean)owner._ebean_getField(i);
+ embeddedBean._ebean_getIntercept().addDirtyPropertyValues(dirtyValues, getProperty(i)+".");
+ }
+ }
+ }
+
+ /**
+ * Return a dirty property hash taking into account embedded beans.
+ */
+ public int getDirtyPropertyHash() {
+ return addDirtyPropertyHash(37);
+ }
+
+ /**
+ * Add and return a dirty property hash recursing into embedded beans.
+ */
+ public int addDirtyPropertyHash(int hash) {
+ int len = getPropertyLength();
+ for (int i = 0; i < len; i++) {
+ if (changedProps != null && changedProps[i]) {
+ // the property has been changed on this bean
+ hash = hash * 31 + (i+1);
+ } else if (embeddedDirty != null && embeddedDirty[i]) {
+ // an embedded property has been changed - recurse
+ EntityBean embeddedBean = (EntityBean)owner._ebean_getField(i);
+ hash = hash * 31 + embeddedBean._ebean_getIntercept().addDirtyPropertyHash(hash);
+ }
+ }
+ return hash;
}
/**
* Return the set of property names for changed properties.
*/
- public Set getChangedProps() {
+ public boolean[] getChanged() {
return changedProps;
}
+ public boolean[] getLoaded() {
+ return loadedProps;
+ }
+
/**
- * Return the property read or write that triggered the lazy load.
+ * Return the index of the property that triggered the lazy load.
+ */
+ public int getLazyLoadPropertyIndex() {
+ return lazyLoadProperty;
+ }
+
+ /**
+ * Return the property that triggered the lazy load.
*/
public String getLazyLoadProperty() {
- return lazyLoadProperty;
+ return getProperty(lazyLoadProperty);
}
/**
* Load the bean when it is a reference.
*/
- protected void loadBean(String loadProperty) {
+ protected void loadBean(int loadProperty) {
synchronized (this) {
if (beanLoader == null) {
@@ -484,29 +680,24 @@ public final class EntityBeanIntercept implements Serializable {
/**
* Invoke the lazy loading. This method is synchronised externally.
*/
- private void loadBeanInternal(String loadProperty, BeanLoader loader) {
+ private void loadBeanInternal(int loadProperty, BeanLoader loader) {
- if (loaded && (loadedProps == null || loadedProps.contains(loadProperty))) {
+ if (loadedProps == null || loadedProps[loadProperty]) {
// race condition where multiple threads calling preGetter concurrently
return;
}
- if (disableLazyLoad) {
- loaded = true;
- return;
- }
-
if (lazyLoadFailure) {
// failed when batch lazy loaded by another bean in the batch
throw new EntityNotFoundException("Bean has been deleted - lazy loading failed");
}
- if (lazyLoadProperty == null) {
+ if (lazyLoadProperty == -1) {
lazyLoadProperty = loadProperty;
if (nodeUsageCollector != null) {
- nodeUsageCollector.setLoadProperty(lazyLoadProperty);
+ nodeUsageCollector.setLoadProperty(getProperty(lazyLoadProperty));
}
loader.loadBean(this);
@@ -521,20 +712,6 @@ public final class EntityBeanIntercept implements Serializable {
}
}
- /**
- * Create a copy of the bean as it is now. This is the original or 'old
- * values' prior to any modification. This is used to perform concurrency
- * testing.
- */
- protected void createOldValues() {
-
- oldValues = owner._ebean_createCopy();
-
- if (nodeUsageCollector != null) {
- nodeUsageCollector.setModified();
- }
- }
-
/**
* Helper method to check if two objects are equal.
*/
@@ -559,7 +736,6 @@ public final class EntityBeanIntercept implements Serializable {
} else {
return false;
}
-
}
if (obj1 instanceof URL) {
// use the string format to determine if dirty
@@ -567,26 +743,28 @@ public final class EntityBeanIntercept implements Serializable {
}
return obj1.equals(obj2);
}
-
+
+ /**
+ * Called when a BeanCollection is initialised automatically.
+ */
+ public void initialisedMany(int propertyIndex) {
+ loadedProps[propertyIndex] = true;
+ }
+
/**
* Method that is called prior to a getter method on the actual entity.
- *
- * This checks if the bean is a reference and should be loaded.
- *
*/
- public void preGetter(String propertyName) {
- if (!intercepting) {
+ public void preGetter(int propertyIndex) {
+ if (state == STATE_NEW || disableLazyLoad) {
return;
}
-
- if (!loaded) {
- loadBean(propertyName);
- } else if (loadedProps != null && !loadedProps.contains(propertyName)) {
- loadBean(propertyName);
+
+ if (!isLoadedProperty(propertyIndex)) {
+ loadBean(propertyIndex);
}
- if (nodeUsageCollector != null && loaded) {
- nodeUsageCollector.addUsed(propertyName);
+ if (nodeUsageCollector != null) {
+ nodeUsageCollector.addUsed(getProperty(propertyIndex));
}
}
@@ -619,245 +797,211 @@ public final class EntityBeanIntercept implements Serializable {
* OneToMany and ManyToMany don't have any interception so just check for
* PropertyChangeSupport.
*/
- public PropertyChangeEvent preSetterMany(boolean interceptField, String propertyName,
- Object oldValue, Object newValue) {
+ public PropertyChangeEvent preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
- // skip setter interception on many's
+ if (readOnly) {
+ throw new IllegalStateException("This bean is readOnly");
+ }
+
+ setLoadedProperty(propertyIndex);
+
+ // Bean itself not considered dirty when many changed
if (pcs != null) {
- return new PropertyChangeEvent(owner, propertyName, oldValue, newValue);
+ return new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
} else {
return null;
}
}
+
+ private void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) {
- private final void addDirty(String propertyName) {
-
- if (!intercepting) {
- return;
- }
if (readOnly) {
throw new IllegalStateException("This bean is readOnly");
}
+ setChangedProperty(propertyIndex);
- if (loaded) {
- if (oldValues == null) {
- // first time this bean is being made dirty
- createOldValues();
+ if (setDirtyState) {
+ setOriginalValue(propertyIndex, origValue);
+ if (!dirty) {
+ dirty = true;
+ if (embeddedOwner != null) {
+ // Cascade dirty state from Embedded bean to parent bean
+ embeddedOwner._ebean_getIntercept().setEmbeddedDirty(embeddedOwnerIndex);
+ }
+ if (nodeUsageCollector != null) {
+ nodeUsageCollector.setModified();
+ }
}
- if (changedProps == null) {
- changedProps = new HashSet();
- }
- changedProps.add(propertyName);
}
}
-
+
/**
* Check to see if the values are not equal. If they are not equal then create
* the old values for use with ConcurrencyMode.ALL.
*/
- public PropertyChangeEvent preSetter(boolean intercept, String propertyName, Object oldValue,
- Object newValue) {
+ public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) {
- boolean changed = !areEqual(oldValue, newValue);
-
- if (intercept && changed) {
- addDirty(propertyName);
+ if (state == STATE_NEW) {
+ setLoadedProperty(propertyIndex);
+ } else if (!areEqual(oldValue, newValue)) {
+ setChangedPropertyValue(propertyIndex, intercept, oldValue);
+ } else {
+ return null;
}
-
- if (changed && pcs != null) {
- return new PropertyChangeEvent(owner, propertyName, oldValue, newValue);
- }
-
- return null;
+
+ return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
-
+
+
/**
* Check for primitive boolean.
*/
- public PropertyChangeEvent preSetter(boolean intercept, String propertyName, boolean oldValue,
- boolean newValue) {
+ public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) {
- boolean changed = oldValue != newValue;
-
- if (intercept && changed) {
- addDirty(propertyName);
+ if (state == STATE_NEW) {
+ setLoadedProperty(propertyIndex);
+ } else if (oldValue != newValue) {
+ setChangedPropertyValue(propertyIndex, intercept, oldValue);
+ } else {
+ return null;
}
-
- if (changed && pcs != null) {
- return new PropertyChangeEvent(owner, propertyName, Boolean.valueOf(oldValue),
- Boolean.valueOf(newValue));
- }
-
- return null;
+ return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
}
/**
* Check for primitive int.
*/
- public PropertyChangeEvent preSetter(boolean intercept, String propertyName, int oldValue,
- int newValue) {
+ public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) {
- boolean changed = oldValue != newValue;
-
- if (intercept && changed) {
- addDirty(propertyName);
+ if (state == STATE_NEW) {
+ setLoadedProperty(propertyIndex);
+ } else if (oldValue != newValue) {
+ setChangedPropertyValue(propertyIndex, intercept, oldValue);
+ } else {
+ return null;
}
-
- if (changed && pcs != null) {
- return new PropertyChangeEvent(owner, propertyName, Integer.valueOf(oldValue),
- Integer.valueOf(newValue));
- }
- return null;
+ return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), Integer.valueOf(oldValue), Integer.valueOf(newValue));
}
/**
* long.
*/
- public PropertyChangeEvent preSetter(boolean intercept, String propertyName, long oldValue,
- long newValue) {
+ public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) {
- boolean changed = oldValue != newValue;
-
- if (intercept && changed) {
- addDirty(propertyName);
+ if (state == STATE_NEW) {
+ setLoadedProperty(propertyIndex);
+ } else if (oldValue != newValue) {
+ setChangedPropertyValue(propertyIndex, intercept, oldValue);
+ } else {
+ return null;
}
-
- if (changed && pcs != null) {
- return new PropertyChangeEvent(owner, propertyName, Long.valueOf(oldValue),
- Long.valueOf(newValue));
- }
- return null;
+
+ return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), Long.valueOf(oldValue), Long.valueOf(newValue));
}
/**
* double.
*/
- public PropertyChangeEvent preSetter(boolean intercept, String propertyName, double oldValue,
- double newValue) {
+ public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) {
- boolean changed = oldValue != newValue;
-
- if (intercept && changed) {
- addDirty(propertyName);
+ if (state == STATE_NEW) {
+ setLoadedProperty(propertyIndex);
+ } else if (oldValue != newValue) {
+ setChangedPropertyValue(propertyIndex, intercept, oldValue);
+ } else {
+ return null;
}
-
- if (changed && pcs != null) {
- return new PropertyChangeEvent(owner, propertyName, Double.valueOf(oldValue),
- Double.valueOf(newValue));
- }
- return null;
+ return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), Double.valueOf(oldValue), Double.valueOf(newValue));
}
/**
* float.
*/
- public PropertyChangeEvent preSetter(boolean intercept, String propertyName, float oldValue,
- float newValue) {
+ public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) {
- boolean changed = oldValue != newValue;
-
- if (intercept && changed) {
- addDirty(propertyName);
+ if (state == STATE_NEW) {
+ setLoadedProperty(propertyIndex);
+ } else if (oldValue != newValue) {
+ setChangedPropertyValue(propertyIndex, intercept, oldValue);
+ } else {
+ return null;
}
-
- if (changed && pcs != null) {
- return new PropertyChangeEvent(owner, propertyName, Float.valueOf(oldValue),
- Float.valueOf(newValue));
- }
- return null;
+ return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), Float.valueOf(oldValue), Float.valueOf(newValue));
}
/**
* short.
*/
- public PropertyChangeEvent preSetter(boolean intercept, String propertyName, short oldValue,
- short newValue) {
+ public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) {
- boolean changed = oldValue != newValue;
-
- if (intercept && changed) {
- addDirty(propertyName);
+ if (state == STATE_NEW) {
+ setLoadedProperty(propertyIndex);
+ } else if (oldValue != newValue) {
+ setChangedPropertyValue(propertyIndex, intercept, oldValue);
+ } else {
+ return null;
}
-
- if (changed && pcs != null) {
- return new PropertyChangeEvent(owner, propertyName, Short.valueOf(oldValue),
- Short.valueOf(newValue));
- }
- return null;
+ return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), Short.valueOf(oldValue), Short.valueOf(newValue));
}
/**
* char.
*/
- public PropertyChangeEvent preSetter(boolean intercept, String propertyName, char oldValue,
- char newValue) {
+ public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) {
- boolean changed = oldValue != newValue;
-
- if (intercept && changed) {
- addDirty(propertyName);
+ if (state == STATE_NEW) {
+ setLoadedProperty(propertyIndex);
+ } else if (oldValue != newValue) {
+ setChangedPropertyValue(propertyIndex, intercept, oldValue);
+ } else {
+ return null;
}
-
- if (changed && pcs != null) {
- return new PropertyChangeEvent(owner, propertyName, Character.valueOf(oldValue),
- Character.valueOf(newValue));
- }
- return null;
+ return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), Character.valueOf(oldValue), Character.valueOf(newValue));
}
/**
- * char.
+ * byte.
*/
- public PropertyChangeEvent preSetter(boolean intercept, String propertyName, byte oldValue,
- byte newValue) {
+ public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) {
- boolean changed = oldValue != newValue;
-
- if (intercept && changed) {
- addDirty(propertyName);
+ if (state == STATE_NEW) {
+ setLoadedProperty(propertyIndex);
+ } else if (oldValue != newValue) {
+ setChangedPropertyValue(propertyIndex, intercept, oldValue);
+ } else {
+ return null;
}
-
- if (changed && pcs != null) {
- return new PropertyChangeEvent(owner, propertyName, Byte.valueOf(oldValue),
- Byte.valueOf(newValue));
- }
- return null;
+ return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), Byte.valueOf(oldValue), Byte.valueOf(newValue));
}
/**
* char[].
*/
- public PropertyChangeEvent preSetter(boolean intercept, String propertyName, char[] oldValue,
- char[] newValue) {
+ public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) {
- boolean changed = !areEqualChars(oldValue, newValue);
-
- if (intercept && changed) {
- addDirty(propertyName);
+ if (state == STATE_NEW) {
+ setLoadedProperty(propertyIndex);
+ } else if (!areEqualChars(oldValue, newValue)) {
+ setChangedPropertyValue(propertyIndex, intercept, oldValue);
+ } else {
+ return null;
}
-
- if (changed && pcs != null) {
- return new PropertyChangeEvent(owner, propertyName, oldValue, newValue);
- }
- return null;
+ return (pcs == null) ? null: new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
/**
* byte[].
*/
- public PropertyChangeEvent preSetter(boolean intercept, String propertyName, byte[] oldValue,
- byte[] newValue) {
+ public PropertyChangeEvent preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) {
- boolean changed = !areEqualBytes(oldValue, newValue);
-
- if (intercept && changed) {
- addDirty(propertyName);
+ if (state == STATE_NEW) {
+ setLoadedProperty(propertyIndex);
+ } else if (!areEqualBytes(oldValue, newValue)) {
+ setChangedPropertyValue(propertyIndex, intercept, oldValue);
+ } else {
+ return null;
}
-
- if (changed && pcs != null) {
- return new PropertyChangeEvent(owner, propertyName, oldValue, newValue);
- }
- return null;
+ return (pcs == null) ? null : new PropertyChangeEvent(owner, getProperty(propertyIndex), oldValue, newValue);
}
private static boolean areEqualBytes(byte[] b1, byte[] b2) {
diff --git a/src/main/java/com/avaje/ebean/common/AbstractBeanCollection.java b/src/main/java/com/avaje/ebean/common/AbstractBeanCollection.java
index a9cacd874..484909963 100644
--- a/src/main/java/com/avaje/ebean/common/AbstractBeanCollection.java
+++ b/src/main/java/com/avaje/ebean/common/AbstractBeanCollection.java
@@ -1,8 +1,6 @@
package com.avaje.ebean.common;
import java.util.Set;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
import javax.persistence.PersistenceException;
@@ -12,7 +10,6 @@ import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.EntityBean;
-import com.avaje.ebean.bean.EntityBeanIntercept;
/**
* Base class for List Set and Map implementations of BeanCollection.
@@ -38,25 +35,16 @@ public abstract class AbstractBeanCollection implements BeanCollection {
protected transient BeanCollectionTouched beanCollectionTouched;
- protected transient Future fetchFuture;
-
/**
* The owning bean (used for lazy fetch).
*/
- protected final Object ownerBean;
+ protected final EntityBean ownerBean;
/**
* The name of this property in the owning bean (used for lazy fetch).
*/
protected final String propertyName;
- /**
- * Can be false when a background thread is used to continue the fetch the
- * rows. It will set this to true when it is finished. If no background thread
- * is used then this should already be true.
- */
- protected boolean finishedFetch = true;
-
/**
* Flag set to true if rows are limited by firstRow maxRows and more rows
* exist. For use by client to enable 'next' for paging.
@@ -70,6 +58,12 @@ public abstract class AbstractBeanCollection implements BeanCollection {
protected boolean modifyRemoveListening;
protected boolean modifyListening;
+ /**
+ * Flag used to tell if empty collections have been cleared etc or just
+ * uninitialised.
+ */
+ protected boolean touched;
+
/**
* Constructor not non-lazy loading collection.
*/
@@ -81,19 +75,15 @@ public abstract class AbstractBeanCollection implements BeanCollection {
/**
* Used to create deferred fetch proxy.
*/
- public AbstractBeanCollection(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
+ public AbstractBeanCollection(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
this.loader = loader;
this.ebeanServerName = loader.getName();
this.ownerBean = ownerBean;
this.propertyName = propertyName;
-
- if (ownerBean instanceof EntityBean) {
- EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept();
- this.readOnly = ebi.isReadOnly();
- }
+ this.readOnly = ownerBean._ebean_getIntercept().isReadOnly();
}
- public Object getOwnerBean() {
+ public EntityBean getOwnerBean() {
return ownerBean;
}
@@ -128,7 +118,14 @@ public abstract class AbstractBeanCollection implements BeanCollection {
checkEmptyLazyLoad();
}
- protected void touched() {
+ /**
+ * Set touched. If setFlag is false then typically an isEmpty() call and still
+ * considering that to be untouched.
+ */
+ protected void touched(boolean setFlag) {
+ if (setFlag) {
+ touched = true;
+ }
if (beanCollectionTouched != null) {
// only call this once
beanCollectionTouched.notifyTouched(this);
@@ -174,46 +171,6 @@ public abstract class AbstractBeanCollection implements BeanCollection {
this.hasMoreRows = hasMoreRows;
}
- /**
- * Returns true if the fetch has finished. False if the fetch is continuing in
- * a background thread.
- */
- public boolean isFinishedFetch() {
- return finishedFetch;
- }
-
- /**
- * Set to true when a fetch has finished. Used when a fetch continues in the
- * background.
- */
- public void setFinishedFetch(boolean finishedFetch) {
- this.finishedFetch = finishedFetch;
- }
-
- public void setBackgroundFetch(Future fetchFuture) {
- this.fetchFuture = fetchFuture;
- }
-
- public void backgroundFetchWait(long wait, TimeUnit timeUnit) {
- if (fetchFuture != null) {
- try {
- fetchFuture.get(wait, timeUnit);
- } catch (Exception e) {
- throw new PersistenceException(e);
- }
- }
- }
-
- public void backgroundFetchWait() {
- if (fetchFuture != null) {
- try {
- fetchFuture.get();
- } catch (Exception e) {
- throw new PersistenceException(e);
- }
- }
- }
-
protected void checkReadOnly() {
if (readOnly) {
String msg = "This collection is in ReadOnly mode";
diff --git a/src/main/java/com/avaje/ebean/common/BeanList.java b/src/main/java/com/avaje/ebean/common/BeanList.java
index d24718ff5..b70325e3e 100644
--- a/src/main/java/com/avaje/ebean/common/BeanList.java
+++ b/src/main/java/com/avaje/ebean/common/BeanList.java
@@ -10,13 +10,15 @@ import java.util.ListIterator;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
+import com.avaje.ebean.bean.EntityBean;
/**
* List capable of lazy loading.
*/
-public final class BeanList extends AbstractBeanCollection implements List,
- BeanCollectionAdd {
+public final class BeanList extends AbstractBeanCollection implements List, BeanCollectionAdd {
+ private static final long serialVersionUID = 1L;
+
/**
* The underlying List implementation.
*/
@@ -40,12 +42,17 @@ public final class BeanList extends AbstractBeanCollection implements List
/**
* Used to create deferred fetch proxy.
*/
- public BeanList(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
+ public BeanList(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
super(loader, ownerBean, propertyName);
}
+ @Override
+ public boolean isEmptyAndUntouched() {
+ return !touched && (list == null || list.isEmpty());
+ }
+
@SuppressWarnings("unchecked")
- public void addBean(Object bean) {
+ public void addBean(EntityBean bean) {
list.add((E) bean);
}
@@ -75,16 +82,24 @@ public final class BeanList extends AbstractBeanCollection implements List
list = new ArrayList();
}
}
- touched();
+ touched(true);
}
}
+ private void initAsUntouched() {
+ init(false);
+ }
+
private void init() {
+ init(true);
+ }
+
+ private void init(boolean setTouched) {
synchronized (this) {
if (list == null) {
lazyLoadCollection(false);
}
- touched();
+ touched(setTouched);
}
}
@@ -257,7 +272,7 @@ public final class BeanList extends AbstractBeanCollection implements List
}
public boolean isEmpty() {
- init();
+ initAsUntouched();
return list.isEmpty();
}
diff --git a/src/main/java/com/avaje/ebean/common/BeanMap.java b/src/main/java/com/avaje/ebean/common/BeanMap.java
index ea257e61a..5c97d0991 100644
--- a/src/main/java/com/avaje/ebean/common/BeanMap.java
+++ b/src/main/java/com/avaje/ebean/common/BeanMap.java
@@ -8,12 +8,15 @@ import java.util.Map;
import java.util.Set;
import com.avaje.ebean.bean.BeanCollectionLoader;
+import com.avaje.ebean.bean.EntityBean;
/**
* Map capable of lazy loading.
*/
public final class BeanMap extends AbstractBeanCollection implements Map {
+ private static final long serialVersionUID = 1L;
+
/**
* The underlying map implementation.
*/
@@ -33,9 +36,13 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
this(new LinkedHashMap());
}
- public BeanMap(BeanCollectionLoader ebeanServer, Object ownerBean, String propertyName) {
+ public BeanMap(BeanCollectionLoader ebeanServer, EntityBean ownerBean, String propertyName) {
super(ebeanServer, ownerBean, propertyName);
}
+
+ public boolean isEmptyAndUntouched() {
+ return !touched && (map == null || map.isEmpty());
+ }
@SuppressWarnings("unchecked")
public void internalPut(Object key, Object bean) {
@@ -83,16 +90,24 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
map = new LinkedHashMap();
}
}
- touched();
+ touched(true);
}
}
+ private void initAsUntouched() {
+ init(false);
+ }
+
private void init() {
+ init(true);
+ }
+
+ private void init(boolean setTouched) {
synchronized (this) {
if (map == null) {
lazyLoadCollection(false);
}
- touched();
+ touched(setTouched);
}
}
@@ -209,7 +224,7 @@ public final class BeanMap extends AbstractBeanCollection implements Ma
}
public boolean isEmpty() {
- init();
+ initAsUntouched();
return map.isEmpty();
}
diff --git a/src/main/java/com/avaje/ebean/common/BeanSet.java b/src/main/java/com/avaje/ebean/common/BeanSet.java
index 36e2472ea..78da3941a 100644
--- a/src/main/java/com/avaje/ebean/common/BeanSet.java
+++ b/src/main/java/com/avaje/ebean/common/BeanSet.java
@@ -8,12 +8,15 @@ import java.util.Set;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
+import com.avaje.ebean.bean.EntityBean;
/**
* Set capable of lazy loading.
*/
public final class BeanSet extends AbstractBeanCollection implements Set, BeanCollectionAdd {
+ private static final long serialVersionUID = 1L;
+
/**
* The underlying Set implementation.
*/
@@ -33,12 +36,16 @@ public final class BeanSet extends AbstractBeanCollection implements Set());
}
- public BeanSet(BeanCollectionLoader loader, Object ownerBean, String propertyName) {
+ public BeanSet(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
super(loader, ownerBean, propertyName);
}
+ public boolean isEmptyAndUntouched() {
+ return !touched && (set == null || set.isEmpty());
+ }
+
@SuppressWarnings("unchecked")
- public void addBean(Object bean) {
+ public void addBean(EntityBean bean) {
set.add((E) bean);
}
@@ -83,16 +90,24 @@ public final class BeanSet extends AbstractBeanCollection implements Set();
}
}
- touched();
+ touched(true);
}
}
+ private void initAsUntouched() {
+ init(false);
+ }
+
private void init() {
+ init(true);
+ }
+
+ private void init(boolean setTouched) {
synchronized (this) {
if (set == null) {
lazyLoadCollection(true);
}
- touched();
+ touched(setTouched);
}
}
@@ -217,7 +232,7 @@ public final class BeanSet extends AbstractBeanCollection implements Set
@@ -31,8 +33,9 @@ import java.util.Set;
*
*
* A BeanPersistListener is either found automatically via class path search or
- * can be added programmatically via ServerConfiguration.addEntity().
+ * can be added programmatically via {@link ServerConfig#add(BeanPersistListener)}.
*
+ * @see ServerConfig#add(BeanPersistListener)
*/
public interface BeanPersistListener {
@@ -52,7 +55,7 @@ public interface BeanPersistListener {
* @param bean
* The bean that was updated.
* @param updatedProperties
- * the properties on the bean that where updated
+ * The properties that were modified by this update.
*/
public boolean updated(T bean, Set updatedProperties);
diff --git a/src/main/java/com/avaje/ebean/event/BeanPersistRequest.java b/src/main/java/com/avaje/ebean/event/BeanPersistRequest.java
index 97573bcf6..388b03325 100644
--- a/src/main/java/com/avaje/ebean/event/BeanPersistRequest.java
+++ b/src/main/java/com/avaje/ebean/event/BeanPersistRequest.java
@@ -1,9 +1,11 @@
package com.avaje.ebean.event;
+import java.util.Map;
import java.util.Set;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.Transaction;
+import com.avaje.ebean.ValuePair;
/**
* Holds the information available for a bean persist (insert, update or
@@ -41,12 +43,8 @@ public interface BeanPersistRequest {
public T getBean();
/**
- * Returns a bean containing the original values prior to the bean being
- * modified.
- *
- * This is for updates only.
- *
+ * Returns a map of the properties that have changed and their new and old values.
*/
- public T getOldValues();
+ public Map getUpdatedValues();
}
diff --git a/src/main/java/com/avaje/ebean/text/csv/CsvReader.java b/src/main/java/com/avaje/ebean/text/csv/CsvReader.java
index 2052648f2..d35adc00e 100644
--- a/src/main/java/com/avaje/ebean/text/csv/CsvReader.java
+++ b/src/main/java/com/avaje/ebean/text/csv/CsvReader.java
@@ -32,7 +32,6 @@ import com.avaje.ebean.text.StringParser;
* csvReader.addDateTime("anniversary", "dd-MMM-yyyy");
* csvReader.addProperty("billingAddress.line1");
* csvReader.addProperty("billingAddress.city");
- * csvReader.addReference("billingAddress.country.code");
*
* csvReader.process(reader);
*
@@ -41,8 +40,6 @@ import com.avaje.ebean.text.StringParser;
* }
*
*
- * @author rbygrave
- *
* @param
* the entity bean type
*/
@@ -129,13 +126,6 @@ public interface CsvReader {
*/
public void addProperty(String propertyName);
- /**
- * Define the next property to be a reference. This effectively means it
- * represents a foreign key. For example, with an Address object a Country
- * Code could be a reference.
- */
- public void addReference(String propertyName);
-
/**
* Define the next property and use a custom StringParser to convert the
* string content into the appropriate type for the property.
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java b/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java
index c148f2e51..b89fd5223 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java
@@ -38,11 +38,6 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
*/
public boolean isDefaultDeleteMissingChildren();
- /**
- * Return true if UpdateNullProperties defaults to true for stateless updates.
- */
- public boolean isDefaultUpdateNullProperties();
-
/**
* Return the DatabasePlatform for this server.
*/
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
index 792e80beb..586818857 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java
@@ -6,7 +6,6 @@ import java.util.List;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.Query;
-import com.avaje.ebean.QueryListener;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.CallStack;
import com.avaje.ebean.bean.EntityBean;
@@ -509,12 +508,6 @@ public interface SpiQuery extends Query {
*/
public String getMapKey();
- /**
- * Return the number of rows after which fetching should occur in a
- * background thread.
- */
- public int getBackgroundFetchAfter();
-
/**
* Return the maximum number of rows to return in the query.
*/
@@ -545,19 +538,6 @@ public interface SpiQuery extends Query {
*/
public Object getId();
- /**
- * Return the queryListener.
- */
- public QueryListener getListener();
-
- /**
- * Return true if this query should use its own transaction.
- *
- * This is true for background fetching and when using QueryListener.
- *
- */
- public boolean createOwnTransaction();
-
/**
* Set the generated sql for debug purposes.
*
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiUpdatePlan.java b/src/main/java/com/avaje/ebeaninternal/api/SpiUpdatePlan.java
index 89588954f..9ac23a9af 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiUpdatePlan.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiUpdatePlan.java
@@ -1,9 +1,9 @@
package com.avaje.ebeaninternal.api;
import java.sql.SQLException;
-import java.util.Set;
import com.avaje.ebean.annotation.ConcurrencyMode;
+import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.persist.dml.DmlHandler;
import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
@@ -21,20 +21,20 @@ import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable;
*/
public interface SpiUpdatePlan {
- /**
- * Return true if the set clause has no columns.
- *
- * Can occur when the only columns updated have a updatable=false in their
- * deployment.
- *
- */
- public boolean isEmptySetClause();
+ /**
+ * Return true if the set clause has no columns.
+ *
+ * Can occur when the only columns updated have a updatable=false in their
+ * deployment.
+ *
+ */
+ public boolean isEmptySetClause();
/**
* Bind given the request and bean. The bean could be the oldValues bean
* when binding a update or delete where clause with ALL concurrency mode.
*/
- public void bindSet(DmlHandler bind, Object bean) throws SQLException;
+ public void bindSet(DmlHandler bind, EntityBean bean) throws SQLException;
/**
* Return the time this plan was created.
@@ -66,10 +66,10 @@ public interface SpiUpdatePlan {
*/
public Bindable getSet();
- /**
- * Return the properties that where changed and should be included in the
- * update statement.
- */
- public Set getProperties();
+// /**
+// * Return the properties that where changed and should be included in the
+// * update statement.
+// */
+// public Set getProperties();
}
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsNodeUsage.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsNodeUsage.java
index 105b765d2..ff4bc9357 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsNodeUsage.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsNodeUsage.java
@@ -86,9 +86,9 @@ public class StatisticsNodeUsage implements Serializable {
}
if ((modified || queryTuningAddVersion) && desc != null) {
- BeanProperty[] versionProps = desc.propertiesVersion();
- if (versionProps.length > 0) {
- pathProps.addToPath(path, versionProps[0].getName());
+ BeanProperty versionProp = desc.getVersionProperty();
+ if (versionProp != null) {
+ pathProps.addToPath(path, versionProp.getName());
}
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanData.java b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanData.java
index 53ebf05a1..29f786ed2 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanData.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanData.java
@@ -1,50 +1,102 @@
package com.avaje.ebeaninternal.server.cache;
-import java.util.Set;
+import java.util.Arrays;
+/**
+ * Data held in the bean cache for cached beans.
+ */
public class CachedBeanData {
- private final Object sharableBean;
- private final Set loadedProperties;
- private final Object[] data;
- private final int naturalKeyUpdate;
-
- public CachedBeanData(Object sharableBean, Set loadedProperties, Object[] data, int naturalKeyUpdate) {
- this.sharableBean = sharableBean;
- this.loadedProperties= loadedProperties;
- this.data = data;
- this.naturalKeyUpdate = naturalKeyUpdate;
- }
-
- public Object getSharableBean() {
- return sharableBean;
- }
+ private final long whenCreated;
+ private final Object sharableBean;
+ private final boolean[] loaded;
+ private final Object[] data;
+
+ private final boolean naturalKeyUpdate;
+ private final Object naturalKey;
+ private final Object oldNaturalKey;
- public boolean isNaturalKeyUpdate() {
- return naturalKeyUpdate > -1;
- }
-
- public Object getNaturalKey() {
- return data[naturalKeyUpdate];
- }
+ public CachedBeanData(Object sharableBean, boolean[] loaded, Object[] data, Object naturalKey, Object oldNaturalKey) {
+ this.whenCreated = System.currentTimeMillis();
+ this.sharableBean = sharableBean;
+ this.loaded = loaded;
+ this.data = data;
+ this.naturalKeyUpdate = naturalKey != null;
+ this.naturalKey = (naturalKey != null) ? naturalKey : oldNaturalKey;
+ this.oldNaturalKey = oldNaturalKey;
+ }
- public boolean containsProperty(String propName) {
- return loadedProperties == null || loadedProperties.contains(propName);
+ public String toString() {
+ return Arrays.toString(data);
+ }
+
+ /**
+ * Return a copy of the property data.
+ */
+ public Object[] copyData() {
+ Object[] dest = new Object[data.length];
+ System.arraycopy(data, 0, dest, 0, data.length);
+ return dest;
+ }
+
+ /**
+ * Return a copy of the loaded status for the properties.
+ */
+ public boolean[] copyLoaded() {
+ boolean[] dest = new boolean[data.length];
+ for (int i = 0; i < dest.length; i++) {
+ dest[i] = loaded[i];
}
-
- public Object getData(int i){
- return data[i];
- }
-
- public Set getLoadedProperties() {
- return loadedProperties;
- }
-
- public Object[] copyData() {
- Object[] dest = new Object[data.length];
- System.arraycopy(data, 0, dest, 0, data.length);
- return dest;
- }
-
+ return dest;
+ }
+
+ /**
+ * Return when the cached data was created.
+ */
+ public long getWhenCreated() {
+ return whenCreated;
+ }
+
+ /**
+ * Return a sharable (immutable read only) bean.
+ */
+ public Object getSharableBean() {
+ return sharableBean;
+ }
+
+ /**
+ * Return true if this data requires an update to the natural key cache.
+ */
+ public boolean isNaturalKeyUpdate() {
+ return naturalKeyUpdate;
+ }
+
+ /**
+ * Return the new/current natural key value.
+ */
+ public Object getNaturalKey() {
+ return naturalKey;
+ }
+
+ /**
+ * Return the old natural key (its entry should be removed).
+ */
+ public Object getOldNaturalKey() {
+ return oldNaturalKey;
+ }
+
+ /**
+ * Return the data for the specific property.
+ */
+ public Object getData(int i) {
+ return data[i];
+ }
+
+ /**
+ * Return true if the property is contained in this data.
+ */
+ public boolean isLoaded(int i) {
+ return loaded[i];
+ }
+
}
-
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataFromBean.java b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataFromBean.java
index c2f5025c2..c6b6797c2 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataFromBean.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataFromBean.java
@@ -1,8 +1,5 @@
package com.avaje.ebeaninternal.server.cache;
-import java.util.HashSet;
-import java.util.Set;
-
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
@@ -10,94 +7,61 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty;
public class CachedBeanDataFromBean {
- private final BeanDescriptor> desc;
- private final Object bean;
- private final EntityBeanIntercept ebi;
-
- private final Set loadedProps;
- private final Set extractProps;
- public static CachedBeanData extract(BeanDescriptor> desc, Object bean){
- if (bean instanceof EntityBean){
- return new CachedBeanDataFromBean(desc, bean, ((EntityBean)bean)._ebean_getIntercept()).extract();
-
- } else {
- return new CachedBeanDataFromBean(desc, bean, null).extract();
- }
- }
-
- public static CachedBeanData extract(BeanDescriptor> desc, Object bean, EntityBeanIntercept ebi){
- return new CachedBeanDataFromBean(desc, bean, ebi).extract();
- }
-
- private CachedBeanDataFromBean(BeanDescriptor> desc, Object bean, EntityBeanIntercept ebi) {
- this.desc = desc;
- this.bean = bean;
- this.ebi = ebi;
- if (ebi != null){
- this.loadedProps = ebi.getLoadedProps();
- this.extractProps = (loadedProps == null) ? null : new HashSet();
- } else {
- this.extractProps = new HashSet();
- this.loadedProps = null;
- }
- }
-
- private CachedBeanData extract(){
+ public static CachedBeanData extract(BeanDescriptor> desc, EntityBean bean) {
- BeanProperty[] props = desc.propertiesNonMany();
+ EntityBeanIntercept ebi = bean._ebean_getIntercept();
+
+ Object[] data = new Object[desc.getPropertyCount()];
+ boolean[] loaded = new boolean[desc.getPropertyCount()];
+
+ BeanProperty[] props = desc.propertiesNonMany();
- Object[] data = new Object[props.length];
-
- int naturalKeyUpdate = -1;
- for (int i = 0; i < props.length; i++) {
- BeanProperty prop = props[i];
- if (includeNonManyProperty(prop.getName())){
-
- data[i] = prop.getCacheDataValue(bean);
- if (prop.isNaturalKey()) {
- naturalKeyUpdate = i;
- }
- if (ebi != null){
- if (extractProps != null){
- extractProps.add(prop.getName());
- }
- } else if (data[i] != null){
- if (extractProps != null){
- extractProps.add(prop.getName());
- }
- }
- }
+ Object naturalKey = null;
+
+ for (int i = 0; i < props.length; i++) {
+ BeanProperty prop = props[i];
+ if (ebi.isLoadedProperty(prop.getPropertyIndex())) {
+ int propertyIndex = prop.getPropertyIndex();
+ data[propertyIndex] = prop.getCacheDataValue(bean);
+ loaded[propertyIndex] = true;
+ if (prop.isNaturalKey()) {
+ naturalKey = prop.getValue(bean);
}
-
- Object sharableBean = null;
- if (desc.isCacheSharableBeans() && ebi != null && loadedProps == null){
- if (ebi.isReadOnly()){
- sharableBean = bean;
- } else {
- // create a readOnly sharable instance by copying the data
- sharableBean = desc.createBean();
- BeanProperty[] propertiesId = desc.propertiesId();
- for (int i = 0; i < propertiesId.length; i++) {
- Object v = propertiesId[i].getValue(bean);
- propertiesId[i].setValue(sharableBean, v);
- }
- BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient();
- for (int i = 0; i < propertiesNonTransient.length; i++) {
- Object v = propertiesNonTransient[i].getValue(bean);
- propertiesNonTransient[i].setValue(sharableBean, v);
- }
- EntityBeanIntercept ebi = ((EntityBean)sharableBean)._ebean_intercept();
- ebi.setReadOnly(true);
- ebi.setLoaded();
- }
- }
-
- return new CachedBeanData(sharableBean, extractProps, data, naturalKeyUpdate);
+ }
}
+
+ EntityBean sharableBean = createSharableBean(desc, bean, ebi);
+
+ return new CachedBeanData(sharableBean, loaded, data, naturalKey, null);
+ }
+
+ private static EntityBean createSharableBean(BeanDescriptor> desc, EntityBean bean, EntityBeanIntercept beanEbi) {
- private boolean includeNonManyProperty(String name) {
- return loadedProps == null || loadedProps.contains(name);
+ if (!desc.isCacheSharableBeans() || !beanEbi.isFullyLoadedBean()) {
+ return null;
}
+ if (beanEbi.isReadOnly()) {
+ return bean;
+ }
+ // create a readOnly sharable instance by copying the data
+ EntityBean sharableBean = desc.createBean();
+ BeanProperty idProp = desc.getIdProperty();
+ if (idProp != null) {
+ Object v = idProp.getValue(bean);
+ idProp.setValue(sharableBean, v);
+ }
+ BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient();
+ for (int i = 0; i < propertiesNonTransient.length; i++) {
+ Object v = propertiesNonTransient[i].getValue(bean);
+ propertiesNonTransient[i].setValue(sharableBean, v);
+ }
+ EntityBeanIntercept intercept = sharableBean._ebean_intercept();
+ intercept.setReadOnly(true);
+ intercept.setLoaded();
+ return sharableBean;
+ }
+
+
}
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataToBean.java b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataToBean.java
index 5199d8375..1c05d4e18 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataToBean.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataToBean.java
@@ -1,8 +1,5 @@
package com.avaje.ebeaninternal.server.cache;
-import java.util.HashSet;
-import java.util.Set;
-
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
@@ -11,107 +8,34 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
public class CachedBeanDataToBean {
- private final BeanDescriptor> desc;
- private final Object bean;
- private final EntityBeanIntercept ebi;
- private final CachedBeanData cacheBeandata;
- private final Set cacheLoadedProperties;
- private final Set loadedProps;
-
- private final Set excludeProps;
- private final Object oldValuesBean;
- private final boolean readOnly;
- public static void load(BeanDescriptor> desc, Object bean, CachedBeanData cacheBeandata) {
- if (bean instanceof EntityBean){
- load(desc, bean, ((EntityBean)bean)._ebean_getIntercept(), cacheBeandata);
- } else {
- load(desc, bean, null, cacheBeandata);
- }
- }
-
- public static void load(BeanDescriptor> desc, Object bean, EntityBeanIntercept ebi, CachedBeanData cacheBeandata) {
- new CachedBeanDataToBean(desc, bean, ebi, cacheBeandata).load();
- }
+ public static boolean load(BeanDescriptor> desc, EntityBean bean, CachedBeanData cacheBeanData) {
- private CachedBeanDataToBean(BeanDescriptor> desc, Object bean, EntityBeanIntercept ebi, CachedBeanData cacheBeandata) {
- this.desc = desc;
- this.bean = bean;
- this.ebi = ebi;
- this.cacheBeandata = cacheBeandata;
- this.cacheLoadedProperties = cacheBeandata.getLoadedProperties();
- this.loadedProps = (cacheLoadedProperties == null) ? null : new HashSet();
-
- if (ebi != null){
- this.excludeProps = ebi.getLoadedProps();
- this.oldValuesBean = ebi.getOldValues();
- this.readOnly = ebi.isReadOnly();
+ EntityBeanIntercept ebi = bean._ebean_getIntercept();
+
+ BeanProperty[] props = desc.propertiesNonMany();
+ for (int i = 0; i < props.length; i++) {
+
+ BeanProperty prop = props[i];
+ int propertyIndex = prop.getPropertyIndex();
+ if (cacheBeanData.isLoaded(propertyIndex)) {
+ if (ebi.isLoadedProperty(propertyIndex)) {
+ // already loaded (lazy load on partially loaded bean)
} else {
- this.excludeProps = null;
- this.oldValuesBean = null;
- this.readOnly = false;
+ Object data = cacheBeanData.getData(propertyIndex);
+ prop.setCacheDataValue(bean, data);
}
+ }
}
-
- private boolean load(){
-
- BeanProperty[] propertiesNonTransient = desc.propertiesNonMany();
- for (int i = 0; i < propertiesNonTransient.length; i++) {
- BeanProperty prop = propertiesNonTransient[i];
- if (includeNonManyProperty(prop.getName())){
- Object data = cacheBeandata.getData(i);
- prop.setCacheDataValue(bean, data, oldValuesBean, readOnly);
- }
- }
- BeanPropertyAssocMany>[] manys = desc.propertiesMany();
- for (int i = 0; i < manys.length; i++) {
- BeanPropertyAssocMany> prop = manys[i];
- if (includeManyProperty(prop.getName())){
- // set a lazy loading proxy
- prop.createReference(bean);
- }
- }
-
- if (ebi != null){
- if (loadedProps == null){
- ebi.setLoadedProps(null);
- } else {
- HashSet mergeProps = new HashSet();
- if (excludeProps != null) {
- mergeProps.addAll(excludeProps);
- }
- mergeProps.addAll(loadedProps);
- ebi.setLoadedProps(mergeProps);
- }
- ebi.setLoadedLazy();
- }
- return true;
+ BeanPropertyAssocMany>[] manys = desc.propertiesMany();
+ for (int i = 0; i < manys.length; i++) {
+ manys[i].createReferenceIfNull(bean);
}
-
- private boolean includeManyProperty(String name) {
- if (excludeProps != null && excludeProps.contains(name)){
- // ignore this property (partial bean lazy loading)
- return false;
- }
- if (loadedProps != null){
- loadedProps.add(name);
- }
- return true;
- }
-
- private boolean includeNonManyProperty(String name) {
- if (excludeProps != null && excludeProps.contains(name)){
- // ignore this property (partial bean lazy loading)
- return false;
- }
- if (cacheLoadedProperties != null && !cacheLoadedProperties.contains(name)){
- return false;
- }
- if (loadedProps != null){
- loadedProps.add(name);
- }
- return true;
- }
-
+
+ ebi.setLoadedLazy();
+
+ return true;
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataUpdate.java
index 93c883213..7235e72a9 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataUpdate.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataUpdate.java
@@ -1,49 +1,44 @@
package com.avaje.ebeaninternal.server.cache;
-import java.util.HashSet;
-import java.util.Set;
-
-import com.avaje.ebeaninternal.server.core.PersistRequestBean;
+import com.avaje.ebean.bean.EntityBean;
+import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
+/**
+ * Create a new CachedBeanData based on the existing CachedBeanData and the updated bean.
+ */
public class CachedBeanDataUpdate {
- public static CachedBeanData update(BeanDescriptor> desc, CachedBeanData data, PersistRequestBean> updateRequest){
+ /**
+ * Create a new CachedBeanData based on the existing CachedBeanData and the updated bean.
+ */
+ public static CachedBeanData update(BeanDescriptor> desc, CachedBeanData existingData, EntityBean updateBean) {
-
- Set loadedProperties = data.getLoadedProperties();
- Object[] copyOfData = data.copyData();
-
- Object updateBean = updateRequest.getBean();
- Set updatedProperties = updateRequest.getUpdatedProperties();
-
- int naturalKeyUpdate = -1;
- boolean mergeProperties = false;
- BeanProperty[] props = desc.propertiesNonMany();
- for (int i = 0; i < props.length; i++) {
- if (updatedProperties.contains(props[i].getName())){
- if (props[i].isNaturalKey()){
- naturalKeyUpdate = i;
- }
- copyOfData[i] = props[i].getCacheDataValue(updateBean);
- if (loadedProperties != null && !mergeProperties && !loadedProperties.contains(props[i].getName())){
- mergeProperties = true;
- }
- }
+ // take a copy of the raw data and loaded status
+ boolean[] copyLoaded = existingData.copyLoaded();
+ Object[] copyData = existingData.copyData();
+
+ EntityBeanIntercept ebi = updateBean._ebean_getIntercept();
+
+ Object newNaturalKey = null;
+ Object oldNaturalKey = existingData.getNaturalKey();
+
+ BeanProperty[] props = desc.propertiesNonMany();
+ for (int i = 0; i < props.length; i++) {
+ // check if the properties was in the update
+ int propertyIndex = props[i].getPropertyIndex();
+ if (ebi.isLoadedProperty(propertyIndex)) {
+ if (props[i].isNaturalKey()) {
+ newNaturalKey = updateBean._ebean_getField(propertyIndex);
}
-
- if (mergeProperties){
- HashSet mergeProps = new HashSet();
- mergeProps.addAll(loadedProperties);
- mergeProps.addAll(updatedProperties);
- loadedProperties = mergeProps;
- }
-
- return new CachedBeanData(null, loadedProperties, copyOfData, naturalKeyUpdate);
-
+ // set the cache safe value for the property and mark it as loaded
+ copyData[propertyIndex] = props[i].getCacheDataValue(updateBean);
+ copyLoaded[propertyIndex] = true;
+ }
}
-
-
+
+ return new CachedBeanData(null, copyLoaded, copyData, newNaturalKey, oldNaturalKey);
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedManyIds.java b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedManyIds.java
index 3f123efa7..822d477e5 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedManyIds.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedManyIds.java
@@ -2,16 +2,26 @@ package com.avaje.ebeaninternal.server.cache;
import java.util.List;
+/**
+ * The cached data for O2M and M2M relationships.
+ *
+ * This is effectively just the Id values for each of the beans in the collection.
+ *
+ */
public class CachedManyIds {
- private final List