diff --git a/pom.xml b/pom.xml index 59ecb6fbf..7ec0eb1ed 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ org.avaje.ebeanorm avaje-ebeanorm-agent - 4.0.1-RC1 + 4.0.1-RC2-SNAPSHOT test @@ -169,7 +169,7 @@ org.avaje.ebeanorm avaje-ebeanorm-mavenenhancer - 4.0.1-RC1 + 4.0.1-RC2-SNAPSHOT main diff --git a/src/main/java/com/avaje/ebean/BeanState.java b/src/main/java/com/avaje/ebean/BeanState.java index df6290ab6..b232ff130 100644 --- a/src/main/java/com/avaje/ebean/BeanState.java +++ b/src/main/java/com/avaje/ebean/BeanState.java @@ -75,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 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/bean/EntityBeanIntercept.java b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java index 7f5b0361e..b1e39d54e 100644 --- a/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java +++ b/src/main/java/com/avaje/ebean/bean/EntityBeanIntercept.java @@ -263,6 +263,20 @@ 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. */ @@ -273,8 +287,17 @@ public final class EntityBeanIntercept implements Serializable { /** * Set this as a reference object. */ - public void setReference() { + 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; + } + } + } } /** @@ -330,16 +353,18 @@ public final class EntityBeanIntercept implements Serializable { } /** - * 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; + } } /** @@ -490,9 +515,13 @@ public final class EntityBeanIntercept implements Serializable { */ public void setNewBeanForUpdate() { + if (changedProps == null) { + changedProps = new boolean[owner._ebean_getPropertyNames().length]; + } + for (int i=0; i< loadedProps.length; i++) { if (loadedProps[i]) { - setChangedProperty(i); + changedProps[i] = true; } } setDirty(true); 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/server/cache/CachedBeanDataFromBean.java b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataFromBean.java index 370d79c33..67bc7cec9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataFromBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/CachedBeanDataFromBean.java @@ -56,10 +56,10 @@ public class CachedBeanDataFromBean { // create a readOnly sharable instance by copying the data EntityBean 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 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++) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java index a6c4348aa..514ab722c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java @@ -317,11 +317,9 @@ public class DefaultBeanLoader { } for (int i = 0; i < ebis.length; i++) { - if (ebis[i].isReference()) { - // The underlying row in DB was deleted. Mark this bean as 'failed' - // but allow processing to continue until it is accessed by client code - ebis[i].setLazyLoadFailure(); - } + // Check if the underlying row in DB was deleted. Mark this bean as 'failed' if + // necessary but allow processing to continue until it is accessed by client code + ebis[i].checkLazyLoadFailure(); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java index 6aeb1fa3c..cf409ca2c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanState.java @@ -70,9 +70,5 @@ public class DefaultBeanState implements BeanState { public void setLoaded() { intercept.setLoaded(); } - - public void setReference() { - intercept.setReference(); - } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index 969d5db85..a8f2e4f37 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -142,8 +142,8 @@ public final class DefaultServer implements SpiEbeanServer { * false; */ private final boolean rollbackOnChecked; + private final boolean defaultDeleteMissingChildren; - private final boolean defaultUpdateNullProperties; /** * Handles the save, delete, updateSql CallableSql. @@ -251,8 +251,6 @@ public final class DefaultServer implements SpiEbeanServer { this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode(); this.maxCallStack = GlobalProperties.getInt("ebean.maxCallStack", 5); - this.defaultUpdateNullProperties = "true" - .equalsIgnoreCase(config.getServerConfig().getProperty("defaultUpdateNullProperties", "false")); this.defaultDeleteMissingChildren = "true".equalsIgnoreCase(config.getServerConfig() .getProperty("defaultDeleteMissingChildren", "true")); @@ -315,10 +313,6 @@ public final class DefaultServer implements SpiEbeanServer { return defaultDeleteMissingChildren; } - public boolean isDefaultUpdateNullProperties() { - return defaultUpdateNullProperties; - } - public int getLazyLoadBatchSize() { return lazyLoadBatchSize; } @@ -656,23 +650,15 @@ public final class DefaultServer implements SpiEbeanServer { // we actually need to do a query because // we don't know the type without the // discriminator value - BeanProperty[] idProps = desc.propertiesId(); - String idNames; - switch (idProps.length) { - case 0: - throw new PersistenceException("No ID properties for this type? " + desc); - case 1: - idNames = idProps[0].getName(); - break; - default: - idNames = Arrays.toString(idProps); - idNames = idNames.substring(1, idNames.length() - 1); + BeanProperty idProp = desc.getIdProperty(); + if (idProp == null) { + throw new PersistenceException("No ID properties for this type? " + desc); } - + // just select the id properties and // the discriminator column (auto added) Query query = createQuery(type); - query.select(idNames).setId(id); + query.select(idProp.getName()).setId(id); ref = query.findUnique(); @@ -1616,39 +1602,24 @@ public final class DefaultServer implements SpiEbeanServer { * Force an update using the bean updating non-null properties. */ public void update(Object bean) { - update(bean, null, null); + update(bean, null); } /** * Force an update using the bean explicitly stating which properties to * include in the update. */ - public void update(Object bean, Set updateProps) { - update(bean, updateProps, null); - } - - /** - * Force an update using the bean updating non-null properties. - */ public void update(Object bean, Transaction t) { - update(bean, null, t); + update(bean, t, defaultDeleteMissingChildren); } /** * Force an update using the bean explicitly stating which properties to * include in the update. */ - public void update(Object bean, Set updateProps, Transaction t) { - update(bean, updateProps, t, defaultDeleteMissingChildren, defaultUpdateNullProperties); - } - - /** - * Force an update using the bean explicitly stating which properties to - * include in the update. - */ - public void update(Object bean, Set updateProps, Transaction t, boolean deleteMissingChildren, boolean updateNullProperties) { + public void update(Object bean, Transaction t, boolean deleteMissingChildren) { - persister.forceUpdate(checkEntityBean(bean), updateProps, t, deleteMissingChildren, updateNullProperties); + persister.forceUpdate(checkEntityBean(bean), t, deleteMissingChildren); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java index e19fd8498..d5eeb86df 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java @@ -258,12 +258,11 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe public Map findMap() { String mapKey = query.getMapKey(); if (mapKey == null) { - BeanProperty[] ids = beanDescriptor.propertiesId(); - if (ids.length == 1) { - query.setMapKey(ids[0].getName()); + BeanProperty idProp = beanDescriptor.getIdProperty(); + if (idProp != null) { + query.setMapKey(idProp.getName()); } else { - String msg = "No mapKey specified for query"; - throw new PersistenceException(msg); + throw new PersistenceException("No mapKey specified for query"); } } return (Map) queryEngine.findMany(this); diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequest.java index 31a7983fd..5ce5a750f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequest.java @@ -12,7 +12,7 @@ import com.avaje.ebeaninternal.server.persist.PersistExecute; public abstract class PersistRequest extends BeanRequest implements BatchPostExecute { public enum Type { - INSERT, UPDATE, DELETE, ORMUPDATE, UPDATESQL, CALLABLESQL + DETERMINE, INSERT, UPDATE, DELETE, ORMUPDATE, UPDATESQL, CALLABLESQL }; boolean persistCascade; @@ -31,10 +31,6 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe super(server, t); this.persistExecute = persistExecute; } - - public void setNotNullAsLoaded() { - // Do nothing by default - } /** * Execute a the request or queue/batch it for later execution. @@ -94,14 +90,6 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe return type; } - /** - * Set the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL or - * CALLABLESQL. - */ - public void setType(Type type) { - this.type = type; - } - /** * Return true if save and delete should cascade. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java index 12d54710f..1201bf44c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java @@ -64,7 +64,6 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist protected ConcurrencyMode concurrencyMode; - /** * The unique id used for logging summary. */ @@ -76,17 +75,15 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist protected Integer beanHash; protected Integer beanIdentityHash; - protected boolean notifyCache; private boolean statelessUpdate; private boolean deleteMissingChildren; - private boolean updateNullProperties; private final Set dirtyPropertyNames; public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, - SpiTransaction t, PersistExecute persistExecute) { + SpiTransaction t, PersistExecute persistExecute, PersistRequest.Type type) { super(server, t, persistExecute); this.entityBean = (EntityBean)bean; @@ -94,21 +91,35 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist this.beanManager = mgr; this.beanDescriptor = mgr.getBeanDescriptor(); this.beanPersistListener = beanDescriptor.getPersistListener(); - this.dirtyPropertyNames = (beanPersistListener == null) ? null : intercept.getDirtyPropertyNames(); + + if (PersistRequest.Type.DETERMINE != type) { + this.type = type; + } else { + this.type = beanDescriptor.isInsertMode(intercept) ? Type.INSERT : Type.UPDATE; + } + + if (this.type == Type.UPDATE && intercept.isNew() ) { + intercept.setNewBeanForUpdate(); + } + + this.dirtyPropertyNames = (beanPersistListener == null) ? null : intercept.getDirtyPropertyNames(); this.bean = bean; this.parentBean = parentBean; this.controller = beanDescriptor.getPersistController(); - this.concurrencyMode = beanDescriptor.getConcurrencyMode(); - if (intercept.isReference()) { - // delete reference objects with no concurrency checking - this.concurrencyMode = ConcurrencyMode.NONE; - } + this.concurrencyMode = beanDescriptor.getConcurrencyMode(intercept); // this is ok to not use isNewOrDirty() as used for updates only this.isDirty = intercept.isDirty(); } - + + /** + * Return true if this is an insert request. + */ + public boolean isInsert() { + return Type.INSERT == type; + } + @Override public Set getLoadedProperties() { return intercept.getLoadedPropertyNames(); @@ -124,26 +135,11 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist return intercept.getDirtyValues(); } - public void setNotNullAsLoaded() { - BeanProperty[] props = beanDescriptor.propertiesNonMany(); - for (int i=0; i< props.length; i++) { - BeanProperty prop = props[i]; - if (!intercept.isLoadedProperty(prop.getPropertyIndex())) { - if (prop.getValue(entityBean) != null) { - intercept.setLoadedProperty(prop.getPropertyIndex()); - } - } - } - } - public boolean isNotify(TransactionEvent txnEvent) { + this.notifyCache = beanDescriptor.isCacheNotify(); return notifyCache || isNotifyPersistListener(); } - public boolean isNotifyCache() { - return notifyCache; - } - public boolean isNotifyPersistListener() { return beanPersistListener != null; } @@ -171,7 +167,7 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist public void addToPersistMap(BeanPersistIdMap beanPersistMap) { - beanPersistMap.add(beanDescriptor, type, idValue); + beanPersistMap.add(beanDescriptor, type, idValue); } public boolean notifyLocalPersistListener() { @@ -248,16 +244,6 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist } } - /** - * Set the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL or - * CALLABLESQL. - */ - @Override - public void setType(Type type) { - this.type = type; - notifyCache = beanDescriptor.isCacheNotify(); - } - public BeanManager getBeanManager() { return beanManager; } @@ -284,14 +270,6 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist return deleteMissingChildren; } - /** - * Return true if null properties should be updated (treated as loaded) for - * stateless updates. - */ - public boolean isUpdateNullProperties() { - return updateNullProperties; - } - /** * Set to true if this is a stateless update. *

@@ -300,10 +278,9 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist * that was probably created from JSON or XML. *

*/ - public void setStatelessUpdate(boolean statelessUpdate, boolean deleteMissingChildren, boolean updateNullProperties) { + public void setStatelessUpdate(boolean statelessUpdate, boolean deleteMissingChildren) { this.statelessUpdate = statelessUpdate; this.deleteMissingChildren = deleteMissingChildren; - this.updateNullProperties = updateNullProperties; } /** @@ -546,7 +523,7 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist if (prop != null && intercept.isLoadedProperty(prop.getPropertyIndex())) { // OK to use version property } else { - concurrencyMode = ConcurrencyMode.NONE;//ALL; + concurrencyMode = ConcurrencyMode.NONE; } } @@ -560,7 +537,7 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist *

*/ public boolean isDynamicUpdateSql() { - return beanDescriptor.isUpdateChangesOnly() || !intercept.isFullyLoadedBean();//(loadedProps != null); + return beanDescriptor.isUpdateChangesOnly() || !intercept.isFullyLoadedBean(); } /** @@ -587,12 +564,15 @@ public class PersistRequestBean extends PersistRequest implements BeanPersist } public void postInsert() { - // mark all properties as loaded after an insert - // to support immediate update + // mark all properties as loaded after an insert to support immediate update int len = intercept.getPropertyLength(); for (int i = 0; i < len; i++) { intercept.setLoadedProperty(i); } } + public boolean isReference() { + return beanDescriptor.isReference(intercept); + } + } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java b/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java index 268ddf8ec..177dd712e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java @@ -1,7 +1,6 @@ package com.avaje.ebeaninternal.server.core; import java.util.Collection; -import java.util.Set; import com.avaje.ebean.CallableSql; import com.avaje.ebean.SqlUpdate; @@ -18,7 +17,7 @@ public interface Persister { /** * Force an Update using the given bean. */ - public void forceUpdate(EntityBean entityBean, Set updateProps, Transaction t, boolean deleteMissingChildren, boolean updateNullProperties); + public void forceUpdate(EntityBean entityBean, Transaction t, boolean deleteMissingChildren); /** * Force an Insert using the given bean. diff --git a/src/main/java/com/avaje/ebeaninternal/server/ddl/CreateTableVisitor.java b/src/main/java/com/avaje/ebeaninternal/server/ddl/CreateTableVisitor.java index b14b1e2ef..1f331dbbd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ddl/CreateTableVisitor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ddl/CreateTableVisitor.java @@ -201,12 +201,12 @@ public class CreateTableVisitor extends AbstractBeanVisitor { } - BeanProperty[] ids = descriptor.propertiesId(); + BeanProperty idProp = descriptor.getIdProperty(); - if (ids.length == 0){ + if (idProp == null){ // No comma + new line ctx.removeLast().removeLast(); - } else if (ids.length > 1 || ddl.isInlinePrimaryKeyConstraint()) { + } else if (ddl.isInlinePrimaryKeyConstraint()) { // The Primary Key constraint was inlined with the column // ... No comma + new line ctx.removeLast().removeLast(); @@ -216,7 +216,7 @@ public class CreateTableVisitor extends AbstractBeanVisitor { String pkName = ddl.getPrimaryKeyName(table); ctx.write(" constraint ").write(pkName).write(" primary key ("); - VisitorUtil.visit(ids, new AbstractPropertyVisitor() { + VisitorUtil.visit(idProp, new AbstractPropertyVisitor() { @Override public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne embedded) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/ddl/VisitorUtil.java b/src/main/java/com/avaje/ebeaninternal/server/ddl/VisitorUtil.java index 3d5341382..d9520e1a3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ddl/VisitorUtil.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ddl/VisitorUtil.java @@ -43,36 +43,36 @@ public class VisitorUtil { /** * Visit the bean using a visitor. */ - public static void visitBean(BeanDescriptor desc, BeanVisitor visitor) { + public static void visitBean(BeanDescriptor desc, BeanVisitor visitor) { - if (visitor.visitBean(desc)) { + if (visitor.visitBean(desc)) { - BeanProperty[] propertiesId = desc.propertiesId(); - for (int i = 0; i < propertiesId.length; i++) { - visit(visitor, propertiesId[i]); - } - BeanPropertyAssocOne unidirectional = desc.getUnidirectional(); - if (unidirectional != null){ - visit(visitor, unidirectional); - } - BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient(); - for (int i = 0; i < propertiesNonTransient.length; i++) { - BeanProperty p = propertiesNonTransient[i]; - if (!p.isFormula() && !p.isSecondaryTable()){ - visit(visitor, p); - } - } - - visitor.visitBeanEnd(desc); - } - } - - private static void visit(BeanVisitor visitor, BeanProperty p) { - PropertyVisitor pv = visitor.visitProperty(p); - if (pv != null){ - visit(p, pv); + BeanProperty idProp = desc.getIdProperty(); + if (idProp != null) { + visit(visitor, idProp); + } + BeanPropertyAssocOne unidirectional = desc.getUnidirectional(); + if (unidirectional != null) { + visit(visitor, unidirectional); + } + BeanProperty[] propertiesNonTransient = desc.propertiesNonTransient(); + for (int i = 0; i < propertiesNonTransient.length; i++) { + BeanProperty p = propertiesNonTransient[i]; + if (!p.isFormula() && !p.isSecondaryTable()) { + visit(visitor, p); } - } + } + + visitor.visitBeanEnd(desc); + } + } + + private static void visit(BeanVisitor visitor, BeanProperty p) { + PropertyVisitor pv = visitor.visitProperty(p); + if (pv != null) { + visit(p, pv); + } + } /** * Visit all the properties. diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index d8dfab285..743f12b1a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1,5 +1,6 @@ package com.avaje.ebeaninternal.server.deploy; +import java.lang.reflect.Modifier; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; @@ -217,12 +218,15 @@ public class BeanDescriptor implements MetaBeanInfo { /** * Derived list of properties that make up the unique id. */ - private final BeanProperty[] propertiesId; + private final BeanProperty idProperty; + private final int idPropertyIndex; /** * Derived list of properties that are used for version concurrency checking. */ private final BeanProperty versionProperty; + private final int versionPropertyIndex; + private final BeanProperty propertiesNaturalKey; /** @@ -281,14 +285,7 @@ public class BeanDescriptor implements MetaBeanInfo { /** * All non transient properties excluding the id properties. */ - final BeanProperty[] propertiesNonTransient; - - - /** - * Set when the Id property is a single non-embedded property. Can make life - * simpler for this case. - */ - private final BeanProperty propertySingleId; + private final BeanProperty[] propertiesNonTransient; /** * The bean class name or the table name for MapBeans. @@ -403,15 +400,15 @@ public class BeanDescriptor implements MetaBeanInfo { // helper object used to derive lists of properties DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy); + this.idProperty = listHelper.getId(); + this.versionProperty = listHelper.getVersionProperty(); this.propMap = listHelper.getPropertyMap(); this.propMapByDbColumn = getReverseMap(propMap); this.propertiesTransient = listHelper.getTransients(); this.propertiesNonTransient = listHelper.getNonTransients(); this.propertiesBaseScalar = listHelper.getBaseScalar(); this.propertiesBaseCompound = listHelper.getBaseCompound(); - this.propertiesId = listHelper.getId(); this.propertiesNaturalKey = listHelper.getNaturalKey(); - this.versionProperty = listHelper.getVersionProperty(); this.propertiesEmbedded = listHelper.getEmbedded(); this.propertiesLocal = listHelper.getLocal(); this.unidirectional = listHelper.getUnidirectional(); @@ -436,12 +433,6 @@ public class BeanDescriptor implements MetaBeanInfo { this.derivedTableJoins = listHelper.getTableJoin(); - if (propertiesId.length == 1) { - this.propertySingleId = propertiesId[0]; - } else { - this.propertySingleId = null; - } - // Check if there are no cascade save associated beans ( subject to change // in initialiseOther()). Note that if we are in an inheritance hierarchy // then we also need to check every BeanDescriptors in the InheritInfo as @@ -454,9 +445,20 @@ public class BeanDescriptor implements MetaBeanInfo { deleteRecurseSkippable = (0 == (propertiesOneExportedDelete.length + propertiesOneImportedDelete.length + propertiesManyDelete.length)); // object used to handle Id values - this.idBinder = owner.createIdBinder(propertiesId); - } + this.idBinder = owner.createIdBinder(idProperty); + // derive the index position of the Id and Version properties + if (Modifier.isAbstract(beanType.getModifiers())) { + this.idPropertyIndex = -1; + this.versionPropertyIndex = -1; + } else { + EntityBean entityBean = createEntityBean(); + EntityBeanIntercept ebi = entityBean._ebean_getIntercept(); + this.idPropertyIndex = (idProperty == null) ? -1 : ebi.findProperty(idProperty.getName()); + this.versionPropertyIndex = (versionProperty == null) ? -1 : ebi.findProperty(versionProperty.getName()); + } + } + private LinkedHashMap getReverseMap(LinkedHashMap propMap) { LinkedHashMap revMap = new LinkedHashMap(propMap.size() * 2); @@ -523,26 +525,6 @@ public class BeanDescriptor implements MetaBeanInfo { return dirtyProperties; } - /** - * Determine the non-null properties of the bean. - */ - public Set determineLoadedProperties(EntityBean bean) { - - HashSet nonNullProps = new HashSet(); - - for (int j = 0; j < propertiesId.length; j++) { - if (propertiesId[j].getValue(bean) != null) { - nonNullProps.add(propertiesId[j].getName()); - } - } - for (int i = 0; i < propertiesNonTransient.length; i++) { - if (propertiesNonTransient[i].getValue(bean) != null) { - nonNullProps.add(propertiesNonTransient[i].getName()); - } - } - return nonNullProps; - } - /** * Return the EbeanServer instance that owns this BeanDescriptor. */ @@ -591,9 +573,8 @@ public class BeanDescriptor implements MetaBeanInfo { } } else { // initialise just the Id properties - BeanProperty[] idProps = propertiesId(); - for (int i = 0; i < idProps.length; i++) { - idProps[i].initialise(); + if (idProperty != null) { + idProperty.initialise(); } } } @@ -1367,7 +1348,7 @@ public class BeanDescriptor implements MetaBeanInfo { // Note: not creating proxies for many's... - ebi.setReference(); + ebi.setReference(idPropertyIndex); return (T) eb; @@ -1508,20 +1489,7 @@ public class BeanDescriptor implements MetaBeanInfo { * properties that make up the unique id. */ public Object getId(EntityBean bean) { - - if (propertySingleId != null) { - return propertySingleId.getValue(bean); - } - - // it is a concatenated id Not embedded - // so return a Map - LinkedHashMap idMap = new LinkedHashMap(); - for (int i = 0; i < propertiesId.length; i++) { - - Object value = propertiesId[i].getValue(bean); - idMap.put(propertiesId[i].getName(), value); - } - return idMap; + return (idProperty == null) ? null : idProperty.getValue(bean); } /** @@ -2013,17 +1981,6 @@ public class BeanDescriptor implements MetaBeanInfo { return propMap.values().iterator(); } - /** - * Return the BeanProperty that make up the unique id. - *

- * The order of these properties can be relied on to be consistent if the bean - * itself doesn't change or the xml deployment order does not change. - *

- */ - public BeanProperty[] propertiesId() { - return propertiesId; - } - /** * Return the non transient non id properties. */ @@ -2038,14 +1995,6 @@ public class BeanDescriptor implements MetaBeanInfo { return propertiesTransient; } - /** - * If the Id is a single non-embedded property then returns that, otherwise - * returns null. - */ - public BeanProperty getSingleIdProperty() { - return propertySingleId; - } - /** * Return the beans that are embedded. These share the base table with the * owner bean. @@ -2054,6 +2003,51 @@ public class BeanDescriptor implements MetaBeanInfo { return propertiesEmbedded; } + public BeanProperty getIdProperty() { + return idProperty; + } + + public boolean isInsertMode(EntityBeanIntercept ebi) { + if (idProperty.isEmbedded()) { + return !ebi.isLoaded(); + } + //if (idGenerator == null) { + // return !ebi.isLoaded(); + //} else { + return !hasIdProperty(ebi); + //} + } + + public boolean isReference(EntityBeanIntercept ebi) { + return ebi.isReference() || hasIdPropertyOnly(ebi); + } + + public boolean hasIdPropertyOnly(EntityBeanIntercept ebi) { + return ebi.hasIdOnly(idPropertyIndex); + } + + public boolean hasIdProperty(EntityBeanIntercept ebi) { + if (idPropertyIndex > -1) { + return ebi.isLoadedProperty(idPropertyIndex); + } + return false; + } + + public boolean hasVersionProperty(EntityBeanIntercept ebi) { + if (versionPropertyIndex > -1) { + return ebi.isLoadedProperty(versionPropertyIndex); + } + return false; + } + + public ConcurrencyMode getConcurrencyMode(EntityBeanIntercept ebi) { + if (!hasVersionProperty(ebi)) { + return ConcurrencyMode.NONE; + } else { + return concurrencyMode; + } + } + /** * All the BeanPropertyAssocOne that are not embedded. These are effectively * joined beans. For ManyToOne and OneToOne associations. @@ -2248,8 +2242,6 @@ public class BeanDescriptor implements MetaBeanInfo { @SuppressWarnings("unchecked") private void jsonWriteProperties(WriteJsonContext ctx, EntityBean bean) { - boolean referenceBean = ctx.isReferenceBean(); - JsonWriteBeanVisitor beanVisitor = (JsonWriteBeanVisitor) ctx.getBeanVisitor(); Set props = ctx.getIncludeProperties(); @@ -2264,11 +2256,11 @@ public class BeanDescriptor implements MetaBeanInfo { } } - for (int i = 0; i < propertiesId.length; i++) { - Object idValue = propertiesId[i].getValue(bean); + if (idProperty != null) { + Object idValue = idProperty.getValue(bean); if (idValue != null) { - if (props == null || props.contains(propertiesId[i].getName())) { - propertiesId[i].jsonWrite(ctx, bean); + if (props == null || props.contains(idProperty.getName())) { + idProperty.jsonWrite(ctx, bean); } } } @@ -2286,7 +2278,7 @@ public class BeanDescriptor implements MetaBeanInfo { } } } else { - if (explicitAllProps || !referenceBean) { + if (explicitAllProps || !isReference(bean._ebean_getIntercept())) { // render all the properties and invoke lazy loading if required for (int j = 0; j < propertiesNonTransient.length; j++) { propertiesNonTransient[j].jsonWrite(ctx, bean); @@ -2386,44 +2378,11 @@ public class BeanDescriptor implements MetaBeanInfo { return ctx.popBeanState(); } - /** - * Set the loaded properties with additional check to see if the bean is a - * reference. - */ - public void setLoadedProps(EntityBeanIntercept ebi, Set loadedProps) { - if (isLoadedReference(loadedProps)) { - ebi.setReference(); - } else { - ebi.setLoaded(); - } - } - - /** - * Return true if the loadedProperties is just the Id property and therefore - * this is really a reference. - */ - public boolean isLoadedReference(Set loadedProps) { - - if (loadedProps != null) { - if (loadedProps.size() == propertiesId.length) { - for (int i = 0; i < propertiesId.length; i++) { - if (!loadedProps.contains(propertiesId[i].getName())) { - return false; - } - } - return true; - } - } - - return false; - } - public void flushPersistenceContextOnIterate(PersistenceContext persistenceContext) { persistenceContext.clear(beanType); for (int i = 0; i < propertiesMany.length; i++) { persistenceContext.clear(propertiesMany[i].getBeanDescriptor().getBeanType()); } - } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java index fad2d4cc4..f9fbab992 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -222,8 +222,8 @@ public class BeanDescriptorManager implements BeanDescriptorMap { } } - public IdBinder createIdBinder(BeanProperty[] uids) { - return idBinderFactory.createIdBinder(uids); + public IdBinder createIdBinder(BeanProperty idProperty) { + return idBinderFactory.createIdBinder(idProperty); } public void deploy() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorMap.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorMap.java index 73a197f5b..a4833f578 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorMap.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorMap.java @@ -32,6 +32,6 @@ public interface BeanDescriptorMap { */ public EncryptKey getEncryptKey(String tableName, String columnName); - public IdBinder createIdBinder(BeanProperty[] uids); + public IdBinder createIdBinder(BeanProperty id); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanFkeyProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanFkeyProperty.java index 4ce2b4d73..f60483f71 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanFkeyProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanFkeyProperty.java @@ -168,7 +168,7 @@ public final class BeanFkeyProperty implements ElPropertyValue { throw new RuntimeException("ElPropertyDeploy only - not implemented"); } - public void elSetValue(EntityBean bean, Object value, boolean populate, boolean reference) { + public void elSetValue(EntityBean bean, Object value, boolean populate) { throw new RuntimeException("ElPropertyDeploy only - not implemented"); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java index 7a98140c7..4b8d868be 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java @@ -757,11 +757,7 @@ public class BeanProperty implements ElPropertyValue { return convertToLogicalType(value); } - public void elSetReference(EntityBean bean) { - throw new RuntimeException("Should not be called"); - } - - public void elSetValue(EntityBean bean, Object value, boolean populate, boolean reference) { + public void elSetValue(EntityBean bean, Object value, boolean populate) { if (bean != null) { // Not using setValueIntercept at this stage setValue(bean, value); diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssoc.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssoc.java index 9d552e140..54583bb77 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssoc.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssoc.java @@ -4,18 +4,18 @@ import java.util.ArrayList; import javax.persistence.PersistenceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.avaje.ebean.bean.EntityBean; import com.avaje.ebeaninternal.server.core.InternString; import com.avaje.ebeaninternal.server.deploy.id.IdBinder; import com.avaje.ebeaninternal.server.deploy.id.ImportedId; import com.avaje.ebeaninternal.server.deploy.id.ImportedIdEmbedded; -import com.avaje.ebeaninternal.server.deploy.id.ImportedIdMultiple; import com.avaje.ebeaninternal.server.deploy.id.ImportedIdSimple; import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc; import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Abstract base for properties mapped to an associated bean, list, set or map. @@ -219,11 +219,9 @@ public abstract class BeanPropertyAssoc extends BeanProperty { public boolean hasId(EntityBean bean) { BeanDescriptor targetDesc = getTargetDescriptor(); - - BeanProperty[] uids = targetDesc.propertiesId(); - for (int i = 0; i < uids.length; i++) { - - Object value = uids[i].getValue(bean); + BeanProperty idProp = targetDesc.getIdProperty(); + if (idProp != null) { + Object value = idProp.getValue(bean); if (value == null) { return false; } @@ -311,39 +309,36 @@ public abstract class BeanPropertyAssoc extends BeanProperty { */ protected ImportedId createImportedId(BeanPropertyAssoc owner, BeanDescriptor target, TableJoin join) { - BeanProperty[] props = target.propertiesId(); + BeanProperty idProp = target.getIdProperty(); BeanProperty[] others = target.propertiesBaseScalar(); if (descriptor.isSqlSelectBased()){ String dbColumn = owner.getDbColumn(); - return new ImportedIdSimple(owner, dbColumn, props[0], 0); + return new ImportedIdSimple(owner, dbColumn, idProp, 0); } TableJoinColumn[] cols = join.columns(); - if (props.length == 1) { - if (!props[0].isEmbedded()) { - // simple single scalar id - if (cols.length != 1){ - String msg = "No Imported Id column for ["+props[0]+"] in table ["+join.getTable()+"]"; - logger.error(msg); - return null; - } else { - return createImportedScalar(owner, cols[0], props, others); - } + if (idProp == null) { + return null; + } + if (!idProp.isEmbedded()) { + // simple single scalar id + if (cols.length != 1){ + String msg = "No Imported Id column for ["+idProp+"] in table ["+join.getTable()+"]"; + logger.error(msg); + return null; } else { - // embedded id - BeanPropertyAssocOne embProp = (BeanPropertyAssocOne)props[0]; - BeanProperty[] embBaseProps = embProp.getTargetDescriptor().propertiesBaseScalar(); - ImportedIdSimple[] scalars = createImportedList(owner, cols, embBaseProps, others); - - return new ImportedIdEmbedded(owner, embProp, scalars); + BeanProperty[] idProps = {idProp}; + return createImportedScalar(owner, cols[0], idProps, others); } - } else { - // Concatenated key that is not embedded - ImportedIdSimple[] scalars = createImportedList(owner, cols, props, others); - return new ImportedIdMultiple(owner, scalars); + // embedded id + BeanPropertyAssocOne embProp = (BeanPropertyAssocOne)idProp; + BeanProperty[] embBaseProps = embProp.getTargetDescriptor().propertiesBaseScalar(); + ImportedIdSimple[] scalars = createImportedList(owner, cols, embBaseProps, others); + + return new ImportedIdEmbedded(owner, embProp, scalars); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index 238368b8b..8ceb86d6a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -588,8 +588,8 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { if (embeddedExportedProperties) { // use the EmbeddedId object instead of the parentBean - BeanProperty[] uids = descriptor.propertiesId(); - parentBean = (EntityBean)uids[0].getValue(parentBean); + BeanProperty idProp = descriptor.getIdProperty(); + parentBean = (EntityBean)idProp.getValue(parentBean); } for (int i = 0; i < exportedProperties.length; i++) { @@ -621,13 +621,13 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { */ private ExportedProperty[] createExported() { - BeanProperty[] uids = descriptor.propertiesId(); + BeanProperty idProp = descriptor.getIdProperty(); ArrayList list = new ArrayList(); - if (uids.length == 1 && uids[0].isEmbedded()) { + if (idProp != null && idProp.isEmbedded()) { - BeanPropertyAssocOne one = (BeanPropertyAssocOne) uids[0]; + BeanPropertyAssocOne one = (BeanPropertyAssocOne) idProp; BeanDescriptor targetDesc = one.getTargetDescriptor(); BeanProperty[] emIds = targetDesc.propertiesBaseScalar(); try { @@ -641,8 +641,8 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { } } else { - for (int i = 0; i < uids.length; i++) { - ExportedProperty expProp = findMatch(false, uids[i]); + if (idProp != null) { + ExportedProperty expProp = findMatch(false, idProp); list.add(expProp); } } @@ -773,8 +773,8 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { private void buildExport(IntersectionRow row, EntityBean parentBean) { if (embeddedExportedProperties) { - BeanProperty[] uids = descriptor.propertiesId(); - parentBean = (EntityBean)uids[0].getValue(parentBean); + BeanProperty idProp = descriptor.getIdProperty(); + parentBean = (EntityBean)idProp.getValue(parentBean); } for (int i = 0; i < exportedProperties.length; i++) { Object val = exportedProperties[i].getValue(parentBean); @@ -856,6 +856,5 @@ public class BeanPropertyAssocMany extends BeanPropertyAssoc { } while(true); setValue(bean, collection); - } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index facdfdc03..e9756f7ed 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -397,13 +397,6 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { return targetDescriptor.createEntityBean(); } - public void elSetReference(EntityBean bean) { - Object value = getValueIntercept(bean); - if (value != null) { - ((EntityBean) value)._ebean_getIntercept().setReference(); - } - } - @Override public Object elGetReference(EntityBean bean) { Object value = getValueIntercept(bean); @@ -441,13 +434,13 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { */ private ExportedProperty[] createExported() { - BeanProperty[] uids = descriptor.propertiesId(); + BeanProperty idProp = descriptor.getIdProperty(); ArrayList list = new ArrayList(); - if (uids.length == 1 && uids[0].isEmbedded()) { + if (idProp != null && idProp.isEmbedded()) { - BeanPropertyAssocOne one = (BeanPropertyAssocOne) uids[0]; + BeanPropertyAssocOne one = (BeanPropertyAssocOne) idProp; BeanDescriptor targetDesc = one.getTargetDescriptor(); BeanProperty[] emIds = targetDesc.propertiesBaseScalar(); try { @@ -461,8 +454,8 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { } } else { - for (int i = 0; i < uids.length; i++) { - ExportedProperty expProp = findMatch(false, uids[i]); + if (idProp != null) { + ExportedProperty expProp = findMatch(false, idProp); list.add(expProp); } } @@ -806,8 +799,7 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { @Override void appendSelect(DbSqlContext ctx, boolean subQuery) { - // set appropriate tableAlias for - // the exported id columns + // set appropriate tableAlias for the exported id columns String relativePrefix = ctx.getRelativePrefix(getName()); ctx.pushTableAlias(relativePrefix); @@ -838,20 +830,29 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { // bi-directional and already rendered parent } else { + // Hmmm, not writing complex non-entity bean + if (value instanceof EntityBean) { ctx.pushParentBean(bean); ctx.beginAssocOne(name); BeanDescriptor refDesc = descriptor.getBeanDescriptor(value.getClass()); - refDesc.jsonWrite(ctx, (EntityBean)value); + refDesc.jsonWrite(ctx, (EntityBean)value); ctx.endAssocOne(); ctx.popParentBean(); + } } } } @Override public void jsonRead(ReadJsonContext ctx, EntityBean bean){ - - T assocBean = targetDescriptor.jsonReadBean(ctx, name); - setValue(bean, assocBean); + if (targetDescriptor != null) { + T assocBean = targetDescriptor.jsonReadBean(ctx, name); + setValue(bean, assocBean); + } + } + + public boolean isReference(Object detailBean) { + EntityBean eb = (EntityBean)detailBean; + return targetDescriptor.isReference(eb._ebean_getIntercept()); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundScalar.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundScalar.java index 67c9725be..d05cf0e89 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundScalar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundScalar.java @@ -90,13 +90,8 @@ public class BeanPropertyCompoundScalar extends BeanProperty { } @Override - public void elSetReference(EntityBean bean) { - super.elSetReference(bean); - } - - @Override - public void elSetValue(EntityBean bean, Object value, boolean populate, boolean reference) { - super.elSetValue(bean, value, populate, reference); + public void elSetValue(EntityBean bean, Object value, boolean populate) {//, boolean reference) { + super.elSetValue(bean, value, populate); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlSelectColumnsParser.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlSelectColumnsParser.java index 1d2507839..10a44e251 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlSelectColumnsParser.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlSelectColumnsParser.java @@ -193,11 +193,10 @@ public final class DRawSqlSelectColumnsParser { } } - BeanProperty[] propertiesId = desc.propertiesId(); - for (int i = 0; i < propertiesId.length; i++) { - BeanProperty prop = propertiesId[i]; - if (isMatch(prop, searchColumn)) { - return prop; + BeanProperty idProp = desc.getIdProperty(); + if (idProp != null) { + if (isMatch(idProp, searchColumn)) { + return idProp; } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinder.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinder.java index f0de30e87..7eab5eaf9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinder.java @@ -44,6 +44,11 @@ public interface IdBinder { */ public String getIdProperty(); + /** + * Return the Id BeanProperty. + */ + public BeanProperty getBeanProperty(); + /** * Find a BeanProperty that is mapped to the database column. */ @@ -154,11 +159,6 @@ public interface IdBinder { */ public String getBindIdSql(String baseTableAlias); - /** - * Return the id properties in flat form. - */ - public BeanProperty[] getProperties(); - /** * Cast or convert the Id value if necessary and optionally set it. *

diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderEmbedded.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderEmbedded.java index dac8f83ea..fed1f003c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderEmbedded.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderEmbedded.java @@ -6,8 +6,6 @@ import java.io.IOException; import java.sql.SQLException; import java.util.List; -import javax.persistence.PersistenceException; - import com.avaje.ebean.bean.EntityBean; import com.avaje.ebeaninternal.api.SpiExpressionRequest; import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; @@ -24,378 +22,382 @@ import com.avaje.ebeaninternal.server.type.DataBind; */ public final class IdBinderEmbedded implements IdBinder { - private final BeanPropertyAssocOne embIdProperty; + private final BeanPropertyAssocOne embIdProperty; - private final boolean idInExpandedForm; - - private BeanProperty[] props; + private final boolean idInExpandedForm; - private BeanDescriptor idDesc; + private BeanProperty[] props; - private String idInValueSql; - - - public IdBinderEmbedded(boolean idInExpandedForm, BeanPropertyAssocOne embIdProperty) { - this.idInExpandedForm = idInExpandedForm; - this.embIdProperty = embIdProperty; + private BeanDescriptor idDesc; + + private String idInValueSql; + + public IdBinderEmbedded(boolean idInExpandedForm, BeanPropertyAssocOne embIdProperty) { + this.idInExpandedForm = idInExpandedForm; + this.embIdProperty = embIdProperty; + } + + public void initialise() { + this.idDesc = embIdProperty.getTargetDescriptor(); + this.props = embIdProperty.getProperties(); + this.idInValueSql = idInExpandedForm ? idInExpanded() : idInCompressed(); + } + + private String idInExpanded() { + + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (int i = 0; i < props.length; i++) { + if (i > 0) { + sb.append(" and "); + } + sb.append(embIdProperty.getName()); + sb.append("."); + sb.append(props[i].getName()); + sb.append("=?"); + } + sb.append(")"); + + return sb.toString(); + } + + private String idInCompressed() { + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (int i = 0; i < props.length; i++) { + if (i > 0) { + sb.append(","); + } + sb.append("?"); + } + sb.append(")"); + + return sb.toString(); + } + + @Override + public BeanProperty getBeanProperty() { + return embIdProperty; + } + + public String getOrderBy(String pathPrefix, boolean ascending) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < props.length; i++) { + if (i > 0) { + sb.append(", "); + } + if (pathPrefix != null) { + sb.append(pathPrefix).append("."); + } + + sb.append(embIdProperty.getName()).append("."); + sb.append(props[i].getName()); + if (!ascending) { + sb.append(" desc"); + } + } + return sb.toString(); + } + + public BeanDescriptor getIdBeanDescriptor() { + return idDesc; + } + + public int getPropertyCount() { + return props.length; + } + + public String getIdProperty() { + return embIdProperty.getName(); + } + + public void buildSelectExpressionChain(String prefix, List selectChain) { + + prefix = SplitName.add(prefix, embIdProperty.getName()); + + for (int i = 0; i < props.length; i++) { + props[i].buildSelectExpressionChain(prefix, selectChain); + } + } + + public BeanProperty findBeanProperty(String dbColumnName) { + for (int i = 0; i < props.length; i++) { + if (dbColumnName.equalsIgnoreCase(props[i].getDbColumn())) { + return props[i]; + } + } + return null; + } + + public boolean isComplexId() { + return true; + } + + public String getDefaultOrderBy() { + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < props.length; i++) { + if (i > 0) { + sb.append(","); + } + + sb.append(embIdProperty.getName()); + sb.append("."); + sb.append(props[i].getName()); } - public void initialise() { - this.idDesc = embIdProperty.getTargetDescriptor(); - this.props = embIdProperty.getProperties(); - this.idInValueSql = idInExpandedForm ? idInExpanded() : idInCompressed(); + return sb.toString(); + } + + public BeanProperty[] getProperties() { + return props; + } + + public void addIdInBindValue(SpiExpressionRequest request, Object value) { + for (int i = 0; i < props.length; i++) { + request.addBindValue(props[i].getValue((EntityBean) value)); + } + } + + public String getIdInValueExprDelete(int size) { + if (!idInExpandedForm) { + return getIdInValueExpr(size); } - private String idInExpanded() { - - StringBuilder sb = new StringBuilder(); - sb.append("("); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(" and "); - } - sb.append(embIdProperty.getName()); - sb.append("."); - sb.append(props[i].getName()); - sb.append("=?"); + StringBuilder sb = new StringBuilder(); + sb.append("("); + + for (int j = 0; j < size; j++) { + if (j > 0) { + sb.append(" or "); + } + sb.append("("); + for (int i = 0; i < props.length; i++) { + if (i > 0) { + sb.append(" and "); } - sb.append(")"); - - return sb.toString(); + sb.append(props[i].getDbColumn()); + sb.append("=?"); + } + sb.append(")"); } - - private String idInCompressed() { - StringBuilder sb = new StringBuilder(); - sb.append("("); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(","); - } - sb.append("?"); - } - sb.append(")"); + sb.append(") "); + return sb.toString(); + } - return sb.toString(); + public String getIdInValueExpr(int size) { + + StringBuilder sb = new StringBuilder(); + + if (!idInExpandedForm) { + sb.append(" in"); } - - public String getOrderBy(String pathPrefix, boolean ascending){ - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(", "); - } - if (pathPrefix != null){ - sb.append(pathPrefix).append("."); - } - - sb.append(embIdProperty.getName()).append("."); - sb.append(props[i].getName()); - if (!ascending){ - sb.append(" desc"); - } - } - return sb.toString(); - } - - public BeanDescriptor getIdBeanDescriptor() { - return idDesc; - } - - public int getPropertyCount() { - return props.length; - } - - public String getIdProperty() { - return embIdProperty.getName(); - } - - public void buildSelectExpressionChain(String prefix, List selectChain) { - - prefix = SplitName.add(prefix, embIdProperty.getName()); - - for (int i = 0; i < props.length; i++) { - props[i].buildSelectExpressionChain(prefix, selectChain); - } - } - - public BeanProperty findBeanProperty(String dbColumnName) { - for (int i = 0; i < props.length; i++) { - if (dbColumnName.equalsIgnoreCase(props[i].getDbColumn())) { - return props[i]; - } - } - return null; - } - - public boolean isComplexId() { - return true; - } - - public String getDefaultOrderBy() { - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(","); - } - - sb.append(embIdProperty.getName()); - sb.append("."); - sb.append(props[i].getName()); - } - - return sb.toString(); - } - - public BeanProperty[] getProperties() { - return props; - } - - public void addIdInBindValue(SpiExpressionRequest request, Object value) { - for (int i = 0; i < props.length; i++) { - request.addBindValue(props[i].getValue((EntityBean)value)); - } - } - - public String getIdInValueExprDelete(int size) { - if (!idInExpandedForm){ - return getIdInValueExpr(size); - } - - StringBuilder sb = new StringBuilder(); - sb.append("("); - - for (int j = 0; j < size; j++) { - if (j > 0){ - sb.append(" or "); - } - sb.append("("); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(" and "); - } - sb.append(props[i].getDbColumn()); - sb.append("=?"); - } - sb.append(")"); - } - sb.append(") "); - return sb.toString(); - } - - public String getIdInValueExpr(int size) { - - StringBuilder sb = new StringBuilder(); - - if (!idInExpandedForm){ - sb.append(" in"); - } - sb.append(" ("); - for (int i = 0; i < size; i++) { - if (i > 0){ - if (idInExpandedForm) { - sb.append(" or "); - } else { - sb.append(","); - } - } - sb.append(idInValueSql); - } - sb.append(") "); - return sb.toString(); - } - - public String getIdInValueExpr() { - return idInValueSql; - } - - public Object[] getIdValues(EntityBean bean) { - Object val = embIdProperty.getValue(bean); - Object[] bindvalues = new Object[props.length]; - for (int i = 0; i < props.length; i++) { - bindvalues[i] = props[i].getValue((EntityBean)val); - } - return bindvalues; - } - - public Object[] getBindValues(Object value) { - - Object[] bindvalues = new Object[props.length]; - for (int i = 0; i < props.length; i++) { - bindvalues[i] = props[i].getValue((EntityBean)value); - } - return bindvalues; - } - - public void bindId(DefaultSqlUpdate sqlUpdate, Object value) { - for (int i = 0; i < props.length; i++) { - Object embFieldValue = props[i].getValue((EntityBean)value); - sqlUpdate.addParameter(embFieldValue); - } - } - - public void bindId(DataBind dataBind, Object value) throws SQLException { - - for (int i = 0; i < props.length; i++) { - Object embFieldValue = props[i].getValue((EntityBean)value); - props[i].bind(dataBind, embFieldValue); - } - } - - public Object readData(DataInput dataInput) throws IOException { - - EntityBean embId = idDesc.createBean(); - boolean notNull = true; - - for (int i = 0; i < props.length; i++) { - Object value = props[i].readData(dataInput); - props[i].setValue(embId, value); - if (value == null) { - notNull = false; - } - } - - if (notNull) { - return embId; + sb.append(" ("); + for (int i = 0; i < size; i++) { + if (i > 0) { + if (idInExpandedForm) { + sb.append(" or "); } else { - return null; + sb.append(","); } + } + sb.append(idInValueSql); + } + sb.append(") "); + return sb.toString(); + } + + public String getIdInValueExpr() { + return idInValueSql; + } + + public Object[] getIdValues(EntityBean bean) { + Object val = embIdProperty.getValue(bean); + Object[] bindvalues = new Object[props.length]; + for (int i = 0; i < props.length; i++) { + bindvalues[i] = props[i].getValue((EntityBean) val); + } + return bindvalues; + } + + public Object[] getBindValues(Object value) { + + Object[] bindvalues = new Object[props.length]; + for (int i = 0; i < props.length; i++) { + bindvalues[i] = props[i].getValue((EntityBean) value); + } + return bindvalues; + } + + public void bindId(DefaultSqlUpdate sqlUpdate, Object value) { + for (int i = 0; i < props.length; i++) { + Object embFieldValue = props[i].getValue((EntityBean) value); + sqlUpdate.addParameter(embFieldValue); + } + } + + public void bindId(DataBind dataBind, Object value) throws SQLException { + + for (int i = 0; i < props.length; i++) { + Object embFieldValue = props[i].getValue((EntityBean) value); + props[i].bind(dataBind, embFieldValue); + } + } + + public Object readData(DataInput dataInput) throws IOException { + + EntityBean embId = idDesc.createBean(); + boolean notNull = true; + + for (int i = 0; i < props.length; i++) { + Object value = props[i].readData(dataInput); + props[i].setValue(embId, value); + if (value == null) { + notNull = false; + } } - public void writeData(DataOutput dataOutput, Object idValue) throws IOException { - for (int i = 0; i < props.length; i++) { - Object embFieldValue = props[i].getValue((EntityBean)idValue); - props[i].writeData(dataOutput, embFieldValue); - } + if (notNull) { + return embId; + } else { + return null; + } + } + + public void writeData(DataOutput dataOutput, Object idValue) throws IOException { + for (int i = 0; i < props.length; i++) { + Object embFieldValue = props[i].getValue((EntityBean) idValue); + props[i].writeData(dataOutput, embFieldValue); + } + } + + public void loadIgnore(DbReadContext ctx) { + for (int i = 0; i < props.length; i++) { + props[i].loadIgnore(ctx); + } + } + + public Object read(DbReadContext ctx) throws SQLException { + + EntityBean embId = idDesc.createBean(); + boolean notNull = true; + + for (int i = 0; i < props.length; i++) { + Object value = props[i].readSet(ctx, embId, null); + if (value == null) { + notNull = false; + } } - public void loadIgnore(DbReadContext ctx) { - for (int i = 0; i < props.length; i++) { - props[i].loadIgnore(ctx); - } + if (notNull) { + return embId; + } else { + return null; + } + } + + public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException { + + Object embId = read(ctx); + if (embId != null) { + embIdProperty.setValue(bean, embId); + return embId; + } else { + return null; + } + } + + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + for (int i = 0; i < props.length; i++) { + props[i].appendSelect(ctx, subQuery); + } + } + + public String getAssocIdInExpr(String prefix) { + + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (int i = 0; i < props.length; i++) { + if (i > 0) { + sb.append(","); + } + if (prefix != null) { + sb.append(prefix); + sb.append("."); + } + sb.append(props[i].getName()); + } + sb.append(")"); + return sb.toString(); + } + + public String getAssocOneIdExpr(String prefix, String operator) { + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < props.length; i++) { + if (i > 0) { + sb.append(" and "); + } + if (prefix != null) { + sb.append(prefix); + sb.append("."); + } + + sb.append(embIdProperty.getName()); + sb.append("."); + sb.append(props[i].getName()); + sb.append(operator); + } + return sb.toString(); + } + + public String getBindIdSql(String baseTableAlias) { + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < props.length; i++) { + if (i > 0) { + sb.append(" and "); + } + if (baseTableAlias != null) { + sb.append(baseTableAlias); + sb.append("."); + } + sb.append(props[i].getDbColumn()); + sb.append(" = ? "); + } + return sb.toString(); + } + + public String getBindIdInSql(String baseTableAlias) { + + if (idInExpandedForm) { + return ""; } - public Object read(DbReadContext ctx) throws SQLException { + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (int i = 0; i < props.length; i++) { + if (i > 0) { + sb.append(","); + } + if (baseTableAlias != null) { + sb.append(baseTableAlias); + sb.append("."); + } + sb.append(props[i].getDbColumn()); + } + sb.append(")"); + return sb.toString(); + } - EntityBean embId = idDesc.createBean(); - boolean notNull = true; + public Object convertSetId(Object idValue, EntityBean bean) { - for (int i = 0; i < props.length; i++) { - Object value = props[i].readSet(ctx, embId, null); - if (value == null) { - notNull = false; - } - } - - if (notNull) { - return embId; - } else { - return null; - } + // can not cast/convert if it is embedded + if (bean != null) { + // support PropertyChangeSupport + embIdProperty.setValueIntercept(bean, idValue); } - public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException { - - Object embId = read(ctx); - if (embId != null) { - embIdProperty.setValue(bean, embId); - return embId; - } else { - return null; - } - } - - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - for (int i = 0; i < props.length; i++) { - props[i].appendSelect(ctx, subQuery); - } - } - - public String getAssocIdInExpr(String prefix) { - - StringBuilder sb = new StringBuilder(); - sb.append("("); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(","); - } - if (prefix != null) { - sb.append(prefix); - sb.append("."); - } - sb.append(props[i].getName()); - } - sb.append(")"); - return sb.toString(); - } - - public String getAssocOneIdExpr(String prefix, String operator) { - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(" and "); - } - if (prefix != null) { - sb.append(prefix); - sb.append("."); - } - - sb.append(embIdProperty.getName()); - sb.append("."); - sb.append(props[i].getName()); - sb.append(operator); - } - return sb.toString(); - } - - public String getBindIdSql(String baseTableAlias) { - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(" and "); - } - if (baseTableAlias != null) { - sb.append(baseTableAlias); - sb.append("."); - } - sb.append(props[i].getDbColumn()); - sb.append(" = ? "); - } - return sb.toString(); - } - - public String getBindIdInSql(String baseTableAlias) { - - if (idInExpandedForm){ - return ""; - } - - StringBuilder sb = new StringBuilder(); - sb.append("("); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(","); - } - if (baseTableAlias != null){ - sb.append(baseTableAlias); - sb.append("."); - } - sb.append(props[i].getDbColumn()); - } - sb.append(")"); - return sb.toString(); - } - - public Object convertSetId(Object idValue, EntityBean bean) { - - // can not cast/convert if it is embedded - if (bean != null) { - // support PropertyChangeSupport - embIdProperty.setValueIntercept(bean, idValue); - } - - return idValue; - } + return idValue; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderEmpty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderEmpty.java index 1bf87c9f4..defe57740 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderEmpty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderEmpty.java @@ -22,8 +22,6 @@ public final class IdBinderEmpty implements IdBinder { private static final String bindIdSql = ""; - private static final BeanProperty[] properties = new BeanProperty[0]; - public IdBinderEmpty() { } @@ -43,6 +41,11 @@ public final class IdBinderEmpty implements IdBinder { return 0; } + @Override + public BeanProperty getBeanProperty() { + return null; + } + public String getIdProperty() { return null; } @@ -60,10 +63,6 @@ public final class IdBinderEmpty implements IdBinder { return ""; } - public BeanProperty[] getProperties() { - return properties; - } - public String getBindIdSql(String baseTableAlias) { return bindIdSql; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderFactory.java index b11ea2f79..502fdfff3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderFactory.java @@ -19,21 +19,17 @@ public class IdBinderFactory { /** * Create the IdConvertSet for the given type of Id properties. */ - public IdBinder createIdBinder(BeanProperty[] uids) { + public IdBinder createIdBinder(BeanProperty id) { - if (uids.length == 0){ + if (id == null){ // for report type beans that don't need an id return EMPTY; - } else if (uids.length == 1){ - if (uids[0].isEmbedded()){ - return new IdBinderEmbedded(idInExpandedForm, (BeanPropertyAssocOne)uids[0]); - } else { - return new IdBinderSimple(uids[0]); - } - + } + if (id.isEmbedded()){ + return new IdBinderEmbedded(idInExpandedForm, (BeanPropertyAssocOne)id); } else { - return new IdBinderMultiple(uids); + return new IdBinderSimple(id); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderMultiple.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderMultiple.java deleted file mode 100644 index 01179b0b8..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderMultiple.java +++ /dev/null @@ -1,399 +0,0 @@ -package com.avaje.ebeaninternal.server.deploy.id; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.api.SpiExpressionRequest; -import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.DbReadContext; -import com.avaje.ebeaninternal.server.deploy.DbSqlContext; -import com.avaje.ebeaninternal.server.lib.util.MapFromString; -import com.avaje.ebeaninternal.server.type.DataBind; - -/** - * Bind an Id that is made up of multiple separate properties. - *

- * The id passed in for binding is expected to be a map with the key being the - * String name of the property and the value being that properties bind value. - *

- */ -public final class IdBinderMultiple implements IdBinder { - - private final BeanProperty[] props; - - private final String idProperties; - - private final String idInValueSql; - - public IdBinderMultiple(BeanProperty[] idProps) { - this.props = idProps; - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < idProps.length; i++) { - if (i > 0){ - sb.append(","); - } - sb.append(idProps[i].getName()); - } - idProperties = InternString.intern(sb.toString()); - - sb = new StringBuilder(); - sb.append("("); - for (int i = 0; i < props.length; i++) { - if (i > 0){ - sb.append(","); - } - sb.append("?"); - } - sb.append(")"); - - idInValueSql = sb.toString(); - } - - public void initialise(){ - // do nothing - } - - public String getOrderBy(String pathPrefix, boolean ascending){ - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(" "); - } - if (pathPrefix != null){ - sb.append(pathPrefix).append("."); - } - sb.append(props[i].getName()); - if (!ascending){ - sb.append(" desc"); - } - } - return sb.toString(); - } - - - public void buildSelectExpressionChain(String prefix, List selectChain) { - - for (int i = 0; i < props.length; i++) { - props[i].buildSelectExpressionChain(prefix, selectChain); - } - } - - - public int getPropertyCount() { - return props.length; - } - - public String getIdProperty() { - return idProperties; - } - - - public BeanProperty findBeanProperty(String dbColumnName) { - - for (int i = 0; i < props.length; i++) { - if (dbColumnName.equalsIgnoreCase(props[i].getDbColumn())){ - return props[i]; - } - } - - return null; - } - - public boolean isComplexId(){ - return true; - } - - public String getDefaultOrderBy() { - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < props.length; i++) { - if (i > 0){ - sb.append(","); - } - - sb.append(props[i].getName()); - } - - return sb.toString(); - } - - public BeanProperty[] getProperties() { - return props; - } - - public void addIdInBindValue(SpiExpressionRequest request, Object value) { - for (int i = 0; i < props.length; i++) { - request.addBindValue(props[i].getValue((EntityBean)value)); - } - } - - public String getIdInValueExprDelete(int size) { - return getIdInValueExpr(size); - } - - public String getIdInValueExpr(int size) { - StringBuilder sb = new StringBuilder(); - sb.append(" in"); - sb.append(" ("); - sb.append(idInValueSql); - for (int i = 1; i < size; i++) { - sb.append(",").append(idInValueSql); - } - sb.append(") "); - return sb.toString(); - } - - public String getBindIdInSql(String baseTableAlias) { - StringBuilder sb = new StringBuilder(); - sb.append("("); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(","); - } - if (baseTableAlias != null){ - sb.append(baseTableAlias); - sb.append("."); - } - sb.append(props[i].getDbColumn()); - } - sb.append(")"); - return sb.toString(); - } - - public Object[] getIdValues(EntityBean bean){ - Object[] bindvalues = new Object[props.length]; - for (int i = 0; i < props.length; i++) { - bindvalues[i] = props[i].getValue(bean); - } - return bindvalues; - } - - @SuppressWarnings("unchecked") - public Object[] getBindValues(Object idValue){ - - Object[] bindvalues = new Object[props.length]; - // concatenated id as a Map - try { - Map uidMap = (Map) idValue; - - for (int i = 0; i < props.length; i++) { - Object value = uidMap.get(props[i].getName()); - bindvalues[i] = value; - } - - return bindvalues; - - } catch (ClassCastException e) { - String msg = "Expecting concatinated idValue to be a Map"; - throw new PersistenceException(msg, e); - } - } - - public Object readData(DataInput dataInput) throws IOException { - - LinkedHashMap map = new LinkedHashMap(); - - boolean notNull = true; - - for (int i = 0; i < props.length; i++) { - Object value = props[i].readData(dataInput); - map.put(props[i].getName(), value); - if (value == null) { - notNull = false; - } - } - - if (notNull) { - return map; - } else { - return null; - } - } - - @SuppressWarnings("unchecked") - public void writeData(DataOutput dataOutput, Object idValue) throws IOException { - - Map map = (Map)idValue; - for (int i = 0; i < props.length; i++) { - Object embFieldValue = map.get(props[i].getName()); - //Object embFieldValue = props[i].getValue(idValue); - props[i].writeData(dataOutput, embFieldValue); - } - } - - public void loadIgnore(DbReadContext ctx) { - for (int i = 0; i < props.length; i++) { - props[i].loadIgnore(ctx); - } - } - - public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException { - - LinkedHashMap map = new LinkedHashMap(); - boolean notNull = false; - for (int i = 0; i < props.length; i++) { - Object value = props[i].readSet(ctx, bean, null); - if (value != null){ - map.put(props[i].getName(), value); - notNull = true; - } - } - if (notNull){ - return map; - } else { - return null; - } - } - - public Object read(DbReadContext ctx) throws SQLException { - - LinkedHashMap map = new LinkedHashMap(); - boolean notNull = false; - for (int i = 0; i < props.length; i++) { - Object value = props[i].read(ctx); - if (value != null){ - map.put(props[i].getName(), value); - notNull = true; - } - } - if (notNull){ - return map; - } else { - return null; - } - } - - @SuppressWarnings("unchecked") - public void bindId(DefaultSqlUpdate sqlUpdate, Object idValue) { - // concatenated id as a Map - try { - Map uidMap = (Map) idValue; - - for (int i = 0; i < props.length; i++) { - Object value = uidMap.get(props[i].getName()); - sqlUpdate.addParameter(value); - } - - } catch (ClassCastException e) { - String msg = "Expecting concatinated idValue to be a Map"; - throw new PersistenceException(msg, e); - } - } - - @SuppressWarnings("unchecked") - public void bindId(DataBind bind, Object idValue) throws SQLException { - - // concatenated id as a Map - try { - Map uidMap = (Map) idValue; - - for (int i = 0; i < props.length; i++) { - Object value = uidMap.get(props[i].getName()); - props[i].bind(bind, value); - } - - } catch (ClassCastException e) { - String msg = "Expecting concatinated idValue to be a Map"; - throw new PersistenceException(msg, e); - } - } - - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - for (int i = 0; i < props.length; i++) { - props[i].appendSelect(ctx, subQuery); - } - } - - public String getAssocIdInExpr(String prefix) { - - StringBuilder sb = new StringBuilder(); - sb.append("("); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(","); - } - if (prefix != null) { - sb.append(prefix); - sb.append("."); - } - sb.append(props[i].getName()); - } - sb.append(")"); - return sb.toString(); - } - - public String getAssocOneIdExpr(String prefix, String operator){ - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(" and "); - } - if (prefix != null){ - sb.append(prefix); - sb.append("."); - } - sb.append(props[i].getName()); - sb.append(operator); - } - return sb.toString(); - } - - public String getBindIdSql(String baseTableAlias) { - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(" and "); - } - if (baseTableAlias != null){ - sb.append(baseTableAlias); - sb.append("."); - } - sb.append(props[i].getDbColumn()); - sb.append(" = ? "); - } - return sb.toString(); - } - - public Object convertSetId(Object idValue, EntityBean bean) { - - // allow Map or String for concatenated id - Map mapVal = null; - if (idValue instanceof Map) { - mapVal = (Map) idValue; - } else { - mapVal = MapFromString.parse(idValue.toString()); - } - - // Use a new LinkedHashMap to control the order - LinkedHashMap newMap = new LinkedHashMap(); - - for (int i = 0; i < props.length; i++) { - BeanProperty prop = props[i]; - - Object value = mapVal.get(prop.getName()); - - // Convert the property type if required - value = props[i].getScalarType().toBeanType(value); - newMap.put(prop.getName(), value); - if (bean != null) { - // support PropertyChangeSupport - prop.setValueIntercept(bean, value); - } - } - - return newMap; - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderSimple.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderSimple.java index 4a23e69ba..babff92ea 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderSimple.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderSimple.java @@ -26,8 +26,6 @@ public final class IdBinderSimple implements IdBinder { private final String bindIdSql; - private final BeanProperty[] properties; - private final Class expectedType; @SuppressWarnings("rawtypes") @@ -37,8 +35,6 @@ public final class IdBinderSimple implements IdBinder { this.idProperty = idProperty; this.scalarType = idProperty.getScalarType(); this.expectedType = idProperty.getPropertyType(); - this.properties = new BeanProperty[1]; - properties[0] = idProperty; bindIdSql = InternString.intern(idProperty.getDbColumn()+" = ? "); } @@ -46,33 +42,37 @@ public final class IdBinderSimple implements IdBinder { // do nothing } + public String getOrderBy(String pathPrefix, boolean ascending) { - public String getOrderBy(String pathPrefix, boolean ascending){ - - StringBuilder sb = new StringBuilder(); - if (pathPrefix != null){ - sb.append(pathPrefix).append("."); - } - sb.append(idProperty.getName()); - if (!ascending){ - sb.append(" desc"); - } - return sb.toString(); + StringBuilder sb = new StringBuilder(); + if (pathPrefix != null) { + sb.append(pathPrefix).append("."); } + sb.append(idProperty.getName()); + if (!ascending) { + sb.append(" desc"); + } + return sb.toString(); + } - public void buildSelectExpressionChain(String prefix, List selectChain) { + public void buildSelectExpressionChain(String prefix, List selectChain) { + + idProperty.buildSelectExpressionChain(prefix, selectChain); + } - idProperty.buildSelectExpressionChain(prefix, selectChain); - } + /** + * Returns 1. + */ + public int getPropertyCount() { + return 1; + } + + @Override + public BeanProperty getBeanProperty() { + return idProperty; + } - /** - * Returns 1. - */ - public int getPropertyCount() { - return 1; - } - - public String getIdProperty() { + public String getIdProperty() { return idProperty.getName(); } @@ -90,128 +90,124 @@ public final class IdBinderSimple implements IdBinder { public String getDefaultOrderBy() { return idProperty.getName(); } - - public BeanProperty[] getProperties() { - return properties; - } - public String getBindIdInSql(String baseTableAlias) { - if (baseTableAlias == null){ - return idProperty.getDbColumn(); - } else { - return baseTableAlias+"."+idProperty.getDbColumn(); - } - } + public String getBindIdInSql(String baseTableAlias) { + if (baseTableAlias == null) { + return idProperty.getDbColumn(); + } else { + return baseTableAlias + "." + idProperty.getDbColumn(); + } + } - public String getBindIdSql(String baseTableAlias) { - if (baseTableAlias == null){ - return bindIdSql; - } else { - return baseTableAlias+"."+bindIdSql; - } - } + public String getBindIdSql(String baseTableAlias) { + if (baseTableAlias == null) { + return bindIdSql; + } else { + return baseTableAlias + "." + bindIdSql; + } + } - public Object[] getIdValues(EntityBean bean){ - return new Object[]{idProperty.getValue(bean)}; - } - - public Object[] getBindValues(Object idValue){ - return new Object[]{idValue}; - } + public Object[] getIdValues(EntityBean bean) { + return new Object[] { idProperty.getValue(bean) }; + } - public String getIdInValueExprDelete(int size) { - return getIdInValueExpr(size); + public Object[] getBindValues(Object idValue) { + return new Object[] { idValue }; + } + + public String getIdInValueExprDelete(int size) { + return getIdInValueExpr(size); + } + + public String getIdInValueExpr(int size) { + StringBuilder sb = new StringBuilder(2 * size + 10); + sb.append(" in"); + sb.append(" (?"); + for (int i = 1; i < size; i++) { + sb.append(",?"); + } + sb.append(") "); + return sb.toString(); + } + + public void addIdInBindValue(SpiExpressionRequest request, Object value) { + value = convertSetId(value, null); + request.addBindValue(value); + } + + public void bindId(DefaultSqlUpdate sqlUpdate, Object value) { + sqlUpdate.addParameter(value); + } + + public void bindId(DataBind dataBind, Object value) throws SQLException { + if (!value.getClass().equals(expectedType)) { + value = scalarType.toBeanType(value); + } + idProperty.bind(dataBind, value); + } + + public void writeData(DataOutput os, Object value) throws IOException { + idProperty.writeData(os, value); + } + + public Object readData(DataInput is) throws IOException { + return idProperty.readData(is); + } + + public void loadIgnore(DbReadContext ctx) { + idProperty.loadIgnore(ctx); + } + + public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException { + Object id = idProperty.read(ctx); + if (id != null) { + idProperty.setValue(bean, id); + } + return id; + } + + public Object read(DbReadContext ctx) throws SQLException { + return idProperty.read(ctx); + } + + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + idProperty.appendSelect(ctx, subQuery); + } + + public String getAssocOneIdExpr(String prefix, String operator) { + + StringBuilder sb = new StringBuilder(); + if (prefix != null) { + sb.append(prefix); + sb.append("."); + } + sb.append(idProperty.getName()); + sb.append(operator); + return sb.toString(); + } + + public String getAssocIdInExpr(String prefix) { + + StringBuilder sb = new StringBuilder(); + if (prefix != null) { + sb.append(prefix); + sb.append("."); + } + sb.append(idProperty.getName()); + return sb.toString(); + } + + public Object convertSetId(Object idValue, EntityBean bean) { + + if (!idValue.getClass().equals(expectedType)) { + idValue = scalarType.toBeanType(idValue); } - public String getIdInValueExpr(int size) { - StringBuilder sb = new StringBuilder(2*size+10); - sb.append(" in"); - sb.append(" (?"); - for (int i = 1; i < size; i++) { - sb.append(",?"); - } - sb.append(") "); - return sb.toString(); + if (bean != null) { + // support PropertyChangeSupport + idProperty.setValueIntercept(bean, idValue); } - public void addIdInBindValue(SpiExpressionRequest request, Object value) { - value = convertSetId(value, null); - request.addBindValue(value); - } - - public void bindId(DefaultSqlUpdate sqlUpdate, Object value) { - sqlUpdate.addParameter(value); - } - - public void bindId(DataBind dataBind, Object value) throws SQLException { - if( !value.getClass().equals(expectedType) ){ - value = scalarType.toBeanType(value); - } - idProperty.bind(dataBind, value); - } - - public void writeData(DataOutput os, Object value) throws IOException { - idProperty.writeData(os, value); - } - - public Object readData(DataInput is) throws IOException { - return idProperty.readData(is); - } - - public void loadIgnore(DbReadContext ctx) { - idProperty.loadIgnore(ctx); - } - - public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException { - Object id = idProperty.read(ctx); - if (id != null){ - idProperty.setValue(bean, id); - } - return id; - } - - public Object read(DbReadContext ctx) throws SQLException { - return idProperty.read(ctx); - } - - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - idProperty.appendSelect(ctx, subQuery); - } - - public String getAssocOneIdExpr(String prefix, String operator){ - - StringBuilder sb = new StringBuilder(); - if (prefix != null){ - sb.append(prefix); - sb.append("."); - } - sb.append(idProperty.getName()); - sb.append(operator); - return sb.toString(); - } - - public String getAssocIdInExpr(String prefix) { - - StringBuilder sb = new StringBuilder(); - if (prefix != null) { - sb.append(prefix); - sb.append("."); - } - sb.append(idProperty.getName()); - return sb.toString(); - } - - public Object convertSetId(Object idValue, EntityBean bean) { - - if (!idValue.getClass().equals(expectedType)){ - idValue = scalarType.toBeanType(idValue); - } - - if (bean != null) { - // support PropertyChangeSupport - idProperty.setValueIntercept(bean, idValue); - } - - return idValue; - } + return idValue; + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java index 7c6e32262..b39aa6b2e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java @@ -185,8 +185,16 @@ public class DeployBeanPropertyLists { return null; } - public BeanProperty[] getId() { - return (BeanProperty[]) ids.toArray(new BeanProperty[ids.size()]); + public BeanProperty getId() { + if (ids.size() > 1) { + String msg = "Ebean does not support multiple @Id properties. You need to convert to using an @EmbeddedId." + +" Please email the ebean google group if you need further clarification."; + throw new IllegalStateException(msg); + } + if (ids.isEmpty()) { + return null; + } + return ids.get(0); } public BeanProperty[] getNonTransients() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChain.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChain.java index 1fefe6d34..c23dc7465 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChain.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChain.java @@ -268,20 +268,7 @@ public class ElPropertyChain implements ElPropertyValue { } } - public void elSetReference(EntityBean bean) { - - for (int i = 0; i < last; i++) { - bean = (EntityBean)chain[i].elGetValue(bean); - if (bean == null){ - break; - } - } - if (bean != null){ - ((EntityBean)bean)._ebean_getIntercept().setReference(); - } - } - - public void elSetValue(EntityBean bean, Object value, boolean populate, boolean reference){ + public void elSetValue(EntityBean bean, Object value, boolean populate) { EntityBean prevBean = bean; if (populate){ @@ -302,12 +289,10 @@ public class ElPropertyChain implements ElPropertyValue { if (lastBeanProperty != null){ // last chain element maps to a real scalar property lastBeanProperty.setValueIntercept(prevBean, value); - if (reference){ - ((EntityBean)prevBean)._ebean_getIntercept().setReference(); - } + } else { // a non-scalar property of a Compound value object - lastElPropertyValue.elSetValue(prevBean, value, populate, reference); + lastElPropertyValue.elSetValue(prevBean, value, populate); } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyValue.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyValue.java index 4f250e092..344e3b4f2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyValue.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyValue.java @@ -104,12 +104,7 @@ public interface ElPropertyValue extends ElPropertyDeploy { * If populate then *

*/ - public void elSetValue(EntityBean bean, Object value, boolean populate, boolean reference); - - /** - * Make the owning bean of this property a reference (as in not new/dirty). - */ - public void elSetReference(EntityBean bean); + public void elSetValue(EntityBean bean, Object value, boolean populate); /** * Convert the value to the expected type. diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java index a09d5eb64..748e41127 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java @@ -2,13 +2,15 @@ package com.avaje.ebeaninternal.server.persist; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.PersistenceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.avaje.ebean.CallableSql; import com.avaje.ebean.Query; import com.avaje.ebean.SqlUpdate; @@ -38,8 +40,6 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; import com.avaje.ebeaninternal.server.deploy.IntersectionRow; import com.avaje.ebeaninternal.server.deploy.ManyType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Persister implementation using DML. @@ -76,7 +76,6 @@ public final class DefaultPersister implements Persister { this.server = server; this.beanDescriptorManager = descMgr; this.persistExecute = new DefaultPersistExecute(binder, pstmtBatch); - } /** @@ -150,44 +149,25 @@ public final class DefaultPersister implements Persister { server.delete(detailBean, t); } - /** - * Force an Update using the given bean. - */ - public void forceUpdate(EntityBean bean, Set updateProps, Transaction t, boolean deleteMissingChildren, boolean updateNullProperties) { + /** + * Force an Update using the given bean. + */ + public void forceUpdate(EntityBean entityBean, Transaction t, boolean deleteMissingChildren) { - EntityBean entityBean = (EntityBean)bean; - EntityBeanIntercept ebi = entityBean._ebean_getIntercept(); - if (ebi.isNew()) { - ebi.setNewBeanForUpdate(); - } + PersistRequestBean req = createRequest(entityBean, t, null, PersistRequest.Type.UPDATE); + req.setStatelessUpdate(true, deleteMissingChildren); + try { + req.initTransIfRequired(); + update(req); + req.commitTransIfRequired(); + // finished a 'normal' update + return; - if (ebi.isDirty() || ebi.isLoaded()) { - // a 'normal' update using 'dirty' properties from internal bean state. - // if not dirty we still update in case any cascading save occurs - PersistRequestBean req = createRequest(bean, t, null); - req.setStatelessUpdate(true, deleteMissingChildren, updateNullProperties); - try { - req.initTransIfRequired(); - update(req); - req.commitTransIfRequired(); - // finished a 'normal' update - return; - - } catch (RuntimeException ex) { - req.rollbackTransIfRequired(); - throw ex; - } - - } else if (ebi.isReference()) { - // just return as no point in cascading (no modified beans/lists) - ((SpiTransaction)t).logSql("-- No update as bean is just a reference bean"); - return; - - } else { - ((SpiTransaction)t).logSql("-- No update as bean is not dirty"); - } - - } + } catch (RuntimeException ex) { + req.rollbackTransIfRequired(); + throw ex; + } + } public void save(EntityBean bean, Transaction t) { saveRecurse(bean, t, null); @@ -198,7 +178,7 @@ public final class DefaultPersister implements Persister { */ public void forceInsert(EntityBean bean, Transaction t) { - PersistRequestBean req = createRequest(bean, t, null); + PersistRequestBean req = createRequest(bean, t, null, PersistRequest.Type.INSERT); try { req.initTransIfRequired(); insert(req); @@ -219,7 +199,7 @@ public final class DefaultPersister implements Persister { throw new IllegalArgumentException("This bean is of type ["+bean.getClass()+"] is not enhanced?"); } - PersistRequestBean req = createRequest(bean, t, parentBean); + PersistRequestBean req = createRequest(bean, t, parentBean, PersistRequest.Type.DETERMINE); try { req.initTransIfRequired(); saveEnhanced(req); @@ -238,21 +218,20 @@ public final class DefaultPersister implements Persister { EntityBeanIntercept intercept = request.getEntityBeanIntercept(); - if (intercept.isReference()) { + if (request.isReference()) { // its a reference... if (request.isPersistCascade()) { // save any associated List held beans intercept.setLoaded(); saveAssocMany(false, request); - intercept.setReference(); + intercept.setReference(-1); } } else { - if (intercept.isLoaded()) { - // Need to call setLoaded(false) to simulate insert - update(request); + if (request.isInsert()) { + insert(request); } else { - insert(request); + update(request); } } } @@ -268,9 +247,6 @@ public final class DefaultPersister implements Persister { } try { - request.setType(PersistRequest.Type.INSERT); - request.setNotNullAsLoaded(); - if (request.isPersistCascade()) { // save associated One beans recursively first saveAssocOne(request); @@ -300,8 +276,6 @@ public final class DefaultPersister implements Persister { } try { - // we have determined that it is an update - request.setType(PersistRequest.Type.UPDATE); if (request.isPersistCascade()) { // save associated One beans recursively first saveAssocOne(request); @@ -331,7 +305,7 @@ public final class DefaultPersister implements Persister { */ public void delete(EntityBean bean, Transaction t) { - PersistRequestBean req = createRequest(bean, t, null); + PersistRequestBean req = createRequest(bean, t, null, PersistRequest.Type.DELETE); if (req.isRegisteredForDeleteBean()) { // skip deleting bean. Used where cascade is on // both sides of a relationship @@ -340,7 +314,7 @@ public final class DefaultPersister implements Persister { } return; } - req.setType(PersistRequest.Type.DELETE); + try { req.initTransIfRequired(); delete(req); @@ -603,7 +577,6 @@ public final class DefaultPersister implements Persister { private final boolean cascade; private final boolean statelessUpdate; private final boolean deleteMissingChildren; - private final boolean updateNullProperties; private SaveManyPropRequest(boolean insertedParent, BeanPropertyAssocMany many, EntityBean parentBean, PersistRequestBean request) { this.insertedParent = insertedParent; @@ -613,7 +586,6 @@ public final class DefaultPersister implements Persister { this.t = request.getTransaction(); this.statelessUpdate = request.isStatelessUpdate(); this.deleteMissingChildren = request.isDeleteMissingChildren(); - this.updateNullProperties = request.isUpdateNullProperties(); } private SaveManyPropRequest(BeanPropertyAssocMany many, EntityBean parentBean, SpiTransaction t) { @@ -624,7 +596,6 @@ public final class DefaultPersister implements Persister { this.cascade = true; this.statelessUpdate = false; this.deleteMissingChildren = false; - this.updateNullProperties = false; } public boolean isSaveIntersection() { @@ -646,10 +617,6 @@ public final class DefaultPersister implements Persister { private boolean isDeleteMissingChildren() { return deleteMissingChildren; } - - private boolean isUpdateNullProperties() { - return updateNullProperties; - } private boolean isInsertedParent() { return insertedParent; @@ -680,7 +647,7 @@ public final class DefaultPersister implements Persister { boolean saveIntersectionFromThisDirection = saveMany.isSaveIntersection(); if (saveMany.isCascade()) { // Need explicit Cascade to save the beans on other side - saveAssocManyDetails(saveMany, false, saveMany.isUpdateNullProperties()); + saveAssocManyDetails(saveMany, false); } // for ManyToMany save the 'relationship' via inserts/deletes // into/from the intersection table @@ -690,7 +657,7 @@ public final class DefaultPersister implements Persister { } } else { if (saveMany.isCascade()) { - saveAssocManyDetails(saveMany, saveMany.isDeleteMissingChildren(), saveMany.isUpdateNullProperties()); + saveAssocManyDetails(saveMany, saveMany.isDeleteMissingChildren()); } if (saveMany.isModifyListenMode()) { removeAssocManyPrivateOwned(saveMany); @@ -731,7 +698,7 @@ public final class DefaultPersister implements Persister { /** * Save the details from a OneToMany collection. */ - private void saveAssocManyDetails(SaveManyPropRequest saveMany, boolean deleteMissingChildren, boolean updateNullProperties) { + private void saveAssocManyDetails(SaveManyPropRequest saveMany, boolean deleteMissingChildren) { BeanPropertyAssocMany prop = saveMany.getMany(); @@ -747,12 +714,12 @@ public final class DefaultPersister implements Persister { return; } + BeanDescriptor targetDescriptor = prop.getTargetDescriptor(); if (saveMany.isInsertedParent()) { // performance optimisation for large collections - prop.getTargetDescriptor().preAllocateIds(collection.size()); + targetDescriptor.preAllocateIds(collection.size()); } - BeanDescriptor targetDescriptor = prop.getTargetDescriptor(); ArrayList detailIds = null; if (deleteMissingChildren) { // collect the Id's (to exclude from deleteManyDetails) @@ -787,16 +754,16 @@ public final class DefaultPersister implements Persister { } else { EntityBean detail = (EntityBean)detailBean; + EntityBeanIntercept ebi = detail._ebean_getIntercept(); if (prop.isManyToMany()) { - skipSavingThisBean = detail._ebean_getIntercept().isReference(); + skipSavingThisBean = targetDescriptor.isReference(ebi); } else { - EntityBeanIntercept ebi = detail._ebean_getIntercept(); if (ebi.isNewOrDirty()) { skipSavingThisBean = false; // set the parent bean to detailBean prop.setJoinValuesToChild(parentBean, detail, mapKeyValue); - } else if (ebi.isReference()) { + } else if (targetDescriptor.isReference(ebi)) { // we can skip this one skipSavingThisBean = true; @@ -820,7 +787,7 @@ public final class DefaultPersister implements Persister { if (targetDescriptor.isStatelessUpdate(detail)) { // update based on the value of Version/Id properties // cascade update in stateless mode - forceUpdate(detail, null, t, deleteMissingChildren, updateNullProperties); + forceUpdate(detail, t, deleteMissingChildren); } else { // cascade insert forceInsert(detail, t); @@ -1113,7 +1080,7 @@ public final class DefaultPersister implements Persister { if (request.isLoadedProperty(prop)) { Object detailBean = prop.getValue(request.getEntityBean()); if (detailBean != null) { - if (isReference(detailBean)) { + if (prop.isReference(detailBean)) { // skip saving a reference } else if (request.isParent(detailBean)) { // skip saving the parent as already saved @@ -1131,13 +1098,6 @@ public final class DefaultPersister implements Persister { } } - /** - * Return true if the bean is a reference. - */ - private boolean isReference(Object bean) { - return (bean instanceof EntityBean) && ((EntityBean) bean)._ebean_getIntercept().isReference(); - } - /** * Support for loading any Imported Associated One properties that are not * loaded but required for Delete cascade. @@ -1196,7 +1156,7 @@ public final class DefaultPersister implements Persister { return; } - BeanProperty idProp = desc.getSingleIdProperty(); + BeanProperty idProp = desc.getIdProperty(); if (idProp == null || idProp.isEmbedded()) { // not supporting IdGeneration for concatenated or Embedded return; @@ -1248,12 +1208,12 @@ public final class DefaultPersister implements Persister { * perform an insert, update or delete. */ @SuppressWarnings("unchecked") - private PersistRequestBean createRequest(T bean, Transaction t, Object parentBean) { + private PersistRequestBean createRequest(T bean, Transaction t, Object parentBean, PersistRequest.Type type) { BeanManager mgr = getBeanManager(bean); if (mgr == null) { throw new PersistenceException(errNotRegistered(bean.getClass())); } - return (PersistRequestBean) createRequest(bean, t, parentBean, mgr); + return (PersistRequestBean) createRequest(bean, t, parentBean, mgr, type); } private String errNotRegistered(Class beanClass) { @@ -1268,9 +1228,9 @@ public final class DefaultPersister implements Persister { * perform an insert, update or delete. */ @SuppressWarnings({ "unchecked", "rawtypes" }) - private PersistRequestBean createRequest(Object bean, Transaction t, Object parentBean, BeanManager mgr) { + private PersistRequestBean createRequest(Object bean, Transaction t, Object parentBean, BeanManager mgr, PersistRequest.Type type) { - return new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute); + return new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute, type); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryId.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryId.java index 8b2b20707..9b85bde19 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryId.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryId.java @@ -17,20 +17,17 @@ public class FactoryId { */ public BindableId createId(BeanDescriptor desc) { - BeanProperty[] uids = desc.propertiesId(); - if (uids.length == 0) { + BeanProperty id = desc.getIdProperty(); + if (id == null) { return new BindableIdEmpty(); - } else if (uids.length == 1) { - if (!uids[0].isEmbedded()) { - return new BindableIdScalar(uids[0]); + } + if (!id.isEmbedded()) { + return new BindableIdScalar(id); - } else { - BeanPropertyAssocOne embId = (BeanPropertyAssocOne) uids[0]; - return new BindableIdEmbedded(embId, desc); - } } else { - return new BindableIdMap(uids, desc); + BeanPropertyAssocOne embId = (BeanPropertyAssocOne) id; + return new BindableIdEmbedded(embId, desc); } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java index 3429d9f14..5fc2c2210 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java @@ -592,7 +592,7 @@ public class CQuery implements DbReadContext, CancelableQuery { } else { // create a new collection to populate and assign to the bean currentDetailCollection = manyProperty.createEmpty(false); - manyPropertyEl.elSetValue(loadedBean, currentDetailCollection, false, false); + manyPropertyEl.elSetValue(loadedBean, currentDetailCollection, false); } if (filterMany != null) { @@ -600,8 +600,7 @@ public class CQuery implements DbReadContext, CancelableQuery { ((BeanCollection) currentDetailCollection).setFilterMany(filterMany); } - // the manyKey is always null for this case, just using default mapKey on - // the property + // the manyKey is always null for this case, just using default mapKey on the property currentDetailAdd = manyProperty.getBeanCollectionAdd(currentDetailCollection, null); addToCurrentDetailCollection(); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java index bde7cd919..42d87158a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java @@ -95,7 +95,7 @@ public class SqlTreeNodeBean implements SqlTreeNode { this.idBinder = desc.getIdBinder(); // the bean has an Id property and we want to use it - this.readId = withId && (desc.propertiesId().length > 0); + this.readId = withId && (desc.getIdProperty() != null); this.disableLazyLoad = !readId || desc.isSqlSelectBased(); this.tableJoins = props.getTableJoins(); @@ -368,7 +368,7 @@ public class SqlTreeNodeBean implements SqlTreeNode { } if (readId) { - appendSelect(ctx, false, idBinder.getProperties()); + appendSelect(ctx, false, idBinder.getBeanProperty()); } appendSelect(ctx, subQuery, properties); appendSelectTableJoins(ctx); @@ -408,6 +408,13 @@ public class SqlTreeNodeBean implements SqlTreeNode { } } + private void appendSelect(DbSqlContext ctx, boolean subQuery, BeanProperty prop) { + + if (prop != null) { + prop.appendSelect(ctx, subQuery); + } + } + public void appendWhere(DbSqlContext ctx) { // Only apply inheritance to root node as any join will alreay have the inheritance join include - see TableJoin diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/csv/TCsvReader.java b/src/main/java/com/avaje/ebeaninternal/server/text/csv/TCsvReader.java index 11598c062..362683993 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/csv/TCsvReader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/csv/TCsvReader.java @@ -29,9 +29,6 @@ import com.avaje.ebeaninternal.server.el.ElPropertyValue; */ public class TCsvReader implements CsvReader { - // private static final Logger logger = - // Logger.getLogger(TCsvReader.class.getName()); - private static final TimeStringParser TIME_PARSER = new TimeStringParser(); private final EbeanServer server; @@ -113,14 +110,6 @@ public class TCsvReader implements CsvReader { addProperty(propertyName, null); } - public void addReference(String propertyName) { - addProperty(propertyName, null, true); - } - - public void addProperty(String propertyName, StringParser parser) { - addProperty(propertyName, parser, false); - } - public void addDateTime(String propertyName, String dateTimeFormat) { addDateTime(propertyName, dateTimeFormat, Locale.getDefault()); } @@ -143,7 +132,7 @@ public class TCsvReader implements CsvReader { SimpleDateFormat sdf = new SimpleDateFormat(dateTimeFormat, locale); DateTimeParser parser = new DateTimeParser(sdf, dateTimeFormat, elProp); - CsvColumn column = new CsvColumn(elProp, parser, false); + CsvColumn column = new CsvColumn(elProp, parser); columnList.add(column); } @@ -161,13 +150,13 @@ public class TCsvReader implements CsvReader { } } - public void addProperty(String propertyName, StringParser parser, boolean reference) { + public void addProperty(String propertyName, StringParser parser) { ElPropertyValue elProp = descriptor.getElGetValue(propertyName); if (parser == null) { parser = elProp.getStringParser(); } - CsvColumn column = new CsvColumn(elProp, parser, reference); + CsvColumn column = new CsvColumn(elProp, parser); columnList.add(column); } @@ -250,7 +239,7 @@ public class TCsvReader implements CsvReader { } else if (elProp.isAssocProperty()) { BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne) elProp.getBeanProperty(); String idProp = assocOne.getBeanDescriptor().getIdBinder().getIdProperty(); - addReference(line[i] + "." + idProp); + addProperty(line[i] + "." + idProp); } else { addProperty(line[i]); } @@ -304,7 +293,6 @@ public class TCsvReader implements CsvReader { private final ElPropertyValue elProp; private final StringParser parser; private final boolean ignore; - private final boolean reference; /** * Constructor for the IGNORE column. @@ -312,17 +300,15 @@ public class TCsvReader implements CsvReader { private CsvColumn() { this.elProp = null; this.parser = null; - this.reference = false; this.ignore = true; } /** * Construct with a property and parser. */ - public CsvColumn(ElPropertyValue elProp, StringParser parser, boolean reference) { + public CsvColumn(ElPropertyValue elProp, StringParser parser) { this.elProp = elProp; this.parser = parser; - this.reference = reference; this.ignore = false; } @@ -333,7 +319,7 @@ public class TCsvReader implements CsvReader { if (!ignore) { Object value = parser.parse(strValue); - elProp.elSetValue(bean, value, true, reference); + elProp.elSetValue(bean, value, true); } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java index 1d75ba806..df0f527da 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java @@ -163,8 +163,7 @@ public class ReadJsonContext extends ReadBasicJsonContext { public void setLoadedState(){ if (ebi != null){ - // takes into account reference beans - beanDescriptor.setLoadedProps(ebi, loadedProps); + ebi.setLoaded(); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java index c3f2b7bf2..7112f8a4e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java @@ -344,7 +344,7 @@ public class WriteJsonContext implements JsonWriter { } public WriteBeanState pushBeanState(Object bean) { - WriteBeanState newState = new WriteBeanState(bean); + WriteBeanState newState = new WriteBeanState();//bean); WriteBeanState prevState = beanState; beanState = newState; return prevState; @@ -354,26 +354,15 @@ public class WriteJsonContext implements JsonWriter { this.beanState = previousState; } - public boolean isReferenceBean() { - return beanState.isReferenceBean(); - } - public static class WriteBeanState { - private final EntityBeanIntercept ebi; - private final boolean referenceBean; private boolean firstKeyOut; - public WriteBeanState(Object bean) { - this.ebi = ((EntityBean)bean)._ebean_getIntercept(); - this.referenceBean = ebi.isReference(); + public WriteBeanState() { + } - public boolean isReferenceBean() { - return referenceBean; - } - public boolean isFirstKey() { if (!firstKeyOut){ firstKeyOut = true; diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundPropertyElAdapter.java b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundPropertyElAdapter.java index a68d82bc3..6e6557630 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundPropertyElAdapter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundPropertyElAdapter.java @@ -45,7 +45,7 @@ public class CtCompoundPropertyElAdapter implements ElPropertyValue { // Do nothing } - public void elSetValue(EntityBean bean, Object value, boolean populate, boolean reference) { + public void elSetValue(EntityBean bean, Object value, boolean populate) { prop.setValue(bean, value); } diff --git a/src/test/java/com/avaje/ebean/BaseTestCase.java b/src/test/java/com/avaje/ebean/BaseTestCase.java index 29e904ec0..c0343e194 100644 --- a/src/test/java/com/avaje/ebean/BaseTestCase.java +++ b/src/test/java/com/avaje/ebean/BaseTestCase.java @@ -10,7 +10,7 @@ public class BaseTestCase { static { logger.debug("... preStart"); - if (!AgentLoader.loadAgentFromClasspath("avaje-ebeanorm-agent","debug=0;packages=com.avaje.tests.**")) { + if (!AgentLoader.loadAgentFromClasspath("avaje-ebeanorm-agent","debug=1;packages=com.avaje.tests.**")) { logger.info("avaje-ebeanorm-agent not found in classpath - not dynamically loaded"); } } diff --git a/src/test/java/com/avaje/ebeaninternal/server/deploy/TestBeanDescriptorHasIdProperty.java b/src/test/java/com/avaje/ebeaninternal/server/deploy/TestBeanDescriptorHasIdProperty.java new file mode 100644 index 000000000..d7e116ae4 --- /dev/null +++ b/src/test/java/com/avaje/ebeaninternal/server/deploy/TestBeanDescriptorHasIdProperty.java @@ -0,0 +1,69 @@ +package com.avaje.ebeaninternal.server.deploy; + +import java.sql.Timestamp; + +import org.junit.Assert; +import org.junit.Test; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.tests.model.basic.Customer; +import com.avaje.tests.model.basic.Order; + +public class TestBeanDescriptorHasIdProperty extends BaseTestCase { + + SpiEbeanServer spiServer; + + public TestBeanDescriptorHasIdProperty() { + EbeanServer server = Ebean.getServer(null); + spiServer = (SpiEbeanServer)server; + } + + @Test + public void testHasId() { + + BeanDescriptor beanDescriptor = spiServer.getBeanDescriptor(Order.class); + Assert.assertNotNull(beanDescriptor.getIdProperty()); + Assert.assertEquals("id", beanDescriptor.getIdProperty().getName()); + + Assert.assertNotNull(beanDescriptor.getVersionProperty()); + Assert.assertEquals("updtime", beanDescriptor.getVersionProperty().getName()); + + Order order = new Order(); + + Assert.assertFalse(beanDescriptor.hasIdProperty(getIntercept(order))); + Assert.assertFalse(beanDescriptor.hasVersionProperty(getIntercept(order))); + + order.setId(23); + order.setUpdtime(new Timestamp(System.currentTimeMillis())); + + Assert.assertTrue(beanDescriptor.hasIdProperty(getIntercept(order))); + Assert.assertTrue(beanDescriptor.hasVersionProperty(getIntercept(order))); + + } + + @Test + public void testIsReference() { + + BeanDescriptor beanDescriptor = spiServer.getBeanDescriptor(Customer.class); + + Customer order = new Customer(); + EntityBeanIntercept ebi = getIntercept(order); + Assert.assertFalse(beanDescriptor.hasIdPropertyOnly(ebi)); + + order.setId(23); + Assert.assertTrue(beanDescriptor.hasIdPropertyOnly(ebi)); + + order.setName("custName"); + Assert.assertFalse(beanDescriptor.hasIdPropertyOnly(ebi)); + } + + private EntityBeanIntercept getIntercept(Object bean) { + return ((EntityBean)bean)._ebean_getIntercept(); + } + +} diff --git a/src/test/java/com/avaje/tests/basic/TestBeanReferenceRefresh.java b/src/test/java/com/avaje/tests/basic/TestBeanReferenceRefresh.java index 8e730ad88..b9c6f61ed 100644 --- a/src/test/java/com/avaje/tests/basic/TestBeanReferenceRefresh.java +++ b/src/test/java/com/avaje/tests/basic/TestBeanReferenceRefresh.java @@ -1,11 +1,14 @@ package com.avaje.tests.basic; +import java.sql.Date; + import junit.framework.Assert; import org.junit.Test; import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; +import com.avaje.tests.model.basic.Customer; import com.avaje.tests.model.basic.Order; import com.avaje.tests.model.basic.Order.Status; import com.avaje.tests.model.basic.ResetBasicData; @@ -19,9 +22,14 @@ public class TestBeanReferenceRefresh extends BaseTestCase { Order order = Ebean.getReference(Order.class, 1); - Assert.assertTrue("isReference",Ebean.getBeanState(order).isReference()); + Assert.assertTrue(Ebean.getBeanState(order).isReference()); - order.getOrderDate(); + // invoke lazy loading + Date orderDate = order.getOrderDate(); + Assert.assertNotNull(orderDate); + + Customer customer = order.getCustomer(); + Assert.assertNotNull(customer); Assert.assertFalse(Ebean.getBeanState(order).isReference()); Assert.assertNotNull(order.getStatus()); diff --git a/src/test/java/com/avaje/tests/batchload/TestBatchLazyWithCacheHits.java b/src/test/java/com/avaje/tests/batchload/TestBatchLazyWithCacheHits.java index 128258080..d39784443 100644 --- a/src/test/java/com/avaje/tests/batchload/TestBatchLazyWithCacheHits.java +++ b/src/test/java/com/avaje/tests/batchload/TestBatchLazyWithCacheHits.java @@ -2,7 +2,6 @@ package com.avaje.tests.batchload; import java.util.ArrayList; import java.util.List; -import java.util.UUID; import junit.framework.Assert; @@ -18,7 +17,6 @@ public class TestBatchLazyWithCacheHits extends BaseTestCase { private UUOne insert(String name) { UUOne one = new UUOne(); - one.setId(UUID.randomUUID()); one.setName("test-BLWCH-"+name); Ebean.save(one); return one; diff --git a/src/test/java/com/avaje/tests/batchload/TestBatchLazyWithDeleted.java b/src/test/java/com/avaje/tests/batchload/TestBatchLazyWithDeleted.java index 3c0d58ada..d83268565 100644 --- a/src/test/java/com/avaje/tests/batchload/TestBatchLazyWithDeleted.java +++ b/src/test/java/com/avaje/tests/batchload/TestBatchLazyWithDeleted.java @@ -21,11 +21,9 @@ public class TestBatchLazyWithDeleted extends BaseTestCase { public void testOnDeleted() { UUOne oneA = new UUOne(); - oneA.setId(UUID.randomUUID()); oneA.setName("oneA"); UUOne oneB = new UUOne(); - oneB.setId(UUID.randomUUID()); oneB.setName("oneB"); UUTwo two = new UUTwo(); diff --git a/src/test/java/com/avaje/tests/compositekeys/TestCKeyLazyLoad.java b/src/test/java/com/avaje/tests/compositekeys/TestCKeyLazyLoad.java index fc43f8c2f..e9de7632b 100644 --- a/src/test/java/com/avaje/tests/compositekeys/TestCKeyLazyLoad.java +++ b/src/test/java/com/avaje/tests/compositekeys/TestCKeyLazyLoad.java @@ -32,7 +32,7 @@ public class TestCKeyLazyLoad extends BaseTestCase { p.add(new CKeyDetail("somethine one")); p.add(new CKeyDetail("somethine two")); - Ebean.save(p); + Ebean.insert(p); CKeyAssoc assoc2 = new CKeyAssoc(); assoc2.setAssocOne("assocTwo"); @@ -46,14 +46,14 @@ public class TestCKeyLazyLoad extends BaseTestCase { p2.add(new CKeyDetail("somethine one")); p2.add(new CKeyDetail("somethine two")); - Ebean.save(p2); + Ebean.insert(p2); CKeyParentId searchId = new CKeyParentId(1, "one"); CKeyParent found = Ebean.find(CKeyParent.class).where().idEq(searchId).findUnique(); Assert.assertNotNull(found); - Assert.assertTrue(found.getDetails().size() == 2); + Assert.assertEquals(2,found.getDetails().size()); List list = Ebean.find(CKeyParent.class).findList(); diff --git a/src/test/java/com/avaje/tests/delete/TestDeleteByIdWithPersistenceContext.java b/src/test/java/com/avaje/tests/delete/TestDeleteByIdWithPersistenceContext.java index 97d3e6b48..e5e49f8cf 100644 --- a/src/test/java/com/avaje/tests/delete/TestDeleteByIdWithPersistenceContext.java +++ b/src/test/java/com/avaje/tests/delete/TestDeleteByIdWithPersistenceContext.java @@ -20,9 +20,9 @@ public class TestDeleteByIdWithPersistenceContext extends BaseTestCase { EbeanServer server = Ebean.getServer(null); Product prod1 = createProduct(100,"apples"); - server.save(prod1); + server.insert(prod1); Product prod2 = createProduct(101, "bananas"); - server.save(prod2); + server.insert(prod2); server.beginTransaction(); // effectively load these into the persistence context diff --git a/src/test/java/com/avaje/tests/el/TestElGetReference.java b/src/test/java/com/avaje/tests/el/TestElGetReference.java index 88004b2ef..a6ecf5f02 100644 --- a/src/test/java/com/avaje/tests/el/TestElGetReference.java +++ b/src/test/java/com/avaje/tests/el/TestElGetReference.java @@ -35,7 +35,7 @@ public class TestElGetReference extends TestCase { elProp.elGetReference((EntityBean)c0); elProp.elGetReference((EntityBean)c1); - addrLine1Prop.elSetValue((EntityBean)c1, "12 someplace", true, false); - addrCityProp.elSetValue((EntityBean)c1, "Auckland", true, false); + addrLine1Prop.elSetValue((EntityBean)c1, "12 someplace", true); + addrCityProp.elSetValue((EntityBean)c1, "Auckland", true); } } diff --git a/src/test/java/com/avaje/tests/enhancement/TestConstructorPutfieldReplacement.java b/src/test/java/com/avaje/tests/enhancement/TestConstructorPutfieldReplacement.java new file mode 100644 index 000000000..cdf35e895 --- /dev/null +++ b/src/test/java/com/avaje/tests/enhancement/TestConstructorPutfieldReplacement.java @@ -0,0 +1,30 @@ +package com.avaje.tests.enhancement; + +import org.junit.Assert; +import org.junit.Test; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.tests.model.basic.PFile; +import com.avaje.tests.model.basic.PFileContent; + +public class TestConstructorPutfieldReplacement extends BaseTestCase { + + @Test + public void test() { + + PFile persistentFile = new PFile("test.txt", new PFileContent("test".getBytes())); + + EntityBean eb = (EntityBean)persistentFile; + EntityBeanIntercept ebi = eb._ebean_getIntercept(); + + int namePos = ebi.findProperty("name"); + int fileContentPos = ebi.findProperty("fileContent"); + + Assert.assertTrue(ebi.isLoadedProperty(namePos)); + Assert.assertTrue(ebi.isLoadedProperty(fileContentPos)); + + } + +} diff --git a/src/test/java/com/avaje/tests/model/basic/NoIdEntityType.java b/src/test/java/com/avaje/tests/model/basic/NoIdEntityType.java deleted file mode 100644 index c80018633..000000000 --- a/src/test/java/com/avaje/tests/model/basic/NoIdEntityType.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.avaje.tests.model.basic; - -import java.sql.Timestamp; - -import javax.persistence.Entity; - -@Entity -public class NoIdEntityType { - - String name; - - Timestamp someDateTime; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Timestamp getSomeDateTime() { - return someDateTime; - } - - public void setSomeDateTime(Timestamp someDateTime) { - this.someDateTime = someDateTime; - } - - -} diff --git a/src/test/java/com/avaje/tests/model/basic/ResetBasicData.java b/src/test/java/com/avaje/tests/model/basic/ResetBasicData.java index 9f339435d..4eb1eb360 100644 --- a/src/test/java/com/avaje/tests/model/basic/ResetBasicData.java +++ b/src/test/java/com/avaje/tests/model/basic/ResetBasicData.java @@ -5,12 +5,15 @@ import java.util.ArrayList; import java.util.List; import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; import com.avaje.ebean.TxRunnable; public class ResetBasicData { private static boolean runOnce; + private static EbeanServer server = Ebean.getServer(null); + public static synchronized void reset() { if (runOnce){ @@ -19,7 +22,7 @@ public class ResetBasicData { final ResetBasicData me = new ResetBasicData(); - Ebean.execute(new TxRunnable() { + server.execute(new TxRunnable() { public void run() { me.deleteAll(); me.insertCountries(); @@ -40,29 +43,29 @@ public class ResetBasicData { //Ebean.currentTransaction().setBatchMode(false); // orm update use bean name and bean properties - Ebean.createUpdate(OrderShipment.class, "delete from orderShipment") + server.createUpdate(OrderShipment.class, "delete from orderShipment") .execute(); - Ebean.createUpdate(OrderDetail.class, "delete from orderDetail") + server.createUpdate(OrderDetail.class, "delete from orderDetail") .execute(); - Ebean.createUpdate(Order.class,"delete from order") + server.createUpdate(Order.class,"delete from order") .execute(); - Ebean.createUpdate(Contact.class,"delete from contact") + server.createUpdate(Contact.class,"delete from contact") .execute(); - Ebean.createUpdate(Customer.class,"delete from Customer") + server.createUpdate(Customer.class,"delete from Customer") .execute(); - Ebean.createUpdate(Address.class,"delete from address") + server.createUpdate(Address.class,"delete from address") .execute(); // sql update uses table and column names - Ebean.createSqlUpdate("delete from o_country") + server.createSqlUpdate("delete from o_country") .execute(); - Ebean.createSqlUpdate("delete from o_product") + server.createSqlUpdate("delete from o_product") .execute(); } @@ -72,17 +75,17 @@ public class ResetBasicData { public void insertCountries() { - Ebean.execute(new TxRunnable() { + server.execute(new TxRunnable() { public void run() { Country c = new Country(); c.setCode("NZ"); c.setName("New Zealand"); - Ebean.save(c); + server.insert(c); Country au = new Country(); au.setCode("AU"); au.setName("Australia"); - Ebean.save(au); + server.insert(au); } }); } @@ -90,31 +93,31 @@ public class ResetBasicData { public void insertProducts() { - Ebean.execute(new TxRunnable() { + server.execute(new TxRunnable() { public void run() { Product p = new Product(); p.setId(1); p.setName("Chair"); p.setSku("C001"); - Ebean.save(p); + server.insert(p); p = new Product(); p.setId(2); p.setName("Desk"); p.setSku("DSK1"); - Ebean.save(p); + server.insert(p); p = new Product(); p.setId(3); p.setName("Computer"); p.setSku("C002"); - Ebean.save(p); + server.insert(p); p = new Product(); p.setId(4); p.setName("Printer"); p.setSku("C003"); - Ebean.save(p); + server.insert(p); } }); } diff --git a/src/test/java/com/avaje/tests/model/map/MpRole.java b/src/test/java/com/avaje/tests/model/map/MpRole.java index 2c35064c2..7b4ac2a4f 100644 --- a/src/test/java/com/avaje/tests/model/map/MpRole.java +++ b/src/test/java/com/avaje/tests/model/map/MpRole.java @@ -7,6 +7,8 @@ public class MpRole { @Id private Long id; + + private String code; private Long organizationId; @@ -18,6 +20,14 @@ public class MpRole { this.id = id; } + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + public Long getOrganizationId() { return organizationId; } diff --git a/src/test/java/com/avaje/tests/model/map/MpUser.java b/src/test/java/com/avaje/tests/model/map/MpUser.java index 48f9dc47e..0c87468c4 100644 --- a/src/test/java/com/avaje/tests/model/map/MpUser.java +++ b/src/test/java/com/avaje/tests/model/map/MpUser.java @@ -16,8 +16,8 @@ public class MpUser { private String name; @OneToMany(cascade = CascadeType.ALL) - @MapKey(name = "id") - public Map roles = new HashMap(); + @MapKey(name = "code") + public Map roles = new HashMap(); public Long getId() { return id; @@ -35,11 +35,11 @@ public class MpUser { this.name = name; } - public Map getRoles() { + public Map getRoles() { return roles; } - public void setRoles(Map roles) { + public void setRoles(Map roles) { this.roles = roles; } } diff --git a/src/test/java/com/avaje/tests/noid/NoIdEntity.java b/src/test/java/com/avaje/tests/noid/NoIdEntity.java deleted file mode 100644 index de73d47f0..000000000 --- a/src/test/java/com/avaje/tests/noid/NoIdEntity.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.avaje.tests.noid; - -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; - -@Entity -@Table(name="No_Id_Entity_Rob") -public class NoIdEntity { - - @Id - private int id; - private String value; - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - -} diff --git a/src/test/java/com/avaje/tests/noid/TestNoId.java b/src/test/java/com/avaje/tests/noid/TestNoId.java deleted file mode 100644 index c33965722..000000000 --- a/src/test/java/com/avaje/tests/noid/TestNoId.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.avaje.tests.noid; - -import java.util.List; - -import junit.framework.Assert; - -import org.junit.Test; - -import com.avaje.ebean.BaseTestCase; -import com.avaje.ebean.Ebean; -import com.avaje.ebean.Update; - -public class TestNoId extends BaseTestCase { - - @Test - public void testNoIdQuery() { - - Update upd = Ebean.createUpdate(NoIdEntity.class, "delete from NoIdEntity"); - upd.execute(); - - NoIdEntity e0 = new NoIdEntity(); - e0.setId(1); - e0.setValue("one"); - - NoIdEntity e1 = new NoIdEntity(); - e1.setId(2); - e1.setValue("two"); - - Ebean.save(e0); - Ebean.save(e1); - - List list = Ebean.createNamedQuery(NoIdEntity.class, "noid").findList(); - - Assert.assertEquals(2, list.size()); - NoIdEntity noIdEntity0 = list.get(0); - Assert.assertNotNull(noIdEntity0); - Assert.assertEquals(noIdEntity0.getValue(), "one"); - - NoIdEntity noIdEntity1 = list.get(1); - Assert.assertNotNull(noIdEntity1); - Assert.assertEquals(noIdEntity1.getValue(), "two"); - - } -} diff --git a/src/test/java/com/avaje/tests/query/other/TestNoIdEntityType.java b/src/test/java/com/avaje/tests/query/other/TestNoIdEntityType.java deleted file mode 100644 index 13e471b67..000000000 --- a/src/test/java/com/avaje/tests/query/other/TestNoIdEntityType.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.avaje.tests.query.other; - -import junit.framework.Assert; - -import org.junit.Test; - -import com.avaje.ebean.BaseTestCase; -import com.avaje.ebean.Ebean; -import com.avaje.tests.model.basic.NoIdEntityType; - -public class TestNoIdEntityType extends BaseTestCase { - - @Test - public void testFindById() { - - try { - // this should fail for this entity - Ebean.find(NoIdEntityType.class, 10); - Assert.assertTrue(false); - - } catch (IllegalStateException e){ - // expecting this exception - Assert.assertTrue(true); - } - - NoIdEntityType noId = new NoIdEntityType(); - noId.setName("foo"); - - Ebean.save(noId); - - try { - // this should fail as no @Id property - Ebean.delete(noId); - Assert.assertTrue(false); - - } catch (IllegalStateException e){ - // expecting this exception - Assert.assertTrue(true); - } - - } -} diff --git a/src/test/java/com/avaje/tests/query/other/TestOneToManyAsMap.java b/src/test/java/com/avaje/tests/query/other/TestOneToManyAsMap.java index 8d86ee380..6e7007268 100644 --- a/src/test/java/com/avaje/tests/query/other/TestOneToManyAsMap.java +++ b/src/test/java/com/avaje/tests/query/other/TestOneToManyAsMap.java @@ -28,15 +28,20 @@ public class TestOneToManyAsMap extends BaseTestCase { u2.setName("Charlie Brown"); MpRole ourl = new MpRole(); ourl.setOrganizationId(47L); - u2.getRoles().put(ourl.getOrganizationId(), ourl); + u2.getRoles().put("one", ourl); eServer.save(u2); MpUser u3 = eServer.find(MpUser.class, u.getId()); Assert.assertEquals("Charlie Brown", u3.getName()); - Map listMap = u3.getRoles(); + Map listMap = u3.getRoles(); Assert.assertEquals(1, listMap.size()); - + + MpRole mpRole = listMap.get("one"); + Assert.assertNotNull(mpRole); + Assert.assertEquals(Long.valueOf(47L), mpRole.getOrganizationId()); + Assert.assertEquals("one", mpRole.getCode()); + } } diff --git a/src/test/java/com/avaje/tests/text/csv/TestCsvReader.java b/src/test/java/com/avaje/tests/text/csv/TestCsvReader.java index 10028e26f..923cd4c1b 100644 --- a/src/test/java/com/avaje/tests/text/csv/TestCsvReader.java +++ b/src/test/java/com/avaje/tests/text/csv/TestCsvReader.java @@ -35,7 +35,7 @@ public class TestCsvReader extends BaseTestCase { csvReader.addDateTime("anniversary", "dd-MMM-yyyy", Locale.GERMAN); csvReader.addProperty("billingAddress.line1"); csvReader.addProperty("billingAddress.city"); - csvReader.addReference("billingAddress.country.code"); + csvReader.addProperty("billingAddress.country.code"); csvReader.process(reader); diff --git a/src/test/java/com/avaje/tests/text/json/TestTextJsonReferenceBean.java b/src/test/java/com/avaje/tests/text/json/TestTextJsonReferenceBean.java index 922b1004d..7c20c4a7c 100644 --- a/src/test/java/com/avaje/tests/text/json/TestTextJsonReferenceBean.java +++ b/src/test/java/com/avaje/tests/text/json/TestTextJsonReferenceBean.java @@ -8,8 +8,12 @@ import org.junit.Test; import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.BeanState; import com.avaje.ebean.Ebean; +import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.text.json.JsonContext; import com.avaje.ebean.text.json.JsonWriteOptions; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.tests.model.basic.Customer; import com.avaje.tests.model.basic.Order; import com.avaje.tests.model.basic.Product; import com.avaje.tests.model.basic.ResetBasicData; @@ -21,6 +25,8 @@ public class TestTextJsonReferenceBean extends BaseTestCase { ResetBasicData.reset(); + SpiEbeanServer server = (SpiEbeanServer)Ebean.getServer(null); + JsonContext jsonContext = Ebean.createJsonContext(); Product product = Ebean.getReference(Product.class, 1); @@ -36,9 +42,10 @@ public class TestTextJsonReferenceBean extends BaseTestCase { Product refProd = jsonContext.toBean(Product.class, jsonString); - BeanState beanState = Ebean.getBeanState(refProd); - Assert.assertTrue(beanState.isReference()); - + BeanDescriptor prodDesc = server.getBeanDescriptor(Product.class); + EntityBean eb = (EntityBean)refProd; + prodDesc.isReference(eb._ebean_getIntercept()); + String name = refProd.getName(); Assert.assertNotNull(name); } @@ -58,9 +65,12 @@ public class TestTextJsonReferenceBean extends BaseTestCase { System.out.println(jsonOrder); Order o2 = jsonContext.toBean(Order.class, jsonOrder); + Customer customer = o2.getCustomer(); + + BeanDescriptor custDesc = server.getBeanDescriptor(Customer.class); + + Assert.assertTrue(custDesc.isReference(((EntityBean)customer)._ebean_getIntercept())); - BeanState beanStateCust = Ebean.getBeanState(o2.getCustomer()); - Assert.assertTrue(beanStateCust.isReference()); } diff --git a/src/test/java/com/avaje/tests/transaction/TransactionNotTerminatedAfterRollback.java b/src/test/java/com/avaje/tests/transaction/TransactionNotTerminatedAfterRollback.java index 4616172d3..686488a4d 100644 --- a/src/test/java/com/avaje/tests/transaction/TransactionNotTerminatedAfterRollback.java +++ b/src/test/java/com/avaje/tests/transaction/TransactionNotTerminatedAfterRollback.java @@ -49,7 +49,7 @@ public class TransactionNotTerminatedAfterRollback { } } - @Entity public class User { + @Entity public static class User { @Id Long id; String name; diff --git a/src/test/java/com/avaje/tests/update/TestStatelessUpdate.java b/src/test/java/com/avaje/tests/update/TestStatelessUpdate.java index fa408c547..d1cd9b514 100644 --- a/src/test/java/com/avaje/tests/update/TestStatelessUpdate.java +++ b/src/test/java/com/avaje/tests/update/TestStatelessUpdate.java @@ -25,18 +25,11 @@ public class TestStatelessUpdate extends BaseTestCase { server.save(e); - // EBasic updateName = new EBasic(); - // updateName.setId(e.getId()); - // updateName.setName("justName"); - // - // - // server.update(updateName, null, null, false, false); - EBasic updateAll = new EBasic(); updateAll.setId(e.getId()); updateAll.setName("updAllProps"); - server.update(updateAll, null, null, false, true); + server.update(updateAll, null, false); EBasic updateDeflt = new EBasic(); updateDeflt.setId(e.getId()); diff --git a/src/test/resources/META-INF/ebean-orm.xml b/src/test/resources/META-INF/ebean-orm.xml index 787ae34a6..e2c1407ec 100644 --- a/src/test/resources/META-INF/ebean-orm.xml +++ b/src/test/resources/META-INF/ebean-orm.xml @@ -54,14 +54,4 @@ - - - - - - select id, value from No_Id_Entity_Rob - - - - \ No newline at end of file