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 7885014b6..5c1b5aaa1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -32,798 +32,795 @@ import java.util.List; */ public class BeanPropertyAssocOne extends BeanPropertyAssoc { - private final boolean oneToOne; + private final boolean oneToOne; - private final boolean oneToOneExported; + private final boolean oneToOneExported; - private final boolean importedPrimaryKey; + private final boolean importedPrimaryKey; - private final LocalHelp localHelp; + private final LocalHelp localHelp; - private final BeanProperty[] embeddedProps; + private final BeanProperty[] embeddedProps; - private final HashMap embeddedPropsMap; - - /** - * The information for Imported foreign Keys. - */ - private ImportedId importedId; - - private ExportedProperty[] exportedProperties; - - private String deleteByParentIdSql; - private String deleteByParentIdInSql; - BeanPropertyAssocMany relationshipProperty; + private final HashMap embeddedPropsMap; - /** - * Create based on deploy information of an EmbeddedId. - */ - public BeanPropertyAssocOne(BeanDescriptorMap owner, DeployBeanPropertyAssocOne deploy) { - this(owner, null, deploy); + /** + * The information for Imported foreign Keys. + */ + private ImportedId importedId; + + private ExportedProperty[] exportedProperties; + + private String deleteByParentIdSql; + private String deleteByParentIdInSql; + BeanPropertyAssocMany relationshipProperty; + + /** + * Create based on deploy information of an EmbeddedId. + */ + public BeanPropertyAssocOne(BeanDescriptorMap owner, DeployBeanPropertyAssocOne deploy) { + this(owner, null, deploy); + } + + /** + * Create the property. + */ + public BeanPropertyAssocOne(BeanDescriptorMap owner, BeanDescriptor descriptor, + DeployBeanPropertyAssocOne deploy) { + + super(owner, descriptor, deploy); + + importedPrimaryKey = deploy.isImportedPrimaryKey(); + oneToOne = deploy.isOneToOne(); + oneToOneExported = deploy.isOneToOneExported(); + + if (embedded) { + // Overriding of the columns and use table alias of owning BeanDescriptor + BeanEmbeddedMeta overrideMeta = BeanEmbeddedMetaFactory.create(owner, deploy, descriptor); + embeddedProps = overrideMeta.getProperties(); + embeddedPropsMap = new HashMap(); + for (int i = 0; i < embeddedProps.length; i++) { + embeddedPropsMap.put(embeddedProps[i].getName(), embeddedProps[i]); + } + + } else { + embeddedProps = null; + embeddedPropsMap = null; + } + localHelp = createHelp(embedded, oneToOneExported); + } + + @Override + public void initialise() { + super.initialise(); + if (!isTransient) { + if (embedded) { + // no imported or exported information + } else if (!oneToOneExported) { + importedId = createImportedId(this, targetDescriptor, tableJoin); + } else { + exportedProperties = createExported(); + + String delStmt = "delete from " + targetDescriptor.getBaseTable() + " where "; + + deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false); + deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true); + + } + } + } + + /** + * Return the property value as an entity bean. + */ + public EntityBean getValueAsEntityBean(EntityBean owner) { + return (EntityBean) getValue(owner); + } + + public void setRelationshipProperty(BeanPropertyAssocMany relationshipProperty) { + this.relationshipProperty = relationshipProperty; + } + + public BeanPropertyAssocMany getRelationshipProperty() { + return relationshipProperty; + } + + public void cacheClear() { + if (targetDescriptor.isBeanCaching() && relationshipProperty != null) { + targetDescriptor.cacheManyPropClear(relationshipProperty.getName()); + } + } + + public void cacheDelete(boolean clearOnNull, EntityBean bean) { + if (targetDescriptor.isBeanCaching() && relationshipProperty != null) { + Object assocBean = getValue(bean); + if (assocBean != null) { + Object parentId = targetDescriptor.getId((EntityBean) assocBean); + if (parentId != null) { + targetDescriptor.cacheManyPropRemove(parentId, relationshipProperty.getName()); + return; + } + } + if (clearOnNull) { + targetDescriptor.cacheManyPropClear(relationshipProperty.getName()); + } + } + } + + public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { + + if (embedded) { + BeanProperty embProp = embeddedPropsMap.get(remainder); + if (embProp == null) { + String msg = "Embedded Property " + remainder + " not found in " + getFullBeanName(); + throw new PersistenceException(msg); + } + if (chain == null) { + chain = new ElPropertyChainBuilder(true, propName); + } + chain.add(this); + return chain.add(embProp).build(); } - /** - * Create the property. - */ - public BeanPropertyAssocOne(BeanDescriptorMap owner, BeanDescriptor descriptor, - DeployBeanPropertyAssocOne deploy) { + return createElPropertyValue(propName, remainder, chain, propertyDeploy); + } - super(owner, descriptor, deploy); + @Override + public String getElPlaceholder(boolean encrypted) { + return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; + } - importedPrimaryKey = deploy.isImportedPrimaryKey(); - oneToOne = deploy.isOneToOne(); - oneToOneExported = deploy.isOneToOneExported(); + public SqlUpdate deleteByParentId(Object parentId, List parentIdist) { + if (parentId != null) { + return deleteByParentId(parentId); + } else { + return deleteByParentIdList(parentIdist); + } + } - if (embedded) { - // Overriding of the columns and use table alias of owning BeanDescriptor - BeanEmbeddedMeta overrideMeta = BeanEmbeddedMetaFactory.create(owner, deploy, descriptor); - embeddedProps = overrideMeta.getProperties(); - embeddedPropsMap = new HashMap(); - for (int i = 0; i < embeddedProps.length; i++) { - embeddedPropsMap.put(embeddedProps[i].getName(), embeddedProps[i]); - } + private SqlUpdate deleteByParentIdList(List parentIdist) { - } else { - embeddedProps = null; - embeddedPropsMap = null; + StringBuilder sb = new StringBuilder(100); + sb.append(deleteByParentIdInSql); + + String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size()); + sb.append(inClause); + + DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); + for (int i = 0; i < parentIdist.size(); i++) { + targetIdBinder.bindId(delete, parentIdist.get(i)); + } + + return delete; + } + + private SqlUpdate deleteByParentId(Object parentId) { + + DefaultSqlUpdate delete = new DefaultSqlUpdate(deleteByParentIdSql); + if (exportedProperties.length == 1) { + delete.addParameter(parentId); + } else { + targetDescriptor.getIdBinder().bindId(delete, parentId); + } + return delete; + } + + public List findIdsByParentId(Object parentId, List parentIdist, Transaction t) { + if (parentId != null) { + return findIdsByParentId(parentId, t); + } else { + return findIdsByParentIdList(parentIdist, t); + } + } + + private List findIdsByParentId(Object parentId, Transaction t) { + + String rawWhere = deriveWhereParentIdSql(false); + + EbeanServer server = getBeanDescriptor().getEbeanServer(); + Query q = server.find(getPropertyType()) + .where().raw(rawWhere).query(); + + bindWhereParendId(q, parentId); + return server.findIds(q, t); + } + + private List findIdsByParentIdList(List parentIdist, Transaction t) { + + String rawWhere = deriveWhereParentIdSql(true); + String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size()); + + String expr = rawWhere + inClause; + + EbeanServer server = getBeanDescriptor().getEbeanServer(); + Query q = (Query) server.find(getPropertyType()) + .where().raw(expr); + + for (int i = 0; i < parentIdist.size(); i++) { + bindWhereParendId(q, parentIdist.get(i)); + } + + return server.findIds(q, t); + } + + private void bindWhereParendId(Query q, Object parentId) { + + if (exportedProperties.length == 1) { + q.setParameter(1, parentId); + + } else { + int pos = 1; + EntityBean parent = (EntityBean) parentId; + for (int i = 0; i < exportedProperties.length; i++) { + Object embVal = exportedProperties[i].getValue(parent); + q.setParameter(pos++, embVal); + } + } + } + + public void addFkey() { + if (importedId != null) { + importedId.addFkeys(name); + } + } + + /** + * Return meta data for the deployment of the embedded bean specific to this + * property. + */ + public BeanProperty[] getProperties() { + return embeddedProps; + } + + @Override + public void buildRawSqlSelectChain(String prefix, List selectChain) { + + prefix = SplitName.add(prefix, name); + + if (!embedded) { + InheritInfo inheritInfo = targetDescriptor.getInheritInfo(); + if (inheritInfo != null) { + // expect the discriminator column to be included in order + // to determine the inheritance type so we add it to the + // selectChain (so that it takes a position in the resultSet) + String discriminatorColumn = inheritInfo.getDiscriminatorColumn(); + String discProperty = prefix + "." + discriminatorColumn; + selectChain.add(discProperty); + } + targetIdBinder.buildRawSqlSelectChain(prefix, selectChain); + + } else { + for (int i = 0; i < embeddedProps.length; i++) { + embeddedProps[i].buildRawSqlSelectChain(prefix, selectChain); + } + } + } + + /** + * Return true if this a OneToOne property. Otherwise assumed ManyToOne. + */ + public boolean isOneToOne() { + return oneToOne; + } + + /** + * Return true if this is the exported side of a OneToOne. + */ + public boolean isOneToOneExported() { + return oneToOneExported; + } + + /** + * If true this bean maps to the primary key. + */ + public boolean isImportedPrimaryKey() { + return importedPrimaryKey; + } + + /** + * Same as getPropertyType(). Return the type of the bean this property + * represents. + */ + public Class getTargetType() { + return getPropertyType(); + } + + public Object getCacheDataValue(EntityBean bean) { + Object ap = getValue(bean); + if (ap == null) { + return null; + } + if (embedded) { + return targetDescriptor.cacheBeanExtractData((EntityBean) ap); + + } else { + return targetDescriptor.getId((EntityBean) ap); + } + } + + @Override + public void setCacheDataValue(EntityBean bean, Object cacheData) { + if (cacheData != null) { + if (embedded) { + EntityBean embeddedBean = targetDescriptor.createEntityBean(); + targetDescriptor.cacheBeanLoadData(embeddedBean, (CachedBeanData) cacheData); + setValue(bean, embeddedBean); + + } else { + T ref = targetDescriptor.createReference(Boolean.FALSE, cacheData); + setValue(bean, ref); + } + } + } + + /** + * Return the Id values from the given bean. + */ + @Override + public Object[] getAssocOneIdValues(EntityBean bean) { + return targetDescriptor.getIdBinder().getIdValues(bean); + } + + /** + * Return the Id expression to add to where clause etc. + */ + public String getAssocOneIdExpr(String prefix, String operator) { + return targetDescriptor.getIdBinder().getAssocOneIdExpr(prefix, operator); + } + + /** + * Return the logical id value expression taking into account embedded id's. + */ + @Override + public String getAssocIdInValueExpr(int size) { + return targetDescriptor.getIdBinder().getIdInValueExpr(size); + } + + /** + * Return the logical id in expression taking into account embedded id's. + */ + @Override + public String getAssocIdInExpr(String prefix) { + return targetDescriptor.getIdBinder().getAssocIdInExpr(prefix); + } + + @Override + public boolean isAssocId() { + return !embedded; + } + + @Override + public boolean isAssocProperty() { + return !embedded; + } + + + /** + * Create a bean of the target type to be used as an embeddedId + * value. + */ + public Object createEmbeddedId() { + return getTargetDescriptor().createEntityBean(); + } + + @Override + public Object elGetReference(EntityBean bean) { + Object value = getValueIntercept(bean); + if (value == null) { + value = targetDescriptor.createEntityBean(); + setValueIntercept(bean, value); + } + return value; + } + + public ImportedId getImportedId() { + return importedId; + } + + private String deriveWhereParentIdSql(boolean inClause) { + + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < exportedProperties.length; i++) { + String fkColumn = exportedProperties[i].getForeignDbColumn(); + if (i > 0) { + String s = inClause ? "," : " and "; + sb.append(s); + } + sb.append(fkColumn); + if (!inClause) { + sb.append("=? "); + } + } + return sb.toString(); + } + + /** + * Create the array of ExportedProperty used to build reference objects. + */ + private ExportedProperty[] createExported() { + + BeanProperty idProp = descriptor.getIdProperty(); + + ArrayList list = new ArrayList(); + + if (idProp != null && idProp.isEmbedded()) { + + BeanPropertyAssocOne one = (BeanPropertyAssocOne) idProp; + BeanDescriptor targetDesc = one.getTargetDescriptor(); + BeanProperty[] emIds = targetDesc.propertiesBaseScalar(); + try { + for (int i = 0; i < emIds.length; i++) { + ExportedProperty expProp = findMatch(true, emIds[i]); + list.add(expProp); } - localHelp = createHelp(embedded, oneToOneExported); + } catch (PersistenceException e) { + // not found as individual scalar properties + e.printStackTrace(); + } + + } else { + if (idProp != null) { + ExportedProperty expProp = findMatch(false, idProp); + list.add(expProp); + } + } + + return list.toArray(new ExportedProperty[list.size()]); + } + + /** + * Find the matching foreignDbColumn for a given local property. + */ + private ExportedProperty findMatch(boolean embeddedProp, BeanProperty prop) { + + String matchColumn = prop.getDbColumn(); + + String searchTable = tableJoin.getTable(); + TableJoinColumn[] columns = tableJoin.columns(); + + for (int i = 0; i < columns.length; i++) { + String matchTo = columns[i].getLocalDbColumn(); + + if (matchColumn.equalsIgnoreCase(matchTo)) { + String foreignCol = columns[i].getForeignDbColumn(); + return new ExportedProperty(embeddedProp, foreignCol, prop); + } + } + + String msg = "Error with the Join on [" + getFullBeanName() + + "]. Could not find the matching foreign key for [" + matchColumn + "] in table[" + searchTable + "]?" + + " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?"; + throw new PersistenceException(msg); + } + + + @Override + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + if (!isTransient) { + localHelp.appendSelect(ctx, subQuery); + } + } + + @Override + public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { + if (!isTransient) { + localHelp.appendFrom(ctx, joinType); + } + } + + @Override + public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { + boolean assignable = (type == null || owningType.isAssignableFrom(type)); + return localHelp.readSet(ctx, bean, assignable); + } + + /** + * Read the data from the resultSet effectively ignoring it and returning null. + */ + @Override + public Object read(DbReadContext ctx) throws SQLException { + // just read the resultSet incrementing the column index + // pass in null for the bean so any data read is ignored + return localHelp.read(ctx); + } + + @Override + public void setValue(EntityBean bean, Object value) { + super.setValue(bean, value); + if (embedded && value instanceof EntityBean) { + EntityBean embedded = (EntityBean) value; + embedded._ebean_getIntercept().setEmbeddedOwner(bean, propertyIndex); + } + } + + @Override + public void setValueIntercept(EntityBean bean, Object value) { + super.setValueIntercept(bean, value); + if (embedded && value instanceof EntityBean) { + EntityBean embedded = (EntityBean) value; + embedded._ebean_getIntercept().setEmbeddedOwner(bean, propertyIndex); + } + } + + @Override + public void loadIgnore(DbReadContext ctx) { + localHelp.loadIgnore(ctx); + } + + @Override + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + Object dbVal = sqlBeanLoad.load(this); + if (embedded && sqlBeanLoad.isLazyLoad()) { + if (dbVal instanceof EntityBean) { + ((EntityBean) dbVal)._ebean_getIntercept().setLoaded(); + } + } + } + + private LocalHelp createHelp(boolean embedded, boolean oneToOneExported) { + if (embedded) { + return new Embedded(); + } else if (oneToOneExported) { + return new ReferenceExported(); + } else { + return new Reference(this); + } + } + + /** + * Local interface to handle Embedded, Reference and Reference Exported + * cases. + */ + private abstract class LocalHelp { + + abstract void loadIgnore(DbReadContext ctx); + + abstract Object read(DbReadContext ctx) throws SQLException; + + abstract Object readSet(DbReadContext ctx, EntityBean bean, boolean assignAble) throws SQLException; + + abstract void appendSelect(DbSqlContext ctx, boolean subQuery); + + abstract void appendFrom(DbSqlContext ctx, SqlJoinType joinType); + + } + + private final class Embedded extends LocalHelp { + + void loadIgnore(DbReadContext ctx) { + for (int i = 0; i < embeddedProps.length; i++) { + embeddedProps[i].loadIgnore(ctx); + } } @Override - public void initialise() { - super.initialise(); - if (!isTransient) { - if (embedded) { - // no imported or exported information - } else if (!oneToOneExported) { - importedId = createImportedId(this, targetDescriptor, tableJoin); - } else { - exportedProperties = createExported(); - - String delStmt = "delete from "+targetDescriptor.getBaseTable()+" where "; - - deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false); - deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true); + Object readSet(DbReadContext ctx, EntityBean bean, boolean assignable) throws SQLException { + Object dbVal = read(ctx); + if (bean != null && assignable) { + // set back to the parent bean + setValue(bean, dbVal); + ctx.propagateState(dbVal); + return dbVal; - } - } - } - - /** - * Return the property value as an entity bean. - */ - public EntityBean getValueAsEntityBean(EntityBean owner) { - return (EntityBean)getValue(owner); - } - - public void setRelationshipProperty(BeanPropertyAssocMany relationshipProperty){ - this.relationshipProperty = relationshipProperty; - } - - public BeanPropertyAssocMany getRelationshipProperty() { - return relationshipProperty; - } - - public void cacheClear() { - if (targetDescriptor.isBeanCaching() && relationshipProperty != null) { - targetDescriptor.cacheManyPropClear(relationshipProperty.getName()); - } - } - - public void cacheDelete(boolean clearOnNull, EntityBean bean) { - if (targetDescriptor.isBeanCaching() && relationshipProperty != null) { - Object assocBean = getValue(bean); - if (assocBean != null) { - Object parentId = targetDescriptor.getId((EntityBean)assocBean); - if (parentId != null) { - targetDescriptor.cacheManyPropRemove(parentId, relationshipProperty.getName()); - return; - } - } - if (clearOnNull) { - targetDescriptor.cacheManyPropClear(relationshipProperty.getName()); - } - } - } - - public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { - - if (embedded){ - BeanProperty embProp = embeddedPropsMap.get(remainder); - if (embProp == null){ - String msg = "Embedded Property "+remainder+" not found in "+getFullBeanName(); - throw new PersistenceException(msg); - } - if (chain == null) { - chain = new ElPropertyChainBuilder(true, propName); - } - chain.add(this); - return chain.add(embProp).build(); - } - - return createElPropertyValue(propName, remainder, chain, propertyDeploy); - } - - @Override - public String getElPlaceholder(boolean encrypted) { - return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; - } - - public SqlUpdate deleteByParentId(Object parentId, List parentIdist) { - if (parentId != null){ - return deleteByParentId(parentId); - } else { - return deleteByParentIdList(parentIdist); - } - } - - private SqlUpdate deleteByParentIdList(List parentIdist) { - - StringBuilder sb = new StringBuilder(100); - sb.append(deleteByParentIdInSql); - - String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size()); - sb.append(inClause); - - DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); - for (int i = 0; i < parentIdist.size(); i++) { - targetIdBinder.bindId(delete, parentIdist.get(i)); - } - - return delete; - } - - private SqlUpdate deleteByParentId(Object parentId) { - - DefaultSqlUpdate delete = new DefaultSqlUpdate(deleteByParentIdSql); - if (exportedProperties.length == 1){ - delete.addParameter(parentId); - } else { - targetDescriptor.getIdBinder().bindId(delete, parentId); - } - return delete; - } - - public List findIdsByParentId(Object parentId, List parentIdist, Transaction t) { - if (parentId != null){ - return findIdsByParentId(parentId, t); - } else { - return findIdsByParentIdList(parentIdist, t); - } - } - - private List findIdsByParentId(Object parentId, Transaction t) { - - String rawWhere = deriveWhereParentIdSql(false); - - EbeanServer server = getBeanDescriptor().getEbeanServer(); - Query q = server.find(getPropertyType()) - .where().raw(rawWhere).query(); - - bindWhereParendId(q, parentId); - return server.findIds(q, t); - } - - private List findIdsByParentIdList(List parentIdist, Transaction t) { - - String rawWhere = deriveWhereParentIdSql(true); - String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size()); - - String expr = rawWhere+inClause; - - EbeanServer server = getBeanDescriptor().getEbeanServer(); - Query q = (Query)server.find(getPropertyType()) - .where().raw(expr); - - for (int i = 0; i < parentIdist.size(); i++) { - bindWhereParendId(q, parentIdist.get(i)); - } - - return server.findIds(q, t); - } - - private void bindWhereParendId(Query q, Object parentId) { - - if (exportedProperties.length == 1) { - q.setParameter(1, parentId); - - } else { - int pos = 1; - EntityBean parent = (EntityBean)parentId; - for (int i = 0; i < exportedProperties.length; i++) { - Object embVal = exportedProperties[i].getValue(parent); - q.setParameter(pos++, embVal); - } - } - } - - public void addFkey() { - if (importedId != null) { - importedId.addFkeys(name); - } - } - - /** - * Return meta data for the deployment of the embedded bean specific to this - * property. - */ - public BeanProperty[] getProperties() { - return embeddedProps; - } - - @Override - public void buildRawSqlSelectChain(String prefix, List selectChain) { - - prefix = SplitName.add(prefix, name); - - if (!embedded){ - InheritInfo inheritInfo = targetDescriptor.getInheritInfo(); - if (inheritInfo != null) { - // expect the discriminator column to be included in order - // to determine the inheritance type so we add it to the - // selectChain (so that it takes a position in the resultSet) - String discriminatorColumn = inheritInfo.getDiscriminatorColumn(); - String discProperty = prefix + "." + discriminatorColumn; - selectChain.add(discProperty); - } - targetIdBinder.buildRawSqlSelectChain(prefix, selectChain); - - } else { - for (int i = 0; i < embeddedProps.length; i++) { - embeddedProps[i].buildRawSqlSelectChain(prefix, selectChain); - } - } - } - - /** - * Return true if this a OneToOne property. Otherwise assumed ManyToOne. - */ - public boolean isOneToOne() { - return oneToOne; - } - - /** - * Return true if this is the exported side of a OneToOne. - */ - public boolean isOneToOneExported() { - return oneToOneExported; - } - - /** - * If true this bean maps to the primary key. - */ - public boolean isImportedPrimaryKey() { - return importedPrimaryKey; - } - - /** - * Same as getPropertyType(). Return the type of the bean this property - * represents. - */ - public Class getTargetType() { - return getPropertyType(); - } - - public Object getCacheDataValue(EntityBean bean){ - Object ap = getValue(bean); - if (ap == null){ + } else { return null; } - if (embedded) { - return targetDescriptor.cacheBeanExtractData((EntityBean) ap); - - } else { - return targetDescriptor.getId((EntityBean)ap); - } - } - - @Override - public void setCacheDataValue(EntityBean bean, Object cacheData){ - if (cacheData != null) { - if (embedded){ - EntityBean embeddedBean = targetDescriptor.createEntityBean(); - targetDescriptor.cacheBeanLoadData(embeddedBean, (CachedBeanData) cacheData); - setValue(bean, embeddedBean); - - } else { - T ref = targetDescriptor.createReference(Boolean.FALSE, cacheData); - setValue(bean, ref); - } - } } - /** - * Return the Id values from the given bean. - */ - @Override - public Object[] getAssocOneIdValues(EntityBean bean) { - return targetDescriptor.getIdBinder().getIdValues(bean); - } + Object read(DbReadContext ctx) throws SQLException { - /** - * Return the Id expression to add to where clause etc. - */ - public String getAssocOneIdExpr(String prefix, String operator) { - return targetDescriptor.getIdBinder().getAssocOneIdExpr(prefix, operator); - } - - /** - * Return the logical id value expression taking into account embedded id's. - */ - @Override - public String getAssocIdInValueExpr(int size){ - return targetDescriptor.getIdBinder().getIdInValueExpr(size); - } - - /** - * Return the logical id in expression taking into account embedded id's. - */ - @Override - public String getAssocIdInExpr(String prefix){ - return targetDescriptor.getIdBinder().getAssocIdInExpr(prefix); - } + EntityBean embeddedBean = targetDescriptor.createEntityBean(); - @Override - public boolean isAssocId() { - return !embedded; - } - - @Override - public boolean isAssocProperty() { - return !embedded; - } - - - /** - * Create a bean of the target type to be used as an embeddedId - * value. - */ - public Object createEmbeddedId() { - return getTargetDescriptor().createEntityBean(); - } - - @Override - public Object elGetReference(EntityBean bean) { - Object value = getValueIntercept(bean); - if (value == null) { - value = targetDescriptor.createEntityBean(); - setValueIntercept(bean, value); + boolean notNull = false; + for (int i = 0; i < embeddedProps.length; i++) { + Object value = embeddedProps[i].readSet(ctx, embeddedBean, null); + if (value != null) { + notNull = true; } - return value; - } - - public ImportedId getImportedId() { - return importedId; - } - - private String deriveWhereParentIdSql(boolean inClause) { - - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < exportedProperties.length; i++) { - String fkColumn = exportedProperties[i].getForeignDbColumn(); - if (i > 0){ - String s = inClause ? "," : " and "; - sb.append(s); - } - sb.append(fkColumn); - if (!inClause){ - sb.append("=? "); - } - } - return sb.toString(); - } - - /** - * Create the array of ExportedProperty used to build reference objects. - */ - private ExportedProperty[] createExported() { - - BeanProperty idProp = descriptor.getIdProperty(); - - ArrayList list = new ArrayList(); - - if (idProp != null && idProp.isEmbedded()) { - - BeanPropertyAssocOne one = (BeanPropertyAssocOne) idProp; - BeanDescriptor targetDesc = one.getTargetDescriptor(); - BeanProperty[] emIds = targetDesc.propertiesBaseScalar(); - try { - for (int i = 0; i < emIds.length; i++) { - ExportedProperty expProp = findMatch(true, emIds[i]); - list.add(expProp); - } - } catch (PersistenceException e){ - // not found as individual scalar properties - e.printStackTrace(); - } - - } else { - if (idProp != null) { - ExportedProperty expProp = findMatch(false, idProp); - list.add(expProp); - } - } - - return list.toArray(new ExportedProperty[list.size()]); - } - - /** - * Find the matching foreignDbColumn for a given local property. - */ - private ExportedProperty findMatch(boolean embeddedProp,BeanProperty prop) { - - String matchColumn = prop.getDbColumn(); - - String searchTable = tableJoin.getTable(); - TableJoinColumn[] columns = tableJoin.columns(); - - for (int i = 0; i < columns.length; i++) { - String matchTo = columns[i].getLocalDbColumn(); - - if (matchColumn.equalsIgnoreCase(matchTo)) { - String foreignCol = columns[i].getForeignDbColumn(); - return new ExportedProperty(embeddedProp, foreignCol, prop); - } - } - - String msg = "Error with the Join on ["+getFullBeanName() - +"]. Could not find the matching foreign key for ["+matchColumn+"] in table["+searchTable+"]?" - +" Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?"; - throw new PersistenceException(msg); - } - - - @Override - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - if (!isTransient) { - localHelp.appendSelect(ctx, subQuery); - } - } - - @Override - public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { - if (!isTransient) { - localHelp.appendFrom(ctx, joinType); - } - } - - @Override - public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { - boolean assignable = (type == null || owningType.isAssignableFrom(type)); - return localHelp.readSet(ctx, bean, assignable); - } - - /** - * Read the data from the resultSet effectively ignoring it and returning null. - */ - @Override - public Object read(DbReadContext ctx) throws SQLException { - // just read the resultSet incrementing the column index - // pass in null for the bean so any data read is ignored - return localHelp.read(ctx); - } - - @Override - public void setValue(EntityBean bean, Object value) { - super.setValue(bean, value); - if (embedded && value instanceof EntityBean) { - EntityBean embedded = (EntityBean)value; - embedded._ebean_getIntercept().setEmbeddedOwner(bean, propertyIndex); } - } - - @Override - public void setValueIntercept(EntityBean bean, Object value) { - super.setValueIntercept(bean, value); - if (embedded && value instanceof EntityBean) { - EntityBean embedded = (EntityBean)value; - embedded._ebean_getIntercept().setEmbeddedOwner(bean, propertyIndex); + if (notNull) { + ctx.propagateState(embeddedBean); + return embeddedBean; + } else { + return null; } } @Override - public void loadIgnore(DbReadContext ctx) { - localHelp.loadIgnore(ctx); + void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { } @Override - public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { - Object dbVal = sqlBeanLoad.load(this); - if (embedded && sqlBeanLoad.isLazyLoad()){ - if (dbVal instanceof EntityBean){ - ((EntityBean)dbVal)._ebean_getIntercept().setLoaded(); - } - } + void appendSelect(DbSqlContext ctx, boolean subQuery) { + for (int i = 0; i < embeddedProps.length; i++) { + embeddedProps[i].appendSelect(ctx, subQuery); + } + } + } + + /** + * For imported reference - this is the common case. + */ + private final class Reference extends LocalHelp { + + Reference(BeanPropertyAssocOne beanProp) { } - private LocalHelp createHelp(boolean embedded, boolean oneToOneExported) { - if (embedded) { - return new Embedded(); - } else if (oneToOneExported) { - return new ReferenceExported(); - } else { - return new Reference(this); - } + void loadIgnore(DbReadContext ctx) { + targetIdBinder.loadIgnore(ctx); + if (targetInheritInfo != null) { + ctx.getDataReader().incrementPos(1); + } + } + + Object readSet(DbReadContext ctx, EntityBean bean, boolean assignable) throws SQLException { + Object val = read(ctx); + if (bean != null && assignable) { + setValue(bean, val); + ctx.propagateState(val); + } + return val; } /** - * Local interface to handle Embedded, Reference and Reference Exported - * cases. + * Read and set a Reference bean. */ - private abstract class LocalHelp { + @Override + Object read(DbReadContext ctx) throws SQLException { - abstract void loadIgnore(DbReadContext ctx); + BeanDescriptor rowDescriptor = null; + Class rowType = targetType; + if (targetInheritInfo != null) { + // read discriminator to determine the type + InheritInfo rowInheritInfo = targetInheritInfo.readType(ctx); + if (rowInheritInfo != null) { + rowType = rowInheritInfo.getType(); + rowDescriptor = rowInheritInfo.getBeanDescriptor(); + } + } - abstract Object read(DbReadContext ctx) throws SQLException; + // read the foreign key column(s) + Object id = targetIdBinder.read(ctx); + if (id == null) { + return null; + } - abstract Object readSet(DbReadContext ctx, EntityBean bean, boolean assignAble) throws SQLException; + // check transaction context to see if it already exists + Object existing = ctx.getPersistenceContext().get(rowType, id); - abstract void appendSelect(DbSqlContext ctx, boolean subQuery); + if (existing != null) { + return existing; + } - abstract void appendFrom(DbSqlContext ctx, SqlJoinType joinType); - + Boolean readOnly = ctx.isReadOnly(); + Object ref; + if (targetInheritInfo != null) { + // for inheritance hierarchy create the correct type for this row... + ref = rowDescriptor.createReference(readOnly, id); + } else { + ref = targetDescriptor.createReference(readOnly, id); + } + + Object existingBean = ctx.getPersistenceContext().putIfAbsent(id, ref); + if (existingBean != null) { + // advanced case when we use multiple concurrent threads to + // build a single object graph, and another thread has since + // loaded a matching bean so we will use that instead. + ref = existingBean; + + } else { + EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); + if (Boolean.TRUE.equals(ctx.isReadOnly())) { + ebi.setReadOnly(true); + } + ctx.register(name, ebi); + } + + return ref; } - private final class Embedded extends LocalHelp { - - void loadIgnore(DbReadContext ctx) { - for (int i = 0; i < embeddedProps.length; i++) { - embeddedProps[i].loadIgnore(ctx); - } - } - - @Override - Object readSet(DbReadContext ctx, EntityBean bean, boolean assignable) throws SQLException { - Object dbVal = read(ctx); - if (bean != null && assignable) { - // set back to the parent bean - setValue(bean, dbVal); - ctx.propagateState(dbVal); - return dbVal; - - } else { - return null; - } - } - - Object read(DbReadContext ctx) throws SQLException { - - EntityBean embeddedBean = targetDescriptor.createEntityBean(); - - boolean notNull = false; - for (int i = 0; i < embeddedProps.length; i++) { - Object value = embeddedProps[i].readSet(ctx, embeddedBean, null); - if (value != null) { - notNull = true; - } - } - if (notNull) { - ctx.propagateState(embeddedBean); - return embeddedBean; - } else { - return null; - } - } - - @Override - void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { - } - - @Override - void appendSelect(DbSqlContext ctx, boolean subQuery) { - for (int i = 0; i < embeddedProps.length; i++) { - embeddedProps[i].appendSelect(ctx, subQuery); - } - } + @Override + void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { + if (targetInheritInfo != null) { + // add join to support the discriminator column + String relativePrefix = ctx.getRelativePrefix(name); + tableJoin.addJoin(joinType, relativePrefix, ctx); + } } /** - * For imported reference - this is the common case. + * Append columns for foreign key columns. */ - private final class Reference extends LocalHelp { + @Override + void appendSelect(DbSqlContext ctx, boolean subQuery) { - //private final BeanPropertyAssocOne beanProp; + if (!subQuery && targetInheritInfo != null) { + // add discriminator column + String relativePrefix = ctx.getRelativePrefix(getName()); + String tableAlias = ctx.getTableAlias(relativePrefix); + ctx.appendColumn(tableAlias, targetInheritInfo.getDiscriminatorColumn()); + } + importedId.sqlAppend(ctx); + } + } - Reference(BeanPropertyAssocOne beanProp) { -// this.beanProp = beanProp; - } - - void loadIgnore(DbReadContext ctx) { - targetIdBinder.loadIgnore(ctx); - if (targetInheritInfo != null) { - ctx.getDataReader().incrementPos(1); - } - } + /** + * For OneToOne exported reference - not so common. + */ + private final class ReferenceExported extends LocalHelp { - Object readSet(DbReadContext ctx, EntityBean bean, boolean assignable) throws SQLException { - Object val = read(ctx); - if (bean != null && assignable) { - setValue(bean, val); - ctx.propagateState(val); - } - return val; - } - - /** - * Read and set a Reference bean. - */ - @Override - Object read(DbReadContext ctx) throws SQLException { - - BeanDescriptor rowDescriptor = null; - Class rowType = targetType; - if (targetInheritInfo != null) { - // read discriminator to determine the type - InheritInfo rowInheritInfo = targetInheritInfo.readType(ctx); - if (rowInheritInfo != null) { - rowType = rowInheritInfo.getType(); - rowDescriptor = rowInheritInfo.getBeanDescriptor(); - } - } - - // read the foreign key column(s) - Object id = targetIdBinder.read(ctx); - if (id == null) { - return null; - } - - // check transaction context to see if it already exists - Object existing = ctx.getPersistenceContext().get(rowType, id); - - if (existing != null) { - return existing; - } - - Boolean readOnly = ctx.isReadOnly(); - Object ref; - if (targetInheritInfo != null) { - // for inheritance hierarchy create the correct type for this row... - ref = rowDescriptor.createReference(readOnly, id); - } else { - ref = targetDescriptor.createReference(readOnly, id); - } - - Object existingBean = ctx.getPersistenceContext().putIfAbsent(id, ref); - if (existingBean != null) { - // advanced case when we use multiple concurrent threads to - // build a single object graph, and another thread has since - // loaded a matching bean so we will use that instead. - ref = existingBean; - - } else { - EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); - if (Boolean.TRUE.equals(ctx.isReadOnly())){ - ebi.setReadOnly(true); - } - ctx.register(name, ebi); - } - - return ref; - } - - @Override - void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { - if (targetInheritInfo != null) { - // add join to support the discriminator column - String relativePrefix = ctx.getRelativePrefix(name); - tableJoin.addJoin(joinType, relativePrefix, ctx); - } - } - - /** - * Append columns for foreign key columns. - */ - @Override - void appendSelect(DbSqlContext ctx, boolean subQuery) { - - if (!subQuery && targetInheritInfo != null) { - // add discriminator column - String relativePrefix = ctx.getRelativePrefix(getName()); - String tableAlias = ctx.getTableAlias(relativePrefix); - ctx.appendColumn(tableAlias, targetInheritInfo.getDiscriminatorColumn()); - } - importedId.sqlAppend(ctx); - } + @Override + void loadIgnore(DbReadContext ctx) { + targetDescriptor.getIdBinder().loadIgnore(ctx); } /** - * For OneToOne exported reference - not so common. + * Read and set a Reference bean. */ - private final class ReferenceExported extends LocalHelp { + @Override + Object readSet(DbReadContext ctx, EntityBean bean, boolean assignable) throws SQLException { - @Override - void loadIgnore(DbReadContext ctx) { - targetDescriptor.getIdBinder().loadIgnore(ctx); - } - - /** - * Read and set a Reference bean. - */ - @Override - Object readSet(DbReadContext ctx, EntityBean bean, boolean assignable) throws SQLException { - - Object dbVal = read(ctx); - if (bean != null && assignable) { - setValue(bean, dbVal); - ctx.propagateState(dbVal); - } - return dbVal; - } - - @Override - Object read(DbReadContext ctx) throws SQLException { - - // TODO: Support for Inheritance hierarchy on exported OneToOne ? - IdBinder idBinder = targetDescriptor.getIdBinder(); - Object id = idBinder.read(ctx); - if (id == null) { - return null; - } - - PersistenceContext persistCtx = ctx.getPersistenceContext(); - Object existing = persistCtx.get(targetType, id); - - if (existing != null) { - return existing; - } - Object ref = targetDescriptor.createReference(ctx.isReadOnly(), id); - - EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); - if (Boolean.TRUE.equals(ctx.isReadOnly())) { - ebi.setReadOnly(true); - } - persistCtx.put(id, ref); - ctx.register(name, ebi); - return ref; - } - - /** - * Append columns for foreign key columns. - */ - @Override - void appendSelect(DbSqlContext ctx, boolean subQuery) { - - // set appropriate tableAlias for the exported id columns - - String relativePrefix = ctx.getRelativePrefix(getName()); - ctx.pushTableAlias(relativePrefix); - - IdBinder idBinder = targetDescriptor.getIdBinder(); - idBinder.appendSelect(ctx, subQuery); - - ctx.popTableAlias(); - } - - @Override - void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { - - String relativePrefix = ctx.getRelativePrefix(getName()); - tableJoin.addJoin(joinType, relativePrefix, ctx); - } + Object dbVal = read(ctx); + if (bean != null && assignable) { + setValue(bean, dbVal); + ctx.propagateState(dbVal); + } + return dbVal; } - + + @Override + Object read(DbReadContext ctx) throws SQLException { + + // TODO: Support for Inheritance hierarchy on exported OneToOne ? + IdBinder idBinder = targetDescriptor.getIdBinder(); + Object id = idBinder.read(ctx); + if (id == null) { + return null; + } + + PersistenceContext persistCtx = ctx.getPersistenceContext(); + Object existing = persistCtx.get(targetType, id); + + if (existing != null) { + return existing; + } + Object ref = targetDescriptor.createReference(ctx.isReadOnly(), id); + + EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); + if (Boolean.TRUE.equals(ctx.isReadOnly())) { + ebi.setReadOnly(true); + } + persistCtx.put(id, ref); + ctx.register(name, ebi); + return ref; + } + + /** + * Append columns for foreign key columns. + */ + @Override + void appendSelect(DbSqlContext ctx, boolean subQuery) { + + // set appropriate tableAlias for the exported id columns + + String relativePrefix = ctx.getRelativePrefix(getName()); + ctx.pushTableAlias(relativePrefix); + + IdBinder idBinder = targetDescriptor.getIdBinder(); + idBinder.appendSelect(ctx, subQuery); + + ctx.popTableAlias(); + } + + @Override + void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { + + String relativePrefix = ctx.getRelativePrefix(getName()); + tableJoin.addJoin(joinType, relativePrefix, ctx); + } + } + @Override public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException { @@ -859,22 +856,22 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { } } - public boolean isReference(Object detailBean) { - EntityBean eb = (EntityBean)detailBean; - return targetDescriptor.isReference(eb._ebean_getIntercept()); - } + public boolean isReference(Object detailBean) { + EntityBean eb = (EntityBean) detailBean; + return targetDescriptor.isReference(eb._ebean_getIntercept()); + } - /** - * Set the parent bean to the child bean if it has not already been set. - */ - public void setParentBeanToChild(EntityBean parent, EntityBean child) { + /** + * Set the parent bean to the child bean if it has not already been set. + */ + public void setParentBeanToChild(EntityBean parent, EntityBean child) { - if (mappedBy != null) { - BeanProperty beanProperty = targetDescriptor.getBeanProperty(mappedBy); - if (beanProperty != null && beanProperty.getValue(child) == null) { - // set the 'parent' bean to the 'child' bean - beanProperty.setValue(child, parent); - } + if (mappedBy != null) { + BeanProperty beanProperty = targetDescriptor.getBeanProperty(mappedBy); + if (beanProperty != null && beanProperty.getValue(child) == null) { + // set the 'parent' bean to the 'child' bean + beanProperty.setValue(child, parent); } } + } } 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 45c1b47fd..b657e5958 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 @@ -22,22 +22,22 @@ public interface IdBinder { /** * Initialise the binder. */ - public void initialise(); + void initialise(); /** * Return true if this is a compound key and must use expanded and or form. */ - public boolean isIdInExpandedForm(); + boolean isIdInExpandedForm(); /** * Write the Id value to binary DataOuput. */ - public void writeData(DataOutput dataOutput, Object idValue) throws IOException; + void writeData(DataOutput dataOutput, Object idValue) throws IOException; /** * Read the Id value from the binary DataInput. */ - public Object readData(DataInput dataInput) throws IOException; + Object readData(DataInput dataInput) throws IOException; /** * Return the name(s) of the Id property(s). Comma delimited if there is more @@ -46,36 +46,36 @@ public interface IdBinder { * This can be used to include in a query. *

*/ - public String getIdProperty(); + String getIdProperty(); /** * Return the Id BeanProperty. */ - public BeanProperty getBeanProperty(); + BeanProperty getBeanProperty(); /** * Find a BeanProperty that is mapped to the database column. */ - public BeanProperty findBeanProperty(String dbColumnName); + BeanProperty findBeanProperty(String dbColumnName); /** * Return the number of scalar properties for this id. */ - public int getPropertyCount(); + int getPropertyCount(); /** * Return false if the id is a simple scalar and false if it is embedded or * concatenated. */ - public boolean isComplexId(); + boolean isComplexId(); /** * Return the default order by that may need to be used if the query includes * a many property. */ - public String getDefaultOrderBy(); + String getDefaultOrderBy(); - public String getOrderBy(String pathPrefix, boolean ascending); + String getOrderBy(String pathPrefix, boolean ascending); /** * Return the values as an array of scalar bindable values. @@ -87,12 +87,12 @@ public interface IdBinder { * Added primarily for Query.addWhere().add(Expr.idEq()) support. *

*/ - public Object[] getBindValues(Object idValue); + Object[] getBindValues(Object idValue); /** * Return the id values for a given bean. */ - public Object[] getIdValues(EntityBean bean); + Object[] getIdValues(EntityBean bean); /** * Build a string of the logical expressions. @@ -100,68 +100,68 @@ public interface IdBinder { * Typically used to build a id = ? string. *

*/ - public String getAssocOneIdExpr(String prefix, String operator); + String getAssocOneIdExpr(String prefix, String operator); /** * Return the logical id in expression taking into account embedded id's. */ - public String getAssocIdInExpr(String prefix); + String getAssocIdInExpr(String prefix); /** * Binds an id value to a prepared statement. */ - public void bindId(DataBind dataBind, Object value) throws SQLException; + void bindId(DataBind dataBind, Object value) throws SQLException; /** * Bind the id value to a SqlUpdate statement. */ - public void bindId(DefaultSqlUpdate sqlUpdate, Object value); + void bindId(DefaultSqlUpdate sqlUpdate, Object value); - public void addIdInBindValue(SpiExpressionRequest request, Object value); + void addIdInBindValue(SpiExpressionRequest request, Object value); /** * Return the sql for binding the id using an IN clause. */ - public String getBindIdInSql(String baseTableAlias); + String getBindIdInSql(String baseTableAlias); /** * Return the binding expression (like "?" or "(?,?)")for the Id. */ - public String getIdInValueExpr(int size); + String getIdInValueExpr(int size); /** * Same as getIdInValueExpr but for delete by id. */ - public String getIdInValueExprDelete(int size); + String getIdInValueExprDelete(int size); - public void buildRawSqlSelectChain(String prefix, List selectChain); + void buildRawSqlSelectChain(String prefix, List selectChain); /** * Read the id value from the result set and set it to the bean also returning * it. */ - public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException; + Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException; /** * Ignore the appropriate number of scalar properties for this id. */ - public void loadIgnore(DbReadContext ctx); + void loadIgnore(DbReadContext ctx); /** * Read the id value from the result set and return it. */ - public Object read(DbReadContext ctx) throws SQLException; + Object read(DbReadContext ctx) throws SQLException; /** * Append to the select clause. */ - public void appendSelect(DbSqlContext ctx, boolean subQuery); + void appendSelect(DbSqlContext ctx, boolean subQuery); /** * Return the sql for binding the id to. This includes table alias and columns * that make up the id. */ - public String getBindIdSql(String baseTableAlias); + String getBindIdSql(String baseTableAlias); /** * Cast or convert the Id value if necessary and optionally set it. @@ -174,6 +174,6 @@ public interface IdBinder { * If the bean is not null, then the value is set to the bean. *

*/ - public Object convertSetId(Object idValue, EntityBean bean); + Object convertSetId(Object idValue, EntityBean bean); }