From 89db75e8c53456b9cf8fb2bc2fa79a5c70dbdc1c Mon Sep 17 00:00:00 2001 From: rbygrave Date: Sat, 9 May 2015 01:08:33 +1200 Subject: [PATCH] No effective change - change newline char --- .../server/deploy/BeanCascadeInfo.java | 244 +- .../server/deploy/BeanDescriptor.java | 3966 ++++++++--------- .../server/deploy/BeanDescriptorManager.java | 2876 ++++++------ .../server/deploy/BeanDescriptorMap.java | 74 +- .../server/deploy/BeanEmbeddedMeta.java | 38 +- .../deploy/BeanEmbeddedMetaFactory.java | 106 +- .../server/deploy/BeanFinderManager.java | 52 +- .../server/deploy/BeanForeignKey.java | 112 +- .../server/deploy/BeanListHelp.java | 302 +- .../server/deploy/BeanManager.java | 66 +- .../server/deploy/BeanManagerFactory.java | 54 +- .../server/deploy/BeanMapHelp.java | 368 +- .../server/deploy/BeanProperty.java | 2236 +++++----- .../server/deploy/BeanProperty.java.orig | 2502 +++++------ .../server/deploy/BeanPropertyAssoc.java | 744 ++-- .../server/deploy/BeanPropertyAssocMany.java | 1768 ++++---- .../server/deploy/BeanPropertyAssocOne.java | 1742 ++++---- .../server/deploy/BeanPropertyCompound.java | 396 +- .../deploy/BeanPropertyCompoundRoot.java | 220 +- .../deploy/BeanPropertyCompoundScalar.java | 210 +- .../server/deploy/BeanPropertyOverride.java | 90 +- .../deploy/BeanPropertySimpleCollection.java | 30 +- .../deploy/BeanQueryAdapterManager.java | 86 +- .../server/deploy/BeanSetHelp.java | 298 +- .../server/deploy/BeanTable.java | 234 +- .../deploy/CompoundUniqueContraint.java | 42 +- .../server/deploy/CopyContext.java | 128 +- .../server/deploy/DRawSqlColumnInfo.java | 76 +- .../deploy/DefaultBeanFinderManager.java | 122 +- .../server/deploy/DeployOrmXml.java | 342 +- .../server/deploy/DetermineManyType.java | 54 +- .../server/deploy/ExportedProperty.java | 106 +- .../server/deploy/InheritInfo.java | 688 +-- .../ebeaninternal/server/deploy/ManyType.java | 118 +- .../deploy/PersistControllerManager.java | 86 +- .../server/deploy/PersistListenerManager.java | 90 +- .../server/deploy/TableJoin.java | 406 +- .../server/deploy/TableJoinColumn.java | 166 +- .../generatedproperty/CounterFactory.java | 128 +- .../generatedproperty/GeneratedCounter.java | 116 +- .../GeneratedCounterInteger.java | 106 +- .../GeneratedCounterLong.java | 106 +- .../GeneratedInsertDate.java | 100 +- .../GeneratedInsertLong.java | 96 +- .../GeneratedInsertTimestamp.java | 100 +- .../generatedproperty/GeneratedProperty.java | 94 +- .../GeneratedPropertyFactory.java | 130 +- .../GeneratedUpdateDate.java | 102 +- .../GeneratedUpdateLong.java | 96 +- .../GeneratedUpdateTimestamp.java | 100 +- .../InsertTimestampFactory.java | 122 +- .../UpdateTimestampFactory.java | 120 +- .../server/deploy/id/IdBinderEmbedded.java | 814 ++-- .../server/deploy/id/IdBinderFactory.java | 72 +- .../server/deploy/id/IdBinderSimple.java | 434 +- .../deploy/meta/DeployBeanDescriptor.java | 1826 ++++---- .../deploy/meta/DeployBeanEmbedded.java | 84 +- .../deploy/meta/DeployBeanProperty.java | 1902 ++++---- .../deploy/meta/DeployBeanPropertyAssoc.java | 312 +- .../meta/DeployBeanPropertyAssocMany.java | 388 +- .../meta/DeployBeanPropertyAssocOne.java | 190 +- .../meta/DeployBeanPropertyCompound.java | 238 +- .../deploy/meta/DeployBeanPropertyLists.java | 774 ++-- .../DeployBeanPropertySimpleCollection.java | 58 +- .../server/deploy/meta/DeployBeanTable.java | 180 +- .../server/deploy/meta/DeployTableJoin.java | 424 +- .../deploy/meta/DeployTableJoinColumn.java | 356 +- .../deploy/parse/AnnotationAssocManys.java | 638 +-- .../deploy/parse/AnnotationAssocOnes.java | 426 +- .../deploy/parse/AnnotationBeanTable.java | 62 +- .../server/deploy/parse/AnnotationClass.java | 338 +- .../server/deploy/parse/AnnotationFields.java | 854 ++-- .../server/deploy/parse/AnnotationParser.java | 154 +- .../server/deploy/parse/AnnotationSql.java | 82 +- .../server/deploy/parse/DeployBeanInfo.java | 156 +- .../deploy/parse/DeployCreateProperties.java | 854 ++-- .../server/deploy/parse/DeployInherit.java | 330 +- .../deploy/parse/DeployInheritInfo.java | 526 +-- .../server/deploy/parse/DeployUtil.java | 456 +- .../server/deploy/parse/ReadAnnotations.java | 116 +- .../deploy/parse/TransientProperties.java | 102 +- 81 files changed, 17950 insertions(+), 17950 deletions(-) diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCascadeInfo.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCascadeInfo.java index 857dc4d0c..23b3aff13 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCascadeInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCascadeInfo.java @@ -1,122 +1,122 @@ -package com.avaje.ebeaninternal.server.deploy; - -import javax.persistence.CascadeType; - -/** - * Persist info for determining if save or delete should be performed. - *

- * This is set to associated Beans, Table joins and List. - *

- */ -public class BeanCascadeInfo { - - /** - * should delete cascade. - */ - boolean delete; - - /** - * Should save cascade. - */ - boolean save; - - /** - * Should validate cascade. - */ - boolean validate; - - /** - * Set the raw deployment attribute. - */ - public void setAttribute(String attr) { - if (attr == null){ - return; - } - attr = attr.toLowerCase(); - delete = (attr.indexOf("delete")>-1); - if (!delete){ - // same as EJB3 remove - delete = (attr.indexOf("remove")>-1); - } - save = (attr.indexOf("save")>-1); - if (!save){ - // same as EJB3 persist - save = (attr.indexOf("persist")>-1); - } - if (attr.indexOf("validate")>-1){ - validate = true; - } - - if (attr.indexOf("all")>-1){ - delete = true; - save = true; - validate = true; - } - } - - public void setTypes(CascadeType[] types) { - for (int i = 0; i < types.length; i++) { - setType(types[i]); - } - } - - private void setType(CascadeType type) { - if (type.equals(CascadeType.ALL)){ - save = true; - delete = true; - } - if (type.equals(CascadeType.REMOVE)){ - delete = true; - } - if (type.equals(CascadeType.PERSIST)){ - save = true; - } - if (type.equals(CascadeType.MERGE)){ - save = true; - } - if (save || delete){ - validate = true; - } - } - - /** - * Return true if delete should cascade. - */ - public boolean isDelete() { - return delete; - } - /** - * Set to true if delete should cascade. - */ - public void setDelete(boolean isDelete) { - this.delete = isDelete; - } - /** - * Return true if save should cascade. - */ - public boolean isSave() { - return save; - } - - /** - * Set to true if save should cascade. - */ - public void setSave(boolean isUpdate) { - this.save = isUpdate; - } - - /** - * Return true if validate should be cascaded. - */ - public boolean isValidate() { - return validate; - } - - /** - * Set validate to cascade or not. - */ - public void setValidate(boolean isValidate) { - this.validate = isValidate; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import javax.persistence.CascadeType; + +/** + * Persist info for determining if save or delete should be performed. + *

+ * This is set to associated Beans, Table joins and List. + *

+ */ +public class BeanCascadeInfo { + + /** + * should delete cascade. + */ + boolean delete; + + /** + * Should save cascade. + */ + boolean save; + + /** + * Should validate cascade. + */ + boolean validate; + + /** + * Set the raw deployment attribute. + */ + public void setAttribute(String attr) { + if (attr == null){ + return; + } + attr = attr.toLowerCase(); + delete = (attr.indexOf("delete")>-1); + if (!delete){ + // same as EJB3 remove + delete = (attr.indexOf("remove")>-1); + } + save = (attr.indexOf("save")>-1); + if (!save){ + // same as EJB3 persist + save = (attr.indexOf("persist")>-1); + } + if (attr.indexOf("validate")>-1){ + validate = true; + } + + if (attr.indexOf("all")>-1){ + delete = true; + save = true; + validate = true; + } + } + + public void setTypes(CascadeType[] types) { + for (int i = 0; i < types.length; i++) { + setType(types[i]); + } + } + + private void setType(CascadeType type) { + if (type.equals(CascadeType.ALL)){ + save = true; + delete = true; + } + if (type.equals(CascadeType.REMOVE)){ + delete = true; + } + if (type.equals(CascadeType.PERSIST)){ + save = true; + } + if (type.equals(CascadeType.MERGE)){ + save = true; + } + if (save || delete){ + validate = true; + } + } + + /** + * Return true if delete should cascade. + */ + public boolean isDelete() { + return delete; + } + /** + * Set to true if delete should cascade. + */ + public void setDelete(boolean isDelete) { + this.delete = isDelete; + } + /** + * Return true if save should cascade. + */ + public boolean isSave() { + return save; + } + + /** + * Set to true if save should cascade. + */ + public void setSave(boolean isUpdate) { + this.save = isUpdate; + } + + /** + * Return true if validate should be cascaded. + */ + public boolean isValidate() { + return validate; + } + + /** + * Set validate to cascade or not. + */ + public void setValidate(boolean isValidate) { + this.validate = isValidate; + } + +} 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 551cdec64..ccd212e8d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1,1983 +1,1983 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebean.SqlUpdate; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.annotation.ConcurrencyMode; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebean.config.EncryptKey; -import com.avaje.ebean.config.dbplatform.IdGenerator; -import com.avaje.ebean.config.dbplatform.IdType; -import com.avaje.ebean.event.BeanFinder; -import com.avaje.ebean.event.BeanPersistController; -import com.avaje.ebean.event.BeanPersistListener; -import com.avaje.ebean.event.BeanQueryAdapter; -import com.avaje.ebean.meta.MetaBeanInfo; -import com.avaje.ebean.meta.MetaQueryPlanStatistic; -import com.avaje.ebeaninternal.api.*; -import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; -import com.avaje.ebeaninternal.server.cache.CachedBeanData; -import com.avaje.ebeaninternal.server.cache.CachedManyIds; -import com.avaje.ebeaninternal.server.core.CacheOptions; -import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.id.IdBinder; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyLists; -import com.avaje.ebeaninternal.server.el.*; -import com.avaje.ebeaninternal.server.query.CQueryPlan; -import com.avaje.ebeaninternal.server.query.CQueryPlanStats.Snapshot; -import com.avaje.ebeaninternal.server.query.SplitName; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; -import com.avaje.ebeaninternal.server.text.json.WriteJson; -import com.avaje.ebeaninternal.server.type.DataBind; -import com.avaje.ebeaninternal.server.type.TypeManager; -import com.avaje.ebeaninternal.util.SortByClause; -import com.avaje.ebeaninternal.util.SortByClause.Property; -import com.avaje.ebeaninternal.util.SortByClauseParser; -import com.fasterxml.jackson.core.JsonParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.persistence.PersistenceException; -import java.io.IOException; -import java.lang.reflect.Modifier; -import java.sql.SQLException; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Describes Beans including their deployment information. - */ -public class BeanDescriptor implements MetaBeanInfo { - - private static final Logger logger = LoggerFactory.getLogger(BeanDescriptor.class); - - private final ConcurrentHashMap updatePlanCache = new ConcurrentHashMap(); - - private final ConcurrentHashMap queryPlanCache = new ConcurrentHashMap(); - - private final ConcurrentHashMap elCache = new ConcurrentHashMap(); - - private final ConcurrentHashMap elDeployCache = new ConcurrentHashMap(); - - private final ConcurrentHashMap> comparatorCache = new ConcurrentHashMap>(); - - public enum EntityType { - ORM, EMBEDDED, SQL - } - - /** - * The EbeanServer name. Same as the plugin name. - */ - private final String serverName; - - /** - * The nature/type of this bean. - */ - private final EntityType entityType; - - /** - * Type of Identity generation strategy used. - */ - private final IdType idType; - - private final IdGenerator idGenerator; - - /** - * The database sequence name (optional). - */ - private final String sequenceName; - - private final int sequenceInitialValue; - - private final int sequenceAllocationSize; - - /** - * SQL used to return last inserted id. Used for Identity columns where - * getGeneratedKeys is not supported. - */ - private final String selectLastInsertedId; - - private final boolean autoFetchTunable; - - private final String lazyFetchIncludes; - - /** - * The concurrency mode for beans of this type. - */ - private final ConcurrencyMode concurrencyMode; - - /** - * The tables this bean is dependent on. - */ - private final String[] dependantTables; - - private final CompoundUniqueContraint[] compoundUniqueConstraints; - - /** - * The base database table. - */ - private final String baseTable; - - /** - * Map of BeanProperty Linked so as to preserve order. - */ - private final LinkedHashMap propMap; - private final LinkedHashMap propMapByDbColumn; - - /** - * The type of bean this describes. - */ - private final Class beanType; - - /** - * This is not sent to a remote client. - */ - private final BeanDescriptorMap owner; - - - private final String[] properties; - - private final int propertyCount; - - - /** - * Intercept pre post on insert,update,delete and postLoad(). Server side - * only. - */ - private volatile BeanPersistController persistController; - - /** - * Listens for post commit insert update and delete events. - */ - private volatile BeanPersistListener persistListener; - - private volatile BeanQueryAdapter queryAdapter; - - /** - * If set overrides the find implementation. Server side only. - */ - private final BeanFinder beanFinder; - - /** - * The table joins for this bean. - */ - private final TableJoin[] derivedTableJoins; - - /** - * Inheritance information. Server side only. - */ - protected final InheritInfo inheritInfo; - - /** - * Derived list of properties that make up the unique id. - */ - protected 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; - - /** - * Properties that are initialised in the constructor need to be 'unloaded' to support partial object queries. - */ - private final int[] unloadProperties; - - /** - * Properties local to this type (not from a super type). - */ - private final BeanProperty[] propertiesLocal; - - /** - * Scalar mutable properties (need to dirty check on update). - */ - private final BeanProperty[] propertiesMutable; - - - private final BeanPropertyAssocOne unidirectional; - - /** - * list of properties that are Lists/Sets/Maps (Derived). - */ - private final BeanProperty[] propertiesNonMany; - private final BeanPropertyAssocMany[] propertiesMany; - private final BeanPropertyAssocMany[] propertiesManySave; - private final BeanPropertyAssocMany[] propertiesManyDelete; - private final BeanPropertyAssocMany[] propertiesManyToMany; - - /** - * list of properties that are associated beans and not embedded (Derived). - */ - private final BeanPropertyAssocOne[] propertiesOne; - - private final BeanPropertyAssocOne[] propertiesOneImported; - private final BeanPropertyAssocOne[] propertiesOneImportedSave; - private final BeanPropertyAssocOne[] propertiesOneImportedDelete; - - private final BeanPropertyAssocOne[] propertiesOneExported; - private final BeanPropertyAssocOne[] propertiesOneExportedSave; - private final BeanPropertyAssocOne[] propertiesOneExportedDelete; - - /** - * list of properties that are embedded beans. - */ - private final BeanPropertyAssocOne[] propertiesEmbedded; - - /** - * List of the scalar properties excluding id and secondary table properties. - */ - private final BeanProperty[] propertiesBaseScalar; - private final BeanPropertyCompound[] propertiesBaseCompound; - - private final BeanProperty[] propertiesTransient; - - /** - * All non transient properties excluding the id properties. - */ - private final BeanProperty[] propertiesNonTransient; - - /** - * The bean class name or the table name for MapBeans. - */ - private final String fullName; - - private final Map namedQueries; - - private final Map namedUpdates; - - /** - * Flag used to determine if saves can be skipped. - */ - private boolean saveRecurseSkippable; - - /** - * Flag used to determine if deletes can be skipped. - */ - private boolean deleteRecurseSkippable; - - /** - * Make the TypeManager available for helping SqlSelect. - */ - private final TypeManager typeManager; - - private final EntityBean prototypeEntityBean; - - private final IdBinder idBinder; - - private String idBinderInLHSSql; - - private String idBinderIdSql; - - private String deleteByIdSql; - - private String deleteByIdInSql; - - private final String name; - - private final String baseTableAlias; - - /** - * If true then only changed properties get updated. - */ - private final boolean updateChangesOnly; - - private final boolean cacheSharableBeans; - - private final BeanDescriptorCacheHelp cacheHelp; - private final BeanDescriptorJsonHelp jsonHelp; - - private final String defaultSelectClause; - private final Set defaultSelectClauseSet; - - private final String descriptorId; - - private SpiEbeanServer ebeanServer; - - /** - * Construct the BeanDescriptor. - */ - public BeanDescriptor(BeanDescriptorMap owner, TypeManager typeManager, DeployBeanDescriptor deploy, String descriptorId) { - - this.owner = owner; - this.serverName = owner.getServerName(); - this.entityType = deploy.getEntityType(); - this.properties = deploy.getProperties(); - this.propertyCount = this.properties.length; - this.name = InternString.intern(deploy.getName()); - this.baseTableAlias = "t0"; - this.fullName = InternString.intern(deploy.getFullName()); - this.descriptorId = descriptorId; - - this.typeManager = typeManager; - this.beanType = deploy.getBeanType(); - this.prototypeEntityBean = createPrototypeEntityBean(beanType); - - this.namedQueries = deploy.getNamedQueries(); - this.namedUpdates = deploy.getNamedUpdates(); - - this.inheritInfo = deploy.getInheritInfo(); - - this.beanFinder = deploy.getBeanFinder(); - this.persistController = deploy.getPersistController(); - this.persistListener = deploy.getPersistListener(); - this.queryAdapter = deploy.getQueryAdapter(); - - this.defaultSelectClause = deploy.getDefaultSelectClause(); - this.defaultSelectClauseSet = deploy.parseDefaultSelectClause(defaultSelectClause); - - this.idType = deploy.getIdType(); - this.idGenerator = deploy.getIdGenerator(); - this.sequenceName = deploy.getSequenceName(); - this.sequenceInitialValue = deploy.getSequenceInitialValue(); - this.sequenceAllocationSize = deploy.getSequenceAllocationSize(); - this.selectLastInsertedId = deploy.getSelectLastInsertedId(); - this.lazyFetchIncludes = InternString.intern(deploy.getLazyFetchIncludes()); - this.concurrencyMode = deploy.getConcurrencyMode(); - this.updateChangesOnly = deploy.isUpdateChangesOnly(); - - this.dependantTables = deploy.getDependantTables(); - this.compoundUniqueConstraints = deploy.getCompoundUniqueConstraints(); - - this.baseTable = InternString.intern(deploy.getBaseTable()); - - this.autoFetchTunable = EntityType.ORM.equals(entityType) && (beanFinder == null); - - // 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.propertiesEmbedded = listHelper.getEmbedded(); - this.propertiesLocal = listHelper.getLocal(); - this.propertiesMutable = listHelper.getMutable(); - this.unidirectional = listHelper.getUnidirectional(); - this.propertiesOne = listHelper.getOnes(); - this.propertiesOneExported = listHelper.getOneExported(); - this.propertiesOneExportedSave = listHelper.getOneExportedSave(); - this.propertiesOneExportedDelete = listHelper.getOneExportedDelete(); - this.propertiesOneImported = listHelper.getOneImported(); - this.propertiesOneImportedSave = listHelper.getOneImportedSave(); - this.propertiesOneImportedDelete = listHelper.getOneImportedDelete(); - - this.propertiesMany = listHelper.getMany(); - this.propertiesNonMany = listHelper.getNonMany(); - this.propertiesManySave = listHelper.getManySave(); - this.propertiesManyDelete = listHelper.getManyDelete(); - this.propertiesManyToMany = listHelper.getManyToMany(); - - this.derivedTableJoins = listHelper.getTableJoin(); - - boolean noRelationships = propertiesOne.length + propertiesMany.length == 0; - - this.cacheSharableBeans = noRelationships && deploy.getCacheOptions().isReadOnly(); - this.cacheHelp = new BeanDescriptorCacheHelp(this, owner.getCacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported); - this.jsonHelp = new BeanDescriptorJsonHelp(this); - - // 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 - // well. We do that later in initialiseOther(). - - saveRecurseSkippable = (0 == (propertiesOneExportedSave.length + propertiesOneImportedSave.length + propertiesManySave.length)); - - // Check if there are no cascade delete associated beans (also subject to - // change in initialiseOther()). - deleteRecurseSkippable = (0 == (propertiesOneExportedDelete.length + propertiesOneImportedDelete.length + propertiesManyDelete.length)); - - // object used to handle Id values - 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; - this.unloadProperties = new int[0]; - - } else { - EntityBeanIntercept ebi = prototypeEntityBean._ebean_getIntercept(); - this.idPropertyIndex = (idProperty == null) ? -1 : ebi.findProperty(idProperty.getName()); - this.versionPropertyIndex = (versionProperty == null) ? -1 : ebi.findProperty(versionProperty.getName()); - this.unloadProperties = derivePropertiesToUnload(prototypeEntityBean); - } - } - - /** - * Derive an array of property positions for properties that are initialised in the constructor. - * These properties need to be unloaded when populating beans for queries. - */ - private int[] derivePropertiesToUnload(EntityBean prototypeEntityBean) { - - boolean[] loaded = prototypeEntityBean._ebean_getIntercept().getLoaded(); - int[] props = new int[loaded.length]; - int pos = 0; - - // collect the positions of the properties initialised in the default constructor. - for (int i = 0; i < loaded.length; i++) { - if (loaded[i]) { - props[pos++] = i; - } - } - - if (pos == 0) { - // nothing set in the constructor - return new int[0]; - } - - // populate a smaller/minimal array - int[] unload = new int[pos]; - for (int i = 0; i < pos; i++) { - unload[i] = props[i]; - } - return unload; - } - - /** - * Create an entity bean that is used as a prototype/factory to create new instances. - */ - private EntityBean createPrototypeEntityBean(Class beanType) { - if (Modifier.isAbstract(beanType.getModifiers())) { - return null; - } - try { - return (EntityBean) beanType.newInstance(); - } catch (Exception e) { - throw new IllegalStateException("Error trying to create the prototypeEntityBean for "+beanType, e); - } - } - - private LinkedHashMap getReverseMap(LinkedHashMap propMap) { - - LinkedHashMap revMap = new LinkedHashMap(propMap.size() * 2); - - for (BeanProperty prop : propMap.values()) { - if (prop.getDbColumn() != null) { - revMap.put(prop.getDbColumn(), prop); - } - } - - return revMap; - } - - /** - * Set the server. Primarily so that the Many's can lazy load. - */ - public void setEbeanServer(SpiEbeanServer ebeanServer) { - this.ebeanServer = ebeanServer; - for (int i = 0; i < propertiesMany.length; i++) { - // used for creating lazy loading lists etc - propertiesMany[i].setLoader(ebeanServer); - } - } - - /** - * Return the EbeanServer instance that owns this BeanDescriptor. - */ - public SpiEbeanServer getEbeanServer() { - return ebeanServer; - } - - /** - * Return the type of this domain object. - */ - public EntityType getEntityType() { - return entityType; - } - - public int getPropertyCount() { - return propertyCount; - } - - public String[] getProperties() { - return properties; - } - - /** - * Initialise the Id properties first. - *

- * These properties need to be initialised prior to the association properties - * as they are used to get the imported and exported properties. - *

- */ - public void initialiseId() { - - if (logger.isTraceEnabled()) { - logger.trace("BeanDescriptor initialise " + fullName); - } - - if (inheritInfo != null) { - inheritInfo.setDescriptor(this); - } - - if (isEmbedded()) { - // initialise all the properties - for (BeanProperty prop : propertiesAll()) { - prop.initialise(); - } - } else { - // initialise just the Id properties - if (idProperty != null) { - idProperty.initialise(); - } - } - } - - /** - * Initialise the exported and imported parts for associated properties. - */ - public void initialiseOther() { - - if (!isEmbedded()) { - // initialise all the non-id properties - for (BeanProperty prop : propertiesAll()) { - if (!prop.isId()) { - prop.initialise(); - } - } - } - - if (unidirectional != null) { - unidirectional.initialise(); - } - - idBinder.initialise(); - idBinderInLHSSql = idBinder.getBindIdInSql(baseTableAlias); - idBinderIdSql = idBinder.getBindIdSql(baseTableAlias); - String idBinderInLHSSqlNoAlias = idBinder.getBindIdInSql(null); - String idEqualsSql = idBinder.getBindIdSql(null); - - deleteByIdSql = "delete from " + baseTable + " where " + idEqualsSql; - deleteByIdInSql = "delete from " + baseTable + " where " + idBinderInLHSSqlNoAlias + " "; - - if (!isEmbedded()) { - // parse every named update up front into sql dml - for (DeployNamedUpdate namedUpdate : namedUpdates.values()) { - DeployUpdateParser parser = new DeployUpdateParser(this); - namedUpdate.initialise(parser); - } - } - } - - public void initInheritInfo() { - if (inheritInfo != null) { - // need to check every BeanDescriptor in the inheritance hierarchy - if (saveRecurseSkippable) { - saveRecurseSkippable = inheritInfo.isSaveRecurseSkippable(); - } - if (deleteRecurseSkippable) { - deleteRecurseSkippable = inheritInfo.isDeleteRecurseSkippable(); - } - } - } - - /** - * Initialise the cache once the server has started. - */ - public void cacheInitialise() { - cacheHelp.initialise(); - } - - public SqlUpdate deleteById(Object id, List idList) { - if (id != null) { - return deleteById(id); - } else { - return deleteByIdList(idList); - } - } - - /** - * Return SQL that can be used to delete a list of Id's without any optimistic - * concurrency checking. - */ - private SqlUpdate deleteByIdList(List idList) { - - StringBuilder sb = new StringBuilder(deleteByIdInSql); - String inClause = idBinder.getIdInValueExprDelete(idList.size()); - sb.append(inClause); - - DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); - for (int i = 0; i < idList.size(); i++) { - idBinder.bindId(delete, idList.get(i)); - } - return delete; - } - - /** - * Return SQL that can be used to delete by Id without any optimistic - * concurrency checking. - */ - private SqlUpdate deleteById(Object id) { - - DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByIdSql); - - Object[] bindValues = idBinder.getBindValues(id); - for (int i = 0; i < bindValues.length; i++) { - sqlDelete.addParameter(bindValues[i]); - } - - return sqlDelete; - } - - /** - * Add objects to ElPropertyDeploy etc. These are used so that expressions on - * foreign keys don't require an extra join. - */ - public void add(BeanFkeyProperty fkey) { - elDeployCache.put(fkey.getName(), fkey); - } - - public void initialiseFkeys() { - for (int i = 0; i < propertiesOneImported.length; i++) { - propertiesOneImported[i].addFkey(); - } - } - - public boolean calculateUseCache(Boolean queryUseCache) { - return (queryUseCache != null) ? queryUseCache.booleanValue() : isBeanCaching(); - } - - public T cacheNaturalKey(SpiQuery query, SpiTransaction t) { - return cacheHelp.naturalKeyLookup(query, t); - } - - /** - * Return the cache options. - */ - public CacheOptions getCacheOptions() { - return cacheHelp.getCacheOptions(); - } - - /** - * Return the Encrypt key given the BeanProperty. - */ - public EncryptKey getEncryptKey(BeanProperty p) { - return owner.getEncryptKey(baseTable, p.getDbColumn()); - } - - /** - * Return the Encrypt key given the table and column name. - */ - public EncryptKey getEncryptKey(String tableName, String columnName) { - return owner.getEncryptKey(tableName, columnName); - } - - /** - * Execute the warming cache query (if defined) and load the cache. - */ - public void runCacheWarming() { - cacheHelp.runCacheWarming(ebeanServer); - } - - /** - * Return true if this bean type has a default select clause that is not - * simply select all properties. - */ - public boolean hasDefaultSelectClause() { - return defaultSelectClause != null; - } - - /** - * Return the default select clause. - */ - public String getDefaultSelectClause() { - return defaultSelectClause; - } - - /** - * Return the default select clause already parsed into an ordered Set. - */ - public Set getDefaultSelectClauseSet() { - return defaultSelectClauseSet; - } - - /** - * Return true if this object is the root level object in its entity - * inheritance. - */ - public boolean isInheritanceRoot() { - return inheritInfo == null || inheritInfo.isRoot(); - } - - /** - * Set the bean caching on or off. - */ - public void setUseCache(boolean useCache) { - cacheHelp.setUseCache(useCache); - } - - /** - * Return true if there is currently query caching for this type of bean. - */ - public boolean isQueryCaching() { - return cacheHelp.isQueryCaching(); - } - - /** - * Return true if there is currently bean caching for this type of bean. - */ - public boolean isBeanCaching() { - return cacheHelp.isBeanCaching(); - } - - public boolean isManyPropCaching() { - return isBeanCaching(); - } - - /** - * Return true if the persist request needs to notify the cache. - */ - public boolean isCacheNotify() { - return cacheHelp.isCacheNotify(); - } - - /** - * Clear the query cache. - */ - public void queryCacheClear() { - cacheHelp.queryCacheClear(); - } - - /** - * Get a query result from the query cache. - */ - public BeanCollection queryCacheGet(Object id) { - return cacheHelp.queryCacheGet(id); - } - - /** - * Put a query result into the query cache. - */ - public void queryCachePut(Object id, BeanCollection query) { - cacheHelp.queryCachePut(id, query); - } - - /** - * Try to load the beanCollection from cache return true if successful. - */ - public boolean cacheManyPropLoad(BeanPropertyAssocMany many, BeanCollection bc, Object parentId, Boolean readOnly) { - return cacheHelp.manyPropLoad(many, bc, parentId, readOnly); - } - - /** - * Put the beanCollection into the cache. - */ - public void cacheManyPropPut(BeanPropertyAssocMany many, BeanCollection bc, Object parentId) { - cacheHelp.manyPropPut(many, bc, parentId); - } - - public void cacheManyPropRemove(Object parentId, String propertyName) { - cacheHelp.manyPropRemove(parentId, propertyName); - } - - public void cacheManyPropClear(String propertyName) { - cacheHelp.manyPropClear(propertyName); - } - - /** - * Return the CachedManyIds for a given bean and property. Returns null if not in the cache. - */ - public CachedManyIds cacheManyPropGet(Object parentId, String propertyName) { - return cacheHelp.manyPropGet(parentId, propertyName); - } - - /** - * Clear the bean cache. - */ - public void cacheBeanClear() { - cacheHelp.beanCacheClear(); - } - - public void cacheBeanPut(T bean) { - cacheBeanPutData((EntityBean)bean); - } - - /** - * Extract the raw cache data from the bean. - */ - public CachedBeanData cacheBeanExtractData(EntityBean bean) { - return cacheHelp.beanExtractData(bean); - } - - /** - * Load the raw cache data into the bean. - */ - public void cacheBeanLoadData(EntityBean bean, CachedBeanData data) { - cacheHelp.beanLoadData(bean, data); - } - - /** - * Put a bean into the bean cache. - */ - public void cacheBeanPutData(EntityBean bean) { - cacheHelp.beanCachePut(bean); - } - - /** - * Return a bean from the bean cache (or null). - */ - public T cacheBeanGet(SpiQuery query, PersistenceContext context) { - return cacheHelp.beanCacheGet(query, context); - } - - /** - * Remove a bean from the cache given its Id. - */ - public void cacheBeanRemove(Object id) { - cacheHelp.beanCacheRemove(id); - } - - /** - * Returns true if it managed to populate/load the bean from the cache. - */ - public boolean cacheBeanLoad(EntityBean bean, EntityBeanIntercept ebi, Object id) { - return cacheHelp.beanCacheLoad(bean, ebi, id); - } - - /** - * Returns true if it managed to populate/load the bean from the cache. - */ - public boolean cacheBeanLoad(EntityBeanIntercept ebi) { - EntityBean bean = ebi.getOwner(); - Object id = getId(bean); - return cacheBeanLoad(bean, ebi, id); - } - - /** - * Try to hit the cache using the natural key. - */ - public T cacheNaturalKeyLookup(SpiQuery query, SpiTransaction t) { - return cacheHelp.naturalKeyLookup(query, t); - } - - /** - * Invalidate parts of cache due to SqlUpdate or external modification etc. - */ - public void cacheHandleBulkUpdate(TableIUD tableIUD) { - cacheHelp.handleBulkUpdate(tableIUD); - } - - /** - * Remove a bean from the cache given its Id. - */ - public void cacheHandleDelete(Object id, PersistRequestBean deleteRequest) { - cacheHelp.handleDelete(id, deleteRequest); - } - - public void cacheHandleInsert(Object id, PersistRequestBean insertRequest) { - cacheHelp.handleInsert(id, insertRequest); - } - - /** - * Update the cached bean data. - */ - public void cacheHandleUpdate(Object id, PersistRequestBean updateRequest) { - cacheHelp.handleUpdate(id, updateRequest); - } - - /** - * Return the base table alias. This is always the first letter of the bean - * name. - */ - public String getBaseTableAlias() { - return baseTableAlias; - } - - public void preAllocateIds(int batchSize) { - if (idGenerator != null) { - idGenerator.preAllocateIds(batchSize); - } - } - - public Object nextId(Transaction t) { - if (idGenerator != null) { - return idGenerator.nextId(t); - } else { - return null; - } - } - - public DeployPropertyParser createDeployPropertyParser() { - return new DeployPropertyParser(this); - } - - /** - * Convert the logical orm update statement into sql by converting the bean - * properties and bean name to database columns and table. - */ - public String convertOrmUpdateToSql(String ormUpdateStatement) { - return new DeployUpdateParser(this).parse(ormUpdateStatement); - } - - @Override - public List collectQueryPlanStatistics(boolean reset) { - return collectQueryPlanStatisticsInternal(reset, false); - } - - @Override - public List collectAllQueryPlanStatistics(boolean reset) { - return collectQueryPlanStatisticsInternal(reset, false); - } - - public List collectQueryPlanStatisticsInternal(boolean reset, boolean collectAll) { - List list = new ArrayList(queryPlanCache.size()); - for (CQueryPlan queryPlan : queryPlanCache.values()) { - Snapshot snapshot = queryPlan.getSnapshot(reset); - if (collectAll || snapshot.getExecutionCount() > 0) { - list.add(snapshot); - } - } - return list; - } - - /** - * Reset the statistics on all the query plans. - */ - public void clearQueryStatistics() { - for (CQueryPlan queryPlan : queryPlanCache.values()) { - queryPlan.resetStatistics(); - } - } - - /** - * Execute the postLoad if a BeanPersistController exists for this bean. - */ - public void postLoad(Object bean, Set includedProperties) { - BeanPersistController c = persistController; - if (c != null) { - c.postLoad(bean, includedProperties); - } - } - - public CQueryPlan getQueryPlan(HashQueryPlan key) { - return queryPlanCache.get(key); - } - - public void putQueryPlan(HashQueryPlan key, CQueryPlan plan) { - queryPlanCache.put(key, plan); - } - - /** - * Get a UpdatePlan for a given hash. - */ - public SpiUpdatePlan getUpdatePlan(Integer key) { - return updatePlanCache.get(key); - } - - /** - * Add a UpdatePlan to the cache with a given hash. - */ - public void putUpdatePlan(Integer key, SpiUpdatePlan plan) { - updatePlanCache.put(key, plan); - } - - /** - * Return true if updates should only include changed properties. Otherwise - * all loaded properties are included in the update. - */ - public boolean isUpdateChangesOnly() { - return updateChangesOnly; - } - - /** - * Return true if save does not recurse to other beans. That is return true if - * there are no assoc one or assoc many beans that cascade save. - */ - public boolean isSaveRecurseSkippable() { - return saveRecurseSkippable; - } - - /** - * Return true if delete does not recurse to other beans. That is return true - * if there are no assoc one or assoc many beans that cascade delete. - */ - public boolean isDeleteRecurseSkippable() { - return deleteRecurseSkippable; - } - - /** - * Return the many property included in the query or null if one is not. - */ - public BeanPropertyAssocMany getManyProperty(SpiQuery query) { - - OrmQueryDetail detail = query.getDetail(); - for (int i = 0; i < propertiesMany.length; i++) { - if (detail.includes(propertiesMany[i].getName())) { - return propertiesMany[i]; - } - } - - return null; - } - - /** - * Return a raw expression for 'where parent id in ...' clause. - */ - public String getParentIdInExpr(int parentIdSize, String rawWhere) { - String inClause = idBinder.getIdInValueExpr(parentIdSize); - return idBinder.isIdInExpandedForm() ? inClause : rawWhere + inClause; - } - - /** - * Return the IdBinder which is helpful for handling the various types of Id. - */ - public IdBinder getIdBinder() { - return idBinder; - } - - /** - * Return the sql for binding an id. This is the columns with table alias that - * make up the id. - */ - public String getIdBinderIdSql() { - return idBinderIdSql; - } - - /** - * Return the sql for binding id's using an IN clause. - */ - public String getIdBinderInLHSSql() { - return idBinderInLHSSql; - } - - /** - * Bind the idValue to the preparedStatement. - *

- * This takes care of the various id types such as embedded beans etc. - *

- */ - public void bindId(DataBind dataBind, Object idValue) throws SQLException { - idBinder.bindId(dataBind, idValue); - } - - /** - * Return the id as an array of scalar bindable values. - *

- * This 'flattens' any EmbeddedId or multiple Id property cases. - *

- */ - public Object[] getBindIdValues(Object idValue) { - return idBinder.getBindValues(idValue); - } - - /** - * Return a named query. - */ - public DeployNamedQuery getNamedQuery(String name) { - return namedQueries.get(name); - } - - public DeployNamedQuery addNamedQuery(DeployNamedQuery deployNamedQuery) { - return namedQueries.put(deployNamedQuery.getName(), deployNamedQuery); - } - - /** - * Return a named update. - */ - public DeployNamedUpdate getNamedUpdate(String name) { - return namedUpdates.get(name); - } - - /** - * Creates a new EntityBean. - */ - public EntityBean createEntityBean() { - try { - EntityBean bean = (EntityBean)prototypeEntityBean._ebean_newInstance(); - - if (unloadProperties.length > 0) { - // 'unload' any properties initialised in the default constructor - EntityBeanIntercept ebi = bean._ebean_getIntercept(); - for (int i = 0; i < unloadProperties.length; i++) { - ebi.setPropertyUnloaded(unloadProperties[i]); - } - } - return bean; - - } catch (Exception ex) { - throw new PersistenceException(ex); - } - } - - /** - * Create a reference bean based on the id. - */ - @SuppressWarnings("unchecked") - public T createReference(Boolean readOnly, Object id) { - - if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) { - CachedBeanData d = cacheHelp.beanCacheGetData(id); - if (d != null) { - Object shareableBean = d.getSharableBean(); - if (shareableBean != null) { - return (T) shareableBean; - } - } - } - try { - EntityBean eb = createEntityBean(); - - convertSetId(id, eb); - - EntityBeanIntercept ebi = eb._ebean_getIntercept(); - ebi.setBeanLoader(ebeanServer); - - // Note: not creating proxies for many's... - ebi.setReference(idPropertyIndex); - - return (T) eb; - - } catch (Exception ex) { - throw new PersistenceException(ex); - } - } - - /** - * Return the bean property traversing the object graph and taking into - * account inheritance. - */ - public BeanProperty getBeanPropertyFromPath(String path) { - - String[] split = SplitName.splitBegin(path); - if (split[1] == null) { - return _findBeanProperty(split[0]); - } - BeanPropertyAssoc assocProp = (BeanPropertyAssoc) _findBeanProperty(split[0]); - BeanDescriptor targetDesc = assocProp.getTargetDescriptor(); - - return targetDesc.getBeanPropertyFromPath(split[1]); - } - - /** - * Return the BeanDescriptor for a given path of Associated One or Many beans. - */ - public BeanDescriptor getBeanDescriptor(String path) { - if (path == null) { - return this; - } - String[] splitBegin = SplitName.splitBegin(path); - - BeanProperty beanProperty = propMap.get(splitBegin[0]); - if (beanProperty instanceof BeanPropertyAssoc) { - BeanPropertyAssoc assocProp = (BeanPropertyAssoc) beanProperty; - return assocProp.getTargetDescriptor().getBeanDescriptor(splitBegin[1]); - - } else { - throw new RuntimeException("Error getting BeanDescriptor for path " + path + " from " + getFullName()); - } - } - - /** - * Return the BeanDescriptor of another bean type. - */ - public BeanDescriptor getBeanDescriptor(Class otherType) { - return owner.getBeanDescriptor(otherType); - } - - /** - * Return the "shadow" property to support unidirectional relationships. - *

- * For bidirectional this is a real property on the bean. For unidirectional - * relationships we have this 'shadow' property which is not externally - * visible. - *

- */ - public BeanPropertyAssocOne getUnidirectional() { - if (unidirectional != null) { - return unidirectional; - } - if (inheritInfo != null && !inheritInfo.isRoot()) { - return inheritInfo.getParent().getBeanDescriptor().getUnidirectional(); - } - return null; - } - - /** - * Get a property value from a bean of this type. - */ - public Object getValue(EntityBean bean, String property) { - return getBeanProperty(property).getValue(bean); - } - - /** - * Return true if this bean type should use IdGeneration. - *

- * If this is false and the Id is null it is assumed that a database auto - * increment feature is being used to populate the id. - *

- */ - public boolean isUseIdGenerator() { - return idGenerator != null; - } - - /** - * Return the alternate "Id" that identifies this BeanDescriptor. This is an - * alternative to using the bean class name. - */ - public String getDescriptorId() { - return descriptorId; - } - - /** - * Return the class type this BeanDescriptor describes. - */ - public Class getBeanType() { - return beanType; - } - - /** - * Return the bean class name this descriptor is used for. - *

- * If this BeanDescriptor is for a table then this returns the table name - * instead. - *

- */ - public String getFullName() { - return fullName; - } - - /** - * Return the short name of the entity bean. - */ - public String getName() { - return name; - } - - /** - * Summary description. - */ - public String toString() { - return fullName; - } - - /** - * Helper method to return the unique property. If only one property makes up - * the unique id then it's value is returned. If there is a concatenated - * unique id then a Map is built with the keys being the names of the - * properties that make up the unique id. - */ - public Object getId(EntityBean bean) { - return (idProperty == null) ? null : idProperty.getValue(bean); - } - - /** - * Return the default order by that may need to be added if a many property is - * included in the query. - */ - public String getDefaultOrderBy() { - return idBinder.getDefaultOrderBy(); - } - - /** - * Convert the type of the idValue if required. - */ - public Object convertId(Object idValue) { - return idBinder.convertSetId(idValue, null); - } - - /** - * Convert and set the id value. - *

- * If the bean is not null, the id value is set to the id property of the bean - * after it has been converted to the correct type. - *

- */ - public Object convertSetId(Object idValue, EntityBean bean) { - return idBinder.convertSetId(idValue, bean); - } - - /** - * Get a BeanProperty by its name. - */ - public BeanProperty getBeanProperty(String propName) { - return propMap.get(propName); - } - - public void sort(List list, String sortByClause) { - - ElComparator comparator = getElComparator(sortByClause); - Collections.sort(list, comparator); - } - - public ElComparator getElComparator(String propNameOrSortBy) { - ElComparator c = comparatorCache.get(propNameOrSortBy); - if (c == null) { - c = createComparator(propNameOrSortBy); - comparatorCache.put(propNameOrSortBy, c); - } - return c; - } - - /** - * Return true if the lazy loading property is a Many in which case just - * define a Reference for the collection and not invoke a query. - */ - public boolean lazyLoadMany(EntityBeanIntercept ebi) { - - int lazyLoadProperty = ebi.getLazyLoadPropertyIndex(); - if (lazyLoadProperty == -1) { - return false; - } - String lazyLoadPropertyName = ebi.getProperty(lazyLoadProperty); - BeanProperty lazyLoadBeanProp = getBeanProperty(lazyLoadPropertyName); - - if (lazyLoadBeanProp instanceof BeanPropertyAssocMany) { - BeanPropertyAssocMany manyProp = (BeanPropertyAssocMany) lazyLoadBeanProp; - manyProp.createReference(ebi.getOwner()); - ebi.setLoadedLazy(); - return true; - } - - return false; - } - - /** - * Return a Comparator for local sorting of lists. - * - * @param sortByClause - * list of property names with optional ASC or DESC suffix. - */ - @SuppressWarnings("unchecked") - private ElComparator createComparator(String sortByClause) { - - SortByClause sortBy = SortByClauseParser.parse(sortByClause); - if (sortBy.size() == 1) { - // simple comparator for a single property - return createPropertyComparator(sortBy.getProperties().get(0)); - } - - // create a compound comparator based on the list of properties - ElComparator[] comparators = new ElComparator[sortBy.size()]; - - List sortProps = sortBy.getProperties(); - for (int i = 0; i < sortProps.size(); i++) { - Property sortProperty = sortProps.get(i); - comparators[i] = createPropertyComparator(sortProperty); - } - - return new ElComparatorCompound(comparators); - } - - private ElComparator createPropertyComparator(Property sortProp) { - - ElPropertyValue elGetValue = getElGetValue(sortProp.getName()); - - Boolean nullsHigh = sortProp.getNullsHigh(); - if (nullsHigh == null) { - nullsHigh = Boolean.TRUE; - } - return new ElComparatorProperty(elGetValue, sortProp.isAscending(), nullsHigh); - } - - /** - * Get an Expression language Value object. - */ - public ElPropertyValue getElGetValue(String propName) { - ElPropertyValue elGetValue = elCache.get(propName); - if (elGetValue != null) { - return elGetValue; - } - elGetValue = buildElGetValue(propName, null, false); - if (elGetValue != null) { - elCache.put(propName, elGetValue); - } - return elGetValue; - } - - /** - * Similar to ElPropertyValue but also uses foreign key shortcuts. - *

- * The foreign key shortcuts means we can avoid unnecessary joins. - *

- */ - public ElPropertyDeploy getElPropertyDeploy(String propName) { - ElPropertyDeploy elProp = elDeployCache.get(propName); - if (elProp != null) { - return elProp; - } - if (!propName.contains(".")) { - // No period means simple property and no need to look for - // foreign key properties (in order to avoid an extra join) - elProp = getElGetValue(propName); - } else { - elProp = buildElGetValue(propName, null, true); - } - if (elProp != null) { - elDeployCache.put(propName, elProp); - } - return elProp; - } - - protected ElPropertyValue buildElGetValue(String propName, ElPropertyChainBuilder chain, boolean propertyDeploy) { - - if (propertyDeploy && chain != null) { - ElPropertyDeploy fk = elDeployCache.get(propName); - if (fk != null && fk instanceof BeanFkeyProperty) { - // propertyDeploy chain for foreign key column - return ((BeanFkeyProperty)fk).create(chain.getExpression(), chain.isContainsMany()); - } - } - - int basePos = propName.indexOf('.'); - if (basePos > -1) { - // nested or embedded property - String baseName = propName.substring(0, basePos); - String remainder = propName.substring(basePos + 1); - - BeanProperty assocProp = _findBeanProperty(baseName); - if (assocProp == null) { - return null; - } - return assocProp.buildElPropertyValue(propName, remainder, chain, propertyDeploy); - } - - BeanProperty property = _findBeanProperty(propName); - if (chain == null) { - return property; - } - if (property == null) { - throw new PersistenceException("No property found for [" + propName + "] in expression " + chain.getExpression()); - } - if (property.containsMany()) { - chain.setContainsMany(true); - } - return chain.add(property).build(); - } - - /** - * Find a BeanProperty including searching the inheritance hierarchy. - *

- * This searches this BeanDescriptor and then searches further down the - * inheritance tree (not up). - *

- */ - public BeanProperty findBeanProperty(String propName) { - int basePos = propName.indexOf('.'); - if (basePos > -1) { - // embedded property - String baseName = propName.substring(0, basePos); - return _findBeanProperty(baseName); - } - - return _findBeanProperty(propName); - } - - private BeanProperty _findBeanProperty(String propName) { - BeanProperty prop = propMap.get(propName); - if (prop == null && inheritInfo != null) { - // search in sub types... - return inheritInfo.findSubTypeProperty(propName); - } - return prop; - } - - /** - * Reset the many properties to empty state ready for reloading. - */ - public void resetManyProperties(Object dbBean) { - - EntityBean bean = (EntityBean)dbBean; - for (int i = 0; i < propertiesMany.length; i++) { - propertiesMany[i].resetMany(bean); - } - } - - /** - * Return the name of the server this BeanDescriptor belongs to. - */ - public String getServerName() { - return serverName; - } - - /** - * Return true if this bean can cache sharable instances. - *

- * This means is has no relationships and has readOnly=true in its cache - * options. - *

- */ - public boolean isCacheSharableBeans() { - return cacheSharableBeans; - } - - /** - * Return true if queries for beans of this type are autoFetch tunable. - */ - public boolean isAutoFetchTunable() { - return autoFetchTunable; - } - - /** - * Returns the Inheritance mapping information. This will be null if this type - * of bean is not involved in any ORM inheritance mapping. - */ - public InheritInfo getInheritInfo() { - return inheritInfo; - } - - /** - * Return true if this is an embedded bean. - */ - public boolean isEmbedded() { - return EntityType.EMBEDDED.equals(entityType); - } - - /** - * Return the tables this bean is dependent on. This implies that if any of - * these tables are modified then cached beans may be invalidated. - */ - public String[] getDependantTables() { - return dependantTables; - } - - /** - * Return the compound unique constraints. - */ - public CompoundUniqueContraint[] getCompoundUniqueConstraints() { - return compoundUniqueConstraints; - } - - /** - * Return the beanListener. - */ - public BeanPersistListener getPersistListener() { - return persistListener; - } - - /** - * Return the beanFinder. Usually null unless overriding the finder. - */ - public BeanFinder getBeanFinder() { - return beanFinder; - } - - /** - * Return the BeanQueryAdapter or null if none is defined. - */ - public BeanQueryAdapter getQueryAdapter() { - return queryAdapter; - } - - /** - * De-register the BeanPersistListener. - */ - @SuppressWarnings("unchecked") - public void deregister(BeanPersistListener listener) { - // volatile read... - BeanPersistListener currListener = persistListener; - if (currListener == null) { - // nothing to deregister - } else { - BeanPersistListener deregListener = listener; - if (currListener instanceof ChainedBeanPersistListener) { - // remove it from the existing chain - persistListener = ((ChainedBeanPersistListener) currListener).deregister(deregListener); - } else if (currListener.equals(deregListener)) { - persistListener = null; - } - } - } - - /** - * De-register the BeanPersistController. - */ - public void deregister(BeanPersistController controller) { - // volatile read... - BeanPersistController c = persistController; - if (c != null) { - if (c instanceof ChainedBeanPersistController) { - // remove it from the existing chain - persistController = ((ChainedBeanPersistController) c).deregister(controller); - } else if (c.equals(controller)) { - persistController = null; - } - } - } - - /** - * Register the new BeanPersistController. - */ - @SuppressWarnings("unchecked") - public void register(BeanPersistListener newPersistListener) { - - if (newPersistListener.isRegisterFor(beanType)) { - // volatile read... - BeanPersistListener currListener = persistListener; - if (currListener == null) { - persistListener = newPersistListener; - } else { - if (currListener instanceof ChainedBeanPersistListener) { - // add it to the existing chain - persistListener = ((ChainedBeanPersistListener) currListener).register(newPersistListener); - } else { - // build new chain of the 2 - persistListener = new ChainedBeanPersistListener(currListener, newPersistListener); - } - } - } - } - - /** - * Register the new BeanPersistController. - */ - public void register(BeanPersistController newController) { - - if (newController.isRegisterFor(beanType)) { - // volatile read... - BeanPersistController c = persistController; - if (c == null) { - persistController = newController; - } else { - if (c instanceof ChainedBeanPersistController) { - // add it to the existing chain - persistController = ((ChainedBeanPersistController) c).register(newController); - } else { - // build new chain of the 2 - persistController = new ChainedBeanPersistController(c, newController); - } - } - } - } - - /** - * Return the Controller. - */ - public BeanPersistController getPersistController() { - return persistController; - } - - /** - * Returns true if this bean is based on a table (or possibly view) and - * returns false if this bean is based on a raw sql select statement. - *

- * When false querying this bean is based on a supplied sql select statement - * placed in the orm xml file (as opposed to Ebean generated sql). - *

- */ - public boolean isSqlSelectBased() { - return EntityType.SQL.equals(entityType); - } - - /** - * Return the base table. Only properties mapped to the base table are by - * default persisted. - */ - public String getBaseTable() { - return baseTable; - } - - /** - * Return the identity generation type. - */ - public IdType getIdType() { - return idType; - } - - /** - * Return the sequence name. - */ - public String getSequenceName() { - return sequenceName; - } - - /** - * Return the sequence initial value. - */ - public int getSequenceInitialValue() { - return sequenceInitialValue; - } - - /** - * Return the sequence allocation size. - */ - public int getSequenceAllocationSize() { - return sequenceAllocationSize; - } - - /** - * Return the SQL used to return the last inserted id. - *

- * This is only used with Identity columns and getGeneratedKeys is not - * supported. - *

- */ - public String getSelectLastInsertedId() { - return selectLastInsertedId; - } - - /** - * Return the IdGenerator. - */ - public IdGenerator getIdGenerator() { - return idGenerator; - } - - /** - * Return the includes for getReference(). - */ - public String getLazyFetchIncludes() { - return lazyFetchIncludes; - } - - /** - * Return the TableJoins. - *

- * For properties mapped to secondary tables rather than the base table. - *

- */ - public TableJoin[] tableJoins() { - return derivedTableJoins; - } - - /** - * Return a collection of all BeanProperty. This includes transient properties. - */ - public Collection propertiesAll() { - return propMap.values(); - } - - /** - * Return the non transient non id properties. - */ - public BeanProperty[] propertiesNonTransient() { - return propertiesNonTransient; - } - - /** - * Return the transient properties. - */ - public BeanProperty[] propertiesTransient() { - return propertiesTransient; - } - - /** - * Return the beans that are embedded. These share the base table with the - * owner bean. - */ - public BeanPropertyAssocOne[] propertiesEmbedded() { - return propertiesEmbedded; - } - - public BeanProperty getIdProperty() { - return idProperty; - } - - /** - * Return true if this bean should be inserted rather than updated. - * - * @param ebi - * The entity bean intercept - * @param insertMode - * true if the 'root request' was an insert rather than an update - */ - public boolean isInsertMode(EntityBeanIntercept ebi, boolean insertMode) { - - if (ebi.isLoaded()) { - // must be an update as the bean is loaded - return false; - } - - if (idProperty.isEmbedded()) { - // not using Id generator so just base on isLoaded() - return !ebi.isLoaded(); - } - if (!hasIdProperty(ebi)) { - // No Id property means it must be an insert - return true; - } - // same as the 'root request' - return insertMode; - } - - public boolean isReference(EntityBeanIntercept ebi) { - return ebi.isReference() || hasIdPropertyOnly(ebi); - } - - public boolean hasIdPropertyOnly(EntityBeanIntercept ebi) { - return ebi.hasIdOnly(idPropertyIndex); - } - - public boolean hasIdProperty(EntityBeanIntercept ebi) { - return idPropertyIndex > -1 && ebi.isLoadedProperty(idPropertyIndex); - } - - public boolean hasVersionProperty(EntityBeanIntercept ebi) { - return versionPropertyIndex > -1 && ebi.isLoadedProperty(versionPropertyIndex); - } - - /** - * Check for mutable scalar types and mark as dirty if necessary. - */ - public void checkMutableProperties(EntityBeanIntercept ebi) { - for (int i = 0; i < propertiesMutable.length; i++) { - BeanProperty beanProperty = propertiesMutable[i]; - if (ebi.isDirtyProperty(beanProperty.getPropertyIndex())) { - // already marked as dirty - } else if (ebi.isLoadedProperty(beanProperty.getPropertyIndex())) { - Object value = beanProperty.getValue(ebi.getOwner()); - if (value == null || beanProperty.isDirtyValue(value)) { - // mutable scalar value which is considered dirty so mark - // it as such so that it is included in an update - ebi.markPropertyAsChanged(beanProperty.getPropertyIndex()); - } - } - } - } - - 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. - */ - public BeanPropertyAssocOne[] propertiesOne() { - return propertiesOne; - } - - /** - * Returns ManyToOnes and OneToOnes on the imported owning side. - *

- * Excludes OneToOnes on the exported side. - *

- */ - public BeanPropertyAssocOne[] propertiesOneImported() { - return propertiesOneImported; - } - - /** - * Imported Assoc Ones with cascade save true. - */ - public BeanPropertyAssocOne[] propertiesOneImportedSave() { - return propertiesOneImportedSave; - } - - /** - * Imported Assoc Ones with cascade delete true. - */ - public BeanPropertyAssocOne[] propertiesOneImportedDelete() { - return propertiesOneImportedDelete; - } - - /** - * Exported assoc ones with cascade save. - */ - public BeanPropertyAssocOne[] propertiesOneExportedSave() { - return propertiesOneExportedSave; - } - - /** - * Exported assoc ones with delete cascade. - */ - public BeanPropertyAssocOne[] propertiesOneExportedDelete() { - return propertiesOneExportedDelete; - } - - /** - * All Non Assoc Many's for this descriptor. - */ - public BeanProperty[] propertiesNonMany() { - return propertiesNonMany; - } - - /** - * All Assoc Many's for this descriptor. - */ - public BeanPropertyAssocMany[] propertiesMany() { - return propertiesMany; - } - - /** - * Assoc Many's with save cascade. - */ - public BeanPropertyAssocMany[] propertiesManySave() { - return propertiesManySave; - } - - /** - * Assoc Many's with delete cascade. - */ - public BeanPropertyAssocMany[] propertiesManyDelete() { - return propertiesManyDelete; - } - - /** - * Assoc ManyToMany's. - */ - public BeanPropertyAssocMany[] propertiesManyToMany() { - return propertiesManyToMany; - } - - /** - * Return the first version property that exists on the bean. Returns null if - * no version property exists on the bean. - *

- * Note that this DOES NOT find a version property on an embedded bean. - *

- */ - public BeanProperty getVersionProperty() { - return versionProperty; - } - - /** - * Scalar properties without the unique id or secondary table properties. - */ - public BeanProperty[] propertiesBaseScalar() { - return propertiesBaseScalar; - } - - /** - * Return properties that are immutable compound value objects. - *

- * These are compound types but are not enhanced (Embedded are enhanced). - *

- */ - public BeanPropertyCompound[] propertiesBaseCompound() { - return propertiesBaseCompound; - } - - /** - * Return the properties local to this type for inheritance. - */ - public BeanProperty[] propertiesLocal() { - return propertiesLocal; - } - - public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException { - jsonHelp.jsonWrite(writeJson, bean, null); - } - - public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException { - jsonHelp.jsonWrite(writeJson, bean, key); - } - - protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException { - jsonHelp.jsonWriteProperties(writeJson, bean); - } - - public T jsonRead(JsonParser parser, String path) throws IOException { - return jsonHelp.jsonRead(parser, path); - } - - protected T jsonReadObject(JsonParser parser, String path) throws IOException { - return jsonHelp.jsonReadObject(parser, path); - } -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebean.SqlUpdate; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.annotation.ConcurrencyMode; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebean.config.EncryptKey; +import com.avaje.ebean.config.dbplatform.IdGenerator; +import com.avaje.ebean.config.dbplatform.IdType; +import com.avaje.ebean.event.BeanFinder; +import com.avaje.ebean.event.BeanPersistController; +import com.avaje.ebean.event.BeanPersistListener; +import com.avaje.ebean.event.BeanQueryAdapter; +import com.avaje.ebean.meta.MetaBeanInfo; +import com.avaje.ebean.meta.MetaQueryPlanStatistic; +import com.avaje.ebeaninternal.api.*; +import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; +import com.avaje.ebeaninternal.server.cache.CachedBeanData; +import com.avaje.ebeaninternal.server.cache.CachedManyIds; +import com.avaje.ebeaninternal.server.core.CacheOptions; +import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.id.IdBinder; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyLists; +import com.avaje.ebeaninternal.server.el.*; +import com.avaje.ebeaninternal.server.query.CQueryPlan; +import com.avaje.ebeaninternal.server.query.CQueryPlanStats.Snapshot; +import com.avaje.ebeaninternal.server.query.SplitName; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; +import com.avaje.ebeaninternal.server.text.json.WriteJson; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.type.TypeManager; +import com.avaje.ebeaninternal.util.SortByClause; +import com.avaje.ebeaninternal.util.SortByClause.Property; +import com.avaje.ebeaninternal.util.SortByClauseParser; +import com.fasterxml.jackson.core.JsonParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.persistence.PersistenceException; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Describes Beans including their deployment information. + */ +public class BeanDescriptor implements MetaBeanInfo { + + private static final Logger logger = LoggerFactory.getLogger(BeanDescriptor.class); + + private final ConcurrentHashMap updatePlanCache = new ConcurrentHashMap(); + + private final ConcurrentHashMap queryPlanCache = new ConcurrentHashMap(); + + private final ConcurrentHashMap elCache = new ConcurrentHashMap(); + + private final ConcurrentHashMap elDeployCache = new ConcurrentHashMap(); + + private final ConcurrentHashMap> comparatorCache = new ConcurrentHashMap>(); + + public enum EntityType { + ORM, EMBEDDED, SQL + } + + /** + * The EbeanServer name. Same as the plugin name. + */ + private final String serverName; + + /** + * The nature/type of this bean. + */ + private final EntityType entityType; + + /** + * Type of Identity generation strategy used. + */ + private final IdType idType; + + private final IdGenerator idGenerator; + + /** + * The database sequence name (optional). + */ + private final String sequenceName; + + private final int sequenceInitialValue; + + private final int sequenceAllocationSize; + + /** + * SQL used to return last inserted id. Used for Identity columns where + * getGeneratedKeys is not supported. + */ + private final String selectLastInsertedId; + + private final boolean autoFetchTunable; + + private final String lazyFetchIncludes; + + /** + * The concurrency mode for beans of this type. + */ + private final ConcurrencyMode concurrencyMode; + + /** + * The tables this bean is dependent on. + */ + private final String[] dependantTables; + + private final CompoundUniqueContraint[] compoundUniqueConstraints; + + /** + * The base database table. + */ + private final String baseTable; + + /** + * Map of BeanProperty Linked so as to preserve order. + */ + private final LinkedHashMap propMap; + private final LinkedHashMap propMapByDbColumn; + + /** + * The type of bean this describes. + */ + private final Class beanType; + + /** + * This is not sent to a remote client. + */ + private final BeanDescriptorMap owner; + + + private final String[] properties; + + private final int propertyCount; + + + /** + * Intercept pre post on insert,update,delete and postLoad(). Server side + * only. + */ + private volatile BeanPersistController persistController; + + /** + * Listens for post commit insert update and delete events. + */ + private volatile BeanPersistListener persistListener; + + private volatile BeanQueryAdapter queryAdapter; + + /** + * If set overrides the find implementation. Server side only. + */ + private final BeanFinder beanFinder; + + /** + * The table joins for this bean. + */ + private final TableJoin[] derivedTableJoins; + + /** + * Inheritance information. Server side only. + */ + protected final InheritInfo inheritInfo; + + /** + * Derived list of properties that make up the unique id. + */ + protected 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; + + /** + * Properties that are initialised in the constructor need to be 'unloaded' to support partial object queries. + */ + private final int[] unloadProperties; + + /** + * Properties local to this type (not from a super type). + */ + private final BeanProperty[] propertiesLocal; + + /** + * Scalar mutable properties (need to dirty check on update). + */ + private final BeanProperty[] propertiesMutable; + + + private final BeanPropertyAssocOne unidirectional; + + /** + * list of properties that are Lists/Sets/Maps (Derived). + */ + private final BeanProperty[] propertiesNonMany; + private final BeanPropertyAssocMany[] propertiesMany; + private final BeanPropertyAssocMany[] propertiesManySave; + private final BeanPropertyAssocMany[] propertiesManyDelete; + private final BeanPropertyAssocMany[] propertiesManyToMany; + + /** + * list of properties that are associated beans and not embedded (Derived). + */ + private final BeanPropertyAssocOne[] propertiesOne; + + private final BeanPropertyAssocOne[] propertiesOneImported; + private final BeanPropertyAssocOne[] propertiesOneImportedSave; + private final BeanPropertyAssocOne[] propertiesOneImportedDelete; + + private final BeanPropertyAssocOne[] propertiesOneExported; + private final BeanPropertyAssocOne[] propertiesOneExportedSave; + private final BeanPropertyAssocOne[] propertiesOneExportedDelete; + + /** + * list of properties that are embedded beans. + */ + private final BeanPropertyAssocOne[] propertiesEmbedded; + + /** + * List of the scalar properties excluding id and secondary table properties. + */ + private final BeanProperty[] propertiesBaseScalar; + private final BeanPropertyCompound[] propertiesBaseCompound; + + private final BeanProperty[] propertiesTransient; + + /** + * All non transient properties excluding the id properties. + */ + private final BeanProperty[] propertiesNonTransient; + + /** + * The bean class name or the table name for MapBeans. + */ + private final String fullName; + + private final Map namedQueries; + + private final Map namedUpdates; + + /** + * Flag used to determine if saves can be skipped. + */ + private boolean saveRecurseSkippable; + + /** + * Flag used to determine if deletes can be skipped. + */ + private boolean deleteRecurseSkippable; + + /** + * Make the TypeManager available for helping SqlSelect. + */ + private final TypeManager typeManager; + + private final EntityBean prototypeEntityBean; + + private final IdBinder idBinder; + + private String idBinderInLHSSql; + + private String idBinderIdSql; + + private String deleteByIdSql; + + private String deleteByIdInSql; + + private final String name; + + private final String baseTableAlias; + + /** + * If true then only changed properties get updated. + */ + private final boolean updateChangesOnly; + + private final boolean cacheSharableBeans; + + private final BeanDescriptorCacheHelp cacheHelp; + private final BeanDescriptorJsonHelp jsonHelp; + + private final String defaultSelectClause; + private final Set defaultSelectClauseSet; + + private final String descriptorId; + + private SpiEbeanServer ebeanServer; + + /** + * Construct the BeanDescriptor. + */ + public BeanDescriptor(BeanDescriptorMap owner, TypeManager typeManager, DeployBeanDescriptor deploy, String descriptorId) { + + this.owner = owner; + this.serverName = owner.getServerName(); + this.entityType = deploy.getEntityType(); + this.properties = deploy.getProperties(); + this.propertyCount = this.properties.length; + this.name = InternString.intern(deploy.getName()); + this.baseTableAlias = "t0"; + this.fullName = InternString.intern(deploy.getFullName()); + this.descriptorId = descriptorId; + + this.typeManager = typeManager; + this.beanType = deploy.getBeanType(); + this.prototypeEntityBean = createPrototypeEntityBean(beanType); + + this.namedQueries = deploy.getNamedQueries(); + this.namedUpdates = deploy.getNamedUpdates(); + + this.inheritInfo = deploy.getInheritInfo(); + + this.beanFinder = deploy.getBeanFinder(); + this.persistController = deploy.getPersistController(); + this.persistListener = deploy.getPersistListener(); + this.queryAdapter = deploy.getQueryAdapter(); + + this.defaultSelectClause = deploy.getDefaultSelectClause(); + this.defaultSelectClauseSet = deploy.parseDefaultSelectClause(defaultSelectClause); + + this.idType = deploy.getIdType(); + this.idGenerator = deploy.getIdGenerator(); + this.sequenceName = deploy.getSequenceName(); + this.sequenceInitialValue = deploy.getSequenceInitialValue(); + this.sequenceAllocationSize = deploy.getSequenceAllocationSize(); + this.selectLastInsertedId = deploy.getSelectLastInsertedId(); + this.lazyFetchIncludes = InternString.intern(deploy.getLazyFetchIncludes()); + this.concurrencyMode = deploy.getConcurrencyMode(); + this.updateChangesOnly = deploy.isUpdateChangesOnly(); + + this.dependantTables = deploy.getDependantTables(); + this.compoundUniqueConstraints = deploy.getCompoundUniqueConstraints(); + + this.baseTable = InternString.intern(deploy.getBaseTable()); + + this.autoFetchTunable = EntityType.ORM.equals(entityType) && (beanFinder == null); + + // 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.propertiesEmbedded = listHelper.getEmbedded(); + this.propertiesLocal = listHelper.getLocal(); + this.propertiesMutable = listHelper.getMutable(); + this.unidirectional = listHelper.getUnidirectional(); + this.propertiesOne = listHelper.getOnes(); + this.propertiesOneExported = listHelper.getOneExported(); + this.propertiesOneExportedSave = listHelper.getOneExportedSave(); + this.propertiesOneExportedDelete = listHelper.getOneExportedDelete(); + this.propertiesOneImported = listHelper.getOneImported(); + this.propertiesOneImportedSave = listHelper.getOneImportedSave(); + this.propertiesOneImportedDelete = listHelper.getOneImportedDelete(); + + this.propertiesMany = listHelper.getMany(); + this.propertiesNonMany = listHelper.getNonMany(); + this.propertiesManySave = listHelper.getManySave(); + this.propertiesManyDelete = listHelper.getManyDelete(); + this.propertiesManyToMany = listHelper.getManyToMany(); + + this.derivedTableJoins = listHelper.getTableJoin(); + + boolean noRelationships = propertiesOne.length + propertiesMany.length == 0; + + this.cacheSharableBeans = noRelationships && deploy.getCacheOptions().isReadOnly(); + this.cacheHelp = new BeanDescriptorCacheHelp(this, owner.getCacheManager(), deploy.getCacheOptions(), cacheSharableBeans, propertiesOneImported); + this.jsonHelp = new BeanDescriptorJsonHelp(this); + + // 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 + // well. We do that later in initialiseOther(). + + saveRecurseSkippable = (0 == (propertiesOneExportedSave.length + propertiesOneImportedSave.length + propertiesManySave.length)); + + // Check if there are no cascade delete associated beans (also subject to + // change in initialiseOther()). + deleteRecurseSkippable = (0 == (propertiesOneExportedDelete.length + propertiesOneImportedDelete.length + propertiesManyDelete.length)); + + // object used to handle Id values + 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; + this.unloadProperties = new int[0]; + + } else { + EntityBeanIntercept ebi = prototypeEntityBean._ebean_getIntercept(); + this.idPropertyIndex = (idProperty == null) ? -1 : ebi.findProperty(idProperty.getName()); + this.versionPropertyIndex = (versionProperty == null) ? -1 : ebi.findProperty(versionProperty.getName()); + this.unloadProperties = derivePropertiesToUnload(prototypeEntityBean); + } + } + + /** + * Derive an array of property positions for properties that are initialised in the constructor. + * These properties need to be unloaded when populating beans for queries. + */ + private int[] derivePropertiesToUnload(EntityBean prototypeEntityBean) { + + boolean[] loaded = prototypeEntityBean._ebean_getIntercept().getLoaded(); + int[] props = new int[loaded.length]; + int pos = 0; + + // collect the positions of the properties initialised in the default constructor. + for (int i = 0; i < loaded.length; i++) { + if (loaded[i]) { + props[pos++] = i; + } + } + + if (pos == 0) { + // nothing set in the constructor + return new int[0]; + } + + // populate a smaller/minimal array + int[] unload = new int[pos]; + for (int i = 0; i < pos; i++) { + unload[i] = props[i]; + } + return unload; + } + + /** + * Create an entity bean that is used as a prototype/factory to create new instances. + */ + private EntityBean createPrototypeEntityBean(Class beanType) { + if (Modifier.isAbstract(beanType.getModifiers())) { + return null; + } + try { + return (EntityBean) beanType.newInstance(); + } catch (Exception e) { + throw new IllegalStateException("Error trying to create the prototypeEntityBean for "+beanType, e); + } + } + + private LinkedHashMap getReverseMap(LinkedHashMap propMap) { + + LinkedHashMap revMap = new LinkedHashMap(propMap.size() * 2); + + for (BeanProperty prop : propMap.values()) { + if (prop.getDbColumn() != null) { + revMap.put(prop.getDbColumn(), prop); + } + } + + return revMap; + } + + /** + * Set the server. Primarily so that the Many's can lazy load. + */ + public void setEbeanServer(SpiEbeanServer ebeanServer) { + this.ebeanServer = ebeanServer; + for (int i = 0; i < propertiesMany.length; i++) { + // used for creating lazy loading lists etc + propertiesMany[i].setLoader(ebeanServer); + } + } + + /** + * Return the EbeanServer instance that owns this BeanDescriptor. + */ + public SpiEbeanServer getEbeanServer() { + return ebeanServer; + } + + /** + * Return the type of this domain object. + */ + public EntityType getEntityType() { + return entityType; + } + + public int getPropertyCount() { + return propertyCount; + } + + public String[] getProperties() { + return properties; + } + + /** + * Initialise the Id properties first. + *

+ * These properties need to be initialised prior to the association properties + * as they are used to get the imported and exported properties. + *

+ */ + public void initialiseId() { + + if (logger.isTraceEnabled()) { + logger.trace("BeanDescriptor initialise " + fullName); + } + + if (inheritInfo != null) { + inheritInfo.setDescriptor(this); + } + + if (isEmbedded()) { + // initialise all the properties + for (BeanProperty prop : propertiesAll()) { + prop.initialise(); + } + } else { + // initialise just the Id properties + if (idProperty != null) { + idProperty.initialise(); + } + } + } + + /** + * Initialise the exported and imported parts for associated properties. + */ + public void initialiseOther() { + + if (!isEmbedded()) { + // initialise all the non-id properties + for (BeanProperty prop : propertiesAll()) { + if (!prop.isId()) { + prop.initialise(); + } + } + } + + if (unidirectional != null) { + unidirectional.initialise(); + } + + idBinder.initialise(); + idBinderInLHSSql = idBinder.getBindIdInSql(baseTableAlias); + idBinderIdSql = idBinder.getBindIdSql(baseTableAlias); + String idBinderInLHSSqlNoAlias = idBinder.getBindIdInSql(null); + String idEqualsSql = idBinder.getBindIdSql(null); + + deleteByIdSql = "delete from " + baseTable + " where " + idEqualsSql; + deleteByIdInSql = "delete from " + baseTable + " where " + idBinderInLHSSqlNoAlias + " "; + + if (!isEmbedded()) { + // parse every named update up front into sql dml + for (DeployNamedUpdate namedUpdate : namedUpdates.values()) { + DeployUpdateParser parser = new DeployUpdateParser(this); + namedUpdate.initialise(parser); + } + } + } + + public void initInheritInfo() { + if (inheritInfo != null) { + // need to check every BeanDescriptor in the inheritance hierarchy + if (saveRecurseSkippable) { + saveRecurseSkippable = inheritInfo.isSaveRecurseSkippable(); + } + if (deleteRecurseSkippable) { + deleteRecurseSkippable = inheritInfo.isDeleteRecurseSkippable(); + } + } + } + + /** + * Initialise the cache once the server has started. + */ + public void cacheInitialise() { + cacheHelp.initialise(); + } + + public SqlUpdate deleteById(Object id, List idList) { + if (id != null) { + return deleteById(id); + } else { + return deleteByIdList(idList); + } + } + + /** + * Return SQL that can be used to delete a list of Id's without any optimistic + * concurrency checking. + */ + private SqlUpdate deleteByIdList(List idList) { + + StringBuilder sb = new StringBuilder(deleteByIdInSql); + String inClause = idBinder.getIdInValueExprDelete(idList.size()); + sb.append(inClause); + + DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); + for (int i = 0; i < idList.size(); i++) { + idBinder.bindId(delete, idList.get(i)); + } + return delete; + } + + /** + * Return SQL that can be used to delete by Id without any optimistic + * concurrency checking. + */ + private SqlUpdate deleteById(Object id) { + + DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByIdSql); + + Object[] bindValues = idBinder.getBindValues(id); + for (int i = 0; i < bindValues.length; i++) { + sqlDelete.addParameter(bindValues[i]); + } + + return sqlDelete; + } + + /** + * Add objects to ElPropertyDeploy etc. These are used so that expressions on + * foreign keys don't require an extra join. + */ + public void add(BeanFkeyProperty fkey) { + elDeployCache.put(fkey.getName(), fkey); + } + + public void initialiseFkeys() { + for (int i = 0; i < propertiesOneImported.length; i++) { + propertiesOneImported[i].addFkey(); + } + } + + public boolean calculateUseCache(Boolean queryUseCache) { + return (queryUseCache != null) ? queryUseCache.booleanValue() : isBeanCaching(); + } + + public T cacheNaturalKey(SpiQuery query, SpiTransaction t) { + return cacheHelp.naturalKeyLookup(query, t); + } + + /** + * Return the cache options. + */ + public CacheOptions getCacheOptions() { + return cacheHelp.getCacheOptions(); + } + + /** + * Return the Encrypt key given the BeanProperty. + */ + public EncryptKey getEncryptKey(BeanProperty p) { + return owner.getEncryptKey(baseTable, p.getDbColumn()); + } + + /** + * Return the Encrypt key given the table and column name. + */ + public EncryptKey getEncryptKey(String tableName, String columnName) { + return owner.getEncryptKey(tableName, columnName); + } + + /** + * Execute the warming cache query (if defined) and load the cache. + */ + public void runCacheWarming() { + cacheHelp.runCacheWarming(ebeanServer); + } + + /** + * Return true if this bean type has a default select clause that is not + * simply select all properties. + */ + public boolean hasDefaultSelectClause() { + return defaultSelectClause != null; + } + + /** + * Return the default select clause. + */ + public String getDefaultSelectClause() { + return defaultSelectClause; + } + + /** + * Return the default select clause already parsed into an ordered Set. + */ + public Set getDefaultSelectClauseSet() { + return defaultSelectClauseSet; + } + + /** + * Return true if this object is the root level object in its entity + * inheritance. + */ + public boolean isInheritanceRoot() { + return inheritInfo == null || inheritInfo.isRoot(); + } + + /** + * Set the bean caching on or off. + */ + public void setUseCache(boolean useCache) { + cacheHelp.setUseCache(useCache); + } + + /** + * Return true if there is currently query caching for this type of bean. + */ + public boolean isQueryCaching() { + return cacheHelp.isQueryCaching(); + } + + /** + * Return true if there is currently bean caching for this type of bean. + */ + public boolean isBeanCaching() { + return cacheHelp.isBeanCaching(); + } + + public boolean isManyPropCaching() { + return isBeanCaching(); + } + + /** + * Return true if the persist request needs to notify the cache. + */ + public boolean isCacheNotify() { + return cacheHelp.isCacheNotify(); + } + + /** + * Clear the query cache. + */ + public void queryCacheClear() { + cacheHelp.queryCacheClear(); + } + + /** + * Get a query result from the query cache. + */ + public BeanCollection queryCacheGet(Object id) { + return cacheHelp.queryCacheGet(id); + } + + /** + * Put a query result into the query cache. + */ + public void queryCachePut(Object id, BeanCollection query) { + cacheHelp.queryCachePut(id, query); + } + + /** + * Try to load the beanCollection from cache return true if successful. + */ + public boolean cacheManyPropLoad(BeanPropertyAssocMany many, BeanCollection bc, Object parentId, Boolean readOnly) { + return cacheHelp.manyPropLoad(many, bc, parentId, readOnly); + } + + /** + * Put the beanCollection into the cache. + */ + public void cacheManyPropPut(BeanPropertyAssocMany many, BeanCollection bc, Object parentId) { + cacheHelp.manyPropPut(many, bc, parentId); + } + + public void cacheManyPropRemove(Object parentId, String propertyName) { + cacheHelp.manyPropRemove(parentId, propertyName); + } + + public void cacheManyPropClear(String propertyName) { + cacheHelp.manyPropClear(propertyName); + } + + /** + * Return the CachedManyIds for a given bean and property. Returns null if not in the cache. + */ + public CachedManyIds cacheManyPropGet(Object parentId, String propertyName) { + return cacheHelp.manyPropGet(parentId, propertyName); + } + + /** + * Clear the bean cache. + */ + public void cacheBeanClear() { + cacheHelp.beanCacheClear(); + } + + public void cacheBeanPut(T bean) { + cacheBeanPutData((EntityBean)bean); + } + + /** + * Extract the raw cache data from the bean. + */ + public CachedBeanData cacheBeanExtractData(EntityBean bean) { + return cacheHelp.beanExtractData(bean); + } + + /** + * Load the raw cache data into the bean. + */ + public void cacheBeanLoadData(EntityBean bean, CachedBeanData data) { + cacheHelp.beanLoadData(bean, data); + } + + /** + * Put a bean into the bean cache. + */ + public void cacheBeanPutData(EntityBean bean) { + cacheHelp.beanCachePut(bean); + } + + /** + * Return a bean from the bean cache (or null). + */ + public T cacheBeanGet(SpiQuery query, PersistenceContext context) { + return cacheHelp.beanCacheGet(query, context); + } + + /** + * Remove a bean from the cache given its Id. + */ + public void cacheBeanRemove(Object id) { + cacheHelp.beanCacheRemove(id); + } + + /** + * Returns true if it managed to populate/load the bean from the cache. + */ + public boolean cacheBeanLoad(EntityBean bean, EntityBeanIntercept ebi, Object id) { + return cacheHelp.beanCacheLoad(bean, ebi, id); + } + + /** + * Returns true if it managed to populate/load the bean from the cache. + */ + public boolean cacheBeanLoad(EntityBeanIntercept ebi) { + EntityBean bean = ebi.getOwner(); + Object id = getId(bean); + return cacheBeanLoad(bean, ebi, id); + } + + /** + * Try to hit the cache using the natural key. + */ + public T cacheNaturalKeyLookup(SpiQuery query, SpiTransaction t) { + return cacheHelp.naturalKeyLookup(query, t); + } + + /** + * Invalidate parts of cache due to SqlUpdate or external modification etc. + */ + public void cacheHandleBulkUpdate(TableIUD tableIUD) { + cacheHelp.handleBulkUpdate(tableIUD); + } + + /** + * Remove a bean from the cache given its Id. + */ + public void cacheHandleDelete(Object id, PersistRequestBean deleteRequest) { + cacheHelp.handleDelete(id, deleteRequest); + } + + public void cacheHandleInsert(Object id, PersistRequestBean insertRequest) { + cacheHelp.handleInsert(id, insertRequest); + } + + /** + * Update the cached bean data. + */ + public void cacheHandleUpdate(Object id, PersistRequestBean updateRequest) { + cacheHelp.handleUpdate(id, updateRequest); + } + + /** + * Return the base table alias. This is always the first letter of the bean + * name. + */ + public String getBaseTableAlias() { + return baseTableAlias; + } + + public void preAllocateIds(int batchSize) { + if (idGenerator != null) { + idGenerator.preAllocateIds(batchSize); + } + } + + public Object nextId(Transaction t) { + if (idGenerator != null) { + return idGenerator.nextId(t); + } else { + return null; + } + } + + public DeployPropertyParser createDeployPropertyParser() { + return new DeployPropertyParser(this); + } + + /** + * Convert the logical orm update statement into sql by converting the bean + * properties and bean name to database columns and table. + */ + public String convertOrmUpdateToSql(String ormUpdateStatement) { + return new DeployUpdateParser(this).parse(ormUpdateStatement); + } + + @Override + public List collectQueryPlanStatistics(boolean reset) { + return collectQueryPlanStatisticsInternal(reset, false); + } + + @Override + public List collectAllQueryPlanStatistics(boolean reset) { + return collectQueryPlanStatisticsInternal(reset, false); + } + + public List collectQueryPlanStatisticsInternal(boolean reset, boolean collectAll) { + List list = new ArrayList(queryPlanCache.size()); + for (CQueryPlan queryPlan : queryPlanCache.values()) { + Snapshot snapshot = queryPlan.getSnapshot(reset); + if (collectAll || snapshot.getExecutionCount() > 0) { + list.add(snapshot); + } + } + return list; + } + + /** + * Reset the statistics on all the query plans. + */ + public void clearQueryStatistics() { + for (CQueryPlan queryPlan : queryPlanCache.values()) { + queryPlan.resetStatistics(); + } + } + + /** + * Execute the postLoad if a BeanPersistController exists for this bean. + */ + public void postLoad(Object bean, Set includedProperties) { + BeanPersistController c = persistController; + if (c != null) { + c.postLoad(bean, includedProperties); + } + } + + public CQueryPlan getQueryPlan(HashQueryPlan key) { + return queryPlanCache.get(key); + } + + public void putQueryPlan(HashQueryPlan key, CQueryPlan plan) { + queryPlanCache.put(key, plan); + } + + /** + * Get a UpdatePlan for a given hash. + */ + public SpiUpdatePlan getUpdatePlan(Integer key) { + return updatePlanCache.get(key); + } + + /** + * Add a UpdatePlan to the cache with a given hash. + */ + public void putUpdatePlan(Integer key, SpiUpdatePlan plan) { + updatePlanCache.put(key, plan); + } + + /** + * Return true if updates should only include changed properties. Otherwise + * all loaded properties are included in the update. + */ + public boolean isUpdateChangesOnly() { + return updateChangesOnly; + } + + /** + * Return true if save does not recurse to other beans. That is return true if + * there are no assoc one or assoc many beans that cascade save. + */ + public boolean isSaveRecurseSkippable() { + return saveRecurseSkippable; + } + + /** + * Return true if delete does not recurse to other beans. That is return true + * if there are no assoc one or assoc many beans that cascade delete. + */ + public boolean isDeleteRecurseSkippable() { + return deleteRecurseSkippable; + } + + /** + * Return the many property included in the query or null if one is not. + */ + public BeanPropertyAssocMany getManyProperty(SpiQuery query) { + + OrmQueryDetail detail = query.getDetail(); + for (int i = 0; i < propertiesMany.length; i++) { + if (detail.includes(propertiesMany[i].getName())) { + return propertiesMany[i]; + } + } + + return null; + } + + /** + * Return a raw expression for 'where parent id in ...' clause. + */ + public String getParentIdInExpr(int parentIdSize, String rawWhere) { + String inClause = idBinder.getIdInValueExpr(parentIdSize); + return idBinder.isIdInExpandedForm() ? inClause : rawWhere + inClause; + } + + /** + * Return the IdBinder which is helpful for handling the various types of Id. + */ + public IdBinder getIdBinder() { + return idBinder; + } + + /** + * Return the sql for binding an id. This is the columns with table alias that + * make up the id. + */ + public String getIdBinderIdSql() { + return idBinderIdSql; + } + + /** + * Return the sql for binding id's using an IN clause. + */ + public String getIdBinderInLHSSql() { + return idBinderInLHSSql; + } + + /** + * Bind the idValue to the preparedStatement. + *

+ * This takes care of the various id types such as embedded beans etc. + *

+ */ + public void bindId(DataBind dataBind, Object idValue) throws SQLException { + idBinder.bindId(dataBind, idValue); + } + + /** + * Return the id as an array of scalar bindable values. + *

+ * This 'flattens' any EmbeddedId or multiple Id property cases. + *

+ */ + public Object[] getBindIdValues(Object idValue) { + return idBinder.getBindValues(idValue); + } + + /** + * Return a named query. + */ + public DeployNamedQuery getNamedQuery(String name) { + return namedQueries.get(name); + } + + public DeployNamedQuery addNamedQuery(DeployNamedQuery deployNamedQuery) { + return namedQueries.put(deployNamedQuery.getName(), deployNamedQuery); + } + + /** + * Return a named update. + */ + public DeployNamedUpdate getNamedUpdate(String name) { + return namedUpdates.get(name); + } + + /** + * Creates a new EntityBean. + */ + public EntityBean createEntityBean() { + try { + EntityBean bean = (EntityBean)prototypeEntityBean._ebean_newInstance(); + + if (unloadProperties.length > 0) { + // 'unload' any properties initialised in the default constructor + EntityBeanIntercept ebi = bean._ebean_getIntercept(); + for (int i = 0; i < unloadProperties.length; i++) { + ebi.setPropertyUnloaded(unloadProperties[i]); + } + } + return bean; + + } catch (Exception ex) { + throw new PersistenceException(ex); + } + } + + /** + * Create a reference bean based on the id. + */ + @SuppressWarnings("unchecked") + public T createReference(Boolean readOnly, Object id) { + + if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) { + CachedBeanData d = cacheHelp.beanCacheGetData(id); + if (d != null) { + Object shareableBean = d.getSharableBean(); + if (shareableBean != null) { + return (T) shareableBean; + } + } + } + try { + EntityBean eb = createEntityBean(); + + convertSetId(id, eb); + + EntityBeanIntercept ebi = eb._ebean_getIntercept(); + ebi.setBeanLoader(ebeanServer); + + // Note: not creating proxies for many's... + ebi.setReference(idPropertyIndex); + + return (T) eb; + + } catch (Exception ex) { + throw new PersistenceException(ex); + } + } + + /** + * Return the bean property traversing the object graph and taking into + * account inheritance. + */ + public BeanProperty getBeanPropertyFromPath(String path) { + + String[] split = SplitName.splitBegin(path); + if (split[1] == null) { + return _findBeanProperty(split[0]); + } + BeanPropertyAssoc assocProp = (BeanPropertyAssoc) _findBeanProperty(split[0]); + BeanDescriptor targetDesc = assocProp.getTargetDescriptor(); + + return targetDesc.getBeanPropertyFromPath(split[1]); + } + + /** + * Return the BeanDescriptor for a given path of Associated One or Many beans. + */ + public BeanDescriptor getBeanDescriptor(String path) { + if (path == null) { + return this; + } + String[] splitBegin = SplitName.splitBegin(path); + + BeanProperty beanProperty = propMap.get(splitBegin[0]); + if (beanProperty instanceof BeanPropertyAssoc) { + BeanPropertyAssoc assocProp = (BeanPropertyAssoc) beanProperty; + return assocProp.getTargetDescriptor().getBeanDescriptor(splitBegin[1]); + + } else { + throw new RuntimeException("Error getting BeanDescriptor for path " + path + " from " + getFullName()); + } + } + + /** + * Return the BeanDescriptor of another bean type. + */ + public BeanDescriptor getBeanDescriptor(Class otherType) { + return owner.getBeanDescriptor(otherType); + } + + /** + * Return the "shadow" property to support unidirectional relationships. + *

+ * For bidirectional this is a real property on the bean. For unidirectional + * relationships we have this 'shadow' property which is not externally + * visible. + *

+ */ + public BeanPropertyAssocOne getUnidirectional() { + if (unidirectional != null) { + return unidirectional; + } + if (inheritInfo != null && !inheritInfo.isRoot()) { + return inheritInfo.getParent().getBeanDescriptor().getUnidirectional(); + } + return null; + } + + /** + * Get a property value from a bean of this type. + */ + public Object getValue(EntityBean bean, String property) { + return getBeanProperty(property).getValue(bean); + } + + /** + * Return true if this bean type should use IdGeneration. + *

+ * If this is false and the Id is null it is assumed that a database auto + * increment feature is being used to populate the id. + *

+ */ + public boolean isUseIdGenerator() { + return idGenerator != null; + } + + /** + * Return the alternate "Id" that identifies this BeanDescriptor. This is an + * alternative to using the bean class name. + */ + public String getDescriptorId() { + return descriptorId; + } + + /** + * Return the class type this BeanDescriptor describes. + */ + public Class getBeanType() { + return beanType; + } + + /** + * Return the bean class name this descriptor is used for. + *

+ * If this BeanDescriptor is for a table then this returns the table name + * instead. + *

+ */ + public String getFullName() { + return fullName; + } + + /** + * Return the short name of the entity bean. + */ + public String getName() { + return name; + } + + /** + * Summary description. + */ + public String toString() { + return fullName; + } + + /** + * Helper method to return the unique property. If only one property makes up + * the unique id then it's value is returned. If there is a concatenated + * unique id then a Map is built with the keys being the names of the + * properties that make up the unique id. + */ + public Object getId(EntityBean bean) { + return (idProperty == null) ? null : idProperty.getValue(bean); + } + + /** + * Return the default order by that may need to be added if a many property is + * included in the query. + */ + public String getDefaultOrderBy() { + return idBinder.getDefaultOrderBy(); + } + + /** + * Convert the type of the idValue if required. + */ + public Object convertId(Object idValue) { + return idBinder.convertSetId(idValue, null); + } + + /** + * Convert and set the id value. + *

+ * If the bean is not null, the id value is set to the id property of the bean + * after it has been converted to the correct type. + *

+ */ + public Object convertSetId(Object idValue, EntityBean bean) { + return idBinder.convertSetId(idValue, bean); + } + + /** + * Get a BeanProperty by its name. + */ + public BeanProperty getBeanProperty(String propName) { + return propMap.get(propName); + } + + public void sort(List list, String sortByClause) { + + ElComparator comparator = getElComparator(sortByClause); + Collections.sort(list, comparator); + } + + public ElComparator getElComparator(String propNameOrSortBy) { + ElComparator c = comparatorCache.get(propNameOrSortBy); + if (c == null) { + c = createComparator(propNameOrSortBy); + comparatorCache.put(propNameOrSortBy, c); + } + return c; + } + + /** + * Return true if the lazy loading property is a Many in which case just + * define a Reference for the collection and not invoke a query. + */ + public boolean lazyLoadMany(EntityBeanIntercept ebi) { + + int lazyLoadProperty = ebi.getLazyLoadPropertyIndex(); + if (lazyLoadProperty == -1) { + return false; + } + String lazyLoadPropertyName = ebi.getProperty(lazyLoadProperty); + BeanProperty lazyLoadBeanProp = getBeanProperty(lazyLoadPropertyName); + + if (lazyLoadBeanProp instanceof BeanPropertyAssocMany) { + BeanPropertyAssocMany manyProp = (BeanPropertyAssocMany) lazyLoadBeanProp; + manyProp.createReference(ebi.getOwner()); + ebi.setLoadedLazy(); + return true; + } + + return false; + } + + /** + * Return a Comparator for local sorting of lists. + * + * @param sortByClause + * list of property names with optional ASC or DESC suffix. + */ + @SuppressWarnings("unchecked") + private ElComparator createComparator(String sortByClause) { + + SortByClause sortBy = SortByClauseParser.parse(sortByClause); + if (sortBy.size() == 1) { + // simple comparator for a single property + return createPropertyComparator(sortBy.getProperties().get(0)); + } + + // create a compound comparator based on the list of properties + ElComparator[] comparators = new ElComparator[sortBy.size()]; + + List sortProps = sortBy.getProperties(); + for (int i = 0; i < sortProps.size(); i++) { + Property sortProperty = sortProps.get(i); + comparators[i] = createPropertyComparator(sortProperty); + } + + return new ElComparatorCompound(comparators); + } + + private ElComparator createPropertyComparator(Property sortProp) { + + ElPropertyValue elGetValue = getElGetValue(sortProp.getName()); + + Boolean nullsHigh = sortProp.getNullsHigh(); + if (nullsHigh == null) { + nullsHigh = Boolean.TRUE; + } + return new ElComparatorProperty(elGetValue, sortProp.isAscending(), nullsHigh); + } + + /** + * Get an Expression language Value object. + */ + public ElPropertyValue getElGetValue(String propName) { + ElPropertyValue elGetValue = elCache.get(propName); + if (elGetValue != null) { + return elGetValue; + } + elGetValue = buildElGetValue(propName, null, false); + if (elGetValue != null) { + elCache.put(propName, elGetValue); + } + return elGetValue; + } + + /** + * Similar to ElPropertyValue but also uses foreign key shortcuts. + *

+ * The foreign key shortcuts means we can avoid unnecessary joins. + *

+ */ + public ElPropertyDeploy getElPropertyDeploy(String propName) { + ElPropertyDeploy elProp = elDeployCache.get(propName); + if (elProp != null) { + return elProp; + } + if (!propName.contains(".")) { + // No period means simple property and no need to look for + // foreign key properties (in order to avoid an extra join) + elProp = getElGetValue(propName); + } else { + elProp = buildElGetValue(propName, null, true); + } + if (elProp != null) { + elDeployCache.put(propName, elProp); + } + return elProp; + } + + protected ElPropertyValue buildElGetValue(String propName, ElPropertyChainBuilder chain, boolean propertyDeploy) { + + if (propertyDeploy && chain != null) { + ElPropertyDeploy fk = elDeployCache.get(propName); + if (fk != null && fk instanceof BeanFkeyProperty) { + // propertyDeploy chain for foreign key column + return ((BeanFkeyProperty)fk).create(chain.getExpression(), chain.isContainsMany()); + } + } + + int basePos = propName.indexOf('.'); + if (basePos > -1) { + // nested or embedded property + String baseName = propName.substring(0, basePos); + String remainder = propName.substring(basePos + 1); + + BeanProperty assocProp = _findBeanProperty(baseName); + if (assocProp == null) { + return null; + } + return assocProp.buildElPropertyValue(propName, remainder, chain, propertyDeploy); + } + + BeanProperty property = _findBeanProperty(propName); + if (chain == null) { + return property; + } + if (property == null) { + throw new PersistenceException("No property found for [" + propName + "] in expression " + chain.getExpression()); + } + if (property.containsMany()) { + chain.setContainsMany(true); + } + return chain.add(property).build(); + } + + /** + * Find a BeanProperty including searching the inheritance hierarchy. + *

+ * This searches this BeanDescriptor and then searches further down the + * inheritance tree (not up). + *

+ */ + public BeanProperty findBeanProperty(String propName) { + int basePos = propName.indexOf('.'); + if (basePos > -1) { + // embedded property + String baseName = propName.substring(0, basePos); + return _findBeanProperty(baseName); + } + + return _findBeanProperty(propName); + } + + private BeanProperty _findBeanProperty(String propName) { + BeanProperty prop = propMap.get(propName); + if (prop == null && inheritInfo != null) { + // search in sub types... + return inheritInfo.findSubTypeProperty(propName); + } + return prop; + } + + /** + * Reset the many properties to empty state ready for reloading. + */ + public void resetManyProperties(Object dbBean) { + + EntityBean bean = (EntityBean)dbBean; + for (int i = 0; i < propertiesMany.length; i++) { + propertiesMany[i].resetMany(bean); + } + } + + /** + * Return the name of the server this BeanDescriptor belongs to. + */ + public String getServerName() { + return serverName; + } + + /** + * Return true if this bean can cache sharable instances. + *

+ * This means is has no relationships and has readOnly=true in its cache + * options. + *

+ */ + public boolean isCacheSharableBeans() { + return cacheSharableBeans; + } + + /** + * Return true if queries for beans of this type are autoFetch tunable. + */ + public boolean isAutoFetchTunable() { + return autoFetchTunable; + } + + /** + * Returns the Inheritance mapping information. This will be null if this type + * of bean is not involved in any ORM inheritance mapping. + */ + public InheritInfo getInheritInfo() { + return inheritInfo; + } + + /** + * Return true if this is an embedded bean. + */ + public boolean isEmbedded() { + return EntityType.EMBEDDED.equals(entityType); + } + + /** + * Return the tables this bean is dependent on. This implies that if any of + * these tables are modified then cached beans may be invalidated. + */ + public String[] getDependantTables() { + return dependantTables; + } + + /** + * Return the compound unique constraints. + */ + public CompoundUniqueContraint[] getCompoundUniqueConstraints() { + return compoundUniqueConstraints; + } + + /** + * Return the beanListener. + */ + public BeanPersistListener getPersistListener() { + return persistListener; + } + + /** + * Return the beanFinder. Usually null unless overriding the finder. + */ + public BeanFinder getBeanFinder() { + return beanFinder; + } + + /** + * Return the BeanQueryAdapter or null if none is defined. + */ + public BeanQueryAdapter getQueryAdapter() { + return queryAdapter; + } + + /** + * De-register the BeanPersistListener. + */ + @SuppressWarnings("unchecked") + public void deregister(BeanPersistListener listener) { + // volatile read... + BeanPersistListener currListener = persistListener; + if (currListener == null) { + // nothing to deregister + } else { + BeanPersistListener deregListener = listener; + if (currListener instanceof ChainedBeanPersistListener) { + // remove it from the existing chain + persistListener = ((ChainedBeanPersistListener) currListener).deregister(deregListener); + } else if (currListener.equals(deregListener)) { + persistListener = null; + } + } + } + + /** + * De-register the BeanPersistController. + */ + public void deregister(BeanPersistController controller) { + // volatile read... + BeanPersistController c = persistController; + if (c != null) { + if (c instanceof ChainedBeanPersistController) { + // remove it from the existing chain + persistController = ((ChainedBeanPersistController) c).deregister(controller); + } else if (c.equals(controller)) { + persistController = null; + } + } + } + + /** + * Register the new BeanPersistController. + */ + @SuppressWarnings("unchecked") + public void register(BeanPersistListener newPersistListener) { + + if (newPersistListener.isRegisterFor(beanType)) { + // volatile read... + BeanPersistListener currListener = persistListener; + if (currListener == null) { + persistListener = newPersistListener; + } else { + if (currListener instanceof ChainedBeanPersistListener) { + // add it to the existing chain + persistListener = ((ChainedBeanPersistListener) currListener).register(newPersistListener); + } else { + // build new chain of the 2 + persistListener = new ChainedBeanPersistListener(currListener, newPersistListener); + } + } + } + } + + /** + * Register the new BeanPersistController. + */ + public void register(BeanPersistController newController) { + + if (newController.isRegisterFor(beanType)) { + // volatile read... + BeanPersistController c = persistController; + if (c == null) { + persistController = newController; + } else { + if (c instanceof ChainedBeanPersistController) { + // add it to the existing chain + persistController = ((ChainedBeanPersistController) c).register(newController); + } else { + // build new chain of the 2 + persistController = new ChainedBeanPersistController(c, newController); + } + } + } + } + + /** + * Return the Controller. + */ + public BeanPersistController getPersistController() { + return persistController; + } + + /** + * Returns true if this bean is based on a table (or possibly view) and + * returns false if this bean is based on a raw sql select statement. + *

+ * When false querying this bean is based on a supplied sql select statement + * placed in the orm xml file (as opposed to Ebean generated sql). + *

+ */ + public boolean isSqlSelectBased() { + return EntityType.SQL.equals(entityType); + } + + /** + * Return the base table. Only properties mapped to the base table are by + * default persisted. + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Return the identity generation type. + */ + public IdType getIdType() { + return idType; + } + + /** + * Return the sequence name. + */ + public String getSequenceName() { + return sequenceName; + } + + /** + * Return the sequence initial value. + */ + public int getSequenceInitialValue() { + return sequenceInitialValue; + } + + /** + * Return the sequence allocation size. + */ + public int getSequenceAllocationSize() { + return sequenceAllocationSize; + } + + /** + * Return the SQL used to return the last inserted id. + *

+ * This is only used with Identity columns and getGeneratedKeys is not + * supported. + *

+ */ + public String getSelectLastInsertedId() { + return selectLastInsertedId; + } + + /** + * Return the IdGenerator. + */ + public IdGenerator getIdGenerator() { + return idGenerator; + } + + /** + * Return the includes for getReference(). + */ + public String getLazyFetchIncludes() { + return lazyFetchIncludes; + } + + /** + * Return the TableJoins. + *

+ * For properties mapped to secondary tables rather than the base table. + *

+ */ + public TableJoin[] tableJoins() { + return derivedTableJoins; + } + + /** + * Return a collection of all BeanProperty. This includes transient properties. + */ + public Collection propertiesAll() { + return propMap.values(); + } + + /** + * Return the non transient non id properties. + */ + public BeanProperty[] propertiesNonTransient() { + return propertiesNonTransient; + } + + /** + * Return the transient properties. + */ + public BeanProperty[] propertiesTransient() { + return propertiesTransient; + } + + /** + * Return the beans that are embedded. These share the base table with the + * owner bean. + */ + public BeanPropertyAssocOne[] propertiesEmbedded() { + return propertiesEmbedded; + } + + public BeanProperty getIdProperty() { + return idProperty; + } + + /** + * Return true if this bean should be inserted rather than updated. + * + * @param ebi + * The entity bean intercept + * @param insertMode + * true if the 'root request' was an insert rather than an update + */ + public boolean isInsertMode(EntityBeanIntercept ebi, boolean insertMode) { + + if (ebi.isLoaded()) { + // must be an update as the bean is loaded + return false; + } + + if (idProperty.isEmbedded()) { + // not using Id generator so just base on isLoaded() + return !ebi.isLoaded(); + } + if (!hasIdProperty(ebi)) { + // No Id property means it must be an insert + return true; + } + // same as the 'root request' + return insertMode; + } + + public boolean isReference(EntityBeanIntercept ebi) { + return ebi.isReference() || hasIdPropertyOnly(ebi); + } + + public boolean hasIdPropertyOnly(EntityBeanIntercept ebi) { + return ebi.hasIdOnly(idPropertyIndex); + } + + public boolean hasIdProperty(EntityBeanIntercept ebi) { + return idPropertyIndex > -1 && ebi.isLoadedProperty(idPropertyIndex); + } + + public boolean hasVersionProperty(EntityBeanIntercept ebi) { + return versionPropertyIndex > -1 && ebi.isLoadedProperty(versionPropertyIndex); + } + + /** + * Check for mutable scalar types and mark as dirty if necessary. + */ + public void checkMutableProperties(EntityBeanIntercept ebi) { + for (int i = 0; i < propertiesMutable.length; i++) { + BeanProperty beanProperty = propertiesMutable[i]; + if (ebi.isDirtyProperty(beanProperty.getPropertyIndex())) { + // already marked as dirty + } else if (ebi.isLoadedProperty(beanProperty.getPropertyIndex())) { + Object value = beanProperty.getValue(ebi.getOwner()); + if (value == null || beanProperty.isDirtyValue(value)) { + // mutable scalar value which is considered dirty so mark + // it as such so that it is included in an update + ebi.markPropertyAsChanged(beanProperty.getPropertyIndex()); + } + } + } + } + + 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. + */ + public BeanPropertyAssocOne[] propertiesOne() { + return propertiesOne; + } + + /** + * Returns ManyToOnes and OneToOnes on the imported owning side. + *

+ * Excludes OneToOnes on the exported side. + *

+ */ + public BeanPropertyAssocOne[] propertiesOneImported() { + return propertiesOneImported; + } + + /** + * Imported Assoc Ones with cascade save true. + */ + public BeanPropertyAssocOne[] propertiesOneImportedSave() { + return propertiesOneImportedSave; + } + + /** + * Imported Assoc Ones with cascade delete true. + */ + public BeanPropertyAssocOne[] propertiesOneImportedDelete() { + return propertiesOneImportedDelete; + } + + /** + * Exported assoc ones with cascade save. + */ + public BeanPropertyAssocOne[] propertiesOneExportedSave() { + return propertiesOneExportedSave; + } + + /** + * Exported assoc ones with delete cascade. + */ + public BeanPropertyAssocOne[] propertiesOneExportedDelete() { + return propertiesOneExportedDelete; + } + + /** + * All Non Assoc Many's for this descriptor. + */ + public BeanProperty[] propertiesNonMany() { + return propertiesNonMany; + } + + /** + * All Assoc Many's for this descriptor. + */ + public BeanPropertyAssocMany[] propertiesMany() { + return propertiesMany; + } + + /** + * Assoc Many's with save cascade. + */ + public BeanPropertyAssocMany[] propertiesManySave() { + return propertiesManySave; + } + + /** + * Assoc Many's with delete cascade. + */ + public BeanPropertyAssocMany[] propertiesManyDelete() { + return propertiesManyDelete; + } + + /** + * Assoc ManyToMany's. + */ + public BeanPropertyAssocMany[] propertiesManyToMany() { + return propertiesManyToMany; + } + + /** + * Return the first version property that exists on the bean. Returns null if + * no version property exists on the bean. + *

+ * Note that this DOES NOT find a version property on an embedded bean. + *

+ */ + public BeanProperty getVersionProperty() { + return versionProperty; + } + + /** + * Scalar properties without the unique id or secondary table properties. + */ + public BeanProperty[] propertiesBaseScalar() { + return propertiesBaseScalar; + } + + /** + * Return properties that are immutable compound value objects. + *

+ * These are compound types but are not enhanced (Embedded are enhanced). + *

+ */ + public BeanPropertyCompound[] propertiesBaseCompound() { + return propertiesBaseCompound; + } + + /** + * Return the properties local to this type for inheritance. + */ + public BeanProperty[] propertiesLocal() { + return propertiesLocal; + } + + public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException { + jsonHelp.jsonWrite(writeJson, bean, null); + } + + public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException { + jsonHelp.jsonWrite(writeJson, bean, key); + } + + protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException { + jsonHelp.jsonWriteProperties(writeJson, bean); + } + + public T jsonRead(JsonParser parser, String path) throws IOException { + return jsonHelp.jsonRead(parser, path); + } + + protected T jsonReadObject(JsonParser parser, String path) throws IOException { + return jsonHelp.jsonReadObject(parser, path); + } +} 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 8099ffe9d..4069f8819 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -1,1438 +1,1438 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebean.BackgroundExecutor; -import com.avaje.ebean.Model; -import com.avaje.ebean.RawSql; -import com.avaje.ebean.RawSqlBuilder; -import com.avaje.ebean.annotation.ConcurrencyMode; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.cache.ServerCacheManager; -import com.avaje.ebean.config.EncryptKey; -import com.avaje.ebean.config.EncryptKeyManager; -import com.avaje.ebean.config.NamingConvention; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebean.config.dbplatform.DbIdentity; -import com.avaje.ebean.config.dbplatform.IdGenerator; -import com.avaje.ebean.config.dbplatform.IdType; -import com.avaje.ebean.event.BeanFinder; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.TransactionEventTable; -import com.avaje.ebeaninternal.server.core.*; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.id.IdBinder; -import com.avaje.ebeaninternal.server.deploy.id.IdBinderEmbedded; -import com.avaje.ebeaninternal.server.deploy.id.IdBinderFactory; -import com.avaje.ebeaninternal.server.deploy.meta.*; -import com.avaje.ebeaninternal.server.deploy.parse.*; -import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator; -import com.avaje.ebeaninternal.server.lib.util.Dnode; -import com.avaje.ebeaninternal.server.properties.BeanPropertiesReader; -import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo; -import com.avaje.ebeaninternal.server.properties.BeanPropertyInfoFactory; -import com.avaje.ebeaninternal.server.properties.EnhanceBeanPropertyInfoFactory; -import com.avaje.ebeaninternal.server.type.TypeManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.persistence.MappedSuperclass; -import javax.persistence.PersistenceException; -import javax.persistence.Transient; -import javax.sql.DataSource; -import java.io.Serializable; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.*; - -/** - * Creates BeanDescriptors. - */ -public class BeanDescriptorManager implements BeanDescriptorMap { - - private static final Logger logger = LoggerFactory.getLogger(BeanDescriptorManager.class); - - private static final BeanDescComparator beanDescComparator = new BeanDescComparator(); - - private final ReadAnnotations readAnnotations = new ReadAnnotations(); - - private final TransientProperties transientProperties; - - /** - * Helper to derive inheritance information. - */ - private final DeployInherit deplyInherit; - - private final BeanPropertyInfoFactory reflectFactory; - - private final DeployUtil deployUtil; - - private final TypeManager typeManager; - - private final PersistControllerManager persistControllerManager; - - private final BeanFinderManager beanFinderManager; - - private final PersistListenerManager persistListenerManager; - - private final BeanQueryAdapterManager beanQueryAdapterManager; - - private final NamingConvention namingConvention; - - private final DeployCreateProperties createProperties; - - private final DeployOrmXml deployOrmXml; - - private final BeanManagerFactory beanManagerFactory; - - private int enhancedClassCount; - - private final boolean updateChangesOnly; - - private final BootupClasses bootupClasses; - - private final String serverName; - - private Map, DeployBeanInfo> deplyInfoMap = new HashMap, DeployBeanInfo>(); - - private final Map, BeanTable> beanTableMap = new HashMap, BeanTable>(); - - private final Map> descMap = new HashMap>(); - private final Map> idDescMap = new HashMap>(); - - private final Map> beanManagerMap = new HashMap>(); - - private final Map>> tableToDescMap = new HashMap>>(); - - private List> immutableDescriptorList; - - private final Set descriptorUniqueIds = new HashSet(); - - private final DbIdentity dbIdentity; - - private final DataSource dataSource; - - private final DatabasePlatform databasePlatform; - - private final UuidIdGenerator uuidIdGenerator = new UuidIdGenerator(); - - private final ServerCacheManager cacheManager; - - private final BackgroundExecutor backgroundExecutor; - - private final int dbSequenceBatchSize; - - private final EncryptKeyManager encryptKeyManager; - - private final IdBinderFactory idBinderFactory; - - private final XmlConfig xmlConfig; - - private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory; - - private final boolean eagerFetchLobs; - - /** - * Create for a given database dbConfig. - */ - public BeanDescriptorManager(InternalConfiguration config) { - - this.serverName = InternString.intern(config.getServerConfig().getName()); - this.cacheManager = config.getCacheManager(); - this.xmlConfig = config.getXmlConfig(); - this.dbSequenceBatchSize = config.getServerConfig().getDatabaseSequenceBatchSize(); - this.backgroundExecutor = config.getBackgroundExecutor(); - this.dataSource = config.getServerConfig().getDataSource(); - this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager(); - this.databasePlatform = config.getServerConfig().getDatabasePlatform(); - this.idBinderFactory = new IdBinderFactory(databasePlatform.isIdInExpandedForm()); - this.eagerFetchLobs = config.getServerConfig().isEagerFetchLobs(); - - this.bootupClasses = config.getBootupClasses(); - this.createProperties = config.getDeployCreateProperties(); - this.typeManager = config.getTypeManager(); - this.namingConvention = config.getServerConfig().getNamingConvention(); - this.dbIdentity = config.getDatabasePlatform().getDbIdentity(); - this.deplyInherit = config.getDeployInherit(); - this.deployOrmXml = config.getDeployOrmXml(); - this.deployUtil = config.getDeployUtil(); - - this.beanManagerFactory = new BeanManagerFactory(config.getServerConfig(), config.getDatabasePlatform()); - - this.updateChangesOnly = config.getServerConfig().isUpdateChangesOnly(); - - this.beanLifecycleAdapterFactory = new BeanLifecycleAdapterFactory(); - this.persistControllerManager = new PersistControllerManager(bootupClasses); - this.persistListenerManager = new PersistListenerManager(bootupClasses); - this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses); - - this.beanFinderManager = new DefaultBeanFinderManager(); - - this.reflectFactory = createReflectionFactory(); - this.transientProperties = new TransientProperties(); - } - - public BeanDescriptor getBeanDescriptorById(String descriptorId) { - return idDescMap.get(descriptorId); - } - - @SuppressWarnings("unchecked") - public BeanDescriptor getBeanDescriptor(Class entityType) { - return (BeanDescriptor) descMap.get(entityType.getName()); - } - - @SuppressWarnings("unchecked") - public BeanDescriptor getBeanDescriptor(String entityClassName) { - return (BeanDescriptor) descMap.get(entityClassName); - } - - public String getServerName() { - return serverName; - } - - public ServerCacheManager getCacheManager() { - return cacheManager; - } - - public NamingConvention getNamingConvention() { - return namingConvention; - } - - /** - * Set the internal EbeanServer instance to all BeanDescriptors. - */ - public void setEbeanServer(SpiEbeanServer internalEbean) { - for (BeanDescriptor desc : immutableDescriptorList) { - desc.setEbeanServer(internalEbean); - } - } - - public IdBinder createIdBinder(BeanProperty idProperty) { - return idBinderFactory.createIdBinder(idProperty); - } - - public void deploy() { - - try { - createListeners(); - readEmbeddedDeployment(); - readEntityDeploymentInitial(); - readEntityBeanTable(); - readEntityDeploymentAssociations(); - readInheritedIdGenerators(); - - // creates the BeanDescriptors - readEntityRelationships(); - readRawSqlQueries(); - - List> list = new ArrayList>(descMap.values()); - Collections.sort(list, beanDescComparator); - immutableDescriptorList = Collections.unmodifiableList(list); - - // put into map using the "desriptorId" (alternative to class name) - for (BeanDescriptor d : list) { - idDescMap.put(d.getDescriptorId(), d); - } - - initialiseAll(); - readForeignKeys(); - - readTableToDescriptor(); - - logStatus(); - - deplyInfoMap.clear(); - deplyInfoMap = null; - } catch (RuntimeException e) { - String msg = "Error in deployment"; - logger.error(msg, e); - throw e; - } - } - - /** - * Return the Encrypt key given the table and column name. - */ - public EncryptKey getEncryptKey(String tableName, String columnName) { - return encryptKeyManager.getEncryptKey(tableName, columnName); - } - - /** - * For SQL based modifications we need to invalidate appropriate parts of the - * cache. - */ - public void cacheNotify(TransactionEventTable.TableIUD tableIUD) { - - List> list = getBeanDescriptors(tableIUD.getTableName()); - if (list != null) { - for (int i = 0; i < list.size(); i++) { - list.get(i).cacheHandleBulkUpdate(tableIUD); - } - } - } - - /** - * Return the BeanDescriptors mapped to the table. - */ - public List> getBeanDescriptors(String tableName) { - return tableToDescMap.get(tableName.toLowerCase()); - } - - /** - * Build a map of table names to BeanDescriptors. - *

- * This is generally used to maintain caches from table names. - *

- */ - private void readTableToDescriptor() { - - for (BeanDescriptor desc : descMap.values()) { - String baseTable = desc.getBaseTable(); - if (baseTable == null) { - - } else { - baseTable = baseTable.toLowerCase(); - - List> list = tableToDescMap.get(baseTable); - if (list == null) { - list = new ArrayList>(1); - tableToDescMap.put(baseTable, list); - } - list.add(desc); - } - } - } - - private void readForeignKeys() { - - for (BeanDescriptor d : descMap.values()) { - d.initialiseFkeys(); - } - } - - /** - * Initialise all the BeanDescriptors. - *

- * This occurs after all the BeanDescriptors have been created. This resolves - * circular relationships between BeanDescriptors. - *

- *

- * Also responsible for creating all the BeanManagers which contain the - * persister, listener etc. - *

- */ - private void initialiseAll() { - - // now that all the BeanDescriptors are in their map - // we can initialise them which sorts out circular - // dependencies for OneToMany and ManyToOne etc - - // PASS 1: - // initialise the ID properties of all the beans - // first (as they are needed to initialise the - // associated properties in the second pass). - for (BeanDescriptor d : descMap.values()) { - d.initialiseId(); - } - - // PASS 2: - // now initialise all the inherit info - for (BeanDescriptor d : descMap.values()) { - d.initInheritInfo(); - } - - // PASS 3: - // now initialise all the associated properties - for (BeanDescriptor d : descMap.values()) { - d.initialiseOther(); - } - - // create BeanManager for each non-embedded entity bean - for (BeanDescriptor d : descMap.values()) { - if (!d.isEmbedded()) { - BeanManager m = beanManagerFactory.create(d); - beanManagerMap.put(d.getFullName(), m); - - checkForValidEmbeddedId(d); - } - } - } - - private void checkForValidEmbeddedId(BeanDescriptor d) { - IdBinder idBinder = d.getIdBinder(); - if (idBinder != null && idBinder instanceof IdBinderEmbedded) { - IdBinderEmbedded embId = (IdBinderEmbedded) idBinder; - BeanDescriptor idBeanDescriptor = embId.getIdBeanDescriptor(); - Class idType = idBeanDescriptor.getBeanType(); - try { - idType.getDeclaredMethod("hashCode", new Class[] {}); - idType.getDeclaredMethod("equals", new Class[] { Object.class }); - } catch (NoSuchMethodException e) { - checkMissingHashCodeOrEquals(e, idType, d.getBeanType()); - } - } - } - - private void checkMissingHashCodeOrEquals(Exception source, Class idType, Class beanType) { - - String msg = "SERIOUS ERROR: The hashCode() and equals() methods *MUST* be implemented "; - msg += "on Embedded bean " + idType + " as it is used as an Id for " + beanType; - throw new PersistenceException(msg, source); - } - - /** - * Return an immutable list of all the BeanDescriptors. - */ - public List> getBeanDescriptorList() { - return immutableDescriptorList; - } - - public Map, BeanTable> getBeanTables() { - return beanTableMap; - } - - public BeanTable getBeanTable(Class type) { - return beanTableMap.get(type); - } - - public Map> getBeanDescriptors() { - return descMap; - } - - @SuppressWarnings("unchecked") - public BeanManager getBeanManager(Class entityType) { - - return (BeanManager) getBeanManager(entityType.getName()); - } - - public BeanManager getBeanManager(String beanClassName) { - return beanManagerMap.get(beanClassName); - } - - public DNativeQuery getNativeQuery(String name) { - return deployOrmXml.getNativeQuery(name); - } - - /** - * Create the BeanControllers, BeanFinders and BeanListeners. - */ - private void createListeners() { - - int qa = beanQueryAdapterManager.getRegisterCount(); - int cc = persistControllerManager.getRegisterCount(); - int lc = persistListenerManager.getRegisterCount(); - int fc = beanFinderManager.createBeanFinders(bootupClasses.getBeanFinders()); - - logger.debug("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "]"); - } - - private void logStatus() { - logger.info("Entities enhanced[" + enhancedClassCount + "]"); - } - - private BeanDescriptor createEmbedded(Class beanClass) { - - DeployBeanInfo info = createDeployBeanInfo(beanClass); - readDeployAssociations(info); - - Integer key = getUniqueHash(info.getDescriptor()); - - return new BeanDescriptor(this, typeManager, info.getDescriptor(), key.toString()); - } - - private void registerBeanDescriptor(BeanDescriptor desc) { - descMap.put(desc.getBeanType().getName(), desc); - } - - /** - * Read deployment information for all the embedded beans. - */ - private void readEmbeddedDeployment() { - - ArrayList> embeddedClasses = bootupClasses.getEmbeddables(); - for (int i = 0; i < embeddedClasses.size(); i++) { - Class cls = embeddedClasses.get(i); - if (logger.isTraceEnabled()) { - String msg = "load deployinfo for embeddable:" + cls.getName(); - logger.trace(msg); - } - BeanDescriptor embDesc = createEmbedded(cls); - registerBeanDescriptor(embDesc); - } - } - - /** - * Read the initial deployment information for the entities. - *

- * This stops short of reading relationship meta data until after the - * BeanTables have all been created. - *

- */ - private void readEntityDeploymentInitial() { - - ArrayList> entityClasses = bootupClasses.getEntities(); - - for (Class entityClass : entityClasses) { - DeployBeanInfo info = createDeployBeanInfo(entityClass); - deplyInfoMap.put(entityClass, info); - } - } - - /** - * Create the BeanTable information which has the base table and id. - *

- * This is determined prior to resolving relationship information. - *

- */ - private void readEntityBeanTable() { - - for (DeployBeanInfo info : deplyInfoMap.values()) { - BeanTable beanTable = createBeanTable(info); - beanTableMap.put(beanTable.getBeanType(), beanTable); - } - } - - /** - * Create the BeanTable information which has the base table and id. - *

- * This is determined prior to resolving relationship information. - *

- */ - private void readEntityDeploymentAssociations() { - - for (DeployBeanInfo info : deplyInfoMap.values()) { - readDeployAssociations(info); - } - } - - private void readInheritedIdGenerators() { - - for (DeployBeanInfo info : deplyInfoMap.values()) { - DeployBeanDescriptor descriptor = info.getDescriptor(); - InheritInfo inheritInfo = descriptor.getInheritInfo(); - if (inheritInfo != null && !inheritInfo.isRoot()) { - DeployBeanInfo rootBeanInfo = deplyInfoMap.get(inheritInfo.getRoot().getType()); - IdGenerator rootIdGen = rootBeanInfo.getDescriptor().getIdGenerator(); - if (rootIdGen != null) { - descriptor.setIdGenerator(rootIdGen); - } - } - } - } - - /** - * Create the BeanTable from the deployment information gathered so far. - */ - private BeanTable createBeanTable(DeployBeanInfo info) { - - DeployBeanDescriptor deployDescriptor = info.getDescriptor(); - DeployBeanTable beanTable = deployDescriptor.createDeployBeanTable(); - return new BeanTable(beanTable, this); - } - - /** - * Parse the named Raw Sql queries using BeanDescriptor. - */ - private void readRawSqlQueries() { - - for (DeployBeanInfo info : deplyInfoMap.values()) { - - DeployBeanDescriptor deployDesc = info.getDescriptor(); - BeanDescriptor desc = getBeanDescriptor(deployDesc.getBeanType()); - - for (DRawSqlMeta rawSqlMeta : deployDesc.getRawSqlMeta()) { - if (rawSqlMeta.getQuery() == null) { - - } else { - DeployNamedQuery nq = new DRawSqlSelectBuilder(namingConvention, desc, rawSqlMeta).parse(); - desc.addNamedQuery(nq); - } - } - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void readEntityRelationships() { - - // We only perform 'circular' checks etc after we have - // all the DeployBeanDescriptors created and in the map. - - for (DeployBeanInfo info : deplyInfoMap.values()) { - checkMappedBy(info); - } - - for (DeployBeanInfo info : deplyInfoMap.values()) { - secondaryPropsJoins(info); - } - - // Set inheritance info - for (DeployBeanInfo info : deplyInfoMap.values()) { - setInheritanceInfo(info); - } - - for (DeployBeanInfo info : deplyInfoMap.values()) { - DeployBeanDescriptor deployBeanDescriptor = info.getDescriptor(); - Integer key = getUniqueHash(deployBeanDescriptor); - registerBeanDescriptor(new BeanDescriptor(this, typeManager, info.getDescriptor(), key.toString())); - } - } - - /** - * Sets the inheritance info. ~EMG fix for join problem - * - * @param info the new inheritance info - */ - private void setInheritanceInfo(DeployBeanInfo info) { - - for (DeployBeanPropertyAssocOne oneProp : info.getDescriptor().propertiesAssocOne()) { - if (!oneProp.isTransient()) { - DeployBeanInfo assoc = deplyInfoMap.get(oneProp.getTargetType()); - if (assoc != null){ - oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo()); - } - } - } - - for (DeployBeanPropertyAssocMany manyProp : info.getDescriptor().propertiesAssocMany()) { - if (!manyProp.isTransient()) { - DeployBeanInfo assoc = deplyInfoMap.get(manyProp.getTargetType()); - if (assoc != null){ - manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo()); - } - } - } - } - - private Integer getUniqueHash(DeployBeanDescriptor deployBeanDescriptor) { - - int hashCode = deployBeanDescriptor.getFullName().hashCode(); - - for (int i = 0; i < 100000; i++) { - Integer key = Integer.valueOf(hashCode + i); - if (!descriptorUniqueIds.contains(key)) { - return key; - } - } - throw new RuntimeException("Failed to generate a unique hash for " + deployBeanDescriptor.getFullName()); - } - - private void secondaryPropsJoins(DeployBeanInfo info) { - - DeployBeanDescriptor descriptor = info.getDescriptor(); - for (DeployBeanProperty prop : descriptor.propertiesBase()) { - if (prop.isSecondaryTable()) { - String tableName = prop.getSecondaryTable(); - // find a join to that table... - DeployBeanPropertyAssocOne assocOne = descriptor.findJoinToTable(tableName); - if (assocOne == null) { - String msg = "Error with property " + prop.getFullBeanName() + ". Could not find a Relationship to table " + tableName - + ". Perhaps you could use a @JoinColumn instead."; - throw new RuntimeException(msg); - } - DeployTableJoin tableJoin = assocOne.getTableJoin(); - prop.setSecondaryTableJoin(tableJoin, assocOne.getName()); - } - } - } - - /** - * Check the mappedBy attributes for properties on this descriptor. - *

- * This will read join information defined on the 'owning/other' side of the - * relationship. It also does some extra work for unidirectional - * relationships. - *

- */ - private void checkMappedBy(DeployBeanInfo info) { - - for (DeployBeanPropertyAssocOne oneProp : info.getDescriptor().propertiesAssocOne()) { - if (!oneProp.isTransient()) { - if (oneProp.getMappedBy() != null) { - checkMappedByOneToOne(info, oneProp); - } - } - } - - for (DeployBeanPropertyAssocMany manyProp : info.getDescriptor().propertiesAssocMany()) { - if (!manyProp.isTransient()) { - if (manyProp.isManyToMany()) { - checkMappedByManyToMany(info, manyProp); - } else { - checkMappedByOneToMany(info, manyProp); - } - } - } - } - - private DeployBeanDescriptor getTargetDescriptor(DeployBeanPropertyAssoc prop) { - - Class targetType = prop.getTargetType(); - DeployBeanInfo info = deplyInfoMap.get(targetType); - if (info == null) { - String msg = "Can not find descriptor [" + targetType + "] for " + prop.getFullBeanName(); - throw new PersistenceException(msg); - } - - return info.getDescriptor(); - } - - /** - * Check that the many property has either an implied mappedBy property or - * mark it as unidirectional. - */ - private boolean findMappedBy(DeployBeanPropertyAssocMany prop) { - - // this is the entity bean type - that owns this property - Class owningType = prop.getOwningType(); - - Set matchSet = new HashSet(); - - // get the bean descriptor that holds the mappedBy property - DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); - List> ones = targetDesc.propertiesAssocOne(); - for (DeployBeanPropertyAssocOne possibleMappedBy : ones) { - Class possibleMappedByType = possibleMappedBy.getTargetType(); - if (possibleMappedByType.equals(owningType)) { - prop.setMappedBy(possibleMappedBy.getName()); - matchSet.add(possibleMappedBy.getName()); - } - } - - if (matchSet.size() == 0) { - // this is a unidirectional relationship - // ... that is no matching property on the 'detail' bean - return false; - } - if (matchSet.size() == 1) { - // all right with the world - return true; - } - if (matchSet.size() == 2) { - // try to find a match implicitly using a common naming convention - // e.g. List loggedBugs; ... search for "logged" in matchSet - String name = prop.getName(); - - // get the target type short name - String targetType = prop.getTargetType().getName(); - String shortTypeName = targetType.substring(targetType.lastIndexOf(".") + 1); - - // name includes (probably ends with) the target type short name? - int p = name.indexOf(shortTypeName); - if (p > 1) { - // ok, get the 'interesting' part of the property name - // That is the name without the target type - String searchName = name.substring(0, p).toLowerCase(); - - // search for this in the possible matches - for (String possibleMappedBy : matchSet) { - String possibleLower = possibleMappedBy.toLowerCase(); - if (possibleLower.indexOf(searchName) > -1) { - // we have a match.. - prop.setMappedBy(possibleMappedBy); - - String m = "Implicitly found mappedBy for " + targetDesc + "." + prop; - m += " by searching for [" + searchName + "] against " + matchSet; - logger.debug(m); - - return true; - } - } - - } - } - // multiple options so should specify mappedBy property - String msg = "Error on " + prop.getFullBeanName() + " missing mappedBy."; - msg += " There are [" + matchSet.size() + "] possible properties in " + targetDesc; - msg += " that this association could be mapped to. Please specify one using "; - msg += "the mappedBy attribute on @OneToMany."; - throw new PersistenceException(msg); - } - - /** - * A OneToMany with no matching mappedBy property in the target so must be - * unidirectional. - *

- * This means that inserts MUST cascade for this property. - *

- *

- * Create a "Shadow"/Unidirectional property on the target. It is used with - * inserts to set the foreign key value (e.g. inserts the foreign key value - * into the order_id column on the order_lines table). - *

- */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void makeUnidirectional(DeployBeanInfo info, DeployBeanPropertyAssocMany oneToMany) { - - DeployBeanDescriptor targetDesc = getTargetDescriptor(oneToMany); - - Class owningType = oneToMany.getOwningType(); - - if (!oneToMany.getCascadeInfo().isSave()) { - // The property MUST have persist cascading so that inserts work. - - Class targetType = oneToMany.getTargetType(); - String msg = "Error on " + oneToMany.getFullBeanName() + ". @OneToMany MUST have "; - msg += "Cascade.PERSIST or Cascade.ALL because this is a unidirectional "; - msg += "relationship. That is, there is no property of type " + owningType + " on " + targetType; - - throw new PersistenceException(msg); - } - - // mark this property as unidirectional - oneToMany.setUnidirectional(true); - - // create the 'shadow' unidirectional property - // which is put on the target descriptor - DeployBeanPropertyAssocOne unidirectional = new DeployBeanPropertyAssocOne(targetDesc, owningType); - unidirectional.setUndirectionalShadow(true); - unidirectional.setNullable(false); - unidirectional.setDbRead(true); - unidirectional.setDbInsertable(true); - unidirectional.setDbUpdateable(false); - - targetDesc.setUnidirectional(unidirectional); - - // specify table and table alias... - BeanTable beanTable = getBeanTable(owningType); - unidirectional.setBeanTable(beanTable); - unidirectional.setName(beanTable.getBaseTable()); - - info.setBeanJoinType(unidirectional, true); - - // define the TableJoin - DeployTableJoin oneToManyJoin = oneToMany.getTableJoin(); - if (!oneToManyJoin.hasJoinColumns()) { - throw new RuntimeException("No join columns"); - } - - // inverse of the oneToManyJoin - DeployTableJoin unidirectionalJoin = unidirectional.getTableJoin(); - unidirectionalJoin.setColumns(oneToManyJoin.columns(), true); - - } - - private void checkMappedByOneToOne(DeployBeanInfo info, DeployBeanPropertyAssocOne prop) { - - // check that the mappedBy property is valid and read - // its associated join information if it is available - String mappedBy = prop.getMappedBy(); - - // get the mappedBy property - DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); - DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy); - if (mappedProp == null) { - String m = "Error on " + prop.getFullBeanName(); - m += " Can not find mappedBy property [" + targetDesc + "." + mappedBy + "] "; - throw new PersistenceException(m); - } - - if (!(mappedProp instanceof DeployBeanPropertyAssocOne)) { - String m = "Error on " + prop.getFullBeanName(); - m += ". mappedBy property [" + targetDesc + "." + mappedBy + "]is not a OneToOne?"; - throw new PersistenceException(m); - } - - DeployBeanPropertyAssocOne mappedAssocOne = (DeployBeanPropertyAssocOne) mappedProp; - - if (!mappedAssocOne.isOneToOne()) { - String m = "Error on " + prop.getFullBeanName(); - m += ". mappedBy property [" + targetDesc + "." + mappedBy + "]is not a OneToOne?"; - throw new PersistenceException(m); - } - - DeployTableJoin tableJoin = prop.getTableJoin(); - if (!tableJoin.hasJoinColumns()) { - // define Join as the inverse of the mappedBy property - DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin(); - otherTableJoin.copyWithoutType(tableJoin, true, tableJoin.getTable()); - } - } - - /** - * If the property has mappedBy set then do two things. Make sure the mappedBy - * property exists, and secondly read its join information. - *

- * We can use the join information from the mappedBy property and reverse it - * for using in the OneToMany direction. - *

- */ - private void checkMappedByOneToMany(DeployBeanInfo info, DeployBeanPropertyAssocMany prop) { - - // get the bean descriptor that holds the mappedBy property - - if (prop.getMappedBy() == null) { - if (!findMappedBy(prop)) { - makeUnidirectional(info, prop); - return; - } - } - - // check that the mappedBy property is valid and read - // its associated join information if it is available - String mappedBy = prop.getMappedBy(); - - // get the mappedBy property - DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); - DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy); - if (mappedProp == null) { - - String m = "Error on " + prop.getFullBeanName(); - m += " Can not find mappedBy property [" + mappedBy + "] "; - m += "in [" + targetDesc + "]"; - throw new PersistenceException(m); - } - - if (!(mappedProp instanceof DeployBeanPropertyAssocOne)) { - String m = "Error on " + prop.getFullBeanName(); - m += ". mappedBy property [" + mappedBy + "]is not a ManyToOne?"; - m += "in [" + targetDesc + "]"; - throw new PersistenceException(m); - } - - DeployBeanPropertyAssocOne mappedAssocOne = (DeployBeanPropertyAssocOne) mappedProp; - - DeployTableJoin tableJoin = prop.getTableJoin(); - if (!tableJoin.hasJoinColumns()) { - // define Join as the inverse of the mappedBy property - DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin(); - otherTableJoin.copyTo(tableJoin, true, tableJoin.getTable()); - } - - } - - /** - * For mappedBy copy the joins from the other side. - */ - private void checkMappedByManyToMany(DeployBeanInfo info, DeployBeanPropertyAssocMany prop) { - - // get the bean descriptor that holds the mappedBy property - String mappedBy = prop.getMappedBy(); - if (mappedBy == null) { - return; - } - - // get the mappedBy property - DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); - DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy); - - if (mappedProp == null) { - String m = "Error on " + prop.getFullBeanName(); - m += " Can not find mappedBy property [" + mappedBy + "] "; - m += "in [" + targetDesc + "]"; - throw new PersistenceException(m); - } - - if (!(mappedProp instanceof DeployBeanPropertyAssocMany)) { - String m = "Error on " + prop.getFullBeanName(); - m += ". mappedBy property [" + targetDesc + "." + mappedBy + "] is not a ManyToMany?"; - throw new PersistenceException(m); - } - - DeployBeanPropertyAssocMany mappedAssocMany = (DeployBeanPropertyAssocMany) mappedProp; - - if (!mappedAssocMany.isManyToMany()) { - String m = "Error on " + prop.getFullBeanName(); - m += ". mappedBy property [" + targetDesc + "." + mappedBy + "] is not a ManyToMany?"; - throw new PersistenceException(m); - } - - // define the relationships/joins on this side as the - // reverse of the other mappedBy side ... - - // DeployTableJoin mappedJoin = mappedAssocMany.getTableJoin(); - DeployTableJoin mappedIntJoin = mappedAssocMany.getIntersectionJoin(); - DeployTableJoin mappendInverseJoin = mappedAssocMany.getInverseJoin(); - - String intTableName = mappedIntJoin.getTable(); - - DeployTableJoin tableJoin = prop.getTableJoin(); - mappedIntJoin.copyTo(tableJoin, true, targetDesc.getBaseTable()); - - DeployTableJoin intJoin = new DeployTableJoin(); - mappendInverseJoin.copyTo(intJoin, false, intTableName); - prop.setIntersectionJoin(intJoin); - - DeployTableJoin inverseJoin = new DeployTableJoin(); - mappedIntJoin.copyTo(inverseJoin, false, intTableName); - prop.setInverseJoin(inverseJoin); - } - - private void setBeanControllerFinderListener(DeployBeanDescriptor descriptor) { - - Class beanType = descriptor.getBeanType(); - - persistControllerManager.addPersistControllers(descriptor); - persistListenerManager.addPersistListeners(descriptor); - beanQueryAdapterManager.addQueryAdapter(descriptor); - - BeanFinder beanFinder = beanFinderManager.getBeanFinder(beanType); - if (beanFinder != null) { - descriptor.setBeanFinder(beanFinder); - logger.debug("BeanFinder on[" + descriptor.getFullName() + "] " + beanFinder.getClass().getName()); - } - } - - /** - * Read the initial deployment information for a given bean type. - */ - private DeployBeanInfo createDeployBeanInfo(Class beanClass) { - - DeployBeanDescriptor desc = new DeployBeanDescriptor(beanClass); - - desc.setUpdateChangesOnly(updateChangesOnly); - - beanLifecycleAdapterFactory.addLifecycleMethods(desc); - - // set bean controller, finder and listener - setBeanControllerFinderListener(desc); - deplyInherit.process(desc); - desc.checkInheritanceMapping(); - - createProperties.createProperties(desc); - - DeployBeanInfo info = new DeployBeanInfo(deployUtil, desc); - - readAnnotations.readInitial(info, eagerFetchLobs); - return info; - } - - private void readDeployAssociations(DeployBeanInfo info) { - - DeployBeanDescriptor desc = info.getDescriptor(); - - readAnnotations.readAssociations(info, this); - - readXml(desc); - - if (!EntityType.ORM.equals(desc.getEntityType())) { - // not using base table - desc.setBaseTable(null); - } - - // mark transient properties - transientProperties.process(desc); - setScalarType(desc); - - if (!desc.isEmbedded()) { - // Set IdGenerator or use DB Identity - setIdGeneration(desc); - - // find the appropriate default concurrency mode - setConcurrencyMode(desc); - } - - // generate the byte code - createByteCode(desc); - } - - /** - * Set the Identity generation mechanism. - */ - private IdType setIdGeneration(DeployBeanDescriptor desc) { - - if (desc.propertiesId().size() == 0) { - // bean doen't have an Id property - if (!desc.isBaseTableType() || desc.getBeanFinder() != null) { - // using BeanFinder so perhaps valid without an id - } else { - // expecting an id property - logger.warn(Message.msg("deploy.nouid", desc.getFullName())); - } - return null; - } - - if (IdType.SEQUENCE.equals(desc.getIdType()) && !dbIdentity.isSupportsSequence()) { - // explicit sequence but not supported by the DatabasePlatform - logger.info("Explicit sequence on " + desc.getFullName() + " but not supported by DB Platform - ignored"); - desc.setIdType(null); - } - if (IdType.IDENTITY.equals(desc.getIdType()) && !dbIdentity.isSupportsIdentity()) { - // explicit identity but not supported by the DatabasePlatform - logger.info("Explicit Identity on " + desc.getFullName() + " but not supported by DB Platform - ignored"); - desc.setIdType(null); - } - - if (desc.getIdType() == null) { - // use the default. IDENTITY or SEQUENCE. - desc.setIdType(dbIdentity.getIdType()); - } - - if (IdType.GENERATOR.equals(desc.getIdType())) { - String genName = desc.getIdGeneratorName(); - if (UuidIdGenerator.AUTO_UUID.equals(genName)) { - desc.setIdGenerator(uuidIdGenerator); - return IdType.GENERATOR; - } - } - - if (desc.getBaseTable() == null) { - // no base table so not going to set Identity - // of sequence information - return null; - } - - if (IdType.IDENTITY.equals(desc.getIdType())) { - // used when getGeneratedKeys is not supported (SQL Server 2000) - String selectLastInsertedId = dbIdentity.getSelectLastInsertedId(desc.getBaseTable()); - desc.setSelectLastInsertedId(selectLastInsertedId); - return IdType.IDENTITY; - } - - String seqName = desc.getIdGeneratorName(); - if (seqName != null) { - logger.debug("explicit sequence " + seqName + " on " + desc.getFullName()); - } else { - String primaryKeyColumn = desc.getSinglePrimaryKeyColumn(); - // use namingConvention to define sequence name - seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn); - } - - // create the sequence based IdGenerator - IdGenerator seqIdGen = createSequenceIdGenerator(seqName); - desc.setIdGenerator(seqIdGen); - - return IdType.SEQUENCE; - } - - private IdGenerator createSequenceIdGenerator(String seqName) { - return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, seqName, dbSequenceBatchSize); - } - - private void createByteCode(DeployBeanDescriptor deploy) { - - // check to see if the bean supports EntityBean interface - // generate a subclass if required - setEntityBeanClass(deploy); - - // use Code generation or Standard reflection to support - // getter and setter methods - setBeanReflect(deploy); - } - - /** - * Set the Scalar Types on all the simple types. This is done AFTER transients - * have been identified. This is because a non-transient field MUST have a - * ScalarType. It is useful for transients to have ScalarTypes because then - * they can be used in a SqlSelect query. - *

- * Enums are treated a bit differently in that they always have a ScalarType - * as one is built for them. - *

- */ - private void setScalarType(DeployBeanDescriptor deployDesc) { - - for (DeployBeanProperty prop : deployDesc.propertiesAll()) { - if (prop instanceof DeployBeanPropertyAssoc == false) { - deployUtil.setScalarType(prop); - } - } - } - - private void readXml(DeployBeanDescriptor deployDesc) { - - List eXml = xmlConfig.findEntityXml(deployDesc.getFullName()); - readXmlRawSql(deployDesc, eXml); - - Dnode entityXml = deployOrmXml.findEntityDeploymentXml(deployDesc.getFullName()); - - if (entityXml != null) { - readXmlNamedQueries(deployDesc, entityXml); - readXmlSql(deployDesc, entityXml); - } - } - - /** - * Read sql-select (FUTURE: additionally sql-insert, sql-update, sql-delete). - * If found this entity bean is based on raw sql. - */ - private void readXmlSql(DeployBeanDescriptor deployDesc, Dnode entityXml) { - - List sqlSelectList = entityXml.findAll("sql-select", entityXml.getLevel() + 1); - for (int i = 0; i < sqlSelectList.size(); i++) { - Dnode sqlSelect = sqlSelectList.get(i); - readSqlSelect(deployDesc, sqlSelect); - } - } - - private String findContent(Dnode node, String nodeName) { - Dnode found = node.find(nodeName); - if (found != null) { - return found.getNodeContent(); - } else { - return null; - } - } - - private void readSqlSelect(DeployBeanDescriptor deployDesc, Dnode sqlSelect) { - - String name = sqlSelect.getStringAttr("name", "default"); - String extend = sqlSelect.getStringAttr("extend", null); - String queryDebug = sqlSelect.getStringAttr("debug", null); - boolean debug = (queryDebug != null && queryDebug.equalsIgnoreCase("true")); - - // the raw sql select - String query = findContent(sqlSelect, "query"); - String where = findContent(sqlSelect, "where"); - String having = findContent(sqlSelect, "having"); - String columnMapping = findContent(sqlSelect, "columnMapping"); - - DRawSqlMeta m = new DRawSqlMeta(name, extend, query, debug, where, having, columnMapping); - - deployDesc.add(m); - - } - - private void readXmlRawSql(DeployBeanDescriptor deployDesc, List entityXml) { - - List rawSqlQueries = xmlConfig.find(entityXml, "raw-sql"); - for (int i = 0; i < rawSqlQueries.size(); i++) { - Dnode rawSqlDnode = rawSqlQueries.get(i); - String name = rawSqlDnode.getAttribute("name"); - if (isEmpty(name)) { - throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " missing name attribute"); - } - Dnode queryNode = rawSqlDnode.find("query"); - if (queryNode == null) { - throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " missing query element"); - } - String sql = queryNode.getNodeContent(); - if (isEmpty(sql)) { - throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " has empty sql in the query element?"); - } - - List columnMappings = rawSqlDnode.findAll("columnMapping", 1); - - RawSqlBuilder rawSqlBuilder = RawSqlBuilder.parse(sql); - for (int j = 0; j < columnMappings.size(); j++) { - Dnode cm = columnMappings.get(j); - String column = cm.getAttribute("column"); - String property = cm.getAttribute("property"); - rawSqlBuilder.columnMapping(column, property); - } - RawSql rawSql = rawSqlBuilder.create(); - - DeployNamedQuery namedQuery = new DeployNamedQuery(name, rawSql); - deployDesc.add(namedQuery); - } - } - - private boolean isEmpty(String s) { - return s == null || s.trim().length() == 0; - } - - /** - * Read named queries for this bean type. - */ - private void readXmlNamedQueries(DeployBeanDescriptor deployDesc, Dnode entityXml) { - - // look for named-query... - List namedQueries = entityXml.findAll("named-query", 1); - - for (Dnode namedQueryXml : namedQueries) { - - String name = (String) namedQueryXml.getAttribute("name"); - Dnode query = namedQueryXml.find("query"); - if (query == null) { - logger.warn("orm.xml " + deployDesc.getFullName() + " named-query missing query element?"); - - } else { - String oql = query.getNodeContent(); - // TODO: QueryHints not read from xml yet - if (name == null || oql == null) { - logger.warn("orm.xml " + deployDesc.getFullName() + " named-query has no query content?"); - } else { - // add the named query - DeployNamedQuery q = new DeployNamedQuery(name, oql, null); - deployDesc.add(q); - } - } - } - } - - private BeanPropertyInfoFactory createReflectionFactory() { - - return new EnhanceBeanPropertyInfoFactory(); - } - - /** - * Set BeanReflect BeanReflectGetter and BeanReflectSetter properties. - *

- * This sets the implementation of constructing entity beans and the setting - * and getting of properties. It is generally faster to use code generation - * rather than reflection to do this. - *

- */ - private void setBeanReflect(DeployBeanDescriptor desc) { - - // Set the BeanReflectGetter and BeanReflectSetter that typically - // use generated code. NB: Due to Bug 166 so now doing this for - // abstract classes as well. - - Class beanType = desc.getBeanType(); - - BeanPropertiesReader reflectProps = new BeanPropertiesReader(beanType); - - BeanPropertyInfo beanReflect = reflectFactory.create(beanType); - desc.setBeanReflect(beanReflect); - desc.setProperties(reflectProps.getProperties()); - - for (DeployBeanProperty prop : desc.propertiesAll()) { - String propName = prop.getName(); - Integer pos = reflectProps.getPropertyIndex(propName); - if (pos == null) { - if (isPersistentField(prop)) { - throw new IllegalStateException("Property "+propName+" not found in "+reflectProps); - } - - } else { - int propertyIndex = pos.intValue(); - prop.setPropertyIndex(propertyIndex); - prop.setGetter(beanReflect.getGetter(propName, propertyIndex)); - prop.setSetter(beanReflect.getSetter(propName, propertyIndex)); - } - } - } - - /** - * Return true if this is a persistent field (not transient or static). - */ - private boolean isPersistentField(DeployBeanProperty prop) { - - Field field = prop.getField(); - int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) { - return false; - } - if (field.isAnnotationPresent(Transient.class)) { - return false; - } - return true; - } - - /** - * DevNote: It is assumed that Embedded can contain version properties. It is - * also assumed that Embedded beans do NOT themselves contain Embedded beans - * which contain version properties. - */ - private void setConcurrencyMode(DeployBeanDescriptor desc) { - - if (desc.getConcurrencyMode() != null) { - // concurrency mode explicitly set during deployment - return; - } - - if (checkForVersionProperties(desc)) { - desc.setConcurrencyMode(ConcurrencyMode.VERSION); - } else { - desc.setConcurrencyMode(ConcurrencyMode.NONE); - } - } - - /** - * Search for version properties also including embedded beans. - */ - private boolean checkForVersionProperties(DeployBeanDescriptor desc) { - - boolean hasVersionProperty = false; - - List props = desc.propertiesBase(); - for (int i = 0; i < props.size(); i++) { - if (props.get(i).isVersionColumn()) { - hasVersionProperty = true; - } - } - - return hasVersionProperty; - } - - private boolean hasEntityBeanInterface(Class beanClass) { - - Class[] interfaces = beanClass.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - if (interfaces[i].equals(EntityBean.class)) { - return true; - } - } - return false; - } - - /** - * Test the bean type to see if it implements EntityBean interface already. - */ - private void setEntityBeanClass(DeployBeanDescriptor desc) { - - Class beanClass = desc.getBeanType(); - - if (!hasEntityBeanInterface(beanClass)) { - throw new IllegalStateException("Bean "+beanClass+" is not enhanced?"); - } - - // the bean already implements EntityBean - checkInheritedClasses(beanClass); - - if (!beanClass.getName().startsWith("com.avaje.ebean.meta")) { - enhancedClassCount++; - } - } - - /** - * Check that the inherited classes are the same as the entity bean (aka all - * enhanced or all dynamically subclassed). - */ - private void checkInheritedClasses(Class beanClass) { - - Class superclass = beanClass.getSuperclass(); - if (Object.class.equals(superclass)) { - // we got to the top of the inheritance - return; - } - if (Model.class.equals(superclass)) { - // top of the inheritance. Not enhancing Model at this stage - return; - } - if (!EntityBean.class.isAssignableFrom(superclass)) { - if (isMappedSuperWithNoProperties(superclass)) { - // ok to stop and treat just the same as Object.class - return; - } - throw new IllegalStateException("Super type "+superclass+" is not enhanced?"); - } - - // recursively continue up the inheritance hierarchy - checkInheritedClasses(superclass); - } - - /** - * Return true if this is a MappedSuperclass bean with no persistent properties. - * If so it is ok for it not to be enhanced. - */ - private boolean isMappedSuperWithNoProperties(Class beanClass) { - - MappedSuperclass annotation = beanClass.getAnnotation(MappedSuperclass.class); - if (annotation == null) { - return false; - } - Field[] fields = beanClass.getDeclaredFields(); - for (Field field : fields) { - if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())) { - // ignore this field - } else if (field.isAnnotationPresent(Transient.class)) { - // ignore this field - } else { - return false; - } - } - return true; - } - - /** - * Comparator to sort the BeanDescriptors by name. - */ - private static final class BeanDescComparator implements Comparator>, Serializable { - - private static final long serialVersionUID = 1L; - - public int compare(BeanDescriptor o1, BeanDescriptor o2) { - - return o1.getName().compareTo(o2.getName()); - } - } -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebean.BackgroundExecutor; +import com.avaje.ebean.Model; +import com.avaje.ebean.RawSql; +import com.avaje.ebean.RawSqlBuilder; +import com.avaje.ebean.annotation.ConcurrencyMode; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.cache.ServerCacheManager; +import com.avaje.ebean.config.EncryptKey; +import com.avaje.ebean.config.EncryptKeyManager; +import com.avaje.ebean.config.NamingConvention; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.DbIdentity; +import com.avaje.ebean.config.dbplatform.IdGenerator; +import com.avaje.ebean.config.dbplatform.IdType; +import com.avaje.ebean.event.BeanFinder; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.TransactionEventTable; +import com.avaje.ebeaninternal.server.core.*; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.id.IdBinder; +import com.avaje.ebeaninternal.server.deploy.id.IdBinderEmbedded; +import com.avaje.ebeaninternal.server.deploy.id.IdBinderFactory; +import com.avaje.ebeaninternal.server.deploy.meta.*; +import com.avaje.ebeaninternal.server.deploy.parse.*; +import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator; +import com.avaje.ebeaninternal.server.lib.util.Dnode; +import com.avaje.ebeaninternal.server.properties.BeanPropertiesReader; +import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo; +import com.avaje.ebeaninternal.server.properties.BeanPropertyInfoFactory; +import com.avaje.ebeaninternal.server.properties.EnhanceBeanPropertyInfoFactory; +import com.avaje.ebeaninternal.server.type.TypeManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.persistence.MappedSuperclass; +import javax.persistence.PersistenceException; +import javax.persistence.Transient; +import javax.sql.DataSource; +import java.io.Serializable; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.*; + +/** + * Creates BeanDescriptors. + */ +public class BeanDescriptorManager implements BeanDescriptorMap { + + private static final Logger logger = LoggerFactory.getLogger(BeanDescriptorManager.class); + + private static final BeanDescComparator beanDescComparator = new BeanDescComparator(); + + private final ReadAnnotations readAnnotations = new ReadAnnotations(); + + private final TransientProperties transientProperties; + + /** + * Helper to derive inheritance information. + */ + private final DeployInherit deplyInherit; + + private final BeanPropertyInfoFactory reflectFactory; + + private final DeployUtil deployUtil; + + private final TypeManager typeManager; + + private final PersistControllerManager persistControllerManager; + + private final BeanFinderManager beanFinderManager; + + private final PersistListenerManager persistListenerManager; + + private final BeanQueryAdapterManager beanQueryAdapterManager; + + private final NamingConvention namingConvention; + + private final DeployCreateProperties createProperties; + + private final DeployOrmXml deployOrmXml; + + private final BeanManagerFactory beanManagerFactory; + + private int enhancedClassCount; + + private final boolean updateChangesOnly; + + private final BootupClasses bootupClasses; + + private final String serverName; + + private Map, DeployBeanInfo> deplyInfoMap = new HashMap, DeployBeanInfo>(); + + private final Map, BeanTable> beanTableMap = new HashMap, BeanTable>(); + + private final Map> descMap = new HashMap>(); + private final Map> idDescMap = new HashMap>(); + + private final Map> beanManagerMap = new HashMap>(); + + private final Map>> tableToDescMap = new HashMap>>(); + + private List> immutableDescriptorList; + + private final Set descriptorUniqueIds = new HashSet(); + + private final DbIdentity dbIdentity; + + private final DataSource dataSource; + + private final DatabasePlatform databasePlatform; + + private final UuidIdGenerator uuidIdGenerator = new UuidIdGenerator(); + + private final ServerCacheManager cacheManager; + + private final BackgroundExecutor backgroundExecutor; + + private final int dbSequenceBatchSize; + + private final EncryptKeyManager encryptKeyManager; + + private final IdBinderFactory idBinderFactory; + + private final XmlConfig xmlConfig; + + private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory; + + private final boolean eagerFetchLobs; + + /** + * Create for a given database dbConfig. + */ + public BeanDescriptorManager(InternalConfiguration config) { + + this.serverName = InternString.intern(config.getServerConfig().getName()); + this.cacheManager = config.getCacheManager(); + this.xmlConfig = config.getXmlConfig(); + this.dbSequenceBatchSize = config.getServerConfig().getDatabaseSequenceBatchSize(); + this.backgroundExecutor = config.getBackgroundExecutor(); + this.dataSource = config.getServerConfig().getDataSource(); + this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager(); + this.databasePlatform = config.getServerConfig().getDatabasePlatform(); + this.idBinderFactory = new IdBinderFactory(databasePlatform.isIdInExpandedForm()); + this.eagerFetchLobs = config.getServerConfig().isEagerFetchLobs(); + + this.bootupClasses = config.getBootupClasses(); + this.createProperties = config.getDeployCreateProperties(); + this.typeManager = config.getTypeManager(); + this.namingConvention = config.getServerConfig().getNamingConvention(); + this.dbIdentity = config.getDatabasePlatform().getDbIdentity(); + this.deplyInherit = config.getDeployInherit(); + this.deployOrmXml = config.getDeployOrmXml(); + this.deployUtil = config.getDeployUtil(); + + this.beanManagerFactory = new BeanManagerFactory(config.getServerConfig(), config.getDatabasePlatform()); + + this.updateChangesOnly = config.getServerConfig().isUpdateChangesOnly(); + + this.beanLifecycleAdapterFactory = new BeanLifecycleAdapterFactory(); + this.persistControllerManager = new PersistControllerManager(bootupClasses); + this.persistListenerManager = new PersistListenerManager(bootupClasses); + this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses); + + this.beanFinderManager = new DefaultBeanFinderManager(); + + this.reflectFactory = createReflectionFactory(); + this.transientProperties = new TransientProperties(); + } + + public BeanDescriptor getBeanDescriptorById(String descriptorId) { + return idDescMap.get(descriptorId); + } + + @SuppressWarnings("unchecked") + public BeanDescriptor getBeanDescriptor(Class entityType) { + return (BeanDescriptor) descMap.get(entityType.getName()); + } + + @SuppressWarnings("unchecked") + public BeanDescriptor getBeanDescriptor(String entityClassName) { + return (BeanDescriptor) descMap.get(entityClassName); + } + + public String getServerName() { + return serverName; + } + + public ServerCacheManager getCacheManager() { + return cacheManager; + } + + public NamingConvention getNamingConvention() { + return namingConvention; + } + + /** + * Set the internal EbeanServer instance to all BeanDescriptors. + */ + public void setEbeanServer(SpiEbeanServer internalEbean) { + for (BeanDescriptor desc : immutableDescriptorList) { + desc.setEbeanServer(internalEbean); + } + } + + public IdBinder createIdBinder(BeanProperty idProperty) { + return idBinderFactory.createIdBinder(idProperty); + } + + public void deploy() { + + try { + createListeners(); + readEmbeddedDeployment(); + readEntityDeploymentInitial(); + readEntityBeanTable(); + readEntityDeploymentAssociations(); + readInheritedIdGenerators(); + + // creates the BeanDescriptors + readEntityRelationships(); + readRawSqlQueries(); + + List> list = new ArrayList>(descMap.values()); + Collections.sort(list, beanDescComparator); + immutableDescriptorList = Collections.unmodifiableList(list); + + // put into map using the "desriptorId" (alternative to class name) + for (BeanDescriptor d : list) { + idDescMap.put(d.getDescriptorId(), d); + } + + initialiseAll(); + readForeignKeys(); + + readTableToDescriptor(); + + logStatus(); + + deplyInfoMap.clear(); + deplyInfoMap = null; + } catch (RuntimeException e) { + String msg = "Error in deployment"; + logger.error(msg, e); + throw e; + } + } + + /** + * Return the Encrypt key given the table and column name. + */ + public EncryptKey getEncryptKey(String tableName, String columnName) { + return encryptKeyManager.getEncryptKey(tableName, columnName); + } + + /** + * For SQL based modifications we need to invalidate appropriate parts of the + * cache. + */ + public void cacheNotify(TransactionEventTable.TableIUD tableIUD) { + + List> list = getBeanDescriptors(tableIUD.getTableName()); + if (list != null) { + for (int i = 0; i < list.size(); i++) { + list.get(i).cacheHandleBulkUpdate(tableIUD); + } + } + } + + /** + * Return the BeanDescriptors mapped to the table. + */ + public List> getBeanDescriptors(String tableName) { + return tableToDescMap.get(tableName.toLowerCase()); + } + + /** + * Build a map of table names to BeanDescriptors. + *

+ * This is generally used to maintain caches from table names. + *

+ */ + private void readTableToDescriptor() { + + for (BeanDescriptor desc : descMap.values()) { + String baseTable = desc.getBaseTable(); + if (baseTable == null) { + + } else { + baseTable = baseTable.toLowerCase(); + + List> list = tableToDescMap.get(baseTable); + if (list == null) { + list = new ArrayList>(1); + tableToDescMap.put(baseTable, list); + } + list.add(desc); + } + } + } + + private void readForeignKeys() { + + for (BeanDescriptor d : descMap.values()) { + d.initialiseFkeys(); + } + } + + /** + * Initialise all the BeanDescriptors. + *

+ * This occurs after all the BeanDescriptors have been created. This resolves + * circular relationships between BeanDescriptors. + *

+ *

+ * Also responsible for creating all the BeanManagers which contain the + * persister, listener etc. + *

+ */ + private void initialiseAll() { + + // now that all the BeanDescriptors are in their map + // we can initialise them which sorts out circular + // dependencies for OneToMany and ManyToOne etc + + // PASS 1: + // initialise the ID properties of all the beans + // first (as they are needed to initialise the + // associated properties in the second pass). + for (BeanDescriptor d : descMap.values()) { + d.initialiseId(); + } + + // PASS 2: + // now initialise all the inherit info + for (BeanDescriptor d : descMap.values()) { + d.initInheritInfo(); + } + + // PASS 3: + // now initialise all the associated properties + for (BeanDescriptor d : descMap.values()) { + d.initialiseOther(); + } + + // create BeanManager for each non-embedded entity bean + for (BeanDescriptor d : descMap.values()) { + if (!d.isEmbedded()) { + BeanManager m = beanManagerFactory.create(d); + beanManagerMap.put(d.getFullName(), m); + + checkForValidEmbeddedId(d); + } + } + } + + private void checkForValidEmbeddedId(BeanDescriptor d) { + IdBinder idBinder = d.getIdBinder(); + if (idBinder != null && idBinder instanceof IdBinderEmbedded) { + IdBinderEmbedded embId = (IdBinderEmbedded) idBinder; + BeanDescriptor idBeanDescriptor = embId.getIdBeanDescriptor(); + Class idType = idBeanDescriptor.getBeanType(); + try { + idType.getDeclaredMethod("hashCode", new Class[] {}); + idType.getDeclaredMethod("equals", new Class[] { Object.class }); + } catch (NoSuchMethodException e) { + checkMissingHashCodeOrEquals(e, idType, d.getBeanType()); + } + } + } + + private void checkMissingHashCodeOrEquals(Exception source, Class idType, Class beanType) { + + String msg = "SERIOUS ERROR: The hashCode() and equals() methods *MUST* be implemented "; + msg += "on Embedded bean " + idType + " as it is used as an Id for " + beanType; + throw new PersistenceException(msg, source); + } + + /** + * Return an immutable list of all the BeanDescriptors. + */ + public List> getBeanDescriptorList() { + return immutableDescriptorList; + } + + public Map, BeanTable> getBeanTables() { + return beanTableMap; + } + + public BeanTable getBeanTable(Class type) { + return beanTableMap.get(type); + } + + public Map> getBeanDescriptors() { + return descMap; + } + + @SuppressWarnings("unchecked") + public BeanManager getBeanManager(Class entityType) { + + return (BeanManager) getBeanManager(entityType.getName()); + } + + public BeanManager getBeanManager(String beanClassName) { + return beanManagerMap.get(beanClassName); + } + + public DNativeQuery getNativeQuery(String name) { + return deployOrmXml.getNativeQuery(name); + } + + /** + * Create the BeanControllers, BeanFinders and BeanListeners. + */ + private void createListeners() { + + int qa = beanQueryAdapterManager.getRegisterCount(); + int cc = persistControllerManager.getRegisterCount(); + int lc = persistListenerManager.getRegisterCount(); + int fc = beanFinderManager.createBeanFinders(bootupClasses.getBeanFinders()); + + logger.debug("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "]"); + } + + private void logStatus() { + logger.info("Entities enhanced[" + enhancedClassCount + "]"); + } + + private BeanDescriptor createEmbedded(Class beanClass) { + + DeployBeanInfo info = createDeployBeanInfo(beanClass); + readDeployAssociations(info); + + Integer key = getUniqueHash(info.getDescriptor()); + + return new BeanDescriptor(this, typeManager, info.getDescriptor(), key.toString()); + } + + private void registerBeanDescriptor(BeanDescriptor desc) { + descMap.put(desc.getBeanType().getName(), desc); + } + + /** + * Read deployment information for all the embedded beans. + */ + private void readEmbeddedDeployment() { + + ArrayList> embeddedClasses = bootupClasses.getEmbeddables(); + for (int i = 0; i < embeddedClasses.size(); i++) { + Class cls = embeddedClasses.get(i); + if (logger.isTraceEnabled()) { + String msg = "load deployinfo for embeddable:" + cls.getName(); + logger.trace(msg); + } + BeanDescriptor embDesc = createEmbedded(cls); + registerBeanDescriptor(embDesc); + } + } + + /** + * Read the initial deployment information for the entities. + *

+ * This stops short of reading relationship meta data until after the + * BeanTables have all been created. + *

+ */ + private void readEntityDeploymentInitial() { + + ArrayList> entityClasses = bootupClasses.getEntities(); + + for (Class entityClass : entityClasses) { + DeployBeanInfo info = createDeployBeanInfo(entityClass); + deplyInfoMap.put(entityClass, info); + } + } + + /** + * Create the BeanTable information which has the base table and id. + *

+ * This is determined prior to resolving relationship information. + *

+ */ + private void readEntityBeanTable() { + + for (DeployBeanInfo info : deplyInfoMap.values()) { + BeanTable beanTable = createBeanTable(info); + beanTableMap.put(beanTable.getBeanType(), beanTable); + } + } + + /** + * Create the BeanTable information which has the base table and id. + *

+ * This is determined prior to resolving relationship information. + *

+ */ + private void readEntityDeploymentAssociations() { + + for (DeployBeanInfo info : deplyInfoMap.values()) { + readDeployAssociations(info); + } + } + + private void readInheritedIdGenerators() { + + for (DeployBeanInfo info : deplyInfoMap.values()) { + DeployBeanDescriptor descriptor = info.getDescriptor(); + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && !inheritInfo.isRoot()) { + DeployBeanInfo rootBeanInfo = deplyInfoMap.get(inheritInfo.getRoot().getType()); + IdGenerator rootIdGen = rootBeanInfo.getDescriptor().getIdGenerator(); + if (rootIdGen != null) { + descriptor.setIdGenerator(rootIdGen); + } + } + } + } + + /** + * Create the BeanTable from the deployment information gathered so far. + */ + private BeanTable createBeanTable(DeployBeanInfo info) { + + DeployBeanDescriptor deployDescriptor = info.getDescriptor(); + DeployBeanTable beanTable = deployDescriptor.createDeployBeanTable(); + return new BeanTable(beanTable, this); + } + + /** + * Parse the named Raw Sql queries using BeanDescriptor. + */ + private void readRawSqlQueries() { + + for (DeployBeanInfo info : deplyInfoMap.values()) { + + DeployBeanDescriptor deployDesc = info.getDescriptor(); + BeanDescriptor desc = getBeanDescriptor(deployDesc.getBeanType()); + + for (DRawSqlMeta rawSqlMeta : deployDesc.getRawSqlMeta()) { + if (rawSqlMeta.getQuery() == null) { + + } else { + DeployNamedQuery nq = new DRawSqlSelectBuilder(namingConvention, desc, rawSqlMeta).parse(); + desc.addNamedQuery(nq); + } + } + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void readEntityRelationships() { + + // We only perform 'circular' checks etc after we have + // all the DeployBeanDescriptors created and in the map. + + for (DeployBeanInfo info : deplyInfoMap.values()) { + checkMappedBy(info); + } + + for (DeployBeanInfo info : deplyInfoMap.values()) { + secondaryPropsJoins(info); + } + + // Set inheritance info + for (DeployBeanInfo info : deplyInfoMap.values()) { + setInheritanceInfo(info); + } + + for (DeployBeanInfo info : deplyInfoMap.values()) { + DeployBeanDescriptor deployBeanDescriptor = info.getDescriptor(); + Integer key = getUniqueHash(deployBeanDescriptor); + registerBeanDescriptor(new BeanDescriptor(this, typeManager, info.getDescriptor(), key.toString())); + } + } + + /** + * Sets the inheritance info. ~EMG fix for join problem + * + * @param info the new inheritance info + */ + private void setInheritanceInfo(DeployBeanInfo info) { + + for (DeployBeanPropertyAssocOne oneProp : info.getDescriptor().propertiesAssocOne()) { + if (!oneProp.isTransient()) { + DeployBeanInfo assoc = deplyInfoMap.get(oneProp.getTargetType()); + if (assoc != null){ + oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo()); + } + } + } + + for (DeployBeanPropertyAssocMany manyProp : info.getDescriptor().propertiesAssocMany()) { + if (!manyProp.isTransient()) { + DeployBeanInfo assoc = deplyInfoMap.get(manyProp.getTargetType()); + if (assoc != null){ + manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo()); + } + } + } + } + + private Integer getUniqueHash(DeployBeanDescriptor deployBeanDescriptor) { + + int hashCode = deployBeanDescriptor.getFullName().hashCode(); + + for (int i = 0; i < 100000; i++) { + Integer key = Integer.valueOf(hashCode + i); + if (!descriptorUniqueIds.contains(key)) { + return key; + } + } + throw new RuntimeException("Failed to generate a unique hash for " + deployBeanDescriptor.getFullName()); + } + + private void secondaryPropsJoins(DeployBeanInfo info) { + + DeployBeanDescriptor descriptor = info.getDescriptor(); + for (DeployBeanProperty prop : descriptor.propertiesBase()) { + if (prop.isSecondaryTable()) { + String tableName = prop.getSecondaryTable(); + // find a join to that table... + DeployBeanPropertyAssocOne assocOne = descriptor.findJoinToTable(tableName); + if (assocOne == null) { + String msg = "Error with property " + prop.getFullBeanName() + ". Could not find a Relationship to table " + tableName + + ". Perhaps you could use a @JoinColumn instead."; + throw new RuntimeException(msg); + } + DeployTableJoin tableJoin = assocOne.getTableJoin(); + prop.setSecondaryTableJoin(tableJoin, assocOne.getName()); + } + } + } + + /** + * Check the mappedBy attributes for properties on this descriptor. + *

+ * This will read join information defined on the 'owning/other' side of the + * relationship. It also does some extra work for unidirectional + * relationships. + *

+ */ + private void checkMappedBy(DeployBeanInfo info) { + + for (DeployBeanPropertyAssocOne oneProp : info.getDescriptor().propertiesAssocOne()) { + if (!oneProp.isTransient()) { + if (oneProp.getMappedBy() != null) { + checkMappedByOneToOne(info, oneProp); + } + } + } + + for (DeployBeanPropertyAssocMany manyProp : info.getDescriptor().propertiesAssocMany()) { + if (!manyProp.isTransient()) { + if (manyProp.isManyToMany()) { + checkMappedByManyToMany(info, manyProp); + } else { + checkMappedByOneToMany(info, manyProp); + } + } + } + } + + private DeployBeanDescriptor getTargetDescriptor(DeployBeanPropertyAssoc prop) { + + Class targetType = prop.getTargetType(); + DeployBeanInfo info = deplyInfoMap.get(targetType); + if (info == null) { + String msg = "Can not find descriptor [" + targetType + "] for " + prop.getFullBeanName(); + throw new PersistenceException(msg); + } + + return info.getDescriptor(); + } + + /** + * Check that the many property has either an implied mappedBy property or + * mark it as unidirectional. + */ + private boolean findMappedBy(DeployBeanPropertyAssocMany prop) { + + // this is the entity bean type - that owns this property + Class owningType = prop.getOwningType(); + + Set matchSet = new HashSet(); + + // get the bean descriptor that holds the mappedBy property + DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); + List> ones = targetDesc.propertiesAssocOne(); + for (DeployBeanPropertyAssocOne possibleMappedBy : ones) { + Class possibleMappedByType = possibleMappedBy.getTargetType(); + if (possibleMappedByType.equals(owningType)) { + prop.setMappedBy(possibleMappedBy.getName()); + matchSet.add(possibleMappedBy.getName()); + } + } + + if (matchSet.size() == 0) { + // this is a unidirectional relationship + // ... that is no matching property on the 'detail' bean + return false; + } + if (matchSet.size() == 1) { + // all right with the world + return true; + } + if (matchSet.size() == 2) { + // try to find a match implicitly using a common naming convention + // e.g. List loggedBugs; ... search for "logged" in matchSet + String name = prop.getName(); + + // get the target type short name + String targetType = prop.getTargetType().getName(); + String shortTypeName = targetType.substring(targetType.lastIndexOf(".") + 1); + + // name includes (probably ends with) the target type short name? + int p = name.indexOf(shortTypeName); + if (p > 1) { + // ok, get the 'interesting' part of the property name + // That is the name without the target type + String searchName = name.substring(0, p).toLowerCase(); + + // search for this in the possible matches + for (String possibleMappedBy : matchSet) { + String possibleLower = possibleMappedBy.toLowerCase(); + if (possibleLower.indexOf(searchName) > -1) { + // we have a match.. + prop.setMappedBy(possibleMappedBy); + + String m = "Implicitly found mappedBy for " + targetDesc + "." + prop; + m += " by searching for [" + searchName + "] against " + matchSet; + logger.debug(m); + + return true; + } + } + + } + } + // multiple options so should specify mappedBy property + String msg = "Error on " + prop.getFullBeanName() + " missing mappedBy."; + msg += " There are [" + matchSet.size() + "] possible properties in " + targetDesc; + msg += " that this association could be mapped to. Please specify one using "; + msg += "the mappedBy attribute on @OneToMany."; + throw new PersistenceException(msg); + } + + /** + * A OneToMany with no matching mappedBy property in the target so must be + * unidirectional. + *

+ * This means that inserts MUST cascade for this property. + *

+ *

+ * Create a "Shadow"/Unidirectional property on the target. It is used with + * inserts to set the foreign key value (e.g. inserts the foreign key value + * into the order_id column on the order_lines table). + *

+ */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void makeUnidirectional(DeployBeanInfo info, DeployBeanPropertyAssocMany oneToMany) { + + DeployBeanDescriptor targetDesc = getTargetDescriptor(oneToMany); + + Class owningType = oneToMany.getOwningType(); + + if (!oneToMany.getCascadeInfo().isSave()) { + // The property MUST have persist cascading so that inserts work. + + Class targetType = oneToMany.getTargetType(); + String msg = "Error on " + oneToMany.getFullBeanName() + ". @OneToMany MUST have "; + msg += "Cascade.PERSIST or Cascade.ALL because this is a unidirectional "; + msg += "relationship. That is, there is no property of type " + owningType + " on " + targetType; + + throw new PersistenceException(msg); + } + + // mark this property as unidirectional + oneToMany.setUnidirectional(true); + + // create the 'shadow' unidirectional property + // which is put on the target descriptor + DeployBeanPropertyAssocOne unidirectional = new DeployBeanPropertyAssocOne(targetDesc, owningType); + unidirectional.setUndirectionalShadow(true); + unidirectional.setNullable(false); + unidirectional.setDbRead(true); + unidirectional.setDbInsertable(true); + unidirectional.setDbUpdateable(false); + + targetDesc.setUnidirectional(unidirectional); + + // specify table and table alias... + BeanTable beanTable = getBeanTable(owningType); + unidirectional.setBeanTable(beanTable); + unidirectional.setName(beanTable.getBaseTable()); + + info.setBeanJoinType(unidirectional, true); + + // define the TableJoin + DeployTableJoin oneToManyJoin = oneToMany.getTableJoin(); + if (!oneToManyJoin.hasJoinColumns()) { + throw new RuntimeException("No join columns"); + } + + // inverse of the oneToManyJoin + DeployTableJoin unidirectionalJoin = unidirectional.getTableJoin(); + unidirectionalJoin.setColumns(oneToManyJoin.columns(), true); + + } + + private void checkMappedByOneToOne(DeployBeanInfo info, DeployBeanPropertyAssocOne prop) { + + // check that the mappedBy property is valid and read + // its associated join information if it is available + String mappedBy = prop.getMappedBy(); + + // get the mappedBy property + DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); + DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy); + if (mappedProp == null) { + String m = "Error on " + prop.getFullBeanName(); + m += " Can not find mappedBy property [" + targetDesc + "." + mappedBy + "] "; + throw new PersistenceException(m); + } + + if (!(mappedProp instanceof DeployBeanPropertyAssocOne)) { + String m = "Error on " + prop.getFullBeanName(); + m += ". mappedBy property [" + targetDesc + "." + mappedBy + "]is not a OneToOne?"; + throw new PersistenceException(m); + } + + DeployBeanPropertyAssocOne mappedAssocOne = (DeployBeanPropertyAssocOne) mappedProp; + + if (!mappedAssocOne.isOneToOne()) { + String m = "Error on " + prop.getFullBeanName(); + m += ". mappedBy property [" + targetDesc + "." + mappedBy + "]is not a OneToOne?"; + throw new PersistenceException(m); + } + + DeployTableJoin tableJoin = prop.getTableJoin(); + if (!tableJoin.hasJoinColumns()) { + // define Join as the inverse of the mappedBy property + DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin(); + otherTableJoin.copyWithoutType(tableJoin, true, tableJoin.getTable()); + } + } + + /** + * If the property has mappedBy set then do two things. Make sure the mappedBy + * property exists, and secondly read its join information. + *

+ * We can use the join information from the mappedBy property and reverse it + * for using in the OneToMany direction. + *

+ */ + private void checkMappedByOneToMany(DeployBeanInfo info, DeployBeanPropertyAssocMany prop) { + + // get the bean descriptor that holds the mappedBy property + + if (prop.getMappedBy() == null) { + if (!findMappedBy(prop)) { + makeUnidirectional(info, prop); + return; + } + } + + // check that the mappedBy property is valid and read + // its associated join information if it is available + String mappedBy = prop.getMappedBy(); + + // get the mappedBy property + DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); + DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy); + if (mappedProp == null) { + + String m = "Error on " + prop.getFullBeanName(); + m += " Can not find mappedBy property [" + mappedBy + "] "; + m += "in [" + targetDesc + "]"; + throw new PersistenceException(m); + } + + if (!(mappedProp instanceof DeployBeanPropertyAssocOne)) { + String m = "Error on " + prop.getFullBeanName(); + m += ". mappedBy property [" + mappedBy + "]is not a ManyToOne?"; + m += "in [" + targetDesc + "]"; + throw new PersistenceException(m); + } + + DeployBeanPropertyAssocOne mappedAssocOne = (DeployBeanPropertyAssocOne) mappedProp; + + DeployTableJoin tableJoin = prop.getTableJoin(); + if (!tableJoin.hasJoinColumns()) { + // define Join as the inverse of the mappedBy property + DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin(); + otherTableJoin.copyTo(tableJoin, true, tableJoin.getTable()); + } + + } + + /** + * For mappedBy copy the joins from the other side. + */ + private void checkMappedByManyToMany(DeployBeanInfo info, DeployBeanPropertyAssocMany prop) { + + // get the bean descriptor that holds the mappedBy property + String mappedBy = prop.getMappedBy(); + if (mappedBy == null) { + return; + } + + // get the mappedBy property + DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); + DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy); + + if (mappedProp == null) { + String m = "Error on " + prop.getFullBeanName(); + m += " Can not find mappedBy property [" + mappedBy + "] "; + m += "in [" + targetDesc + "]"; + throw new PersistenceException(m); + } + + if (!(mappedProp instanceof DeployBeanPropertyAssocMany)) { + String m = "Error on " + prop.getFullBeanName(); + m += ". mappedBy property [" + targetDesc + "." + mappedBy + "] is not a ManyToMany?"; + throw new PersistenceException(m); + } + + DeployBeanPropertyAssocMany mappedAssocMany = (DeployBeanPropertyAssocMany) mappedProp; + + if (!mappedAssocMany.isManyToMany()) { + String m = "Error on " + prop.getFullBeanName(); + m += ". mappedBy property [" + targetDesc + "." + mappedBy + "] is not a ManyToMany?"; + throw new PersistenceException(m); + } + + // define the relationships/joins on this side as the + // reverse of the other mappedBy side ... + + // DeployTableJoin mappedJoin = mappedAssocMany.getTableJoin(); + DeployTableJoin mappedIntJoin = mappedAssocMany.getIntersectionJoin(); + DeployTableJoin mappendInverseJoin = mappedAssocMany.getInverseJoin(); + + String intTableName = mappedIntJoin.getTable(); + + DeployTableJoin tableJoin = prop.getTableJoin(); + mappedIntJoin.copyTo(tableJoin, true, targetDesc.getBaseTable()); + + DeployTableJoin intJoin = new DeployTableJoin(); + mappendInverseJoin.copyTo(intJoin, false, intTableName); + prop.setIntersectionJoin(intJoin); + + DeployTableJoin inverseJoin = new DeployTableJoin(); + mappedIntJoin.copyTo(inverseJoin, false, intTableName); + prop.setInverseJoin(inverseJoin); + } + + private void setBeanControllerFinderListener(DeployBeanDescriptor descriptor) { + + Class beanType = descriptor.getBeanType(); + + persistControllerManager.addPersistControllers(descriptor); + persistListenerManager.addPersistListeners(descriptor); + beanQueryAdapterManager.addQueryAdapter(descriptor); + + BeanFinder beanFinder = beanFinderManager.getBeanFinder(beanType); + if (beanFinder != null) { + descriptor.setBeanFinder(beanFinder); + logger.debug("BeanFinder on[" + descriptor.getFullName() + "] " + beanFinder.getClass().getName()); + } + } + + /** + * Read the initial deployment information for a given bean type. + */ + private DeployBeanInfo createDeployBeanInfo(Class beanClass) { + + DeployBeanDescriptor desc = new DeployBeanDescriptor(beanClass); + + desc.setUpdateChangesOnly(updateChangesOnly); + + beanLifecycleAdapterFactory.addLifecycleMethods(desc); + + // set bean controller, finder and listener + setBeanControllerFinderListener(desc); + deplyInherit.process(desc); + desc.checkInheritanceMapping(); + + createProperties.createProperties(desc); + + DeployBeanInfo info = new DeployBeanInfo(deployUtil, desc); + + readAnnotations.readInitial(info, eagerFetchLobs); + return info; + } + + private void readDeployAssociations(DeployBeanInfo info) { + + DeployBeanDescriptor desc = info.getDescriptor(); + + readAnnotations.readAssociations(info, this); + + readXml(desc); + + if (!EntityType.ORM.equals(desc.getEntityType())) { + // not using base table + desc.setBaseTable(null); + } + + // mark transient properties + transientProperties.process(desc); + setScalarType(desc); + + if (!desc.isEmbedded()) { + // Set IdGenerator or use DB Identity + setIdGeneration(desc); + + // find the appropriate default concurrency mode + setConcurrencyMode(desc); + } + + // generate the byte code + createByteCode(desc); + } + + /** + * Set the Identity generation mechanism. + */ + private IdType setIdGeneration(DeployBeanDescriptor desc) { + + if (desc.propertiesId().size() == 0) { + // bean doen't have an Id property + if (!desc.isBaseTableType() || desc.getBeanFinder() != null) { + // using BeanFinder so perhaps valid without an id + } else { + // expecting an id property + logger.warn(Message.msg("deploy.nouid", desc.getFullName())); + } + return null; + } + + if (IdType.SEQUENCE.equals(desc.getIdType()) && !dbIdentity.isSupportsSequence()) { + // explicit sequence but not supported by the DatabasePlatform + logger.info("Explicit sequence on " + desc.getFullName() + " but not supported by DB Platform - ignored"); + desc.setIdType(null); + } + if (IdType.IDENTITY.equals(desc.getIdType()) && !dbIdentity.isSupportsIdentity()) { + // explicit identity but not supported by the DatabasePlatform + logger.info("Explicit Identity on " + desc.getFullName() + " but not supported by DB Platform - ignored"); + desc.setIdType(null); + } + + if (desc.getIdType() == null) { + // use the default. IDENTITY or SEQUENCE. + desc.setIdType(dbIdentity.getIdType()); + } + + if (IdType.GENERATOR.equals(desc.getIdType())) { + String genName = desc.getIdGeneratorName(); + if (UuidIdGenerator.AUTO_UUID.equals(genName)) { + desc.setIdGenerator(uuidIdGenerator); + return IdType.GENERATOR; + } + } + + if (desc.getBaseTable() == null) { + // no base table so not going to set Identity + // of sequence information + return null; + } + + if (IdType.IDENTITY.equals(desc.getIdType())) { + // used when getGeneratedKeys is not supported (SQL Server 2000) + String selectLastInsertedId = dbIdentity.getSelectLastInsertedId(desc.getBaseTable()); + desc.setSelectLastInsertedId(selectLastInsertedId); + return IdType.IDENTITY; + } + + String seqName = desc.getIdGeneratorName(); + if (seqName != null) { + logger.debug("explicit sequence " + seqName + " on " + desc.getFullName()); + } else { + String primaryKeyColumn = desc.getSinglePrimaryKeyColumn(); + // use namingConvention to define sequence name + seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn); + } + + // create the sequence based IdGenerator + IdGenerator seqIdGen = createSequenceIdGenerator(seqName); + desc.setIdGenerator(seqIdGen); + + return IdType.SEQUENCE; + } + + private IdGenerator createSequenceIdGenerator(String seqName) { + return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, seqName, dbSequenceBatchSize); + } + + private void createByteCode(DeployBeanDescriptor deploy) { + + // check to see if the bean supports EntityBean interface + // generate a subclass if required + setEntityBeanClass(deploy); + + // use Code generation or Standard reflection to support + // getter and setter methods + setBeanReflect(deploy); + } + + /** + * Set the Scalar Types on all the simple types. This is done AFTER transients + * have been identified. This is because a non-transient field MUST have a + * ScalarType. It is useful for transients to have ScalarTypes because then + * they can be used in a SqlSelect query. + *

+ * Enums are treated a bit differently in that they always have a ScalarType + * as one is built for them. + *

+ */ + private void setScalarType(DeployBeanDescriptor deployDesc) { + + for (DeployBeanProperty prop : deployDesc.propertiesAll()) { + if (prop instanceof DeployBeanPropertyAssoc == false) { + deployUtil.setScalarType(prop); + } + } + } + + private void readXml(DeployBeanDescriptor deployDesc) { + + List eXml = xmlConfig.findEntityXml(deployDesc.getFullName()); + readXmlRawSql(deployDesc, eXml); + + Dnode entityXml = deployOrmXml.findEntityDeploymentXml(deployDesc.getFullName()); + + if (entityXml != null) { + readXmlNamedQueries(deployDesc, entityXml); + readXmlSql(deployDesc, entityXml); + } + } + + /** + * Read sql-select (FUTURE: additionally sql-insert, sql-update, sql-delete). + * If found this entity bean is based on raw sql. + */ + private void readXmlSql(DeployBeanDescriptor deployDesc, Dnode entityXml) { + + List sqlSelectList = entityXml.findAll("sql-select", entityXml.getLevel() + 1); + for (int i = 0; i < sqlSelectList.size(); i++) { + Dnode sqlSelect = sqlSelectList.get(i); + readSqlSelect(deployDesc, sqlSelect); + } + } + + private String findContent(Dnode node, String nodeName) { + Dnode found = node.find(nodeName); + if (found != null) { + return found.getNodeContent(); + } else { + return null; + } + } + + private void readSqlSelect(DeployBeanDescriptor deployDesc, Dnode sqlSelect) { + + String name = sqlSelect.getStringAttr("name", "default"); + String extend = sqlSelect.getStringAttr("extend", null); + String queryDebug = sqlSelect.getStringAttr("debug", null); + boolean debug = (queryDebug != null && queryDebug.equalsIgnoreCase("true")); + + // the raw sql select + String query = findContent(sqlSelect, "query"); + String where = findContent(sqlSelect, "where"); + String having = findContent(sqlSelect, "having"); + String columnMapping = findContent(sqlSelect, "columnMapping"); + + DRawSqlMeta m = new DRawSqlMeta(name, extend, query, debug, where, having, columnMapping); + + deployDesc.add(m); + + } + + private void readXmlRawSql(DeployBeanDescriptor deployDesc, List entityXml) { + + List rawSqlQueries = xmlConfig.find(entityXml, "raw-sql"); + for (int i = 0; i < rawSqlQueries.size(); i++) { + Dnode rawSqlDnode = rawSqlQueries.get(i); + String name = rawSqlDnode.getAttribute("name"); + if (isEmpty(name)) { + throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " missing name attribute"); + } + Dnode queryNode = rawSqlDnode.find("query"); + if (queryNode == null) { + throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " missing query element"); + } + String sql = queryNode.getNodeContent(); + if (isEmpty(sql)) { + throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " has empty sql in the query element?"); + } + + List columnMappings = rawSqlDnode.findAll("columnMapping", 1); + + RawSqlBuilder rawSqlBuilder = RawSqlBuilder.parse(sql); + for (int j = 0; j < columnMappings.size(); j++) { + Dnode cm = columnMappings.get(j); + String column = cm.getAttribute("column"); + String property = cm.getAttribute("property"); + rawSqlBuilder.columnMapping(column, property); + } + RawSql rawSql = rawSqlBuilder.create(); + + DeployNamedQuery namedQuery = new DeployNamedQuery(name, rawSql); + deployDesc.add(namedQuery); + } + } + + private boolean isEmpty(String s) { + return s == null || s.trim().length() == 0; + } + + /** + * Read named queries for this bean type. + */ + private void readXmlNamedQueries(DeployBeanDescriptor deployDesc, Dnode entityXml) { + + // look for named-query... + List namedQueries = entityXml.findAll("named-query", 1); + + for (Dnode namedQueryXml : namedQueries) { + + String name = (String) namedQueryXml.getAttribute("name"); + Dnode query = namedQueryXml.find("query"); + if (query == null) { + logger.warn("orm.xml " + deployDesc.getFullName() + " named-query missing query element?"); + + } else { + String oql = query.getNodeContent(); + // TODO: QueryHints not read from xml yet + if (name == null || oql == null) { + logger.warn("orm.xml " + deployDesc.getFullName() + " named-query has no query content?"); + } else { + // add the named query + DeployNamedQuery q = new DeployNamedQuery(name, oql, null); + deployDesc.add(q); + } + } + } + } + + private BeanPropertyInfoFactory createReflectionFactory() { + + return new EnhanceBeanPropertyInfoFactory(); + } + + /** + * Set BeanReflect BeanReflectGetter and BeanReflectSetter properties. + *

+ * This sets the implementation of constructing entity beans and the setting + * and getting of properties. It is generally faster to use code generation + * rather than reflection to do this. + *

+ */ + private void setBeanReflect(DeployBeanDescriptor desc) { + + // Set the BeanReflectGetter and BeanReflectSetter that typically + // use generated code. NB: Due to Bug 166 so now doing this for + // abstract classes as well. + + Class beanType = desc.getBeanType(); + + BeanPropertiesReader reflectProps = new BeanPropertiesReader(beanType); + + BeanPropertyInfo beanReflect = reflectFactory.create(beanType); + desc.setBeanReflect(beanReflect); + desc.setProperties(reflectProps.getProperties()); + + for (DeployBeanProperty prop : desc.propertiesAll()) { + String propName = prop.getName(); + Integer pos = reflectProps.getPropertyIndex(propName); + if (pos == null) { + if (isPersistentField(prop)) { + throw new IllegalStateException("Property "+propName+" not found in "+reflectProps); + } + + } else { + int propertyIndex = pos.intValue(); + prop.setPropertyIndex(propertyIndex); + prop.setGetter(beanReflect.getGetter(propName, propertyIndex)); + prop.setSetter(beanReflect.getSetter(propName, propertyIndex)); + } + } + } + + /** + * Return true if this is a persistent field (not transient or static). + */ + private boolean isPersistentField(DeployBeanProperty prop) { + + Field field = prop.getField(); + int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) { + return false; + } + if (field.isAnnotationPresent(Transient.class)) { + return false; + } + return true; + } + + /** + * DevNote: It is assumed that Embedded can contain version properties. It is + * also assumed that Embedded beans do NOT themselves contain Embedded beans + * which contain version properties. + */ + private void setConcurrencyMode(DeployBeanDescriptor desc) { + + if (desc.getConcurrencyMode() != null) { + // concurrency mode explicitly set during deployment + return; + } + + if (checkForVersionProperties(desc)) { + desc.setConcurrencyMode(ConcurrencyMode.VERSION); + } else { + desc.setConcurrencyMode(ConcurrencyMode.NONE); + } + } + + /** + * Search for version properties also including embedded beans. + */ + private boolean checkForVersionProperties(DeployBeanDescriptor desc) { + + boolean hasVersionProperty = false; + + List props = desc.propertiesBase(); + for (int i = 0; i < props.size(); i++) { + if (props.get(i).isVersionColumn()) { + hasVersionProperty = true; + } + } + + return hasVersionProperty; + } + + private boolean hasEntityBeanInterface(Class beanClass) { + + Class[] interfaces = beanClass.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + if (interfaces[i].equals(EntityBean.class)) { + return true; + } + } + return false; + } + + /** + * Test the bean type to see if it implements EntityBean interface already. + */ + private void setEntityBeanClass(DeployBeanDescriptor desc) { + + Class beanClass = desc.getBeanType(); + + if (!hasEntityBeanInterface(beanClass)) { + throw new IllegalStateException("Bean "+beanClass+" is not enhanced?"); + } + + // the bean already implements EntityBean + checkInheritedClasses(beanClass); + + if (!beanClass.getName().startsWith("com.avaje.ebean.meta")) { + enhancedClassCount++; + } + } + + /** + * Check that the inherited classes are the same as the entity bean (aka all + * enhanced or all dynamically subclassed). + */ + private void checkInheritedClasses(Class beanClass) { + + Class superclass = beanClass.getSuperclass(); + if (Object.class.equals(superclass)) { + // we got to the top of the inheritance + return; + } + if (Model.class.equals(superclass)) { + // top of the inheritance. Not enhancing Model at this stage + return; + } + if (!EntityBean.class.isAssignableFrom(superclass)) { + if (isMappedSuperWithNoProperties(superclass)) { + // ok to stop and treat just the same as Object.class + return; + } + throw new IllegalStateException("Super type "+superclass+" is not enhanced?"); + } + + // recursively continue up the inheritance hierarchy + checkInheritedClasses(superclass); + } + + /** + * Return true if this is a MappedSuperclass bean with no persistent properties. + * If so it is ok for it not to be enhanced. + */ + private boolean isMappedSuperWithNoProperties(Class beanClass) { + + MappedSuperclass annotation = beanClass.getAnnotation(MappedSuperclass.class); + if (annotation == null) { + return false; + } + Field[] fields = beanClass.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())) { + // ignore this field + } else if (field.isAnnotationPresent(Transient.class)) { + // ignore this field + } else { + return false; + } + } + return true; + } + + /** + * Comparator to sort the BeanDescriptors by name. + */ + private static final class BeanDescComparator implements Comparator>, Serializable { + + private static final long serialVersionUID = 1L; + + public int compare(BeanDescriptor o1, BeanDescriptor o2) { + + return o1.getName().compareTo(o2.getName()); + } + } +} 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 a4833f578..fced60c5f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorMap.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorMap.java @@ -1,37 +1,37 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebean.cache.ServerCacheManager; -import com.avaje.ebean.config.EncryptKey; -import com.avaje.ebeaninternal.server.deploy.id.IdBinder; - -/** - * Provides a method to find a BeanDescriptor. - *

- * Used during deployment of to resolve relationships between beans. - *

- */ -public interface BeanDescriptorMap { - - /** - * Return the name of the server/database. - */ - public String getServerName(); - - /** - * Return the Cache Manager. - */ - public ServerCacheManager getCacheManager(); - - /** - * Return the BeanDescriptor for a given class. - */ - public BeanDescriptor getBeanDescriptor(Class entityType); - - /** - * Return the Encrypt key given the table and column name. - */ - public EncryptKey getEncryptKey(String tableName, String columnName); - - public IdBinder createIdBinder(BeanProperty id); - -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebean.cache.ServerCacheManager; +import com.avaje.ebean.config.EncryptKey; +import com.avaje.ebeaninternal.server.deploy.id.IdBinder; + +/** + * Provides a method to find a BeanDescriptor. + *

+ * Used during deployment of to resolve relationships between beans. + *

+ */ +public interface BeanDescriptorMap { + + /** + * Return the name of the server/database. + */ + public String getServerName(); + + /** + * Return the Cache Manager. + */ + public ServerCacheManager getCacheManager(); + + /** + * Return the BeanDescriptor for a given class. + */ + public BeanDescriptor getBeanDescriptor(Class entityType); + + /** + * Return the Encrypt key given the table and column name. + */ + public EncryptKey getEncryptKey(String tableName, String columnName); + + public IdBinder createIdBinder(BeanProperty id); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMeta.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMeta.java index 7209b6d42..db3ff928d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMeta.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMeta.java @@ -1,19 +1,19 @@ -package com.avaje.ebeaninternal.server.deploy; - -public class BeanEmbeddedMeta { - - - final BeanProperty[] properties; - - public BeanEmbeddedMeta(BeanProperty[] properties) { - this.properties = properties; - } - - /** - * Return the properties with over ridden mapping information. - */ - public BeanProperty[] getProperties() { - return properties; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +public class BeanEmbeddedMeta { + + + final BeanProperty[] properties; + + public BeanEmbeddedMeta(BeanProperty[] properties) { + this.properties = properties; + } + + /** + * Return the properties with over ridden mapping information. + */ + public BeanProperty[] getProperties() { + return properties; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java index e1f463e2c..9c35d464c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java @@ -1,53 +1,53 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.util.Map; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; - -/** - * Creates BeanProperties for Embedded beans that have deployment information - * such as the actual DB column name and table alias. - */ -public class BeanEmbeddedMetaFactory { - - /** - * Create BeanProperties for embedded beans using the deployment specific DB column name and table alias. - */ - public static BeanEmbeddedMeta create(BeanDescriptorMap owner, DeployBeanPropertyAssocOne prop, - BeanDescriptor descriptor) { - - // we can get a BeanDescriptor for an Embedded bean - // and know that it is NOT recursive, as Embedded beans are - // only allow to hold simple scalar types... - BeanDescriptor targetDesc = owner.getBeanDescriptor(prop.getTargetType()); - if (targetDesc == null){ - String msg = "Could not find BeanDescriptor for "+prop.getTargetType() - +". Perhaps the EmbeddedId class is not registered?"; - throw new PersistenceException(msg); - } - - // deployment override information (column names) - Map propColMap = prop.getDeployEmbedded().getPropertyColumnMap(); - - BeanProperty[] sourceProperties = targetDesc.propertiesBaseScalar(); - - BeanProperty[] embeddedProperties = new BeanProperty[sourceProperties.length]; - - for (int i = 0; i < sourceProperties.length; i++) { - - String propertyName = sourceProperties[i].getName(); - String dbColumn = propColMap.get(propertyName); - if (dbColumn == null) { - // dbColumn not overridden so take original - dbColumn = sourceProperties[i].getDbColumn(); - } - - BeanPropertyOverride overrides = new BeanPropertyOverride(dbColumn); - embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides); - } - - return new BeanEmbeddedMeta(embeddedProperties); - } -} +package com.avaje.ebeaninternal.server.deploy; + +import java.util.Map; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; + +/** + * Creates BeanProperties for Embedded beans that have deployment information + * such as the actual DB column name and table alias. + */ +public class BeanEmbeddedMetaFactory { + + /** + * Create BeanProperties for embedded beans using the deployment specific DB column name and table alias. + */ + public static BeanEmbeddedMeta create(BeanDescriptorMap owner, DeployBeanPropertyAssocOne prop, + BeanDescriptor descriptor) { + + // we can get a BeanDescriptor for an Embedded bean + // and know that it is NOT recursive, as Embedded beans are + // only allow to hold simple scalar types... + BeanDescriptor targetDesc = owner.getBeanDescriptor(prop.getTargetType()); + if (targetDesc == null){ + String msg = "Could not find BeanDescriptor for "+prop.getTargetType() + +". Perhaps the EmbeddedId class is not registered?"; + throw new PersistenceException(msg); + } + + // deployment override information (column names) + Map propColMap = prop.getDeployEmbedded().getPropertyColumnMap(); + + BeanProperty[] sourceProperties = targetDesc.propertiesBaseScalar(); + + BeanProperty[] embeddedProperties = new BeanProperty[sourceProperties.length]; + + for (int i = 0; i < sourceProperties.length; i++) { + + String propertyName = sourceProperties[i].getName(); + String dbColumn = propColMap.get(propertyName); + if (dbColumn == null) { + // dbColumn not overridden so take original + dbColumn = sourceProperties[i].getDbColumn(); + } + + BeanPropertyOverride overrides = new BeanPropertyOverride(dbColumn); + embeddedProperties[i] = new BeanProperty(sourceProperties[i], overrides); + } + + return new BeanEmbeddedMeta(embeddedProperties); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanFinderManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanFinderManager.java index c48a1bdc7..8309a1f41 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanFinderManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanFinderManager.java @@ -1,26 +1,26 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.util.List; - -import com.avaje.ebean.event.BeanFinder; - -/** - * Factory for controlling the construction of BeanFinders. - */ -public interface BeanFinderManager { - - /** - * Return the number of beans with a registered finder. - */ - public int getRegisterCount(); - - /** - * Create the appropriate BeanController. - */ - public int createBeanFinders(List> finderClassList); - - /** - * Return the BeanController for a given entity type. - */ - public BeanFinder getBeanFinder(Class entityType); -} +package com.avaje.ebeaninternal.server.deploy; + +import java.util.List; + +import com.avaje.ebean.event.BeanFinder; + +/** + * Factory for controlling the construction of BeanFinders. + */ +public interface BeanFinderManager { + + /** + * Return the number of beans with a registered finder. + */ + public int getRegisterCount(); + + /** + * Create the appropriate BeanController. + */ + public int createBeanFinders(List> finderClassList); + + /** + * Return the BeanController for a given entity type. + */ + public BeanFinder getBeanFinder(Class entityType); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanForeignKey.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanForeignKey.java index 6227bc88a..dc0ff3033 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanForeignKey.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanForeignKey.java @@ -1,56 +1,56 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebeaninternal.server.core.InternString; - -/** - * Represents a database foreign key which can map to an object relationship. - */ -public class BeanForeignKey { - - private final String dbColumn; - - private final int dbType; - - /** - * Construct the BeanForeignKey. - */ - public BeanForeignKey(String dbColumn, int dbType) { - this.dbColumn = InternString.intern(dbColumn); - this.dbType = dbType; - } - - /** - * Return the database column. - */ - public String getDbColumn() { - return dbColumn; - } - - /** - * Return the JDBC datatype of the database column. - */ - public int getDbType() { - return dbType; - } - - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (obj instanceof BeanForeignKey) { - return obj.hashCode() == hashCode(); - } - return false; - } - - public int hashCode() { - int hc = getClass().hashCode(); - hc = hc * 31 + (dbColumn != null ? dbColumn.hashCode() : 0); - return hc; - } - - public String toString() { - return dbColumn; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebeaninternal.server.core.InternString; + +/** + * Represents a database foreign key which can map to an object relationship. + */ +public class BeanForeignKey { + + private final String dbColumn; + + private final int dbType; + + /** + * Construct the BeanForeignKey. + */ + public BeanForeignKey(String dbColumn, int dbType) { + this.dbColumn = InternString.intern(dbColumn); + this.dbType = dbType; + } + + /** + * Return the database column. + */ + public String getDbColumn() { + return dbColumn; + } + + /** + * Return the JDBC datatype of the database column. + */ + public int getDbType() { + return dbType; + } + + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj instanceof BeanForeignKey) { + return obj.hashCode() == hashCode(); + } + return false; + } + + public int hashCode() { + int hc = getClass().hashCode(); + hc = hc * 31 + (dbColumn != null ? dbColumn.hashCode() : 0); + return hc; + } + + public String toString() { + return dbColumn; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java index 286f5326a..c3aa410cf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanListHelp.java @@ -1,151 +1,151 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.Query; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.BeanCollectionAdd; -import com.avaje.ebean.bean.BeanCollectionLoader; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.common.BeanList; -import com.avaje.ebeaninternal.server.text.json.WriteJson; - -/** - * Helper object for dealing with Lists. - */ -public final class BeanListHelp implements BeanCollectionHelp { - - private final BeanPropertyAssocMany many; - private final BeanDescriptor targetDescriptor; - private final String propertyName; - - private BeanCollectionLoader loader; - - public BeanListHelp(BeanPropertyAssocMany many) { - this.many = many; - this.targetDescriptor = many.getTargetDescriptor(); - this.propertyName = many.getName(); - } - - public BeanListHelp() { - this.many = null; - this.targetDescriptor = null; - this.propertyName = null; - } - - @Override - public void setLoader(BeanCollectionLoader loader) { - this.loader = loader; - } - - /** - * Internal add bypassing any modify listening. - */ - @Override - public void add(BeanCollection collection, EntityBean bean) { - collection.internalAdd(bean); - } - - @Override - public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { - - if (bc instanceof BeanList) { - - BeanList bl = (BeanList) bc; - if (bl.getActualList() == null) { - bl.setActualList(new ArrayList()); - } - return bl; - - } else { - throw new RuntimeException("Unhandled type " + bc); - } - } - - @Override - public BeanCollection createEmptyNoParent() { - return new BeanList(); - } - - @Override - public BeanCollection createEmpty(EntityBean parentBean) { - BeanList beanList = new BeanList(loader, parentBean, propertyName); - if (many != null) { - beanList.setModifyListening(many.getModifyListenMode()); - } - return beanList; - } - - @Override - public BeanCollection createReference(EntityBean parentBean) { - - BeanList beanList = new BeanList(loader, parentBean, propertyName); - beanList.setModifyListening(many.getModifyListenMode()); - return beanList; - } - - @Override - public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { - - BeanList newBeanList = (BeanList) server.findList(query, t); - refresh(newBeanList, parentBean); - } - - @Override - public void refresh(BeanCollection bc, EntityBean parentBean) { - - BeanList newBeanList = (BeanList) bc; - - List currentList = (List) many.getValue(parentBean); - - newBeanList.setModifyListening(many.getModifyListenMode()); - - if (currentList == null) { - // the currentList is null? Not really expecting this... - many.setValue(parentBean, newBeanList); - - } else if (currentList instanceof BeanList) { - // normally this case, replace just the underlying list - BeanList currentBeanList = (BeanList) currentList; - currentBeanList.setActualList(newBeanList.getActualList()); - currentBeanList.setModifyListening(many.getModifyListenMode()); - - } else { - // replace the entire list with the BeanList - many.setValue(parentBean, newBeanList); - } - } - - @Override - public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException { - - List list; - if (collection instanceof BeanCollection) { - BeanList beanList = (BeanList) collection; - if (!beanList.isPopulated()) { - if (explicitInclude) { - // invoke lazy loading as collection - // is explicitly included in the output - beanList.size(); - } else { - return; - } - } - list = beanList.getActualList(); - } else { - list = (List) collection; - } - - ctx.writeStartArray(name); - for (int j = 0; j < list.size(); j++) { - targetDescriptor.jsonWrite(ctx, (EntityBean) list.get(j)); - } - ctx.writeEndArray(); - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Query; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.BeanCollectionAdd; +import com.avaje.ebean.bean.BeanCollectionLoader; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.common.BeanList; +import com.avaje.ebeaninternal.server.text.json.WriteJson; + +/** + * Helper object for dealing with Lists. + */ +public final class BeanListHelp implements BeanCollectionHelp { + + private final BeanPropertyAssocMany many; + private final BeanDescriptor targetDescriptor; + private final String propertyName; + + private BeanCollectionLoader loader; + + public BeanListHelp(BeanPropertyAssocMany many) { + this.many = many; + this.targetDescriptor = many.getTargetDescriptor(); + this.propertyName = many.getName(); + } + + public BeanListHelp() { + this.many = null; + this.targetDescriptor = null; + this.propertyName = null; + } + + @Override + public void setLoader(BeanCollectionLoader loader) { + this.loader = loader; + } + + /** + * Internal add bypassing any modify listening. + */ + @Override + public void add(BeanCollection collection, EntityBean bean) { + collection.internalAdd(bean); + } + + @Override + public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { + + if (bc instanceof BeanList) { + + BeanList bl = (BeanList) bc; + if (bl.getActualList() == null) { + bl.setActualList(new ArrayList()); + } + return bl; + + } else { + throw new RuntimeException("Unhandled type " + bc); + } + } + + @Override + public BeanCollection createEmptyNoParent() { + return new BeanList(); + } + + @Override + public BeanCollection createEmpty(EntityBean parentBean) { + BeanList beanList = new BeanList(loader, parentBean, propertyName); + if (many != null) { + beanList.setModifyListening(many.getModifyListenMode()); + } + return beanList; + } + + @Override + public BeanCollection createReference(EntityBean parentBean) { + + BeanList beanList = new BeanList(loader, parentBean, propertyName); + beanList.setModifyListening(many.getModifyListenMode()); + return beanList; + } + + @Override + public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { + + BeanList newBeanList = (BeanList) server.findList(query, t); + refresh(newBeanList, parentBean); + } + + @Override + public void refresh(BeanCollection bc, EntityBean parentBean) { + + BeanList newBeanList = (BeanList) bc; + + List currentList = (List) many.getValue(parentBean); + + newBeanList.setModifyListening(many.getModifyListenMode()); + + if (currentList == null) { + // the currentList is null? Not really expecting this... + many.setValue(parentBean, newBeanList); + + } else if (currentList instanceof BeanList) { + // normally this case, replace just the underlying list + BeanList currentBeanList = (BeanList) currentList; + currentBeanList.setActualList(newBeanList.getActualList()); + currentBeanList.setModifyListening(many.getModifyListenMode()); + + } else { + // replace the entire list with the BeanList + many.setValue(parentBean, newBeanList); + } + } + + @Override + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException { + + List list; + if (collection instanceof BeanCollection) { + BeanList beanList = (BeanList) collection; + if (!beanList.isPopulated()) { + if (explicitInclude) { + // invoke lazy loading as collection + // is explicitly included in the output + beanList.size(); + } else { + return; + } + } + list = beanList.getActualList(); + } else { + list = (List) collection; + } + + ctx.writeStartArray(name); + for (int j = 0; j < list.size(); j++) { + targetDescriptor.jsonWrite(ctx, (EntityBean) list.get(j)); + } + ctx.writeEndArray(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManager.java index f042978cb..e31716dc3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManager.java @@ -1,33 +1,33 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebeaninternal.server.persist.BeanPersister; - -/** - * Holds the BeanDescriptor and its associated BeanPersister. - */ -public class BeanManager { - - private final BeanPersister persister; - - private final BeanDescriptor descriptor; - - public BeanManager(BeanDescriptor descriptor, BeanPersister persister) { - this.descriptor = descriptor; - this.persister = persister; - } - - /** - * Return the associated BeanPersister. - */ - public BeanPersister getBeanPersister() { - return persister; - } - - /** - * Return the BeanDescriptor. - */ - public BeanDescriptor getBeanDescriptor() { - return descriptor; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebeaninternal.server.persist.BeanPersister; + +/** + * Holds the BeanDescriptor and its associated BeanPersister. + */ +public class BeanManager { + + private final BeanPersister persister; + + private final BeanDescriptor descriptor; + + public BeanManager(BeanDescriptor descriptor, BeanPersister persister) { + this.descriptor = descriptor; + this.persister = persister; + } + + /** + * Return the associated BeanPersister. + */ + public BeanPersister getBeanPersister() { + return persister; + } + + /** + * Return the BeanDescriptor. + */ + public BeanDescriptor getBeanDescriptor() { + return descriptor; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManagerFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManagerFactory.java index b795d3c9d..b09d75c83 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManagerFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManagerFactory.java @@ -1,27 +1,27 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebeaninternal.server.persist.BeanPersister; -import com.avaje.ebeaninternal.server.persist.BeanPersisterFactory; -import com.avaje.ebeaninternal.server.persist.dml.DmlBeanPersisterFactory; - -/** - * Creates BeanManagers. - */ -public class BeanManagerFactory { - - final BeanPersisterFactory peristerFactory; - - public BeanManagerFactory(ServerConfig config, DatabasePlatform dbPlatform) { - peristerFactory = new DmlBeanPersisterFactory(dbPlatform); - } - - public BeanManager create(BeanDescriptor desc) { - - BeanPersister persister = peristerFactory.create(desc); - - return new BeanManager(desc, persister); - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebeaninternal.server.persist.BeanPersister; +import com.avaje.ebeaninternal.server.persist.BeanPersisterFactory; +import com.avaje.ebeaninternal.server.persist.dml.DmlBeanPersisterFactory; + +/** + * Creates BeanManagers. + */ +public class BeanManagerFactory { + + final BeanPersisterFactory peristerFactory; + + public BeanManagerFactory(ServerConfig config, DatabasePlatform dbPlatform) { + peristerFactory = new DmlBeanPersisterFactory(dbPlatform); + } + + public BeanManager create(BeanDescriptor desc) { + + BeanPersister persister = peristerFactory.create(desc); + + return new BeanManager(desc, persister); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java index 466d6916e..43e8ad313 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanMapHelp.java @@ -1,184 +1,184 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.Query; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.BeanCollectionAdd; -import com.avaje.ebean.bean.BeanCollectionLoader; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.common.BeanMap; -import com.avaje.ebeaninternal.server.text.json.WriteJson; - -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Helper specifically for dealing with Maps. - */ -public final class BeanMapHelp implements BeanCollectionHelp { - - private final BeanPropertyAssocMany many; - private final BeanDescriptor targetDescriptor; - private final String propertyName; - private final BeanProperty beanProperty; - private BeanCollectionLoader loader; - - /** - * When created for a given query that will return a map. - */ - public BeanMapHelp(BeanDescriptor targetDescriptor, String mapKey) { - this.targetDescriptor = targetDescriptor; - this.beanProperty = targetDescriptor.getBeanProperty(mapKey); - this.many = null; - this.propertyName = null; - } - - /** - * When help is attached to a specific many property. - */ public BeanMapHelp(BeanPropertyAssocMany many) { - this.many = many; - this.targetDescriptor = many.getTargetDescriptor(); - this.propertyName = many.getName(); - this.beanProperty = targetDescriptor.getBeanProperty(many.getMapKey()); - } - - @Override - public void setLoader(BeanCollectionLoader loader) { - this.loader = loader; - } - - @Override - @SuppressWarnings("unchecked") - public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { - - if (mapKey == null) { - mapKey = many.getMapKey(); - } - BeanProperty beanProp = targetDescriptor.getBeanProperty(mapKey); - - if (bc instanceof BeanMap) { - BeanMap bm = (BeanMap) bc; - Map actualMap = bm.getActualMap(); - if (actualMap == null) { - actualMap = new LinkedHashMap(); - bm.setActualMap(actualMap); - } - return new Adder(beanProp, actualMap); - - } else { - throw new RuntimeException("Unhandled type " + bc); - } - } - - static class Adder implements BeanCollectionAdd { - - private final BeanProperty beanProperty; - - private final Map map; - - Adder(BeanProperty beanProperty, Map map) { - this.beanProperty = beanProperty; - this.map = map; - } - - public void addBean(EntityBean bean) { - Object keyValue = beanProperty.getValue(bean); - map.put(keyValue, bean); - } - } - - @Override - public BeanCollection createEmptyNoParent() { - return new BeanMap(); - } - - @Override - @SuppressWarnings("rawtypes") - public BeanCollection createEmpty(EntityBean ownerBean) { - - BeanMap beanMap = new BeanMap(loader, ownerBean, propertyName); - if (many != null) { - beanMap.setModifyListening(many.getModifyListenMode()); - } - return beanMap; - } - - @Override - public void add(BeanCollection collection, EntityBean bean) { - - Object keyValue = beanProperty.getValueIntercept(bean); - ((BeanMap) collection).internalPut(keyValue, bean); - } - - @Override - @SuppressWarnings({"unchecked", "rawtypes"}) - public BeanCollection createReference(EntityBean parentBean) { - - BeanMap beanMap = new BeanMap(loader, parentBean, propertyName); - if (many != null) { - beanMap.setModifyListening(many.getModifyListenMode()); - } - return beanMap; - } - - @Override - public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { - BeanMap newBeanMap = (BeanMap) server.findMap(query, t); - refresh(newBeanMap, parentBean); - } - - @Override - public void refresh(BeanCollection bc, EntityBean parentBean) { - - BeanMap newBeanMap = (BeanMap) bc; - Map current = (Map) many.getValue(parentBean); - - newBeanMap.setModifyListening(many.getModifyListenMode()); - if (current == null) { - // the currentMap is null? Not really expecting this... - many.setValue(parentBean, newBeanMap); - - } else if (current instanceof BeanMap) { - // normally this case, replace just the underlying list - BeanMap currentBeanMap = (BeanMap) current; - currentBeanMap.setActualMap(newBeanMap.getActualMap()); - currentBeanMap.setModifyListening(many.getModifyListenMode()); - - } else { - // replace the entire set - many.setValue(parentBean, newBeanMap); - } - } - - @Override - public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException { - - Map map; - if (collection instanceof BeanCollection) { - BeanMap bc = (BeanMap) collection; - if (!bc.isPopulated()) { - if (explicitInclude) { - // invoke lazy loading as collection - // is explicitly included in the output - bc.size(); - } else { - return; - } - } - map = bc.getActualMap(); - } else { - map = (Map) collection; - } - - ctx.writeStartArray(name); - for (Entry entry : map.entrySet()) { - //FIXME: json write map key ... - targetDescriptor.jsonWrite(ctx, (EntityBean) entry.getValue()); - } - ctx.writeEndArray(); - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Query; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.BeanCollectionAdd; +import com.avaje.ebean.bean.BeanCollectionLoader; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.common.BeanMap; +import com.avaje.ebeaninternal.server.text.json.WriteJson; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Helper specifically for dealing with Maps. + */ +public final class BeanMapHelp implements BeanCollectionHelp { + + private final BeanPropertyAssocMany many; + private final BeanDescriptor targetDescriptor; + private final String propertyName; + private final BeanProperty beanProperty; + private BeanCollectionLoader loader; + + /** + * When created for a given query that will return a map. + */ + public BeanMapHelp(BeanDescriptor targetDescriptor, String mapKey) { + this.targetDescriptor = targetDescriptor; + this.beanProperty = targetDescriptor.getBeanProperty(mapKey); + this.many = null; + this.propertyName = null; + } + + /** + * When help is attached to a specific many property. + */ public BeanMapHelp(BeanPropertyAssocMany many) { + this.many = many; + this.targetDescriptor = many.getTargetDescriptor(); + this.propertyName = many.getName(); + this.beanProperty = targetDescriptor.getBeanProperty(many.getMapKey()); + } + + @Override + public void setLoader(BeanCollectionLoader loader) { + this.loader = loader; + } + + @Override + @SuppressWarnings("unchecked") + public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { + + if (mapKey == null) { + mapKey = many.getMapKey(); + } + BeanProperty beanProp = targetDescriptor.getBeanProperty(mapKey); + + if (bc instanceof BeanMap) { + BeanMap bm = (BeanMap) bc; + Map actualMap = bm.getActualMap(); + if (actualMap == null) { + actualMap = new LinkedHashMap(); + bm.setActualMap(actualMap); + } + return new Adder(beanProp, actualMap); + + } else { + throw new RuntimeException("Unhandled type " + bc); + } + } + + static class Adder implements BeanCollectionAdd { + + private final BeanProperty beanProperty; + + private final Map map; + + Adder(BeanProperty beanProperty, Map map) { + this.beanProperty = beanProperty; + this.map = map; + } + + public void addBean(EntityBean bean) { + Object keyValue = beanProperty.getValue(bean); + map.put(keyValue, bean); + } + } + + @Override + public BeanCollection createEmptyNoParent() { + return new BeanMap(); + } + + @Override + @SuppressWarnings("rawtypes") + public BeanCollection createEmpty(EntityBean ownerBean) { + + BeanMap beanMap = new BeanMap(loader, ownerBean, propertyName); + if (many != null) { + beanMap.setModifyListening(many.getModifyListenMode()); + } + return beanMap; + } + + @Override + public void add(BeanCollection collection, EntityBean bean) { + + Object keyValue = beanProperty.getValueIntercept(bean); + ((BeanMap) collection).internalPut(keyValue, bean); + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public BeanCollection createReference(EntityBean parentBean) { + + BeanMap beanMap = new BeanMap(loader, parentBean, propertyName); + if (many != null) { + beanMap.setModifyListening(many.getModifyListenMode()); + } + return beanMap; + } + + @Override + public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { + BeanMap newBeanMap = (BeanMap) server.findMap(query, t); + refresh(newBeanMap, parentBean); + } + + @Override + public void refresh(BeanCollection bc, EntityBean parentBean) { + + BeanMap newBeanMap = (BeanMap) bc; + Map current = (Map) many.getValue(parentBean); + + newBeanMap.setModifyListening(many.getModifyListenMode()); + if (current == null) { + // the currentMap is null? Not really expecting this... + many.setValue(parentBean, newBeanMap); + + } else if (current instanceof BeanMap) { + // normally this case, replace just the underlying list + BeanMap currentBeanMap = (BeanMap) current; + currentBeanMap.setActualMap(newBeanMap.getActualMap()); + currentBeanMap.setModifyListening(many.getModifyListenMode()); + + } else { + // replace the entire set + many.setValue(parentBean, newBeanMap); + } + } + + @Override + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException { + + Map map; + if (collection instanceof BeanCollection) { + BeanMap bc = (BeanMap) collection; + if (!bc.isPopulated()) { + if (explicitInclude) { + // invoke lazy loading as collection + // is explicitly included in the output + bc.size(); + } else { + return; + } + } + map = bc.getActualMap(); + } else { + map = (Map) collection; + } + + ctx.writeStartArray(name); + for (Entry entry : map.entrySet()) { + //FIXME: json write map key ... + targetDescriptor.jsonWrite(ctx, (EntityBean) entry.getValue()); + } + ctx.writeEndArray(); + } + +} 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 6c0ec7c73..58fa8227a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java @@ -1,1118 +1,1118 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.sql.SQLException; -import java.sql.Types; -import java.util.List; -import java.util.Map; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.config.EncryptKey; -import com.avaje.ebean.config.dbplatform.DbEncryptFunction; -import com.avaje.ebean.config.dbplatform.DbType; -import com.avaje.ebean.text.StringFormatter; -import com.avaje.ebean.text.StringParser; -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.query.SqlJoinType; -import com.avaje.ebeaninternal.server.properties.BeanPropertyGetter; -import com.avaje.ebeaninternal.server.properties.BeanPropertySetter; -import com.avaje.ebeaninternal.server.text.json.WriteJson; -import com.avaje.ebeaninternal.server.type.DataBind; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; - -/** - * Description of a property of a bean. Includes its deployment information such - * as database column mapping information. - */ -public class BeanProperty implements ElPropertyValue { - - /** - * Flag to mark this at part of the unique id. - */ - final boolean id; - - /** - * Flag to make this as a dummy property for unidirecitonal relationships. - */ - final boolean unidirectionalShadow; - - /** - * Flag set if this maps to the inheritance discriminator column - */ - final boolean discriminator; - - /** - * Flag to mark the property as embedded. This could be on - * BeanPropertyAssocOne rather than here. Put it here for checking Id type - * (embedded or not). - */ - final boolean embedded; - - /** - * Flag indicating if this the version property. - */ - final boolean version; - - final boolean naturalKey; - - /** - * Set if this property is nullable. - */ - final boolean nullable; - - final boolean unique; - - /** - * Is this property include in database resultSet. - */ - final boolean dbRead; - - /** - * Include in DB insert. - */ - final boolean dbInsertable; - - /** - * Include in DB update. - */ - final boolean dbUpdatable; - - /** - * True if the property is based on a SECONDARY table. - */ - final boolean secondaryTable; - - final TableJoin secondaryTableJoin; - final String secondaryTableJoinPrefix; - - /** - * The property is inherited from a super class. - */ - final boolean inherited; - - final Class owningType; - - final boolean local; - - /** - * True if the property is a Clob, Blob LongVarchar or LongVarbinary. - */ - final boolean lob; - - final boolean fetchEager; - - final boolean isTransient; - - /** - * The logical bean property name. - */ - final String name; - - final int propertyIndex; - - /** - * The reflected field. - */ - final Field field; - - /** - * The bean type. - */ - final Class propertyType; - - final String dbBind; - - /** - * The database column. This can include quoted identifiers. - */ - final String dbColumn; - - final String elPlaceHolder; - final String elPlaceHolderEncrypted; - - /** - * Select part of a SQL Formula used to populate this property. - */ - final String sqlFormulaSelect; - - /** - * Join part of a SQL Formula. - */ - final String sqlFormulaJoin; - - final boolean formula; - - /** - * Set to true if stored encrypted. - */ - final boolean dbEncrypted; - - final boolean localEncrypted; - - final int dbEncryptedType; - - /** - * The jdbc data type this maps to. - */ - final int dbType; - - /** - * The default value to insert if null. - */ - final Object defaultValue; - - /** - * Extra deployment parameters. - */ - final Map extraAttributeMap; - - /** - * The method used to read the property. - */ - final Method readMethod; - - /** - * The method used to write the property. - */ - final Method writeMethod; - - /** - * Generator for insert or update timestamp etc. - */ - final GeneratedProperty generatedProperty; - - final BeanPropertyGetter getter; - - final BeanPropertySetter setter; - - final BeanDescriptor descriptor; - - /** - * Used for non-jdbc native types (java.util.Date Enums etc). Converts from - * logical to jdbc types. - */ - @SuppressWarnings("rawtypes") - final ScalarType scalarType; - - boolean cascadeValidate; - - /** - * The length or precision for DB column. - */ - final int dbLength; - - /** - * The scale for DB column (decimal). - */ - final int dbScale; - - /** - * Deployment defined DB column definition. - */ - final String dbColumnDefn; - - /** - * DB Constraint (typically check constraint on enum) - */ - final String dbConstraintExpression; - - final DbEncryptFunction dbEncryptFunction; - - int deployOrder; - - final boolean jsonSerialize; - - final boolean jsonDeserialize; - - final boolean indexed; - - final String indexName; - public BeanProperty(DeployBeanProperty deploy) { - this(null, null, deploy); - } - - public BeanProperty(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanProperty deploy) { - - this.descriptor = descriptor; - this.name = InternString.intern(deploy.getName()); - this.propertyIndex = deploy.getPropertyIndex(); - this.indexed = deploy.isIndexed(); - this.indexName = deploy.getIndexName(); - this.unidirectionalShadow = deploy.isUndirectionalShadow(); - this.discriminator = deploy.isDiscriminator(); - this.localEncrypted = deploy.isLocalEncrypted(); - this.dbEncrypted = deploy.isDbEncrypted(); - this.dbEncryptedType = deploy.getDbEncryptedType(); - this.dbEncryptFunction = deploy.getDbEncryptFunction(); - this.dbBind = deploy.getDbBind(); - this.dbRead = deploy.isDbRead(); - this.dbInsertable = deploy.isDbInsertable(); - this.dbUpdatable = deploy.isDbUpdateable(); - - this.secondaryTable = deploy.isSecondaryTable(); - if (secondaryTable) { - this.secondaryTableJoin = new TableJoin(deploy.getSecondaryTableJoin(), null); - this.secondaryTableJoinPrefix = deploy.getSecondaryTableJoinPrefix(); - } else { - this.secondaryTableJoin = null; - this.secondaryTableJoinPrefix = null; - } - this.fetchEager = deploy.isFetchEager(); - this.isTransient = deploy.isTransient(); - this.nullable = deploy.isNullable(); - this.unique = deploy.isUnique(); - this.naturalKey = deploy.isNaturalKey(); - this.dbLength = deploy.getDbLength(); - this.dbScale = deploy.getDbScale(); - this.dbColumnDefn = InternString.intern(deploy.getDbColumnDefn()); - this.dbConstraintExpression = InternString.intern(deploy.getDbConstraintExpression()); - - this.inherited = false;// deploy.isInherited(); - this.owningType = deploy.getOwningType(); - this.local = deploy.isLocal(); - - this.version = deploy.isVersionColumn(); - this.embedded = deploy.isEmbedded(); - this.id = deploy.isId(); - this.generatedProperty = deploy.getGeneratedProperty(); - this.readMethod = deploy.getReadMethod(); - this.writeMethod = deploy.getWriteMethod(); - this.getter = deploy.getGetter(); - this.setter = deploy.getSetter(); - - this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null); - this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin()); - this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect()); - this.formula = sqlFormulaSelect != null; - - this.extraAttributeMap = deploy.getExtraAttributeMap(); - this.defaultValue = deploy.getDefaultValue(); - this.dbType = deploy.getDbType(); - this.scalarType = deploy.getScalarType(); - this.lob = isLobType(dbType); - this.propertyType = deploy.getPropertyType(); - this.field = deploy.getField(); - - EntityType et = descriptor == null ? null : descriptor.getEntityType(); - this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null); - this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn); - - this.jsonSerialize = deploy.isExposeSerialize(); - this.jsonDeserialize = deploy.isExposeDeserialize(); - } - - private String tableAliasIntern(BeanDescriptor descriptor, String s, boolean dbEncrypted, String dbColumn) { - if (descriptor != null) { - s = StringHelper.replaceString(s, "${ta}.", "${}"); - s = StringHelper.replaceString(s, "${ta}", "${}"); - - if (dbEncrypted) { - s = dbEncryptFunction.getDecryptSql(s); - String namedParam = ":encryptkey_" + descriptor.getBaseTable() + "___" + dbColumn; - s = StringHelper.replaceString(s, "?", namedParam); - } - } - return InternString.intern(s); - } - - /** - * Create a Matching BeanProperty with some attributes overridden. - *

- * Primarily for supporting Embedded beans with overridden dbColumn - * mappings. - *

- */ - public BeanProperty(BeanProperty source, BeanPropertyOverride override) { - - this.descriptor = source.descriptor; - this.name = InternString.intern(source.getName()); - this.propertyIndex = source.propertyIndex; - - this.indexed = source.isIndexed(); - this.indexName = source.getIndexName(); - this.dbColumn = InternString.intern(override.getDbColumn()); - this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin()); - this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect()); - this.formula = sqlFormulaSelect != null; - - this.fetchEager = source.fetchEager; - this.unidirectionalShadow = source.unidirectionalShadow; - this.discriminator = source.discriminator; - this.localEncrypted = source.isLocalEncrypted(); - this.isTransient = source.isTransient(); - this.secondaryTable = source.isSecondaryTable(); - this.secondaryTableJoin = source.secondaryTableJoin; - this.secondaryTableJoinPrefix = source.secondaryTableJoinPrefix; - - this.dbBind = source.getDbBind(); - this.dbEncrypted = source.isDbEncrypted(); - this.dbEncryptedType = source.getDbEncryptedType(); - this.dbEncryptFunction = source.dbEncryptFunction; - this.dbRead = source.isDbRead(); - this.dbInsertable = source.isDbInsertable(); - this.dbUpdatable = source.isDbUpdatable(); - this.nullable = source.isNullable(); - this.unique = source.isUnique(); - this.naturalKey = source.isNaturalKey(); - this.dbLength = source.getDbLength(); - this.dbScale = source.getDbScale(); - this.dbColumnDefn = InternString.intern(source.getDbColumnDefn()); - this.dbConstraintExpression = InternString.intern(source.getDbConstraintExpression()); - - this.inherited = source.isInherited(); - this.owningType = source.owningType; - this.local = owningType.equals(descriptor.getBeanType()); - - this.version = source.isVersion(); - this.embedded = source.isEmbedded(); - this.id = source.isId(); - this.generatedProperty = source.getGeneratedProperty(); - this.readMethod = source.getReadMethod(); - this.writeMethod = source.getWriteMethod(); - this.getter = source.getter; - this.setter = source.setter; - this.extraAttributeMap = source.extraAttributeMap; - this.defaultValue = source.getDefaultValue(); - this.dbType = source.getDbType(); - this.scalarType = source.scalarType; - this.lob = isLobType(dbType); - this.propertyType = source.getPropertyType(); - this.field = source.getField(); - - this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn); - this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn); - - this.jsonSerialize = source.jsonSerialize; - this.jsonDeserialize = source.jsonDeserialize; - } - - /** - * Initialise the property before returning to client code. Used to - * initialise variables that can't be done in construction due to recursive - * issues. - */ - public void initialise() { - // do nothing for normal BeanProperty - if (!isTransient && scalarType == null) { - String msg = "No ScalarType assigned to " + descriptor.getFullName() + "." + getName(); - throw new RuntimeException(msg); - } - } - - /** - * Return the order this property appears in the bean. - */ - public int getDeployOrder() { - return deployOrder; - } - - /** - * Set the order this property appears in the bean. - */ - public void setDeployOrder(int deployOrder) { - this.deployOrder = deployOrder; - } - - public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, - boolean propertyDeploy) { - throw new PersistenceException("Not valid on scalar bean property " + getFullBeanName()); - } - - /** - * Return the BeanDescriptor that owns this property. - */ - public BeanDescriptor getBeanDescriptor() { - return descriptor; - } - - /** - * Return true is this is a simple scalar property. - */ - public boolean isScalar() { - return true; - } - - /** - * Return true if this property is based on a formula. - */ - public boolean isFormula() { - return formula; - } - - /** - * Return true if this property maps to the inheritance discriminator column. - */ - public boolean isDiscriminator() { - return discriminator; - } - - /** - * Return true if the underlying type is mutable. - */ - public boolean isMutableScalarType() { - return scalarType != null && scalarType.isMutable(); - } - - /** - * Return the encrypt key for the column matching this property. - */ - public EncryptKey getEncryptKey() { - return descriptor.getEncryptKey(this); - } - - public String getDecryptProperty(String propertyName) { - return dbEncryptFunction.getDecryptSql(propertyName); - } - - public String getDecryptSql() { - return dbEncryptFunction.getDecryptSql(this.getDbColumn()); - } - - public String getDecryptSql(String tableAlias) { - return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()); - } - - /** - * Add any extra joins required to support this property. Generally a no - * operation except for a OneToOne exported. - */ - public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { - if (formula && sqlFormulaJoin != null) { - ctx.appendFormulaJoin(sqlFormulaJoin, joinType); - - } else if (secondaryTableJoin != null) { - - String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); - secondaryTableJoin.addJoin(joinType, relativePrefix, ctx); - } - } - - /** - * Returns null unless this property is using a secondary table. In that - * case this returns the logical property prefix. - */ - public String getSecondaryTableJoinPrefix() { - return secondaryTableJoinPrefix; - } - - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - if (formula) { - ctx.appendFormulaSelect(sqlFormulaSelect); - - } else if (!isTransient) { - - if (secondaryTableJoin != null) { - String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); - ctx.pushTableAlias(relativePrefix); - } - - if (dbEncrypted) { - String decryptSql = getDecryptSql(ctx.peekTableAlias()); - ctx.appendRawColumn(decryptSql); - ctx.addEncryptedProp(this); - - } else { - ctx.appendColumn(dbColumn); - } - - if (secondaryTableJoin != null) { - ctx.popTableAlias(); - } - } - } - - public boolean isAssignableFrom(Class type) { - return owningType.isAssignableFrom(type); - } - - public void loadIgnore(DbReadContext ctx) { - scalarType.loadIgnore(ctx.getDataReader()); - } - - public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { - sqlBeanLoad.load(this); - } - - public void buildSelectExpressionChain(String prefix, List selectChain) { - if (prefix == null) { - selectChain.add(name); - } else { - selectChain.add(prefix + "." + name); - } - } - - public Object read(DbReadContext ctx) throws SQLException { - return scalarType.read(ctx.getDataReader()); - } - - public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { - - try { - Object value = scalarType.read(ctx.getDataReader()); - if (bean != null && ((type == null || owningType.isAssignableFrom(type)))) { - setValue(bean, value); - } - return value; - } catch (Exception e) { - throw new PersistenceException("Error readSet on " + descriptor + "." + name, e); - } - } - - /** - * Convert the type to the bean type if required. - *

- * Generally only used to ensure id properties are converted for - * Query.setId() use. - *

- */ - public Object toBeanType(Object value) { - return scalarType.toBeanType(value); - } - - @SuppressWarnings("unchecked") - public void bind(DataBind b, Object value) throws SQLException { - scalarType.bind(b, value); - } - - public void writeData(DataOutput dataOutput, Object value) throws IOException { - scalarType.writeData(dataOutput, value); - } - - public Object readData(DataInput dataInput) throws IOException { - return scalarType.readData(dataInput); - } - - public BeanProperty getBeanProperty() { - return this; - } - - public boolean isIndexed() { - return indexed; - } - - public String getIndexName() { - return indexName; - } - - /** - * Return the getter method. - */ - public Method getReadMethod() { - return readMethod; - } - - /** - * Return the setter method. - */ - public Method getWriteMethod() { - return writeMethod; - } - - /** - * Return true if this object is part of an inheritance hierarchy. - */ - public boolean isInherited() { - return inherited; - } - - /** - * Return true is this type is not from a super type. - */ - public boolean isLocal() { - return local; - } - - /** - * Set the value of the property without interception or - * PropertyChangeSupport. - */ - public void setValue(EntityBean bean, Object value) { - try { - setter.set(bean, value); - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "set " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType - + "] threw error"; - throw new RuntimeException(msg, ex); - } - } - - /** - * Set the value of the property. - */ - public void setValueIntercept(EntityBean bean, Object value) { - try { - setter.setIntercept(bean, value); - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "setIntercept " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType - + "] threw error"; - throw new RuntimeException(msg, ex); - } - } - - public Object getCacheDataValue(EntityBean bean) { - return getValue(bean); - } - - public void setCacheDataValue(EntityBean bean, Object cacheData) { - setValue(bean, cacheData); - } - - /** - * Get the property value from a compound value type. - */ - public Object getValueObject(Object bean) { - throw new RuntimeException("Expected to be called only on BeanPropertyCompoundScalar"); - } - - /** - * Return the value of the property method. - */ - public Object getValue(EntityBean bean) { - try { - return getter.get(bean); - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; - throw new RuntimeException(msg, ex); - } - } - - public Object getValueIntercept(EntityBean bean) { - try { - return getter.getIntercept(bean); - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "getIntercept " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; - throw new RuntimeException(msg, ex); - } - } - - public Object elConvertType(Object value) { - if (value == null) { - return null; - } - return convertToLogicalType(value); - } - - public void elSetValue(EntityBean bean, Object value, boolean populate) { - if (bean != null) { - // Not using setValueIntercept at this stage - setValue(bean, value); - } - } - - public Object elGetValue(EntityBean bean) { - if (bean == null) { - return null; - } - return getValueIntercept(bean); - } - - public Object elGetReference(EntityBean bean) { - throw new RuntimeException("Not expected to call this"); - } - - /** - * Return the name of the property. - */ - public String getName() { - return name; - } - - /** - * Return the position of this property in the enhanced bean. - */ - public int getPropertyIndex() { - return propertyIndex; - } - - public String getElName() { - return name; - } - - @Override - public boolean containsFormulaWithJoin() { - return formula && sqlFormulaJoin != null; - } - - public boolean containsManySince(String sinceProperty) { - return containsMany(); - } - - public boolean containsMany() { - return false; - } - - public Object[] getAssocOneIdValues(EntityBean bean) { - // Returns null as not an AssocOne. - return null; - } - - public String getAssocOneIdExpr(String prefix, String operator) { - // Returns null as not an AssocOne. - return null; - } - - public String getAssocIdInExpr(String prefix) { - // Returns null as not an AssocOne. - return null; - } - - public String getAssocIdInValueExpr(int size) { - // Returns null as not an AssocOne. - return null; - } - - public boolean isAssocId() { - // Returns false - override in BeanPropertyAssocOne. - return false; - } - - public boolean isAssocProperty() { - // Returns false - override in BeanPropertyAssocOne. - return false; - } - - public String getElPlaceholder(boolean encrypted) { - return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; - } - - public String getElPrefix() { - return secondaryTableJoinPrefix; - } - - /** - * Return the full name of this property. - */ - public String getFullBeanName() { - return descriptor.getFullName() + "." + name; - } - - /** - * Return true if the mutable value is considered dirty. - * This is only used for 'mutable' scalar types like hstore etc. - */ - public boolean isDirtyValue(Object value) { - return scalarType.isDirty(value); - } - - /** - * Return the scalarType. - */ - public ScalarType getScalarType() { - return scalarType; - } - - public StringFormatter getStringFormatter() { - return scalarType; - } - - public StringParser getStringParser() { - return scalarType; - } - - public boolean isDateTimeCapable() { - return scalarType != null && scalarType.isDateTimeCapable(); - } - - public int getJdbcType() { - return scalarType == null ? 0 : scalarType.getJdbcType(); - } - - public Object parseDateTime(long systemTimeMillis) { - return scalarType.convertFromMillis(systemTimeMillis); - } - - /** - * Return the DB max length (varchar) or precision (decimal). - */ - public int getDbLength() { - return dbLength; - } - - /** - * Return the DB scale for numeric columns. - */ - public int getDbScale() { - return dbScale; - } - - /** - * Return a specific column DDL definition if specified (otherwise null). - */ - public String getDbColumnDefn() { - return dbColumnDefn; - } - - /** - * Return the DB constraint expression (can be null). - *

- * For an Enum returns IN expression for the set of Enum values. - *

- */ - public String getDbConstraintExpression() { - return dbConstraintExpression; - } - - /** - * Return the DB column type definition. - */ - public String renderDbType(DbType dbType) { - if (dbColumnDefn != null) { - return dbColumnDefn; - } - return dbType.renderType(dbLength, dbScale); - } - - /** - * Return the bean Field associated with this property. - */ - public Field getField() { - return field; - } - - /** - * Return the GeneratedValue. Used to generate update timestamp etc. - */ - public GeneratedProperty getGeneratedProperty() { - return generatedProperty; - } - - /** - * Return true if this is the natural key property. - */ - public boolean isNaturalKey() { - return naturalKey; - } - - /** - * Return true if this property is mandatory. - */ - public boolean isNullable() { - return nullable; - } - - /** - * Return true if DDL Not NULL constraint should be defined for this column - * based on it being a version column or having a generated property. - */ - public boolean isDDLNotNull() { - return isVersion() || (generatedProperty != null && generatedProperty.isDDLNotNullable()); - } - - /** - * Return true if the DB column should be unique. - */ - public boolean isUnique() { - return unique; - } - - /** - * Return true if the property is transient. - */ - public boolean isTransient() { - return isTransient; - } - - /** - * Return true if this property is loadable from a resultSet. - */ - public boolean isLoadProperty() { - return !isTransient || formula; - } - - /** - * Return true if this is a version column used for concurrency checking. - */ - public boolean isVersion() { - return version; - } - - /** - * The database column name this is mapped to. - */ - public String getDbColumn() { - return dbColumn; - } - - /** - * Return the database jdbc data type this is mapped to. - */ - public int getDbType() { - return dbType; - } - - /** - * Perform DB to Logical type conversion (if necessary). - */ - public Object convertToLogicalType(Object value) { - if (scalarType != null) { - return scalarType.toBeanType(value); - } - return value; - } - - /** - * Return true if by default this property is set to fetch eager. - * Lob's usually default to fetch lazy. - */ - public boolean isFetchEager() { - return fetchEager; - } - - /** - * Return true if this is mapped to a Clob Blob LongVarchar or - * LongVarbinary. - */ - public boolean isLob() { - return lob; - } - - private boolean isLobType(int type) { - switch (type) { - case Types.CLOB: - return true; - case Types.BLOB: - return true; - case Types.LONGVARBINARY: - return true; - case Types.LONGVARCHAR: - return true; - - default: - return false; - } - } - - /** - * Return the DB bind parameter. Typically is "?" but different for - * encrypted bind. - */ - public String getDbBind() { - return dbBind; - } - - /** - * Returns true if DB encrypted. - */ - public boolean isLocalEncrypted() { - return localEncrypted; - } - - /** - * Return true if this property is stored encrypted. - */ - public boolean isDbEncrypted() { - return dbEncrypted; - } - - public int getDbEncryptedType() { - return dbEncryptedType; - } - - /** - * Return true if this property should be included in an Insert. - */ - public boolean isDbInsertable() { - return dbInsertable; - } - - /** - * Return true if this property should be included in an Update. - */ - public boolean isDbUpdatable() { - return dbUpdatable; - } - - /** - * Return true if this property is included in database queries. - */ - public boolean isDbRead() { - return dbRead; - } - - /** - * Return true if this property is based on a secondary table (not the base - * table). - */ - public boolean isSecondaryTable() { - return secondaryTable; - } - - /** - * Return the property type. - */ - public Class getPropertyType() { - return propertyType; - } - - /** - * Return true if this is included in the unique id. - */ - public boolean isId() { - return id; - } - - /** - * Return true if this is an Embedded property. In this case it shares the - * table and primary key of its owner object. - */ - public boolean isEmbedded() { - return embedded; - } - - /** - * Return the default value. - */ - public Object getDefaultValue() { - return defaultValue; - } - - public String toString() { - return name; - } - - public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException { - if (!jsonSerialize) { - return; - } - Object value = getValueIntercept(bean); - if (value == null) { - writeJson.writeNull(name); - } else { - scalarType.jsonWrite(writeJson.gen(), name, value); - } - } - - public void jsonRead(JsonParser ctx, EntityBean bean) throws IOException { - if (!jsonDeserialize) { - return; - } - - JsonToken event = ctx.nextToken(); - if (JsonToken.VALUE_NULL == event) { - setValue(bean, null); - } else { - // expect to read non-null json value - Object objValue = scalarType.jsonRead(ctx, event); - setValue(bean, objValue); - } - } -} +package com.avaje.ebeaninternal.server.deploy; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.sql.Types; +import java.util.List; +import java.util.Map; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.config.EncryptKey; +import com.avaje.ebean.config.dbplatform.DbEncryptFunction; +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.text.StringFormatter; +import com.avaje.ebean.text.StringParser; +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.query.SqlBeanLoad; +import com.avaje.ebeaninternal.server.query.SqlJoinType; +import com.avaje.ebeaninternal.server.properties.BeanPropertyGetter; +import com.avaje.ebeaninternal.server.properties.BeanPropertySetter; +import com.avaje.ebeaninternal.server.text.json.WriteJson; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; + +/** + * Description of a property of a bean. Includes its deployment information such + * as database column mapping information. + */ +public class BeanProperty implements ElPropertyValue { + + /** + * Flag to mark this at part of the unique id. + */ + final boolean id; + + /** + * Flag to make this as a dummy property for unidirecitonal relationships. + */ + final boolean unidirectionalShadow; + + /** + * Flag set if this maps to the inheritance discriminator column + */ + final boolean discriminator; + + /** + * Flag to mark the property as embedded. This could be on + * BeanPropertyAssocOne rather than here. Put it here for checking Id type + * (embedded or not). + */ + final boolean embedded; + + /** + * Flag indicating if this the version property. + */ + final boolean version; + + final boolean naturalKey; + + /** + * Set if this property is nullable. + */ + final boolean nullable; + + final boolean unique; + + /** + * Is this property include in database resultSet. + */ + final boolean dbRead; + + /** + * Include in DB insert. + */ + final boolean dbInsertable; + + /** + * Include in DB update. + */ + final boolean dbUpdatable; + + /** + * True if the property is based on a SECONDARY table. + */ + final boolean secondaryTable; + + final TableJoin secondaryTableJoin; + final String secondaryTableJoinPrefix; + + /** + * The property is inherited from a super class. + */ + final boolean inherited; + + final Class owningType; + + final boolean local; + + /** + * True if the property is a Clob, Blob LongVarchar or LongVarbinary. + */ + final boolean lob; + + final boolean fetchEager; + + final boolean isTransient; + + /** + * The logical bean property name. + */ + final String name; + + final int propertyIndex; + + /** + * The reflected field. + */ + final Field field; + + /** + * The bean type. + */ + final Class propertyType; + + final String dbBind; + + /** + * The database column. This can include quoted identifiers. + */ + final String dbColumn; + + final String elPlaceHolder; + final String elPlaceHolderEncrypted; + + /** + * Select part of a SQL Formula used to populate this property. + */ + final String sqlFormulaSelect; + + /** + * Join part of a SQL Formula. + */ + final String sqlFormulaJoin; + + final boolean formula; + + /** + * Set to true if stored encrypted. + */ + final boolean dbEncrypted; + + final boolean localEncrypted; + + final int dbEncryptedType; + + /** + * The jdbc data type this maps to. + */ + final int dbType; + + /** + * The default value to insert if null. + */ + final Object defaultValue; + + /** + * Extra deployment parameters. + */ + final Map extraAttributeMap; + + /** + * The method used to read the property. + */ + final Method readMethod; + + /** + * The method used to write the property. + */ + final Method writeMethod; + + /** + * Generator for insert or update timestamp etc. + */ + final GeneratedProperty generatedProperty; + + final BeanPropertyGetter getter; + + final BeanPropertySetter setter; + + final BeanDescriptor descriptor; + + /** + * Used for non-jdbc native types (java.util.Date Enums etc). Converts from + * logical to jdbc types. + */ + @SuppressWarnings("rawtypes") + final ScalarType scalarType; + + boolean cascadeValidate; + + /** + * The length or precision for DB column. + */ + final int dbLength; + + /** + * The scale for DB column (decimal). + */ + final int dbScale; + + /** + * Deployment defined DB column definition. + */ + final String dbColumnDefn; + + /** + * DB Constraint (typically check constraint on enum) + */ + final String dbConstraintExpression; + + final DbEncryptFunction dbEncryptFunction; + + int deployOrder; + + final boolean jsonSerialize; + + final boolean jsonDeserialize; + + final boolean indexed; + + final String indexName; + public BeanProperty(DeployBeanProperty deploy) { + this(null, null, deploy); + } + + public BeanProperty(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanProperty deploy) { + + this.descriptor = descriptor; + this.name = InternString.intern(deploy.getName()); + this.propertyIndex = deploy.getPropertyIndex(); + this.indexed = deploy.isIndexed(); + this.indexName = deploy.getIndexName(); + this.unidirectionalShadow = deploy.isUndirectionalShadow(); + this.discriminator = deploy.isDiscriminator(); + this.localEncrypted = deploy.isLocalEncrypted(); + this.dbEncrypted = deploy.isDbEncrypted(); + this.dbEncryptedType = deploy.getDbEncryptedType(); + this.dbEncryptFunction = deploy.getDbEncryptFunction(); + this.dbBind = deploy.getDbBind(); + this.dbRead = deploy.isDbRead(); + this.dbInsertable = deploy.isDbInsertable(); + this.dbUpdatable = deploy.isDbUpdateable(); + + this.secondaryTable = deploy.isSecondaryTable(); + if (secondaryTable) { + this.secondaryTableJoin = new TableJoin(deploy.getSecondaryTableJoin(), null); + this.secondaryTableJoinPrefix = deploy.getSecondaryTableJoinPrefix(); + } else { + this.secondaryTableJoin = null; + this.secondaryTableJoinPrefix = null; + } + this.fetchEager = deploy.isFetchEager(); + this.isTransient = deploy.isTransient(); + this.nullable = deploy.isNullable(); + this.unique = deploy.isUnique(); + this.naturalKey = deploy.isNaturalKey(); + this.dbLength = deploy.getDbLength(); + this.dbScale = deploy.getDbScale(); + this.dbColumnDefn = InternString.intern(deploy.getDbColumnDefn()); + this.dbConstraintExpression = InternString.intern(deploy.getDbConstraintExpression()); + + this.inherited = false;// deploy.isInherited(); + this.owningType = deploy.getOwningType(); + this.local = deploy.isLocal(); + + this.version = deploy.isVersionColumn(); + this.embedded = deploy.isEmbedded(); + this.id = deploy.isId(); + this.generatedProperty = deploy.getGeneratedProperty(); + this.readMethod = deploy.getReadMethod(); + this.writeMethod = deploy.getWriteMethod(); + this.getter = deploy.getGetter(); + this.setter = deploy.getSetter(); + + this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null); + this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin()); + this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect()); + this.formula = sqlFormulaSelect != null; + + this.extraAttributeMap = deploy.getExtraAttributeMap(); + this.defaultValue = deploy.getDefaultValue(); + this.dbType = deploy.getDbType(); + this.scalarType = deploy.getScalarType(); + this.lob = isLobType(dbType); + this.propertyType = deploy.getPropertyType(); + this.field = deploy.getField(); + + EntityType et = descriptor == null ? null : descriptor.getEntityType(); + this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null); + this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn); + + this.jsonSerialize = deploy.isExposeSerialize(); + this.jsonDeserialize = deploy.isExposeDeserialize(); + } + + private String tableAliasIntern(BeanDescriptor descriptor, String s, boolean dbEncrypted, String dbColumn) { + if (descriptor != null) { + s = StringHelper.replaceString(s, "${ta}.", "${}"); + s = StringHelper.replaceString(s, "${ta}", "${}"); + + if (dbEncrypted) { + s = dbEncryptFunction.getDecryptSql(s); + String namedParam = ":encryptkey_" + descriptor.getBaseTable() + "___" + dbColumn; + s = StringHelper.replaceString(s, "?", namedParam); + } + } + return InternString.intern(s); + } + + /** + * Create a Matching BeanProperty with some attributes overridden. + *

+ * Primarily for supporting Embedded beans with overridden dbColumn + * mappings. + *

+ */ + public BeanProperty(BeanProperty source, BeanPropertyOverride override) { + + this.descriptor = source.descriptor; + this.name = InternString.intern(source.getName()); + this.propertyIndex = source.propertyIndex; + + this.indexed = source.isIndexed(); + this.indexName = source.getIndexName(); + this.dbColumn = InternString.intern(override.getDbColumn()); + this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin()); + this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect()); + this.formula = sqlFormulaSelect != null; + + this.fetchEager = source.fetchEager; + this.unidirectionalShadow = source.unidirectionalShadow; + this.discriminator = source.discriminator; + this.localEncrypted = source.isLocalEncrypted(); + this.isTransient = source.isTransient(); + this.secondaryTable = source.isSecondaryTable(); + this.secondaryTableJoin = source.secondaryTableJoin; + this.secondaryTableJoinPrefix = source.secondaryTableJoinPrefix; + + this.dbBind = source.getDbBind(); + this.dbEncrypted = source.isDbEncrypted(); + this.dbEncryptedType = source.getDbEncryptedType(); + this.dbEncryptFunction = source.dbEncryptFunction; + this.dbRead = source.isDbRead(); + this.dbInsertable = source.isDbInsertable(); + this.dbUpdatable = source.isDbUpdatable(); + this.nullable = source.isNullable(); + this.unique = source.isUnique(); + this.naturalKey = source.isNaturalKey(); + this.dbLength = source.getDbLength(); + this.dbScale = source.getDbScale(); + this.dbColumnDefn = InternString.intern(source.getDbColumnDefn()); + this.dbConstraintExpression = InternString.intern(source.getDbConstraintExpression()); + + this.inherited = source.isInherited(); + this.owningType = source.owningType; + this.local = owningType.equals(descriptor.getBeanType()); + + this.version = source.isVersion(); + this.embedded = source.isEmbedded(); + this.id = source.isId(); + this.generatedProperty = source.getGeneratedProperty(); + this.readMethod = source.getReadMethod(); + this.writeMethod = source.getWriteMethod(); + this.getter = source.getter; + this.setter = source.setter; + this.extraAttributeMap = source.extraAttributeMap; + this.defaultValue = source.getDefaultValue(); + this.dbType = source.getDbType(); + this.scalarType = source.scalarType; + this.lob = isLobType(dbType); + this.propertyType = source.getPropertyType(); + this.field = source.getField(); + + this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn); + this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn); + + this.jsonSerialize = source.jsonSerialize; + this.jsonDeserialize = source.jsonDeserialize; + } + + /** + * Initialise the property before returning to client code. Used to + * initialise variables that can't be done in construction due to recursive + * issues. + */ + public void initialise() { + // do nothing for normal BeanProperty + if (!isTransient && scalarType == null) { + String msg = "No ScalarType assigned to " + descriptor.getFullName() + "." + getName(); + throw new RuntimeException(msg); + } + } + + /** + * Return the order this property appears in the bean. + */ + public int getDeployOrder() { + return deployOrder; + } + + /** + * Set the order this property appears in the bean. + */ + public void setDeployOrder(int deployOrder) { + this.deployOrder = deployOrder; + } + + public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, + boolean propertyDeploy) { + throw new PersistenceException("Not valid on scalar bean property " + getFullBeanName()); + } + + /** + * Return the BeanDescriptor that owns this property. + */ + public BeanDescriptor getBeanDescriptor() { + return descriptor; + } + + /** + * Return true is this is a simple scalar property. + */ + public boolean isScalar() { + return true; + } + + /** + * Return true if this property is based on a formula. + */ + public boolean isFormula() { + return formula; + } + + /** + * Return true if this property maps to the inheritance discriminator column. + */ + public boolean isDiscriminator() { + return discriminator; + } + + /** + * Return true if the underlying type is mutable. + */ + public boolean isMutableScalarType() { + return scalarType != null && scalarType.isMutable(); + } + + /** + * Return the encrypt key for the column matching this property. + */ + public EncryptKey getEncryptKey() { + return descriptor.getEncryptKey(this); + } + + public String getDecryptProperty(String propertyName) { + return dbEncryptFunction.getDecryptSql(propertyName); + } + + public String getDecryptSql() { + return dbEncryptFunction.getDecryptSql(this.getDbColumn()); + } + + public String getDecryptSql(String tableAlias) { + return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()); + } + + /** + * Add any extra joins required to support this property. Generally a no + * operation except for a OneToOne exported. + */ + public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { + if (formula && sqlFormulaJoin != null) { + ctx.appendFormulaJoin(sqlFormulaJoin, joinType); + + } else if (secondaryTableJoin != null) { + + String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); + secondaryTableJoin.addJoin(joinType, relativePrefix, ctx); + } + } + + /** + * Returns null unless this property is using a secondary table. In that + * case this returns the logical property prefix. + */ + public String getSecondaryTableJoinPrefix() { + return secondaryTableJoinPrefix; + } + + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + if (formula) { + ctx.appendFormulaSelect(sqlFormulaSelect); + + } else if (!isTransient) { + + if (secondaryTableJoin != null) { + String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); + ctx.pushTableAlias(relativePrefix); + } + + if (dbEncrypted) { + String decryptSql = getDecryptSql(ctx.peekTableAlias()); + ctx.appendRawColumn(decryptSql); + ctx.addEncryptedProp(this); + + } else { + ctx.appendColumn(dbColumn); + } + + if (secondaryTableJoin != null) { + ctx.popTableAlias(); + } + } + } + + public boolean isAssignableFrom(Class type) { + return owningType.isAssignableFrom(type); + } + + public void loadIgnore(DbReadContext ctx) { + scalarType.loadIgnore(ctx.getDataReader()); + } + + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + sqlBeanLoad.load(this); + } + + public void buildSelectExpressionChain(String prefix, List selectChain) { + if (prefix == null) { + selectChain.add(name); + } else { + selectChain.add(prefix + "." + name); + } + } + + public Object read(DbReadContext ctx) throws SQLException { + return scalarType.read(ctx.getDataReader()); + } + + public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { + + try { + Object value = scalarType.read(ctx.getDataReader()); + if (bean != null && ((type == null || owningType.isAssignableFrom(type)))) { + setValue(bean, value); + } + return value; + } catch (Exception e) { + throw new PersistenceException("Error readSet on " + descriptor + "." + name, e); + } + } + + /** + * Convert the type to the bean type if required. + *

+ * Generally only used to ensure id properties are converted for + * Query.setId() use. + *

+ */ + public Object toBeanType(Object value) { + return scalarType.toBeanType(value); + } + + @SuppressWarnings("unchecked") + public void bind(DataBind b, Object value) throws SQLException { + scalarType.bind(b, value); + } + + public void writeData(DataOutput dataOutput, Object value) throws IOException { + scalarType.writeData(dataOutput, value); + } + + public Object readData(DataInput dataInput) throws IOException { + return scalarType.readData(dataInput); + } + + public BeanProperty getBeanProperty() { + return this; + } + + public boolean isIndexed() { + return indexed; + } + + public String getIndexName() { + return indexName; + } + + /** + * Return the getter method. + */ + public Method getReadMethod() { + return readMethod; + } + + /** + * Return the setter method. + */ + public Method getWriteMethod() { + return writeMethod; + } + + /** + * Return true if this object is part of an inheritance hierarchy. + */ + public boolean isInherited() { + return inherited; + } + + /** + * Return true is this type is not from a super type. + */ + public boolean isLocal() { + return local; + } + + /** + * Set the value of the property without interception or + * PropertyChangeSupport. + */ + public void setValue(EntityBean bean, Object value) { + try { + setter.set(bean, value); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "set " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType + + "] threw error"; + throw new RuntimeException(msg, ex); + } + } + + /** + * Set the value of the property. + */ + public void setValueIntercept(EntityBean bean, Object value) { + try { + setter.setIntercept(bean, value); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "setIntercept " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType + + "] threw error"; + throw new RuntimeException(msg, ex); + } + } + + public Object getCacheDataValue(EntityBean bean) { + return getValue(bean); + } + + public void setCacheDataValue(EntityBean bean, Object cacheData) { + setValue(bean, cacheData); + } + + /** + * Get the property value from a compound value type. + */ + public Object getValueObject(Object bean) { + throw new RuntimeException("Expected to be called only on BeanPropertyCompoundScalar"); + } + + /** + * Return the value of the property method. + */ + public Object getValue(EntityBean bean) { + try { + return getter.get(bean); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + public Object getValueIntercept(EntityBean bean) { + try { + return getter.getIntercept(bean); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "getIntercept " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + public Object elConvertType(Object value) { + if (value == null) { + return null; + } + return convertToLogicalType(value); + } + + public void elSetValue(EntityBean bean, Object value, boolean populate) { + if (bean != null) { + // Not using setValueIntercept at this stage + setValue(bean, value); + } + } + + public Object elGetValue(EntityBean bean) { + if (bean == null) { + return null; + } + return getValueIntercept(bean); + } + + public Object elGetReference(EntityBean bean) { + throw new RuntimeException("Not expected to call this"); + } + + /** + * Return the name of the property. + */ + public String getName() { + return name; + } + + /** + * Return the position of this property in the enhanced bean. + */ + public int getPropertyIndex() { + return propertyIndex; + } + + public String getElName() { + return name; + } + + @Override + public boolean containsFormulaWithJoin() { + return formula && sqlFormulaJoin != null; + } + + public boolean containsManySince(String sinceProperty) { + return containsMany(); + } + + public boolean containsMany() { + return false; + } + + public Object[] getAssocOneIdValues(EntityBean bean) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocOneIdExpr(String prefix, String operator) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocIdInExpr(String prefix) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocIdInValueExpr(int size) { + // Returns null as not an AssocOne. + return null; + } + + public boolean isAssocId() { + // Returns false - override in BeanPropertyAssocOne. + return false; + } + + public boolean isAssocProperty() { + // Returns false - override in BeanPropertyAssocOne. + return false; + } + + public String getElPlaceholder(boolean encrypted) { + return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; + } + + public String getElPrefix() { + return secondaryTableJoinPrefix; + } + + /** + * Return the full name of this property. + */ + public String getFullBeanName() { + return descriptor.getFullName() + "." + name; + } + + /** + * Return true if the mutable value is considered dirty. + * This is only used for 'mutable' scalar types like hstore etc. + */ + public boolean isDirtyValue(Object value) { + return scalarType.isDirty(value); + } + + /** + * Return the scalarType. + */ + public ScalarType getScalarType() { + return scalarType; + } + + public StringFormatter getStringFormatter() { + return scalarType; + } + + public StringParser getStringParser() { + return scalarType; + } + + public boolean isDateTimeCapable() { + return scalarType != null && scalarType.isDateTimeCapable(); + } + + public int getJdbcType() { + return scalarType == null ? 0 : scalarType.getJdbcType(); + } + + public Object parseDateTime(long systemTimeMillis) { + return scalarType.convertFromMillis(systemTimeMillis); + } + + /** + * Return the DB max length (varchar) or precision (decimal). + */ + public int getDbLength() { + return dbLength; + } + + /** + * Return the DB scale for numeric columns. + */ + public int getDbScale() { + return dbScale; + } + + /** + * Return a specific column DDL definition if specified (otherwise null). + */ + public String getDbColumnDefn() { + return dbColumnDefn; + } + + /** + * Return the DB constraint expression (can be null). + *

+ * For an Enum returns IN expression for the set of Enum values. + *

+ */ + public String getDbConstraintExpression() { + return dbConstraintExpression; + } + + /** + * Return the DB column type definition. + */ + public String renderDbType(DbType dbType) { + if (dbColumnDefn != null) { + return dbColumnDefn; + } + return dbType.renderType(dbLength, dbScale); + } + + /** + * Return the bean Field associated with this property. + */ + public Field getField() { + return field; + } + + /** + * Return the GeneratedValue. Used to generate update timestamp etc. + */ + public GeneratedProperty getGeneratedProperty() { + return generatedProperty; + } + + /** + * Return true if this is the natural key property. + */ + public boolean isNaturalKey() { + return naturalKey; + } + + /** + * Return true if this property is mandatory. + */ + public boolean isNullable() { + return nullable; + } + + /** + * Return true if DDL Not NULL constraint should be defined for this column + * based on it being a version column or having a generated property. + */ + public boolean isDDLNotNull() { + return isVersion() || (generatedProperty != null && generatedProperty.isDDLNotNullable()); + } + + /** + * Return true if the DB column should be unique. + */ + public boolean isUnique() { + return unique; + } + + /** + * Return true if the property is transient. + */ + public boolean isTransient() { + return isTransient; + } + + /** + * Return true if this property is loadable from a resultSet. + */ + public boolean isLoadProperty() { + return !isTransient || formula; + } + + /** + * Return true if this is a version column used for concurrency checking. + */ + public boolean isVersion() { + return version; + } + + /** + * The database column name this is mapped to. + */ + public String getDbColumn() { + return dbColumn; + } + + /** + * Return the database jdbc data type this is mapped to. + */ + public int getDbType() { + return dbType; + } + + /** + * Perform DB to Logical type conversion (if necessary). + */ + public Object convertToLogicalType(Object value) { + if (scalarType != null) { + return scalarType.toBeanType(value); + } + return value; + } + + /** + * Return true if by default this property is set to fetch eager. + * Lob's usually default to fetch lazy. + */ + public boolean isFetchEager() { + return fetchEager; + } + + /** + * Return true if this is mapped to a Clob Blob LongVarchar or + * LongVarbinary. + */ + public boolean isLob() { + return lob; + } + + private boolean isLobType(int type) { + switch (type) { + case Types.CLOB: + return true; + case Types.BLOB: + return true; + case Types.LONGVARBINARY: + return true; + case Types.LONGVARCHAR: + return true; + + default: + return false; + } + } + + /** + * Return the DB bind parameter. Typically is "?" but different for + * encrypted bind. + */ + public String getDbBind() { + return dbBind; + } + + /** + * Returns true if DB encrypted. + */ + public boolean isLocalEncrypted() { + return localEncrypted; + } + + /** + * Return true if this property is stored encrypted. + */ + public boolean isDbEncrypted() { + return dbEncrypted; + } + + public int getDbEncryptedType() { + return dbEncryptedType; + } + + /** + * Return true if this property should be included in an Insert. + */ + public boolean isDbInsertable() { + return dbInsertable; + } + + /** + * Return true if this property should be included in an Update. + */ + public boolean isDbUpdatable() { + return dbUpdatable; + } + + /** + * Return true if this property is included in database queries. + */ + public boolean isDbRead() { + return dbRead; + } + + /** + * Return true if this property is based on a secondary table (not the base + * table). + */ + public boolean isSecondaryTable() { + return secondaryTable; + } + + /** + * Return the property type. + */ + public Class getPropertyType() { + return propertyType; + } + + /** + * Return true if this is included in the unique id. + */ + public boolean isId() { + return id; + } + + /** + * Return true if this is an Embedded property. In this case it shares the + * table and primary key of its owner object. + */ + public boolean isEmbedded() { + return embedded; + } + + /** + * Return the default value. + */ + public Object getDefaultValue() { + return defaultValue; + } + + public String toString() { + return name; + } + + public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException { + if (!jsonSerialize) { + return; + } + Object value = getValueIntercept(bean); + if (value == null) { + writeJson.writeNull(name); + } else { + scalarType.jsonWrite(writeJson.gen(), name, value); + } + } + + public void jsonRead(JsonParser ctx, EntityBean bean) throws IOException { + if (!jsonDeserialize) { + return; + } + + JsonToken event = ctx.nextToken(); + if (JsonToken.VALUE_NULL == event) { + setValue(bean, null); + } else { + // expect to read non-null json value + Object objValue = scalarType.jsonRead(ctx, event); + setValue(bean, objValue); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java.orig b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java.orig index ef3fa4795..339c6f8b6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java.orig +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java.orig @@ -1,1257 +1,1257 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.sql.SQLException; -import java.sql.Types; -import java.util.List; -import java.util.Map; - -import javax.json.stream.JsonParser; -import javax.json.stream.JsonParser.Event; -import javax.persistence.PersistenceException; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.config.EncryptKey; -import com.avaje.ebean.config.dbplatform.DbEncryptFunction; -import com.avaje.ebean.config.dbplatform.DbType; -import com.avaje.ebean.text.StringFormatter; -import com.avaje.ebean.text.StringParser; -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.query.SqlJoinType; -import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter; -import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; -import com.avaje.ebeaninternal.server.text.json.WriteJson; -import com.avaje.ebeaninternal.server.type.DataBind; -import com.avaje.ebeaninternal.server.type.ScalarType; - -/** - * Description of a property of a bean. Includes its deployment information such - * as database column mapping information. - */ -public class BeanProperty implements ElPropertyValue { - - /** - * Advanced bean deployment. To exclude this property from update where - * clause. - */ - public static final String EXCLUDE_FROM_UPDATE_WHERE = "EXCLUDE_FROM_UPDATE_WHERE"; - - /** - * Advanced bean deployment. To exclude this property from delete where - * clause. - */ - public static final String EXCLUDE_FROM_DELETE_WHERE = "EXCLUDE_FROM_DELETE_WHERE"; - - /** - * Advanced bean deployment. To exclude this property from insert. - */ - public static final String EXCLUDE_FROM_INSERT = "EXCLUDE_FROM_INSERT"; - - /** - * Advanced bean deployment. To exclude this property from update set - * clause. - */ - public static final String EXCLUDE_FROM_UPDATE = "EXCLUDE_FROM_UPDATE"; - - /** - * Flag to mark this at part of the unique id. - */ - final boolean id; - - /** - * Flag to make this as a dummy property for unidirecitonal relationships. - */ - final boolean unidirectionalShadow; - - /** - * Flag set if this maps to the inheritance discriminator column - */ - final boolean discriminator; - - /** - * Flag to mark the property as embedded. This could be on - * BeanPropertyAssocOne rather than here. Put it here for checking Id type - * (embedded or not). - */ - final boolean embedded; - - /** - * Flag indicating if this the version property. - */ - final boolean version; - - final boolean naturalKey; - - /** - * Set if this property is nullable. - */ - final boolean nullable; - - final boolean unique; - - /** - * Is this property include in database resultSet. - */ - final boolean dbRead; - - /** - * Include in DB insert. - */ - final boolean dbInsertable; - - /** - * Include in DB update. - */ - final boolean dbUpdatable; - - /** - * True if the property is based on a SECONDARY table. - */ - final boolean secondaryTable; - - final TableJoin secondaryTableJoin; - final String secondaryTableJoinPrefix; - - /** - * The property is inherited from a super class. - */ - final boolean inherited; - - final Class owningType; - - final boolean local; - - /** - * True if the property is a Clob, Blob LongVarchar or LongVarbinary. - */ - final boolean lob; - - final boolean fetchEager; - - final boolean isTransient; - - /** - * The logical bean property name. - */ - final String name; - - final int propertyIndex; - - /** - * The reflected field. - */ - final Field field; - - /** - * The bean type. - */ - final Class propertyType; - - final String dbBind; - - /** - * The database column. This can include quoted identifiers. - */ - final String dbColumn; - - final String elPlaceHolder; - final String elPlaceHolderEncrypted; - - /** - * Select part of a SQL Formula used to populate this property. - */ - final String sqlFormulaSelect; - - /** - * Join part of a SQL Formula. - */ - final String sqlFormulaJoin; - - final boolean formula; - - /** - * Set to true if stored encrypted. - */ - final boolean dbEncrypted; - - final boolean localEncrypted; - - final int dbEncryptedType; - - /** - * The jdbc data type this maps to. - */ - final int dbType; - - /** - * The default value to insert if null. - */ - final Object defaultValue; - - /** - * Extra deployment parameters. - */ - final Map extraAttributeMap; - - /** - * The method used to read the property. - */ - final Method readMethod; - - /** - * The method used to write the property. - */ - final Method writeMethod; - - /** - * Generator for insert or update timestamp etc. - */ - final GeneratedProperty generatedProperty; - - final BeanReflectGetter getter; - - final BeanReflectSetter setter; - - final BeanDescriptor descriptor; - - /** - * Used for non-jdbc native types (java.util.Date Enums etc). Converts from - * logical to jdbc types. - */ - @SuppressWarnings("rawtypes") - final ScalarType scalarType; - - boolean cascadeValidate; - - /** - * The length or precision for DB column. - */ - final int dbLength; - - /** - * The scale for DB column (decimal). - */ - final int dbScale; - - /** - * Deployment defined DB column definition. - */ - final String dbColumnDefn; - - /** - * DB Constraint (typically check constraint on enum) - */ - final String dbConstraintExpression; - - final DbEncryptFunction dbEncryptFunction; - - int deployOrder; - - final boolean jsonSerialize; - - final boolean jsonDeserialize; - - final boolean indexed; - - final String indexName; - - public BeanProperty(DeployBeanProperty deploy) { - this(null, null, deploy); - } - - public BeanProperty(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanProperty deploy) { - - this.descriptor = descriptor; - this.name = InternString.intern(deploy.getName()); - this.propertyIndex = deploy.getPropertyIndex(); - - this.indexed = deploy.isIndexed(); - this.indexName = deploy.getIndexName(); - - this.unidirectionalShadow = deploy.isUndirectionalShadow(); - this.discriminator = deploy.isDiscriminator(); - this.localEncrypted = deploy.isLocalEncrypted(); - this.dbEncrypted = deploy.isDbEncrypted(); - this.dbEncryptedType = deploy.getDbEncryptedType(); - this.dbEncryptFunction = deploy.getDbEncryptFunction(); - this.dbBind = deploy.getDbBind(); - this.dbRead = deploy.isDbRead(); - this.dbInsertable = deploy.isDbInsertable(); - this.dbUpdatable = deploy.isDbUpdateable(); - - this.secondaryTable = deploy.isSecondaryTable(); - if (secondaryTable) { - this.secondaryTableJoin = new TableJoin(deploy.getSecondaryTableJoin(), null); - this.secondaryTableJoinPrefix = deploy.getSecondaryTableJoinPrefix(); - } else { - this.secondaryTableJoin = null; - this.secondaryTableJoinPrefix = null; - } - this.fetchEager = deploy.isFetchEager(); - this.isTransient = deploy.isTransient(); - this.nullable = deploy.isNullable(); - this.unique = deploy.isUnique(); - this.naturalKey = deploy.isNaturalKey(); - this.dbLength = deploy.getDbLength(); - this.dbScale = deploy.getDbScale(); - this.dbColumnDefn = InternString.intern(deploy.getDbColumnDefn()); - this.dbConstraintExpression = InternString.intern(deploy.getDbConstraintExpression()); - - this.inherited = false;// deploy.isInherited(); - this.owningType = deploy.getOwningType(); - this.local = deploy.isLocal(); - - this.version = deploy.isVersionColumn(); - this.embedded = deploy.isEmbedded(); - this.id = deploy.isId(); - this.generatedProperty = deploy.getGeneratedProperty(); - this.readMethod = deploy.getReadMethod(); - this.writeMethod = deploy.getWriteMethod(); - this.getter = deploy.getGetter(); - this.setter = deploy.getSetter(); - - this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null); - this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin()); - this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect()); - this.formula = sqlFormulaSelect != null; - - this.extraAttributeMap = deploy.getExtraAttributeMap(); - this.defaultValue = deploy.getDefaultValue(); - this.dbType = deploy.getDbType(); - this.scalarType = deploy.getScalarType(); - this.lob = isLobType(dbType); - this.propertyType = deploy.getPropertyType(); - this.field = deploy.getField(); - - EntityType et = descriptor == null ? null : descriptor.getEntityType(); - this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null); - this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn); - - this.jsonSerialize = deploy.isExposeSerialize(); - this.jsonDeserialize = deploy.isExposeDeserialize(); - } - - private String tableAliasIntern(BeanDescriptor descriptor, String s, boolean dbEncrypted, String dbColumn) { - if (descriptor != null) { - s = StringHelper.replaceString(s, "${ta}.", "${}"); - s = StringHelper.replaceString(s, "${ta}", "${}"); - - if (dbEncrypted) { - s = dbEncryptFunction.getDecryptSql(s); - String namedParam = ":encryptkey_" + descriptor.getBaseTable() + "___" + dbColumn; - s = StringHelper.replaceString(s, "?", namedParam); - } - } - return InternString.intern(s); - } - - /** - * Create a Matching BeanProperty with some attributes overridden. - *

- * Primarily for supporting Embedded beans with overridden dbColumn - * mappings. - *

- */ - public BeanProperty(BeanProperty source, BeanPropertyOverride override) { - - this.descriptor = source.descriptor; - this.name = InternString.intern(source.getName()); - this.propertyIndex = source.propertyIndex; - - this.indexed = source.isIndexed(); - this.indexName = source.getIndexName(); - - this.dbColumn = InternString.intern(override.getDbColumn()); - this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin()); - this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect()); - this.formula = sqlFormulaSelect != null; - - this.fetchEager = source.fetchEager; - this.unidirectionalShadow = source.unidirectionalShadow; - this.discriminator = source.discriminator; - this.localEncrypted = source.isLocalEncrypted(); - this.isTransient = source.isTransient(); - this.secondaryTable = source.isSecondaryTable(); - this.secondaryTableJoin = source.secondaryTableJoin; - this.secondaryTableJoinPrefix = source.secondaryTableJoinPrefix; - - this.dbBind = source.getDbBind(); - this.dbEncrypted = source.isDbEncrypted(); - this.dbEncryptedType = source.getDbEncryptedType(); - this.dbEncryptFunction = source.dbEncryptFunction; - this.dbRead = source.isDbRead(); - this.dbInsertable = source.isDbInsertable(); - this.dbUpdatable = source.isDbUpdatable(); - this.nullable = source.isNullable(); - this.unique = source.isUnique(); - this.naturalKey = source.isNaturalKey(); - this.dbLength = source.getDbLength(); - this.dbScale = source.getDbScale(); - this.dbColumnDefn = InternString.intern(source.getDbColumnDefn()); - this.dbConstraintExpression = InternString.intern(source.getDbConstraintExpression()); - - this.inherited = source.isInherited(); - this.owningType = source.owningType; - this.local = owningType.equals(descriptor.getBeanType()); - - this.version = source.isVersion(); - this.embedded = source.isEmbedded(); - this.id = source.isId(); - this.generatedProperty = source.getGeneratedProperty(); - this.readMethod = source.getReadMethod(); - this.writeMethod = source.getWriteMethod(); - this.getter = source.getter; - this.setter = source.setter; - this.extraAttributeMap = source.extraAttributeMap; - this.defaultValue = source.getDefaultValue(); - this.dbType = source.getDbType(); - this.scalarType = source.scalarType; - this.lob = isLobType(dbType); - this.propertyType = source.getPropertyType(); - this.field = source.getField(); - - this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn); - this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn); - - this.jsonSerialize = source.jsonSerialize; - this.jsonDeserialize = source.jsonDeserialize; - } - - /** - * Initialise the property before returning to client code. Used to - * initialise variables that can't be done in construction due to recursive - * issues. - */ - public void initialise() { - // do nothing for normal BeanProperty - if (!isTransient && scalarType == null) { - String msg = "No ScalarType assigned to " + descriptor.getFullName() + "." + getName(); - throw new RuntimeException(msg); - } - } - - /** - * Return the order this property appears in the bean. - */ - public int getDeployOrder() { - return deployOrder; - } - - /** - * Set the order this property appears in the bean. - */ - public void setDeployOrder(int deployOrder) { - this.deployOrder = deployOrder; - } - - public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, - boolean propertyDeploy) { - throw new PersistenceException("Not valid on scalar bean property " + getFullBeanName()); - } - - /** - * Return the BeanDescriptor that owns this property. - */ - public BeanDescriptor getBeanDescriptor() { - return descriptor; - } - - /** - * Return true is this is a simple scalar property. - */ - public boolean isScalar() { - return true; - } - - /** - * Return true if this property is based on a formula. - */ - public boolean isFormula() { - return formula; - } - - /** - * Return true if this property maps to the inheritance discriminator column. - */ - public boolean isDiscriminator() { - return discriminator; - } - - /** - * Return true if the underlying type is mutable. - */ - public boolean isMutableScalarType() { - if (scalarType == null) { - return false; - } - return scalarType.isMutable(); - } - - public void copyProperty(EntityBean sourceBean, EntityBean destBean) { - Object value = getValue(sourceBean); - setValue(destBean, value); - } - - /** - * Return the encrypt key for the column matching this property. - */ - public EncryptKey getEncryptKey() { - return descriptor.getEncryptKey(this); - } - - public String getDecryptProperty() { - return dbEncryptFunction.getDecryptSql(this.getName()); - } - - public String getDecryptProperty(String propertyName) { - return dbEncryptFunction.getDecryptSql(propertyName); - } - - public String getDecryptSql() { - return dbEncryptFunction.getDecryptSql(this.getDbColumn()); - } - - public String getDecryptSql(String tableAlias) { - return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()); - } - - /** - * Add any extra joins required to support this property. Generally a no - * operation except for a OneToOne exported. - */ - public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { - if (formula && sqlFormulaJoin != null) { - ctx.appendFormulaJoin(sqlFormulaJoin, joinType); - - } else if (secondaryTableJoin != null) { - - String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); - secondaryTableJoin.addJoin(joinType, relativePrefix, ctx); - } - } - - /** - * Returns null unless this property is using a secondary table. In that - * case this returns the logical property prefix. - */ - public String getSecondaryTableJoinPrefix() { - return secondaryTableJoinPrefix; - } - - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - if (formula) { - ctx.appendFormulaSelect(sqlFormulaSelect); - - } else if (!isTransient) { - - if (secondaryTableJoin != null) { - String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); - ctx.pushTableAlias(relativePrefix); - } - - if (dbEncrypted) { - String decryptSql = getDecryptSql(ctx.peekTableAlias()); - ctx.appendRawColumn(decryptSql); - ctx.addEncryptedProp(this); - - } else { - ctx.appendColumn(dbColumn); - } - - if (secondaryTableJoin != null) { - ctx.popTableAlias(); - } - } - } - - public boolean isAssignableFrom(Class type) { - return owningType.isAssignableFrom(type); - } - - public Object readSetOwning(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { - - try { - Object value = scalarType.read(ctx.getDataReader()); - if (value == null || bean == null) { - // not setting the value... - } else { - if (owningType.equals(type)) { - setValue(bean, value); - } - } - return value; - } catch (Exception e) { - String msg = "Error readSet on " + descriptor + "." + name; - throw new PersistenceException(msg, e); - } - } - - public void loadIgnore(DbReadContext ctx) { - scalarType.loadIgnore(ctx.getDataReader()); - } - - public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { - sqlBeanLoad.load(this); - } - - public void buildSelectExpressionChain(String prefix, List selectChain) { - if (prefix == null) { - selectChain.add(name); - } else { - selectChain.add(prefix + "." + name); - } - } - - public Object read(DbReadContext ctx) throws SQLException { - return scalarType.read(ctx.getDataReader()); - } - - public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { - - try { - Object value = scalarType.read(ctx.getDataReader()); - if (bean == null || (type != null && !owningType.isAssignableFrom(type))) { - // not setting the value... - } else { - setValue(bean, value); - } - return value; - } catch (Exception e) { - String msg = "Error readSet on " + descriptor + "." + name; - throw new PersistenceException(msg, e); - } - } - - /** - * Convert the type to the bean type if required. - *

- * Generally only used to ensure id properties are converted for - * Query.setId() use. - *

- */ - public Object toBeanType(Object value) { - return scalarType.toBeanType(value); - } - - @SuppressWarnings("unchecked") - public void bind(DataBind b, Object value) throws SQLException { - scalarType.bind(b, value); - } - - public void writeData(DataOutput dataOutput, Object value) throws IOException { - scalarType.writeData(dataOutput, value); - } - - public Object readData(DataInput dataInput) throws IOException { - return scalarType.readData(dataInput); - } - - public boolean isCascadeValidate() { - return cascadeValidate; - } - - /** - * Checks to see if a bean is a reference (will be lazy loaded) or a - * BeanCollection that has not yet been populated. - *

- * For base types this returns true. - *

- */ - public boolean isValueLoaded(Object value) { - return true; - } - - public BeanProperty getBeanProperty() { - return this; - } - - /** - * Return the getter method. - */ - public Method getReadMethod() { - return readMethod; - } - - /** - * Return the setter method. - */ - public Method getWriteMethod() { - return writeMethod; - } - - /** - * Return true if this object is part of an inheritance hierarchy. - */ - public boolean isInherited() { - return inherited; - } - - /** - * Return true is this type is not from a super type. - */ - public boolean isLocal() { - return local; - } - - /** - * Set the value of the property without interception or - * PropertyChangeSupport. - */ - public void setValue(EntityBean bean, Object value) { - try { - setter.set(bean, value); - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "set " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType - + "] threw error"; - throw new RuntimeException(msg, ex); - } - } - - /** - * Set the value of the property. - */ - public void setValueIntercept(EntityBean bean, Object value) { - try { - setter.setIntercept(bean, value); - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "setIntercept " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType - + "] threw error"; - throw new RuntimeException(msg, ex); - } - } - - private static Object[] NO_ARGS = new Object[0]; - - public Object getCacheDataValue(EntityBean bean) { - return getValue(bean); - } - - public void setCacheDataValue(EntityBean bean, Object cacheData) { - setValue(bean, cacheData); - } - - /** - * Return the value of the property method. - */ - public Object getValue(EntityBean bean) { - try { - return getter.get(bean); - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; - throw new RuntimeException(msg, ex); - } - } - - /** - * Explicitly use reflection to get value. - */ - public Object getValueViaReflection(Object bean) { - try { - return readMethod.invoke(bean, NO_ARGS); - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; - throw new RuntimeException(msg, ex); - } - } - - public Object getValueIntercept(EntityBean bean) { - try { - return getter.getIntercept(bean); - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "getIntercept " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; - throw new RuntimeException(msg, ex); - } - } - - public Object elConvertType(Object value) { - if (value == null) { - return null; - } - return convertToLogicalType(value); - } - - public void elSetValue(EntityBean bean, Object value, boolean populate) { - if (bean != null) { - // Not using setValueIntercept at this stage - setValue(bean, value); - } - } - - public Object elGetValue(EntityBean bean) { - if (bean == null) { - return null; - } - return getValueIntercept(bean); - } - - public Object elGetReference(EntityBean bean) { - throw new RuntimeException("Not expected to call this"); - } - - /** - * Return the name of the property. - */ - public String getName() { - return name; - } - - /** - * Return the position of this property in the enhanced bean. - */ - public int getPropertyIndex() { - return propertyIndex; - } - - public String getElName() { - return name; - } - - /** - * This is a full ElGetValue. - */ - public boolean isDeployOnly() { - return false; - } - - - @Override - public boolean containsFormulaWithJoin() { - return formula && sqlFormulaJoin != null; - } - - public boolean containsManySince(String sinceProperty) { - return containsMany(); - } - - public boolean containsMany() { - return false; - } - - public Object[] getAssocOneIdValues(EntityBean bean) { - // Returns null as not an AssocOne. - return null; - } - - public String getAssocOneIdExpr(String prefix, String operator) { - // Returns null as not an AssocOne. - return null; - } - - public String getAssocIdInExpr(String prefix) { - // Returns null as not an AssocOne. - return null; - } - - public String getAssocIdInValueExpr(int size) { - // Returns null as not an AssocOne. - return null; - } - - public boolean isAssocId() { - // Returns false - override in BeanPropertyAssocOne. - return false; - } - - public boolean isAssocProperty() { - // Returns false - override in BeanPropertyAssocOne. - return false; - } - - public String getElPlaceholder(boolean encrypted) { - return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; - } - - public String getElPrefix() { - return secondaryTableJoinPrefix; - } - - /** - * Return the full name of this property. - */ - public String getFullBeanName() { - return descriptor.getFullName() + "." + name; - } - - /** - * Return true if the mutable value is considered dirty. - * This is only used for 'mutable' scalar types like hstore etc. - */ - public boolean isDirtyValue(Object value) { - return scalarType.isDirty(value); - } - - /** - * Return the scalarType. - */ - public ScalarType getScalarType() { - return scalarType; - } - - public StringFormatter getStringFormatter() { - return scalarType; - } - - public StringParser getStringParser() { - return scalarType; - } - - public boolean isDateTimeCapable() { - return scalarType != null && scalarType.isDateTimeCapable(); - } - - public int getJdbcType() { - return scalarType == null ? 0 : scalarType.getJdbcType(); - } - - public Object parseDateTime(long systemTimeMillis) { - return scalarType.parseDateTime(systemTimeMillis); - } - - /** - * Return the DB max length (varchar) or precision (decimal). - */ - public int getDbLength() { - return dbLength; - } - - /** - * Return the DB scale for numeric columns. - */ - public int getDbScale() { - return dbScale; - } - - /** - * Return a specific column DDL definition if specified (otherwise null). - */ - public String getDbColumnDefn() { - return dbColumnDefn; - } - - /** - * Return the DB constraint expression (can be null). - *

- * For an Enum returns IN expression for the set of Enum values. - *

- */ - public String getDbConstraintExpression() { - return dbConstraintExpression; - } - - /** - * Return the DB column type definition. - */ - public String renderDbType(DbType dbType) { - if (dbColumnDefn != null) { - return dbColumnDefn; - } - return dbType.renderType(dbLength, dbScale); - } - - /** - * Return the bean Field associated with this property. - */ - public Field getField() { - return field; - } - - /** - * Return the GeneratedValue. Used to generate update timestamp etc. - */ - public GeneratedProperty getGeneratedProperty() { - return generatedProperty; - } - - /** - * Return true if this is the natural key property. - */ - public boolean isNaturalKey() { - return naturalKey; - } - - /** - * Return true if this property is mandatory. - */ - public boolean isNullable() { - return nullable; - } - - /** - * Return true if DDL Not NULL constraint should be defined for this column - * based on it being a version column or having a generated property. - */ - public boolean isDDLNotNull() { - return isVersion() || (generatedProperty != null && generatedProperty.isDDLNotNullable()); - } - - /** - * Return true if the DB column should be unique. - */ - public boolean isUnique() { - return unique; - } - - /** - * Return true if the property is transient. - */ - public boolean isTransient() { - return isTransient; - } - - /** - * Return true if this property is loadable from a resultSet. - */ - public boolean isLoadProperty() { - return !isTransient || formula; - } - - /** - * Return true if this is a version column used for concurrency checking. - */ - public boolean isVersion() { - return version; - } - - public String getDeployProperty() { - return dbColumn; - } - - /** - * The database column name this is mapped to. - */ - public String getDbColumn() { - return dbColumn; - } - - /** - * Return the database jdbc data type this is mapped to. - */ - public int getDbType() { - return dbType; - } - - /** - * Perform DB to Logical type conversion (if necessary). - */ - public Object convertToLogicalType(Object value) { - if (scalarType != null) { - return scalarType.toBeanType(value); - } - return value; - } - - /** - * Return true if by default this property is set to fetch eager. - * Lob's usually default to fetch lazy. - */ - public boolean isFetchEager() { - return fetchEager; - } - - /** - * Return true if this is mapped to a Clob Blob LongVarchar or - * LongVarbinary. - */ - public boolean isLob() { - return lob; - } - - private boolean isLobType(int type) { - switch (type) { - case Types.CLOB: - return true; - case Types.BLOB: - return true; - case Types.LONGVARBINARY: - return true; - case Types.LONGVARCHAR: - return true; - - default: - return false; - } - } - - /** - * Return the DB bind parameter. Typically is "?" but different for - * encrypted bind. - */ - public String getDbBind() { - return dbBind; - } - - /** - * Returns true if DB encrypted. - */ - public boolean isLocalEncrypted() { - return localEncrypted; - } - - /** - * Return true if this property is stored encrypted. - */ - public boolean isDbEncrypted() { - return dbEncrypted; - } - - public int getDbEncryptedType() { - return dbEncryptedType; - } - - /** - * Return true if this property should be included in an Insert. - */ - public boolean isDbInsertable() { - return dbInsertable; - } - - /** - * Return true if this property should be included in an Update. - */ - public boolean isDbUpdatable() { - return dbUpdatable; - } - - /** - * Return true if this property is included in database queries. - */ - public boolean isDbRead() { - return dbRead; - } - - /** - * Return true if this property is based on a secondary table (not the base - * table). - */ - public boolean isSecondaryTable() { - return secondaryTable; - } - - /** - * Return the property type. - */ - public Class getPropertyType() { - return propertyType; - } - - /** - * Return true if this is included in the unique id. - */ - public boolean isId() { - return id; - } - - /** - * Return true if this is an Embedded property. In this case it shares the - * table and primary key of its owner object. - */ - public boolean isEmbedded() { - return embedded; - } - - /** - * Return an extra attribute set on this property. - */ - public String getExtraAttribute(String key) { - return extraAttributeMap.get(key); - } - - /** - * Return the default value. - */ - public Object getDefaultValue() { - return defaultValue; - } - - public String toString() { - return name; - } - +package com.avaje.ebeaninternal.server.deploy; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.sql.Types; +import java.util.List; +import java.util.Map; + +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; +import javax.persistence.PersistenceException; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.config.EncryptKey; +import com.avaje.ebean.config.dbplatform.DbEncryptFunction; +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.text.StringFormatter; +import com.avaje.ebean.text.StringParser; +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.query.SqlBeanLoad; +import com.avaje.ebeaninternal.server.query.SqlJoinType; +import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter; +import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; +import com.avaje.ebeaninternal.server.text.json.WriteJson; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.type.ScalarType; + +/** + * Description of a property of a bean. Includes its deployment information such + * as database column mapping information. + */ +public class BeanProperty implements ElPropertyValue { + + /** + * Advanced bean deployment. To exclude this property from update where + * clause. + */ + public static final String EXCLUDE_FROM_UPDATE_WHERE = "EXCLUDE_FROM_UPDATE_WHERE"; + + /** + * Advanced bean deployment. To exclude this property from delete where + * clause. + */ + public static final String EXCLUDE_FROM_DELETE_WHERE = "EXCLUDE_FROM_DELETE_WHERE"; + + /** + * Advanced bean deployment. To exclude this property from insert. + */ + public static final String EXCLUDE_FROM_INSERT = "EXCLUDE_FROM_INSERT"; + + /** + * Advanced bean deployment. To exclude this property from update set + * clause. + */ + public static final String EXCLUDE_FROM_UPDATE = "EXCLUDE_FROM_UPDATE"; + + /** + * Flag to mark this at part of the unique id. + */ + final boolean id; + + /** + * Flag to make this as a dummy property for unidirecitonal relationships. + */ + final boolean unidirectionalShadow; + + /** + * Flag set if this maps to the inheritance discriminator column + */ + final boolean discriminator; + + /** + * Flag to mark the property as embedded. This could be on + * BeanPropertyAssocOne rather than here. Put it here for checking Id type + * (embedded or not). + */ + final boolean embedded; + + /** + * Flag indicating if this the version property. + */ + final boolean version; + + final boolean naturalKey; + + /** + * Set if this property is nullable. + */ + final boolean nullable; + + final boolean unique; + + /** + * Is this property include in database resultSet. + */ + final boolean dbRead; + + /** + * Include in DB insert. + */ + final boolean dbInsertable; + + /** + * Include in DB update. + */ + final boolean dbUpdatable; + + /** + * True if the property is based on a SECONDARY table. + */ + final boolean secondaryTable; + + final TableJoin secondaryTableJoin; + final String secondaryTableJoinPrefix; + + /** + * The property is inherited from a super class. + */ + final boolean inherited; + + final Class owningType; + + final boolean local; + + /** + * True if the property is a Clob, Blob LongVarchar or LongVarbinary. + */ + final boolean lob; + + final boolean fetchEager; + + final boolean isTransient; + + /** + * The logical bean property name. + */ + final String name; + + final int propertyIndex; + + /** + * The reflected field. + */ + final Field field; + + /** + * The bean type. + */ + final Class propertyType; + + final String dbBind; + + /** + * The database column. This can include quoted identifiers. + */ + final String dbColumn; + + final String elPlaceHolder; + final String elPlaceHolderEncrypted; + + /** + * Select part of a SQL Formula used to populate this property. + */ + final String sqlFormulaSelect; + + /** + * Join part of a SQL Formula. + */ + final String sqlFormulaJoin; + + final boolean formula; + + /** + * Set to true if stored encrypted. + */ + final boolean dbEncrypted; + + final boolean localEncrypted; + + final int dbEncryptedType; + + /** + * The jdbc data type this maps to. + */ + final int dbType; + + /** + * The default value to insert if null. + */ + final Object defaultValue; + + /** + * Extra deployment parameters. + */ + final Map extraAttributeMap; + + /** + * The method used to read the property. + */ + final Method readMethod; + + /** + * The method used to write the property. + */ + final Method writeMethod; + + /** + * Generator for insert or update timestamp etc. + */ + final GeneratedProperty generatedProperty; + + final BeanReflectGetter getter; + + final BeanReflectSetter setter; + + final BeanDescriptor descriptor; + + /** + * Used for non-jdbc native types (java.util.Date Enums etc). Converts from + * logical to jdbc types. + */ + @SuppressWarnings("rawtypes") + final ScalarType scalarType; + + boolean cascadeValidate; + + /** + * The length or precision for DB column. + */ + final int dbLength; + + /** + * The scale for DB column (decimal). + */ + final int dbScale; + + /** + * Deployment defined DB column definition. + */ + final String dbColumnDefn; + + /** + * DB Constraint (typically check constraint on enum) + */ + final String dbConstraintExpression; + + final DbEncryptFunction dbEncryptFunction; + + int deployOrder; + + final boolean jsonSerialize; + + final boolean jsonDeserialize; + + final boolean indexed; + + final String indexName; + + public BeanProperty(DeployBeanProperty deploy) { + this(null, null, deploy); + } + + public BeanProperty(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanProperty deploy) { + + this.descriptor = descriptor; + this.name = InternString.intern(deploy.getName()); + this.propertyIndex = deploy.getPropertyIndex(); + + this.indexed = deploy.isIndexed(); + this.indexName = deploy.getIndexName(); + + this.unidirectionalShadow = deploy.isUndirectionalShadow(); + this.discriminator = deploy.isDiscriminator(); + this.localEncrypted = deploy.isLocalEncrypted(); + this.dbEncrypted = deploy.isDbEncrypted(); + this.dbEncryptedType = deploy.getDbEncryptedType(); + this.dbEncryptFunction = deploy.getDbEncryptFunction(); + this.dbBind = deploy.getDbBind(); + this.dbRead = deploy.isDbRead(); + this.dbInsertable = deploy.isDbInsertable(); + this.dbUpdatable = deploy.isDbUpdateable(); + + this.secondaryTable = deploy.isSecondaryTable(); + if (secondaryTable) { + this.secondaryTableJoin = new TableJoin(deploy.getSecondaryTableJoin(), null); + this.secondaryTableJoinPrefix = deploy.getSecondaryTableJoinPrefix(); + } else { + this.secondaryTableJoin = null; + this.secondaryTableJoinPrefix = null; + } + this.fetchEager = deploy.isFetchEager(); + this.isTransient = deploy.isTransient(); + this.nullable = deploy.isNullable(); + this.unique = deploy.isUnique(); + this.naturalKey = deploy.isNaturalKey(); + this.dbLength = deploy.getDbLength(); + this.dbScale = deploy.getDbScale(); + this.dbColumnDefn = InternString.intern(deploy.getDbColumnDefn()); + this.dbConstraintExpression = InternString.intern(deploy.getDbConstraintExpression()); + + this.inherited = false;// deploy.isInherited(); + this.owningType = deploy.getOwningType(); + this.local = deploy.isLocal(); + + this.version = deploy.isVersionColumn(); + this.embedded = deploy.isEmbedded(); + this.id = deploy.isId(); + this.generatedProperty = deploy.getGeneratedProperty(); + this.readMethod = deploy.getReadMethod(); + this.writeMethod = deploy.getWriteMethod(); + this.getter = deploy.getGetter(); + this.setter = deploy.getSetter(); + + this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null); + this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin()); + this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect()); + this.formula = sqlFormulaSelect != null; + + this.extraAttributeMap = deploy.getExtraAttributeMap(); + this.defaultValue = deploy.getDefaultValue(); + this.dbType = deploy.getDbType(); + this.scalarType = deploy.getScalarType(); + this.lob = isLobType(dbType); + this.propertyType = deploy.getPropertyType(); + this.field = deploy.getField(); + + EntityType et = descriptor == null ? null : descriptor.getEntityType(); + this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null); + this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn); + + this.jsonSerialize = deploy.isExposeSerialize(); + this.jsonDeserialize = deploy.isExposeDeserialize(); + } + + private String tableAliasIntern(BeanDescriptor descriptor, String s, boolean dbEncrypted, String dbColumn) { + if (descriptor != null) { + s = StringHelper.replaceString(s, "${ta}.", "${}"); + s = StringHelper.replaceString(s, "${ta}", "${}"); + + if (dbEncrypted) { + s = dbEncryptFunction.getDecryptSql(s); + String namedParam = ":encryptkey_" + descriptor.getBaseTable() + "___" + dbColumn; + s = StringHelper.replaceString(s, "?", namedParam); + } + } + return InternString.intern(s); + } + + /** + * Create a Matching BeanProperty with some attributes overridden. + *

+ * Primarily for supporting Embedded beans with overridden dbColumn + * mappings. + *

+ */ + public BeanProperty(BeanProperty source, BeanPropertyOverride override) { + + this.descriptor = source.descriptor; + this.name = InternString.intern(source.getName()); + this.propertyIndex = source.propertyIndex; + + this.indexed = source.isIndexed(); + this.indexName = source.getIndexName(); + + this.dbColumn = InternString.intern(override.getDbColumn()); + this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin()); + this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect()); + this.formula = sqlFormulaSelect != null; + + this.fetchEager = source.fetchEager; + this.unidirectionalShadow = source.unidirectionalShadow; + this.discriminator = source.discriminator; + this.localEncrypted = source.isLocalEncrypted(); + this.isTransient = source.isTransient(); + this.secondaryTable = source.isSecondaryTable(); + this.secondaryTableJoin = source.secondaryTableJoin; + this.secondaryTableJoinPrefix = source.secondaryTableJoinPrefix; + + this.dbBind = source.getDbBind(); + this.dbEncrypted = source.isDbEncrypted(); + this.dbEncryptedType = source.getDbEncryptedType(); + this.dbEncryptFunction = source.dbEncryptFunction; + this.dbRead = source.isDbRead(); + this.dbInsertable = source.isDbInsertable(); + this.dbUpdatable = source.isDbUpdatable(); + this.nullable = source.isNullable(); + this.unique = source.isUnique(); + this.naturalKey = source.isNaturalKey(); + this.dbLength = source.getDbLength(); + this.dbScale = source.getDbScale(); + this.dbColumnDefn = InternString.intern(source.getDbColumnDefn()); + this.dbConstraintExpression = InternString.intern(source.getDbConstraintExpression()); + + this.inherited = source.isInherited(); + this.owningType = source.owningType; + this.local = owningType.equals(descriptor.getBeanType()); + + this.version = source.isVersion(); + this.embedded = source.isEmbedded(); + this.id = source.isId(); + this.generatedProperty = source.getGeneratedProperty(); + this.readMethod = source.getReadMethod(); + this.writeMethod = source.getWriteMethod(); + this.getter = source.getter; + this.setter = source.setter; + this.extraAttributeMap = source.extraAttributeMap; + this.defaultValue = source.getDefaultValue(); + this.dbType = source.getDbType(); + this.scalarType = source.scalarType; + this.lob = isLobType(dbType); + this.propertyType = source.getPropertyType(); + this.field = source.getField(); + + this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn); + this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn); + + this.jsonSerialize = source.jsonSerialize; + this.jsonDeserialize = source.jsonDeserialize; + } + + /** + * Initialise the property before returning to client code. Used to + * initialise variables that can't be done in construction due to recursive + * issues. + */ + public void initialise() { + // do nothing for normal BeanProperty + if (!isTransient && scalarType == null) { + String msg = "No ScalarType assigned to " + descriptor.getFullName() + "." + getName(); + throw new RuntimeException(msg); + } + } + + /** + * Return the order this property appears in the bean. + */ + public int getDeployOrder() { + return deployOrder; + } + + /** + * Set the order this property appears in the bean. + */ + public void setDeployOrder(int deployOrder) { + this.deployOrder = deployOrder; + } + + public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, + boolean propertyDeploy) { + throw new PersistenceException("Not valid on scalar bean property " + getFullBeanName()); + } + + /** + * Return the BeanDescriptor that owns this property. + */ + public BeanDescriptor getBeanDescriptor() { + return descriptor; + } + + /** + * Return true is this is a simple scalar property. + */ + public boolean isScalar() { + return true; + } + + /** + * Return true if this property is based on a formula. + */ + public boolean isFormula() { + return formula; + } + + /** + * Return true if this property maps to the inheritance discriminator column. + */ + public boolean isDiscriminator() { + return discriminator; + } + + /** + * Return true if the underlying type is mutable. + */ + public boolean isMutableScalarType() { + if (scalarType == null) { + return false; + } + return scalarType.isMutable(); + } + + public void copyProperty(EntityBean sourceBean, EntityBean destBean) { + Object value = getValue(sourceBean); + setValue(destBean, value); + } + + /** + * Return the encrypt key for the column matching this property. + */ + public EncryptKey getEncryptKey() { + return descriptor.getEncryptKey(this); + } + + public String getDecryptProperty() { + return dbEncryptFunction.getDecryptSql(this.getName()); + } + + public String getDecryptProperty(String propertyName) { + return dbEncryptFunction.getDecryptSql(propertyName); + } + + public String getDecryptSql() { + return dbEncryptFunction.getDecryptSql(this.getDbColumn()); + } + + public String getDecryptSql(String tableAlias) { + return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()); + } + + /** + * Add any extra joins required to support this property. Generally a no + * operation except for a OneToOne exported. + */ + public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { + if (formula && sqlFormulaJoin != null) { + ctx.appendFormulaJoin(sqlFormulaJoin, joinType); + + } else if (secondaryTableJoin != null) { + + String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); + secondaryTableJoin.addJoin(joinType, relativePrefix, ctx); + } + } + + /** + * Returns null unless this property is using a secondary table. In that + * case this returns the logical property prefix. + */ + public String getSecondaryTableJoinPrefix() { + return secondaryTableJoinPrefix; + } + + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + if (formula) { + ctx.appendFormulaSelect(sqlFormulaSelect); + + } else if (!isTransient) { + + if (secondaryTableJoin != null) { + String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); + ctx.pushTableAlias(relativePrefix); + } + + if (dbEncrypted) { + String decryptSql = getDecryptSql(ctx.peekTableAlias()); + ctx.appendRawColumn(decryptSql); + ctx.addEncryptedProp(this); + + } else { + ctx.appendColumn(dbColumn); + } + + if (secondaryTableJoin != null) { + ctx.popTableAlias(); + } + } + } + + public boolean isAssignableFrom(Class type) { + return owningType.isAssignableFrom(type); + } + + public Object readSetOwning(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { + + try { + Object value = scalarType.read(ctx.getDataReader()); + if (value == null || bean == null) { + // not setting the value... + } else { + if (owningType.equals(type)) { + setValue(bean, value); + } + } + return value; + } catch (Exception e) { + String msg = "Error readSet on " + descriptor + "." + name; + throw new PersistenceException(msg, e); + } + } + + public void loadIgnore(DbReadContext ctx) { + scalarType.loadIgnore(ctx.getDataReader()); + } + + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + sqlBeanLoad.load(this); + } + + public void buildSelectExpressionChain(String prefix, List selectChain) { + if (prefix == null) { + selectChain.add(name); + } else { + selectChain.add(prefix + "." + name); + } + } + + public Object read(DbReadContext ctx) throws SQLException { + return scalarType.read(ctx.getDataReader()); + } + + public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { + + try { + Object value = scalarType.read(ctx.getDataReader()); + if (bean == null || (type != null && !owningType.isAssignableFrom(type))) { + // not setting the value... + } else { + setValue(bean, value); + } + return value; + } catch (Exception e) { + String msg = "Error readSet on " + descriptor + "." + name; + throw new PersistenceException(msg, e); + } + } + + /** + * Convert the type to the bean type if required. + *

+ * Generally only used to ensure id properties are converted for + * Query.setId() use. + *

+ */ + public Object toBeanType(Object value) { + return scalarType.toBeanType(value); + } + + @SuppressWarnings("unchecked") + public void bind(DataBind b, Object value) throws SQLException { + scalarType.bind(b, value); + } + + public void writeData(DataOutput dataOutput, Object value) throws IOException { + scalarType.writeData(dataOutput, value); + } + + public Object readData(DataInput dataInput) throws IOException { + return scalarType.readData(dataInput); + } + + public boolean isCascadeValidate() { + return cascadeValidate; + } + + /** + * Checks to see if a bean is a reference (will be lazy loaded) or a + * BeanCollection that has not yet been populated. + *

+ * For base types this returns true. + *

+ */ + public boolean isValueLoaded(Object value) { + return true; + } + + public BeanProperty getBeanProperty() { + return this; + } + + /** + * Return the getter method. + */ + public Method getReadMethod() { + return readMethod; + } + + /** + * Return the setter method. + */ + public Method getWriteMethod() { + return writeMethod; + } + + /** + * Return true if this object is part of an inheritance hierarchy. + */ + public boolean isInherited() { + return inherited; + } + + /** + * Return true is this type is not from a super type. + */ + public boolean isLocal() { + return local; + } + + /** + * Set the value of the property without interception or + * PropertyChangeSupport. + */ + public void setValue(EntityBean bean, Object value) { + try { + setter.set(bean, value); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "set " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType + + "] threw error"; + throw new RuntimeException(msg, ex); + } + } + + /** + * Set the value of the property. + */ + public void setValueIntercept(EntityBean bean, Object value) { + try { + setter.setIntercept(bean, value); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "setIntercept " + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType + + "] threw error"; + throw new RuntimeException(msg, ex); + } + } + + private static Object[] NO_ARGS = new Object[0]; + + public Object getCacheDataValue(EntityBean bean) { + return getValue(bean); + } + + public void setCacheDataValue(EntityBean bean, Object cacheData) { + setValue(bean, cacheData); + } + + /** + * Return the value of the property method. + */ + public Object getValue(EntityBean bean) { + try { + return getter.get(bean); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + /** + * Explicitly use reflection to get value. + */ + public Object getValueViaReflection(Object bean) { + try { + return readMethod.invoke(bean, NO_ARGS); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + public Object getValueIntercept(EntityBean bean) { + try { + return getter.getIntercept(bean); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "getIntercept " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + public Object elConvertType(Object value) { + if (value == null) { + return null; + } + return convertToLogicalType(value); + } + + public void elSetValue(EntityBean bean, Object value, boolean populate) { + if (bean != null) { + // Not using setValueIntercept at this stage + setValue(bean, value); + } + } + + public Object elGetValue(EntityBean bean) { + if (bean == null) { + return null; + } + return getValueIntercept(bean); + } + + public Object elGetReference(EntityBean bean) { + throw new RuntimeException("Not expected to call this"); + } + + /** + * Return the name of the property. + */ + public String getName() { + return name; + } + + /** + * Return the position of this property in the enhanced bean. + */ + public int getPropertyIndex() { + return propertyIndex; + } + + public String getElName() { + return name; + } + + /** + * This is a full ElGetValue. + */ + public boolean isDeployOnly() { + return false; + } + + + @Override + public boolean containsFormulaWithJoin() { + return formula && sqlFormulaJoin != null; + } + + public boolean containsManySince(String sinceProperty) { + return containsMany(); + } + + public boolean containsMany() { + return false; + } + + public Object[] getAssocOneIdValues(EntityBean bean) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocOneIdExpr(String prefix, String operator) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocIdInExpr(String prefix) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocIdInValueExpr(int size) { + // Returns null as not an AssocOne. + return null; + } + + public boolean isAssocId() { + // Returns false - override in BeanPropertyAssocOne. + return false; + } + + public boolean isAssocProperty() { + // Returns false - override in BeanPropertyAssocOne. + return false; + } + + public String getElPlaceholder(boolean encrypted) { + return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; + } + + public String getElPrefix() { + return secondaryTableJoinPrefix; + } + + /** + * Return the full name of this property. + */ + public String getFullBeanName() { + return descriptor.getFullName() + "." + name; + } + + /** + * Return true if the mutable value is considered dirty. + * This is only used for 'mutable' scalar types like hstore etc. + */ + public boolean isDirtyValue(Object value) { + return scalarType.isDirty(value); + } + + /** + * Return the scalarType. + */ + public ScalarType getScalarType() { + return scalarType; + } + + public StringFormatter getStringFormatter() { + return scalarType; + } + + public StringParser getStringParser() { + return scalarType; + } + + public boolean isDateTimeCapable() { + return scalarType != null && scalarType.isDateTimeCapable(); + } + + public int getJdbcType() { + return scalarType == null ? 0 : scalarType.getJdbcType(); + } + + public Object parseDateTime(long systemTimeMillis) { + return scalarType.parseDateTime(systemTimeMillis); + } + + /** + * Return the DB max length (varchar) or precision (decimal). + */ + public int getDbLength() { + return dbLength; + } + + /** + * Return the DB scale for numeric columns. + */ + public int getDbScale() { + return dbScale; + } + + /** + * Return a specific column DDL definition if specified (otherwise null). + */ + public String getDbColumnDefn() { + return dbColumnDefn; + } + + /** + * Return the DB constraint expression (can be null). + *

+ * For an Enum returns IN expression for the set of Enum values. + *

+ */ + public String getDbConstraintExpression() { + return dbConstraintExpression; + } + + /** + * Return the DB column type definition. + */ + public String renderDbType(DbType dbType) { + if (dbColumnDefn != null) { + return dbColumnDefn; + } + return dbType.renderType(dbLength, dbScale); + } + + /** + * Return the bean Field associated with this property. + */ + public Field getField() { + return field; + } + + /** + * Return the GeneratedValue. Used to generate update timestamp etc. + */ + public GeneratedProperty getGeneratedProperty() { + return generatedProperty; + } + + /** + * Return true if this is the natural key property. + */ + public boolean isNaturalKey() { + return naturalKey; + } + + /** + * Return true if this property is mandatory. + */ + public boolean isNullable() { + return nullable; + } + + /** + * Return true if DDL Not NULL constraint should be defined for this column + * based on it being a version column or having a generated property. + */ + public boolean isDDLNotNull() { + return isVersion() || (generatedProperty != null && generatedProperty.isDDLNotNullable()); + } + + /** + * Return true if the DB column should be unique. + */ + public boolean isUnique() { + return unique; + } + + /** + * Return true if the property is transient. + */ + public boolean isTransient() { + return isTransient; + } + + /** + * Return true if this property is loadable from a resultSet. + */ + public boolean isLoadProperty() { + return !isTransient || formula; + } + + /** + * Return true if this is a version column used for concurrency checking. + */ + public boolean isVersion() { + return version; + } + + public String getDeployProperty() { + return dbColumn; + } + + /** + * The database column name this is mapped to. + */ + public String getDbColumn() { + return dbColumn; + } + + /** + * Return the database jdbc data type this is mapped to. + */ + public int getDbType() { + return dbType; + } + + /** + * Perform DB to Logical type conversion (if necessary). + */ + public Object convertToLogicalType(Object value) { + if (scalarType != null) { + return scalarType.toBeanType(value); + } + return value; + } + + /** + * Return true if by default this property is set to fetch eager. + * Lob's usually default to fetch lazy. + */ + public boolean isFetchEager() { + return fetchEager; + } + + /** + * Return true if this is mapped to a Clob Blob LongVarchar or + * LongVarbinary. + */ + public boolean isLob() { + return lob; + } + + private boolean isLobType(int type) { + switch (type) { + case Types.CLOB: + return true; + case Types.BLOB: + return true; + case Types.LONGVARBINARY: + return true; + case Types.LONGVARCHAR: + return true; + + default: + return false; + } + } + + /** + * Return the DB bind parameter. Typically is "?" but different for + * encrypted bind. + */ + public String getDbBind() { + return dbBind; + } + + /** + * Returns true if DB encrypted. + */ + public boolean isLocalEncrypted() { + return localEncrypted; + } + + /** + * Return true if this property is stored encrypted. + */ + public boolean isDbEncrypted() { + return dbEncrypted; + } + + public int getDbEncryptedType() { + return dbEncryptedType; + } + + /** + * Return true if this property should be included in an Insert. + */ + public boolean isDbInsertable() { + return dbInsertable; + } + + /** + * Return true if this property should be included in an Update. + */ + public boolean isDbUpdatable() { + return dbUpdatable; + } + + /** + * Return true if this property is included in database queries. + */ + public boolean isDbRead() { + return dbRead; + } + + /** + * Return true if this property is based on a secondary table (not the base + * table). + */ + public boolean isSecondaryTable() { + return secondaryTable; + } + + /** + * Return the property type. + */ + public Class getPropertyType() { + return propertyType; + } + + /** + * Return true if this is included in the unique id. + */ + public boolean isId() { + return id; + } + + /** + * Return true if this is an Embedded property. In this case it shares the + * table and primary key of its owner object. + */ + public boolean isEmbedded() { + return embedded; + } + + /** + * Return an extra attribute set on this property. + */ + public String getExtraAttribute(String key) { + return extraAttributeMap.get(key); + } + + /** + * Return the default value. + */ + public Object getDefaultValue() { + return defaultValue; + } + + public String toString() { + return name; + } + <<<<<<< HEAD - @SuppressWarnings("unchecked") - public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { - if (!jsonSerialize) { - return; - } - Object value = getValueIntercept(bean); - if (value == null) { - ctx.appendNull(name); - } else { - ctx.appendNameValue(name, scalarType, value); - } + @SuppressWarnings("unchecked") + public void jsonWrite(WriteJsonContext ctx, EntityBean bean) { + if (!jsonSerialize) { + return; + } + Object value = getValueIntercept(bean); + if (value == null) { + ctx.appendNull(name); + } else { + ctx.appendNameValue(name, scalarType, value); + } ======= - public void jsonWrite(WriteJson writeJson, EntityBean bean) { - if (!jsonSerialize) { - return; - } - Object value = getValueIntercept(bean); - if (value == null) { - writeJson.gen().writeNull(name); - } else { - scalarType.jsonWrite(writeJson.gen(), name, value); + public void jsonWrite(WriteJson writeJson, EntityBean bean) { + if (!jsonSerialize) { + return; + } + Object value = getValueIntercept(bean); + if (value == null) { + writeJson.gen().writeNull(name); + } else { + scalarType.jsonWrite(writeJson.gen(), name, value); >>>>>>> json-refactor - } - } - + } + } + <<<<<<< HEAD - public void jsonRead(ReadJsonContext ctx, EntityBean bean) { - if (!jsonDeserialize) { - return; - } - String jsonValue; - try { - jsonValue = ctx.readScalarValue(); - } catch (TextException e) { - throw new TextException("Error reading property " + getFullBeanName(), e); - } - Object objValue; - if (jsonValue == null) { - objValue = null; - } else { - objValue = scalarType.jsonFromString(jsonValue, ctx.getValueAdapter()); - } - setValue(bean, objValue); - } - - public boolean isIndexed() { - return indexed; - } - - public String getIndexName() { - return indexName; - } + public void jsonRead(ReadJsonContext ctx, EntityBean bean) { + if (!jsonDeserialize) { + return; + } + String jsonValue; + try { + jsonValue = ctx.readScalarValue(); + } catch (TextException e) { + throw new TextException("Error reading property " + getFullBeanName(), e); + } + Object objValue; + if (jsonValue == null) { + objValue = null; + } else { + objValue = scalarType.jsonFromString(jsonValue, ctx.getValueAdapter()); + } + setValue(bean, objValue); + } + + public boolean isIndexed() { + return indexed; + } + + public String getIndexName() { + return indexName; + } ======= - public void jsonRead(JsonParser ctx, EntityBean bean) { - if (!jsonDeserialize) { - return; - } - if (!ctx.hasNext()) { - throw new RuntimeException(ctx.getLocation().toString()); - } - Event event = ctx.next(); - if (Event.VALUE_NULL == event) { - setValue(bean, null); - } else { - Object objValue = scalarType.jsonRead(ctx, event); - setValue(bean, objValue); - } - - } + public void jsonRead(JsonParser ctx, EntityBean bean) { + if (!jsonDeserialize) { + return; + } + if (!ctx.hasNext()) { + throw new RuntimeException(ctx.getLocation().toString()); + } + Event event = ctx.next(); + if (Event.VALUE_NULL == event) { + setValue(bean, null); + } else { + Object objValue = scalarType.jsonRead(ctx, event); + setValue(bean, objValue); + } + + } >>>>>>> json-refactor -} +} 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 a8dbae9e6..76382aa43 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssoc.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssoc.java @@ -1,372 +1,372 @@ -package com.avaje.ebeaninternal.server.deploy; - -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.ImportedIdSimple; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc; -import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.query.SqlJoinType; - -/** - * Abstract base for properties mapped to an associated bean, list, set or map. - */ -public abstract class BeanPropertyAssoc extends BeanProperty { - - private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssoc.class); - - /** - * The descriptor of the target. This MUST be initialised after construction - * so as to avoid a dependency loop between BeanDescriptors. - */ - BeanDescriptor targetDescriptor; - - IdBinder targetIdBinder; - - InheritInfo targetInheritInfo; - - String targetIdProperty; - - /** - * Persist settings. - */ - final BeanCascadeInfo cascadeInfo; - - /** - * Join between the beans. - */ - final TableJoin tableJoin; - - /** - * The type of the joined bean. - */ - final Class targetType; - - /** - * The join table information. - */ - final BeanTable beanTable; - - final String mappedBy; - - /** - * Whether the associated join type should be an outer join. - */ - final boolean isOuterJoin; - - String extraWhere; - - boolean saveRecurseSkippable; - - boolean deleteRecurseSkippable; - - /** - * Construct the property. - */ - public BeanPropertyAssoc(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertyAssoc deploy) { - super(owner, descriptor, deploy); - this.extraWhere = InternString.intern(deploy.getExtraWhere()); - this.isOuterJoin = deploy.isOuterJoin(); - this.beanTable = deploy.getBeanTable(); - this.mappedBy = InternString.intern(deploy.getMappedBy()); - - this.tableJoin = new TableJoin(deploy.getTableJoin(), null); - - this.targetType = deploy.getTargetType(); - this.cascadeInfo = deploy.getCascadeInfo(); - } - - /** - * Initialise post construction. - */ - @Override - public void initialise() { - // this *MUST* execute after the BeanDescriptor is - // put into the map to stop infinite recursion - if (!isTransient){ - targetDescriptor = descriptor.getBeanDescriptor(targetType); - targetIdBinder = targetDescriptor.getIdBinder(); - targetInheritInfo = targetDescriptor.getInheritInfo(); - - saveRecurseSkippable = targetDescriptor.isSaveRecurseSkippable(); - deleteRecurseSkippable = targetDescriptor.isDeleteRecurseSkippable(); - - cascadeValidate = cascadeInfo.isValidate(); - - if (!targetIdBinder.isComplexId()){ - targetIdProperty = targetIdBinder.getIdProperty(); - } - } - } - - /** - * Create a ElPropertyValue for a *ToOne or *ToMany. - */ - protected ElPropertyValue createElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { - - // associated or embedded bean - BeanDescriptor embDesc = getTargetDescriptor(); - - if (chain == null) { - chain = new ElPropertyChainBuilder(isEmbedded(), propName); - } - chain.add(this); - if (containsMany()) { - chain.setContainsMany(true); - } - return embDesc.buildElGetValue(remainder, chain, propertyDeploy); - } - - /** - * Add table join with table alias based on prefix. - */ - public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) { - return tableJoin.addJoin(joinType, prefix, ctx); - } - - /** - * Add table join with explicit table alias. - */ - public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) { - return tableJoin.addJoin(joinType, a1, a2, ctx); - } - - /** - * Return false. - */ - public boolean isScalar() { - return false; - } - - /** - * Return the mappedBy property. - * This will be null on the owning side. - */ - public String getMappedBy() { - return mappedBy; - } - - /** - * Return the Id property of the target entity type. - *

- * This will return null for multiple Id properties. - *

- */ - public String getTargetIdProperty() { - return targetIdProperty; - } - - /** - * Return the BeanDescriptor of the target. - */ - public BeanDescriptor getTargetDescriptor() { - return targetDescriptor; - } - - public boolean isSaveRecurseSkippable(Object bean) { - if (!saveRecurseSkippable){ - // we have to saveRecurse even if the bean is not dirty - // as this bean has cascade save on some of its properties - return false; - } - if (bean instanceof EntityBean){ - return !((EntityBean)bean)._ebean_getIntercept().isNewOrDirty(); - } else { - // we don't know so we say no - return false; - } - } - - /** - * Return true if save can be skipped for unmodified bean(s) of this - * property. - *

- * That is, if a bean of this property is unmodified we don't need to - * saveRecurse because none of its associated beans have cascade save set to - * true. - *

- */ - public boolean isSaveRecurseSkippable() { - return saveRecurseSkippable; - } - - /** - * Similar to isSaveRecurseSkippable but in terms of delete. - */ - public boolean isDeleteRecurseSkippable() { - return deleteRecurseSkippable; - } - - /** - * Return true if the unique id properties are all not null for this bean. - */ - public boolean hasId(EntityBean bean) { - - BeanDescriptor targetDesc = getTargetDescriptor(); - BeanProperty idProp = targetDesc.getIdProperty(); - if (idProp != null) { - Object value = idProp.getValue(bean); - if (value == null) { - return false; - } - } - // all the unique properties are non-null - return true; - } - - /** - * Return the type of the target. - *

- * This is the class of the associated bean, or beans contained in a list, - * set or map. - *

- */ - public Class getTargetType() { - return targetType; - } - - /** - * Return an extra clause to add to the query for loading or joining - * to this bean type. - */ - public String getExtraWhere() { - return extraWhere; - } - - /** - * Return if this association should use an Outer join. - */ - public boolean isOuterJoin() { - return isOuterJoin; - } - - /** - * Return true if this association is updateable. - */ - public boolean isUpdateable() { - if (tableJoin.columns().length > 0) { - return tableJoin.columns()[0].isUpdateable(); - } - - return true; - } - - /** - * Return true if this association is insertable. - */ - public boolean isInsertable() { - if (tableJoin.columns().length > 0) { - return tableJoin.columns()[0].isInsertable(); - } - - return true; - } - - /** - * return the join to use for the bean. - */ - public TableJoin getTableJoin() { - return tableJoin; - } - - /** - * Return the BeanTable for this association. - *

- * This has the table name which is used to determine the relationship for - * this association. - *

- */ - public BeanTable getBeanTable() { - return beanTable; - } - - /** - * Get the persist info. - */ - public BeanCascadeInfo getCascadeInfo() { - return cascadeInfo; - } - - /** - * Build the list of imported property. Matches BeanProperty from the target - * descriptor back to local database columns in the TableJoin. - */ - protected ImportedId createImportedId(BeanPropertyAssoc owner, BeanDescriptor target, TableJoin join) { - - BeanProperty idProp = target.getIdProperty(); - BeanProperty[] others = target.propertiesBaseScalar(); - - if (descriptor.isSqlSelectBased()){ - String dbColumn = owner.getDbColumn(); - return new ImportedIdSimple(owner, dbColumn, idProp, 0); - } - - TableJoinColumn[] cols = join.columns(); - - 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 { - BeanProperty[] idProps = {idProp}; - return createImportedScalar(owner, cols[0], idProps, others); - } - } else { - // embedded id - BeanPropertyAssocOne embProp = (BeanPropertyAssocOne)idProp; - BeanProperty[] embBaseProps = embProp.getTargetDescriptor().propertiesBaseScalar(); - ImportedIdSimple[] scalars = createImportedList(owner, cols, embBaseProps, others); - - return new ImportedIdEmbedded(owner, embProp, scalars); - } - } - - private ImportedIdSimple[] createImportedList(BeanPropertyAssoc owner, TableJoinColumn[] cols, BeanProperty[] props, BeanProperty[] others) { - - ArrayList list = new ArrayList(); - - for (int i = 0; i < cols.length; i++) { - list.add(createImportedScalar(owner, cols[i], props, others)); - } - - return ImportedIdSimple.sort(list); - } - - private ImportedIdSimple createImportedScalar(BeanPropertyAssoc owner, TableJoinColumn col, BeanProperty[] props, BeanProperty[] others) { - - String matchColumn = col.getForeignDbColumn(); - String localColumn = col.getLocalDbColumn(); - - for (int j = 0; j < props.length; j++) { - if (props[j].getDbColumn().equalsIgnoreCase(matchColumn)) { - return new ImportedIdSimple(owner, localColumn, props[j], j); - } - } - - for (int j = 0; j < others.length; j++) { - if (others[j].getDbColumn().equalsIgnoreCase(matchColumn)) { - return new ImportedIdSimple(owner, localColumn, others[j], j+props.length); - } - } - - String msg = "Error with the Join on ["+getFullBeanName() - +"]. Could not find the local match for ["+matchColumn+"] "//in table["+searchTable+"]?" - +" Perhaps an error in a @JoinColumn"; - throw new PersistenceException(msg); - } -} +package com.avaje.ebeaninternal.server.deploy; + +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.ImportedIdSimple; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc; +import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.query.SqlJoinType; + +/** + * Abstract base for properties mapped to an associated bean, list, set or map. + */ +public abstract class BeanPropertyAssoc extends BeanProperty { + + private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssoc.class); + + /** + * The descriptor of the target. This MUST be initialised after construction + * so as to avoid a dependency loop between BeanDescriptors. + */ + BeanDescriptor targetDescriptor; + + IdBinder targetIdBinder; + + InheritInfo targetInheritInfo; + + String targetIdProperty; + + /** + * Persist settings. + */ + final BeanCascadeInfo cascadeInfo; + + /** + * Join between the beans. + */ + final TableJoin tableJoin; + + /** + * The type of the joined bean. + */ + final Class targetType; + + /** + * The join table information. + */ + final BeanTable beanTable; + + final String mappedBy; + + /** + * Whether the associated join type should be an outer join. + */ + final boolean isOuterJoin; + + String extraWhere; + + boolean saveRecurseSkippable; + + boolean deleteRecurseSkippable; + + /** + * Construct the property. + */ + public BeanPropertyAssoc(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertyAssoc deploy) { + super(owner, descriptor, deploy); + this.extraWhere = InternString.intern(deploy.getExtraWhere()); + this.isOuterJoin = deploy.isOuterJoin(); + this.beanTable = deploy.getBeanTable(); + this.mappedBy = InternString.intern(deploy.getMappedBy()); + + this.tableJoin = new TableJoin(deploy.getTableJoin(), null); + + this.targetType = deploy.getTargetType(); + this.cascadeInfo = deploy.getCascadeInfo(); + } + + /** + * Initialise post construction. + */ + @Override + public void initialise() { + // this *MUST* execute after the BeanDescriptor is + // put into the map to stop infinite recursion + if (!isTransient){ + targetDescriptor = descriptor.getBeanDescriptor(targetType); + targetIdBinder = targetDescriptor.getIdBinder(); + targetInheritInfo = targetDescriptor.getInheritInfo(); + + saveRecurseSkippable = targetDescriptor.isSaveRecurseSkippable(); + deleteRecurseSkippable = targetDescriptor.isDeleteRecurseSkippable(); + + cascadeValidate = cascadeInfo.isValidate(); + + if (!targetIdBinder.isComplexId()){ + targetIdProperty = targetIdBinder.getIdProperty(); + } + } + } + + /** + * Create a ElPropertyValue for a *ToOne or *ToMany. + */ + protected ElPropertyValue createElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { + + // associated or embedded bean + BeanDescriptor embDesc = getTargetDescriptor(); + + if (chain == null) { + chain = new ElPropertyChainBuilder(isEmbedded(), propName); + } + chain.add(this); + if (containsMany()) { + chain.setContainsMany(true); + } + return embDesc.buildElGetValue(remainder, chain, propertyDeploy); + } + + /** + * Add table join with table alias based on prefix. + */ + public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) { + return tableJoin.addJoin(joinType, prefix, ctx); + } + + /** + * Add table join with explicit table alias. + */ + public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) { + return tableJoin.addJoin(joinType, a1, a2, ctx); + } + + /** + * Return false. + */ + public boolean isScalar() { + return false; + } + + /** + * Return the mappedBy property. + * This will be null on the owning side. + */ + public String getMappedBy() { + return mappedBy; + } + + /** + * Return the Id property of the target entity type. + *

+ * This will return null for multiple Id properties. + *

+ */ + public String getTargetIdProperty() { + return targetIdProperty; + } + + /** + * Return the BeanDescriptor of the target. + */ + public BeanDescriptor getTargetDescriptor() { + return targetDescriptor; + } + + public boolean isSaveRecurseSkippable(Object bean) { + if (!saveRecurseSkippable){ + // we have to saveRecurse even if the bean is not dirty + // as this bean has cascade save on some of its properties + return false; + } + if (bean instanceof EntityBean){ + return !((EntityBean)bean)._ebean_getIntercept().isNewOrDirty(); + } else { + // we don't know so we say no + return false; + } + } + + /** + * Return true if save can be skipped for unmodified bean(s) of this + * property. + *

+ * That is, if a bean of this property is unmodified we don't need to + * saveRecurse because none of its associated beans have cascade save set to + * true. + *

+ */ + public boolean isSaveRecurseSkippable() { + return saveRecurseSkippable; + } + + /** + * Similar to isSaveRecurseSkippable but in terms of delete. + */ + public boolean isDeleteRecurseSkippable() { + return deleteRecurseSkippable; + } + + /** + * Return true if the unique id properties are all not null for this bean. + */ + public boolean hasId(EntityBean bean) { + + BeanDescriptor targetDesc = getTargetDescriptor(); + BeanProperty idProp = targetDesc.getIdProperty(); + if (idProp != null) { + Object value = idProp.getValue(bean); + if (value == null) { + return false; + } + } + // all the unique properties are non-null + return true; + } + + /** + * Return the type of the target. + *

+ * This is the class of the associated bean, or beans contained in a list, + * set or map. + *

+ */ + public Class getTargetType() { + return targetType; + } + + /** + * Return an extra clause to add to the query for loading or joining + * to this bean type. + */ + public String getExtraWhere() { + return extraWhere; + } + + /** + * Return if this association should use an Outer join. + */ + public boolean isOuterJoin() { + return isOuterJoin; + } + + /** + * Return true if this association is updateable. + */ + public boolean isUpdateable() { + if (tableJoin.columns().length > 0) { + return tableJoin.columns()[0].isUpdateable(); + } + + return true; + } + + /** + * Return true if this association is insertable. + */ + public boolean isInsertable() { + if (tableJoin.columns().length > 0) { + return tableJoin.columns()[0].isInsertable(); + } + + return true; + } + + /** + * return the join to use for the bean. + */ + public TableJoin getTableJoin() { + return tableJoin; + } + + /** + * Return the BeanTable for this association. + *

+ * This has the table name which is used to determine the relationship for + * this association. + *

+ */ + public BeanTable getBeanTable() { + return beanTable; + } + + /** + * Get the persist info. + */ + public BeanCascadeInfo getCascadeInfo() { + return cascadeInfo; + } + + /** + * Build the list of imported property. Matches BeanProperty from the target + * descriptor back to local database columns in the TableJoin. + */ + protected ImportedId createImportedId(BeanPropertyAssoc owner, BeanDescriptor target, TableJoin join) { + + BeanProperty idProp = target.getIdProperty(); + BeanProperty[] others = target.propertiesBaseScalar(); + + if (descriptor.isSqlSelectBased()){ + String dbColumn = owner.getDbColumn(); + return new ImportedIdSimple(owner, dbColumn, idProp, 0); + } + + TableJoinColumn[] cols = join.columns(); + + 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 { + BeanProperty[] idProps = {idProp}; + return createImportedScalar(owner, cols[0], idProps, others); + } + } else { + // embedded id + BeanPropertyAssocOne embProp = (BeanPropertyAssocOne)idProp; + BeanProperty[] embBaseProps = embProp.getTargetDescriptor().propertiesBaseScalar(); + ImportedIdSimple[] scalars = createImportedList(owner, cols, embBaseProps, others); + + return new ImportedIdEmbedded(owner, embProp, scalars); + } + } + + private ImportedIdSimple[] createImportedList(BeanPropertyAssoc owner, TableJoinColumn[] cols, BeanProperty[] props, BeanProperty[] others) { + + ArrayList list = new ArrayList(); + + for (int i = 0; i < cols.length; i++) { + list.add(createImportedScalar(owner, cols[i], props, others)); + } + + return ImportedIdSimple.sort(list); + } + + private ImportedIdSimple createImportedScalar(BeanPropertyAssoc owner, TableJoinColumn col, BeanProperty[] props, BeanProperty[] others) { + + String matchColumn = col.getForeignDbColumn(); + String localColumn = col.getLocalDbColumn(); + + for (int j = 0; j < props.length; j++) { + if (props[j].getDbColumn().equalsIgnoreCase(matchColumn)) { + return new ImportedIdSimple(owner, localColumn, props[j], j); + } + } + + for (int j = 0; j < others.length; j++) { + if (others[j].getDbColumn().equalsIgnoreCase(matchColumn)) { + return new ImportedIdSimple(owner, localColumn, others[j], j+props.length); + } + } + + String msg = "Error with the Join on ["+getFullBeanName() + +"]. Could not find the local match for ["+matchColumn+"] "//in table["+searchTable+"]?" + +" Perhaps an error in a @JoinColumn"; + throw new PersistenceException(msg); + } +} 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 e557e4422..878050f53 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -1,884 +1,884 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebean.*; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; -import com.avaje.ebean.bean.BeanCollectionAdd; -import com.avaje.ebean.bean.BeanCollectionLoader; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; -import com.avaje.ebeaninternal.server.deploy.id.ImportedId; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.text.json.WriteJson; -import com.fasterxml.jackson.core.JsonParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.persistence.PersistenceException; -import java.io.IOException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * Property mapped to a List Set or Map. - */ -public class BeanPropertyAssocMany extends BeanPropertyAssoc { - - private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssocMany.class); - - private final BeanPropertyAssocManyJsonHelp jsonHelp; - - /** - * Join for manyToMany intersection table. - */ - private final TableJoin intersectionJoin; - - /** - * For ManyToMany this is the Inverse join used to build reference queries. - */ - private final TableJoin inverseJoin; - - /** - * Flag to indicate that this is a unidirectional relationship. - */ - private final boolean unidirectional; - - /** - * Flag to indicate manyToMany relationship. - */ - private final boolean manyToMany; - - /** - * Order by used when fetch joining the associated many. - */ - private final String fetchOrderBy; - - /** - * Order by used when lazy loading the associated many. - */ - private String lazyFetchOrderBy; - - private final String mapKey; - - /** - * The type of the many, set, list or map. - */ - private final ManyType manyType; - - private final ModifyListenMode modifyListenMode; - - private BeanProperty mapKeyProperty; - - /** - * Derived list of exported property and matching foreignKey - */ - private ExportedProperty[] exportedProperties; - - private String exportedPropertyBindProto = "?"; - - /** - * Property on the 'child' bean that links back to the 'master'. - */ - protected BeanPropertyAssocOne childMasterProperty; - - private boolean embeddedExportedProperties; - - private BeanCollectionHelp help; - - private ImportedId importedId; - - private String deleteByParentIdSql; - - private String deleteByParentIdInSql; - - /** - * Create this property. - */ - public BeanPropertyAssocMany(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertyAssocMany deploy) { - super(owner, descriptor, deploy); - this.unidirectional = deploy.isUnidirectional(); - this.manyToMany = deploy.isManyToMany(); - this.manyType = deploy.getManyType(); - this.mapKey = deploy.getMapKey(); - this.fetchOrderBy = deploy.getFetchOrderBy(); - this.intersectionJoin = deploy.createIntersectionTableJoin(); - this.inverseJoin = deploy.createInverseTableJoin(); - this.modifyListenMode = deploy.getModifyListenMode(); - this.jsonHelp = new BeanPropertyAssocManyJsonHelp(this); - } - - public void initialise() { - super.initialise(); - - if (!isTransient) { - this.help = BeanCollectionHelpFactory.create(this); - - if (manyToMany) { - // only manyToMany's have imported properties - importedId = createImportedId(this, targetDescriptor, tableJoin); - - } else { - // find the property in the many that matches - // back to the master (Order in the OrderDetail bean) - childMasterProperty = initChildMasterProperty(); - if (childMasterProperty != null) { - childMasterProperty.setRelationshipProperty(this); - } - } - - if (mapKey != null) { - mapKeyProperty = initMapKeyProperty(); - } - - exportedProperties = createExported(); - if (exportedProperties.length > 0) { - embeddedExportedProperties = exportedProperties[0].isEmbedded(); - exportedPropertyBindProto = deriveExportedPropertyBindProto(); - - if (fetchOrderBy != null) { - // derive lazyFetchOrderBy - StringBuilder sb = new StringBuilder(50); - for (int i = 0; i < exportedProperties.length; i++) { - if (i > 0) { - sb.append(", "); - } - // these fk columns are either on the intersection (int_) or base table (t0) - String fkTableAlias = isManyToMany() ? "int_" : "t0"; - sb.append(fkTableAlias).append(".").append(exportedProperties[i].getForeignDbColumn()); - } - sb.append(", ").append(fetchOrderBy); - lazyFetchOrderBy = sb.toString().trim(); - } - } - - String delStmt; - if (manyToMany) { - delStmt = "delete from " + inverseJoin.getTable() + " where "; - } else { - delStmt = "delete from " + targetDescriptor.getBaseTable() + " where "; - } - deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false, ""); - deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true, ""); - } - } - - /** - * Add the bean to the appropriate collection on the parent bean. - */ - public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean) { - BeanCollection bc = (BeanCollection) super.getValue(parentBean); - if (bc == null) { - bc = help.createEmpty(parentBean); - setValue(parentBean, bc); - } - help.add(bc, detailBean); - } - - public boolean isEmptyBeanCollection(EntityBean bean) { - Object val = getValue(bean); - return val == null || (val instanceof BeanCollection) && ((BeanCollection) val).isEmptyAndUntouched(); - } - - /** - * Reset the many properties to be empty and ready for reloading. - *

- * Used in bean refresh. - */ - public void resetMany(EntityBean bean) { - Object value = getValue(bean); - if (value == null) { - // not expecting this - set an empty reference - createReference(bean); - } else { - // reset the collection back to empty - ((BeanCollection)value).reset(bean, name); - } - } - - @Override - public Object getValue(EntityBean bean) { - return super.getValue(bean); - } - - @Override - public Object getValueIntercept(EntityBean bean) { - return super.getValueIntercept(bean); - } - - @Override - public void setValue(EntityBean bean, Object value) { - super.setValue(bean, value); - } - - @Override - public void setValueIntercept(EntityBean bean, Object value) { - super.setValueIntercept(bean, value); - } - - public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { - return createElPropertyValue(propName, remainder, chain, propertyDeploy); - } - - public void buildSelectExpressionChain(String prefix, List selectChain) { - // do not add to the selectChain at the top level of the Many bean - } - - public SqlUpdate deleteByParentId(Object parentId, List parentIdist) { - if (parentId != null) { - return deleteByParentId(parentId); - } else { - return deleteByParentIdList(parentIdist); - } - } - - private SqlUpdate deleteByParentId(Object parentId) { - DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql); - bindWhereParendId(sqlDelete, parentId); - return sqlDelete; - } - - /** - * Find the Id's of detail beans given a parent Id or list of parent Id's. - */ - public List findIdsByParentId(Object parentId, List parentIdist, Transaction t, ArrayList excludeDetailIds) { - if (parentId != null) { - return findIdsByParentId(parentId, t, excludeDetailIds); - } else { - return findIdsByParentIdList(parentIdist, t, excludeDetailIds); - } - } - - private List findIdsByParentId(Object parentId, Transaction t, ArrayList excludeDetailIds) { - - String rawWhere = deriveWhereParentIdSql(false, ""); - - EbeanServer server = getBeanDescriptor().getEbeanServer(); - Query q = server.find(getPropertyType()) - .where().raw(rawWhere).query(); - - bindWhereParendId(1, q, parentId); - - if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { - Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds); - q.where().not(idIn); - } - - return server.findIds(q, t); - } - - /** - * Add a where clause to the query for a given list of parent Id's. - */ - public void addWhereParentIdIn(SpiQuery query, List parentIds) { - - String tableAlias = manyToMany ? "int_." : "t0."; - if (manyToMany) { - query.setIncludeTableJoin(inverseJoin); - } - String rawWhere = deriveWhereParentIdSql(true, tableAlias); - String expr = descriptor.getParentIdInExpr(parentIds.size(), rawWhere); - - // Flatten the bind values if needed (embeddedId) - List bindValues = getBindParentIds(parentIds); - - query.where().raw(expr, bindValues.toArray()); - } - - private List findIdsByParentIdList(List parentIdist, Transaction t, ArrayList excludeDetailIds) { - - String rawWhere = deriveWhereParentIdSql(true, ""); - String inClause = buildInClauseBinding(parentIdist.size(), exportedPropertyBindProto); - - String expr = rawWhere + inClause; - - EbeanServer server = getBeanDescriptor().getEbeanServer(); - Query q = server.find(getPropertyType()).where().raw(expr).query(); - - int pos = 1; - for (int i = 0; i < parentIdist.size(); i++) { - pos = bindWhereParendId(pos, q, parentIdist.get(i)); - } - - if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { - Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds); - q.where().not(idIn); - } - - return server.findIds(q, t); - } - - private SqlUpdate deleteByParentIdList(List parentIdist) { - - StringBuilder sb = new StringBuilder(100); - sb.append(deleteByParentIdInSql); - - String inClause = buildInClauseBinding(parentIdist.size(), exportedPropertyBindProto); - sb.append(inClause); - - DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); - for (int i = 0; i < parentIdist.size(); i++) { - bindWhereParendId(delete, parentIdist.get(i)); - } - - return delete; - } - - private String deriveExportedPropertyBindProto() { - if (exportedProperties.length == 1) { - return "?"; - } - StringBuilder sb = new StringBuilder(); - sb.append("("); - for (int i = 0; i < exportedProperties.length; i++) { - if (i > 0) { - sb.append(","); - } - sb.append("?"); - } - sb.append(")"); - return sb.toString(); - } - - private String buildInClauseBinding(int size, String bindProto) { - - StringBuilder sb = new StringBuilder(10 + (size * (bindProto.length() + 1))); - sb.append(" in"); - - sb.append(" ("); - for (int i = 0; i < size; i++) { - if (i > 0) { - sb.append(","); - } - sb.append(bindProto); - } - sb.append(") "); - return sb.toString(); - } - - /** - * Set the lazy load server to help create reference collections (that lazy - * load on demand). - */ - public void setLoader(BeanCollectionLoader loader) { - if (help != null) { - help.setLoader(loader); - } - } - - /** - * Return the mode for listening to modifications to collections for this - * association. - */ - public ModifyListenMode getModifyListenMode() { - return modifyListenMode; - } - - @Override - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - } - - @Override - public void loadIgnore(DbReadContext ctx) { - // nothing to ignore for Many - } - - @Override - public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { - sqlBeanLoad.loadAssocMany(this); - } - - @Override - public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { - return null; - } - - @Override - public Object read(DbReadContext ctx) throws SQLException { - return null; - } - - public void add(BeanCollection collection, EntityBean bean) { - help.add(collection, bean); - } - - /** - * Refresh the appropriate list set or map. - */ - public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { - help.refresh(server, query, t, parentBean); - } - - /** - * Apply the refreshed BeanCollection to the property of the parentBean. - */ - public void refresh(BeanCollection bc, EntityBean parentBean) { - help.refresh(bc, parentBean); - } - - /** - * 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 true; - } - - @Override - public boolean isAssocProperty() { - return true; - } - - /** - * Returns true. - */ - @Override - public boolean containsMany() { - return true; - } - - /** - * Return the many type. - */ - public ManyType getManyType() { - return manyType; - } - - /** - * Return true if this is many to many. - */ - public boolean isManyToMany() { - return manyToMany; - } - - /** - * ManyToMany only, join from local table to intersection table. - */ - public TableJoin getIntersectionTableJoin() { - return intersectionJoin; - } - - /** - * Set the join properties from the parent bean to the child bean. - * This is only valid for OneToMany and NOT valid for ManyToMany. - */ - public void setJoinValuesToChild(EntityBean parent, EntityBean child, Object mapKeyValue) { - - if (mapKeyProperty != null) { - mapKeyProperty.setValue(child, mapKeyValue); - } - - if (!manyToMany && childMasterProperty != null) { - // bidirectional in the sense that the 'master' property - // exists on the 'detail' bean - childMasterProperty.setValue(child, parent); - } - } - - /** - * Return the order by clause used to order the fetching of the data for - * this list, set or map. - */ - public String getFetchOrderBy() { - return fetchOrderBy; - } - - /** - * Return the order by for use when lazy loading the associated collection. - */ - public String getLazyFetchOrderBy() { - return lazyFetchOrderBy; - } - - /** - * Return the default mapKey when returning a Map. - */ - public String getMapKey() { - return mapKey; - } - - public BeanCollection createReferenceIfNull(EntityBean parentBean) { - - Object v = getValue(parentBean); - if (v instanceof BeanCollection) { - BeanCollection bc = (BeanCollection) v; - return bc.isReference() ? bc : null; - } else { - return createReference(parentBean); - } - } - - public BeanCollection createReference(EntityBean parentBean) { - - BeanCollection ref = help.createReference(parentBean); - setValue(parentBean, ref); - return ref; - } - - public BeanCollection createEmpty(EntityBean parentBean) { - return help.createEmpty(parentBean); - } - - public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { - return help.getBeanCollectionAdd(bc, mapKey); - } - - public Object getParentId(EntityBean parentBean) { - return descriptor.getId(parentBean); - } - - public List getBindParentIds(List parentIds) { - if (exportedProperties.length == 1) { - return parentIds; - } - List expandedList = new ArrayList(parentIds.size() * exportedProperties.length); - for (int i = 0; i < parentIds.size(); i++) { - for (int y = 0; y < exportedProperties.length; y++) { - Object compId = parentIds.get(i); - expandedList.add(exportedProperties[y].getValue((EntityBean) compId)); - } - } - return expandedList; - } - - private void bindWhereParendId(DefaultSqlUpdate sqlUpd, Object parentId) { - - if (exportedProperties.length == 1) { - sqlUpd.addParameter(parentId); - return; - } - EntityBean parent = (EntityBean) parentId; - for (int i = 0; i < exportedProperties.length; i++) { - Object embVal = exportedProperties[i].getValue(parent); - sqlUpd.addParameter(embVal); - } - } - - private int bindWhereParendId(int pos, Query q, Object parentId) { - - if (exportedProperties.length == 1) { - q.setParameter(pos++, parentId); - - } else { - - EntityBean parent = (EntityBean) parentId; - for (int i = 0; i < exportedProperties.length; i++) { - Object embVal = exportedProperties[i].getValue(parent); - q.setParameter(pos++, embVal); - } - } - return pos; - } - - public void addSelectExported(DbSqlContext ctx, String tableAlias) { - - String alias = manyToMany ? "int_" : tableAlias; - if (alias == null) { - alias = "t0"; - } - for (int i = 0; i < exportedProperties.length; i++) { - ctx.appendColumn(alias, exportedProperties[i].getForeignDbColumn()); - } - } - - private String deriveWhereParentIdSql(boolean inClause, String tableAlias) { - - StringBuilder sb = new StringBuilder(); - - if (inClause) { - sb.append("("); - } - 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(tableAlias).append(fkColumn); - if (!inClause) { - sb.append("=? "); - } - } - if (inClause) { - sb.append(")"); - } - return sb.toString(); - } - -// public void setPredicates(SpiQuery query, EntityBean parentBean) { -// -// if (manyToMany){ -// // for ManyToMany lazy loading we need to include a -// // join to the intersection table. The predicate column -// // is not on the 'destination many table'. -// query.setIncludeTableJoin(inverseJoin); -// } -// -// if (embeddedExportedProperties) { -// // use the EmbeddedId object instead of the parentBean -// BeanProperty idProp = descriptor.getIdProperty(); -// parentBean = (EntityBean)idProp.getValue(parentBean); -// } -// -// for (int i = 0; i < exportedProperties.length; i++) { -// Object val = exportedProperties[i].getValue(parentBean); -// String fkColumn = exportedProperties[i].getForeignDbColumn(); -// if (!manyToMany){ -// fkColumn = targetDescriptor.getBaseTableAlias()+"."+fkColumn; -// } else { -// // use hard coded alias for intersection table -// fkColumn = "int_."+fkColumn; -// } -// query.where().eq(fkColumn, val); -// } -// -// if (extraWhere != null){ -// // replace the table alias place holder -// String ta = targetDescriptor.getBaseTableAlias(); -// String where = StringHelper.replaceString(extraWhere, "${ta}", ta); -// query.where().raw(where); -// } -// -// if (fetchOrderBy != null){ -// query.order(fetchOrderBy); -// } -// } - - /** - * 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 - logger.error("Could not find a exported property?", e); - } - - } 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 embedded, BeanProperty prop) { - - String matchColumn = prop.getDbColumn(); - - String searchTable; - TableJoinColumn[] columns; - if (manyToMany) { - // look for column going to intersection - columns = intersectionJoin.columns(); - searchTable = intersectionJoin.getTable(); - - } else { - columns = tableJoin.columns(); - searchTable = tableJoin.getTable(); - } - 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(embedded, 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); - } - - /** - * Return the child property that links back to the master bean. - *

- * Note that childMasterProperty will be null if a field is used instead of - * a ManyToOne bean association. - *

- */ - private BeanPropertyAssocOne initChildMasterProperty() { - - if (unidirectional) { - return null; - } - - // search for the property, to see if it exists - Class beanType = descriptor.getBeanType(); - BeanDescriptor targetDesc = getTargetDescriptor(); - - BeanPropertyAssocOne[] ones = targetDesc.propertiesOne(); - for (int i = 0; i < ones.length; i++) { - BeanPropertyAssocOne prop = ones[i]; - if (mappedBy != null) { - // match using mappedBy as property name - if (mappedBy.equalsIgnoreCase(prop.getName())) { - return prop; - } - } else { - // assume only one property that matches parent object type - if (prop.getTargetType().equals(beanType)) { - // found it, stop search - return prop; - } - } - } - - throw new RuntimeException("Can not find Master [" + beanType + "] in Child[" + targetDesc + "]"); - } - - /** - * Search for and return the mapKey property. - */ - private BeanProperty initMapKeyProperty() { - - // search for the property - BeanDescriptor targetDesc = getTargetDescriptor(); - for (BeanProperty prop : targetDesc.propertiesAll()) { - if (mapKey.equalsIgnoreCase(prop.getName())) { - return prop; - } - } - - String from = descriptor.getFullName(); - String to = targetDesc.getFullName(); - throw new PersistenceException(from + ": Could not find mapKey property [" + mapKey + "] on [" + to + "]"); - } - - public IntersectionRow buildManyDeleteChildren(EntityBean parentBean, ArrayList excludeDetailIds) { - - IntersectionRow row = new IntersectionRow(tableJoin.getTable()); - if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { - row.setExcludeIds(excludeDetailIds, getTargetDescriptor()); - } - buildExport(row, parentBean); - return row; - } - - public IntersectionRow buildManyToManyDeleteChildren(EntityBean parentBean) { - - IntersectionRow row = new IntersectionRow(intersectionJoin.getTable()); - buildExport(row, parentBean); - return row; - } - - public IntersectionRow buildManyToManyMapBean(EntityBean parent, EntityBean other) { - - IntersectionRow row = new IntersectionRow(intersectionJoin.getTable()); - - buildExport(row, parent); - buildImport(row, other); - return row; - } - - private void buildExport(IntersectionRow row, EntityBean parentBean) { - - if (embeddedExportedProperties) { - BeanProperty idProp = descriptor.getIdProperty(); - parentBean = (EntityBean) idProp.getValue(parentBean); - } - for (int i = 0; i < exportedProperties.length; i++) { - Object val = exportedProperties[i].getValue(parentBean); - String fkColumn = exportedProperties[i].getForeignDbColumn(); - - row.put(fkColumn, val); - } - } - - /** - * Set the predicates for lazy loading of the association. - * Handles predicates for both OneToMany and ManyToMany. - */ - private void buildImport(IntersectionRow row, EntityBean otherBean) { - - importedId.buildImport(row, otherBean); - } - - /** - * Return true if the otherBean has an Id value. - */ - public boolean hasImportedId(EntityBean otherBean) { - - return null != targetDescriptor.getId(otherBean); - } - - public void jsonWrite(WriteJson ctx, EntityBean bean) throws IOException { - if (!this.jsonSerialize) { - return; - } - Boolean include = ctx.includeMany(name); - if (Boolean.FALSE.equals(include)) { - return; - } - - Object value = getValueIntercept(bean); - if (value != null) { - ctx.pushParentBeanMany(bean); - if (help != null) { - help.jsonWrite(ctx, name, value, include != null); - } else { - ctx.toJson(name, (Collection) value); - } - ctx.popParentBeanMany(); - } - } - - public void jsonRead(JsonParser parser, EntityBean parentBean) throws IOException { - jsonHelp.jsonRead(parser, parentBean); - } -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebean.*; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; +import com.avaje.ebean.bean.BeanCollectionAdd; +import com.avaje.ebean.bean.BeanCollectionLoader; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.query.SqlBeanLoad; +import com.avaje.ebeaninternal.server.text.json.WriteJson; +import com.fasterxml.jackson.core.JsonParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.persistence.PersistenceException; +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Property mapped to a List Set or Map. + */ +public class BeanPropertyAssocMany extends BeanPropertyAssoc { + + private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssocMany.class); + + private final BeanPropertyAssocManyJsonHelp jsonHelp; + + /** + * Join for manyToMany intersection table. + */ + private final TableJoin intersectionJoin; + + /** + * For ManyToMany this is the Inverse join used to build reference queries. + */ + private final TableJoin inverseJoin; + + /** + * Flag to indicate that this is a unidirectional relationship. + */ + private final boolean unidirectional; + + /** + * Flag to indicate manyToMany relationship. + */ + private final boolean manyToMany; + + /** + * Order by used when fetch joining the associated many. + */ + private final String fetchOrderBy; + + /** + * Order by used when lazy loading the associated many. + */ + private String lazyFetchOrderBy; + + private final String mapKey; + + /** + * The type of the many, set, list or map. + */ + private final ManyType manyType; + + private final ModifyListenMode modifyListenMode; + + private BeanProperty mapKeyProperty; + + /** + * Derived list of exported property and matching foreignKey + */ + private ExportedProperty[] exportedProperties; + + private String exportedPropertyBindProto = "?"; + + /** + * Property on the 'child' bean that links back to the 'master'. + */ + protected BeanPropertyAssocOne childMasterProperty; + + private boolean embeddedExportedProperties; + + private BeanCollectionHelp help; + + private ImportedId importedId; + + private String deleteByParentIdSql; + + private String deleteByParentIdInSql; + + /** + * Create this property. + */ + public BeanPropertyAssocMany(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertyAssocMany deploy) { + super(owner, descriptor, deploy); + this.unidirectional = deploy.isUnidirectional(); + this.manyToMany = deploy.isManyToMany(); + this.manyType = deploy.getManyType(); + this.mapKey = deploy.getMapKey(); + this.fetchOrderBy = deploy.getFetchOrderBy(); + this.intersectionJoin = deploy.createIntersectionTableJoin(); + this.inverseJoin = deploy.createInverseTableJoin(); + this.modifyListenMode = deploy.getModifyListenMode(); + this.jsonHelp = new BeanPropertyAssocManyJsonHelp(this); + } + + public void initialise() { + super.initialise(); + + if (!isTransient) { + this.help = BeanCollectionHelpFactory.create(this); + + if (manyToMany) { + // only manyToMany's have imported properties + importedId = createImportedId(this, targetDescriptor, tableJoin); + + } else { + // find the property in the many that matches + // back to the master (Order in the OrderDetail bean) + childMasterProperty = initChildMasterProperty(); + if (childMasterProperty != null) { + childMasterProperty.setRelationshipProperty(this); + } + } + + if (mapKey != null) { + mapKeyProperty = initMapKeyProperty(); + } + + exportedProperties = createExported(); + if (exportedProperties.length > 0) { + embeddedExportedProperties = exportedProperties[0].isEmbedded(); + exportedPropertyBindProto = deriveExportedPropertyBindProto(); + + if (fetchOrderBy != null) { + // derive lazyFetchOrderBy + StringBuilder sb = new StringBuilder(50); + for (int i = 0; i < exportedProperties.length; i++) { + if (i > 0) { + sb.append(", "); + } + // these fk columns are either on the intersection (int_) or base table (t0) + String fkTableAlias = isManyToMany() ? "int_" : "t0"; + sb.append(fkTableAlias).append(".").append(exportedProperties[i].getForeignDbColumn()); + } + sb.append(", ").append(fetchOrderBy); + lazyFetchOrderBy = sb.toString().trim(); + } + } + + String delStmt; + if (manyToMany) { + delStmt = "delete from " + inverseJoin.getTable() + " where "; + } else { + delStmt = "delete from " + targetDescriptor.getBaseTable() + " where "; + } + deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false, ""); + deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true, ""); + } + } + + /** + * Add the bean to the appropriate collection on the parent bean. + */ + public void addBeanToCollectionWithCreate(EntityBean parentBean, EntityBean detailBean) { + BeanCollection bc = (BeanCollection) super.getValue(parentBean); + if (bc == null) { + bc = help.createEmpty(parentBean); + setValue(parentBean, bc); + } + help.add(bc, detailBean); + } + + public boolean isEmptyBeanCollection(EntityBean bean) { + Object val = getValue(bean); + return val == null || (val instanceof BeanCollection) && ((BeanCollection) val).isEmptyAndUntouched(); + } + + /** + * Reset the many properties to be empty and ready for reloading. + *

+ * Used in bean refresh. + */ + public void resetMany(EntityBean bean) { + Object value = getValue(bean); + if (value == null) { + // not expecting this - set an empty reference + createReference(bean); + } else { + // reset the collection back to empty + ((BeanCollection)value).reset(bean, name); + } + } + + @Override + public Object getValue(EntityBean bean) { + return super.getValue(bean); + } + + @Override + public Object getValueIntercept(EntityBean bean) { + return super.getValueIntercept(bean); + } + + @Override + public void setValue(EntityBean bean, Object value) { + super.setValue(bean, value); + } + + @Override + public void setValueIntercept(EntityBean bean, Object value) { + super.setValueIntercept(bean, value); + } + + public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { + return createElPropertyValue(propName, remainder, chain, propertyDeploy); + } + + public void buildSelectExpressionChain(String prefix, List selectChain) { + // do not add to the selectChain at the top level of the Many bean + } + + public SqlUpdate deleteByParentId(Object parentId, List parentIdist) { + if (parentId != null) { + return deleteByParentId(parentId); + } else { + return deleteByParentIdList(parentIdist); + } + } + + private SqlUpdate deleteByParentId(Object parentId) { + DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql); + bindWhereParendId(sqlDelete, parentId); + return sqlDelete; + } + + /** + * Find the Id's of detail beans given a parent Id or list of parent Id's. + */ + public List findIdsByParentId(Object parentId, List parentIdist, Transaction t, ArrayList excludeDetailIds) { + if (parentId != null) { + return findIdsByParentId(parentId, t, excludeDetailIds); + } else { + return findIdsByParentIdList(parentIdist, t, excludeDetailIds); + } + } + + private List findIdsByParentId(Object parentId, Transaction t, ArrayList excludeDetailIds) { + + String rawWhere = deriveWhereParentIdSql(false, ""); + + EbeanServer server = getBeanDescriptor().getEbeanServer(); + Query q = server.find(getPropertyType()) + .where().raw(rawWhere).query(); + + bindWhereParendId(1, q, parentId); + + if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { + Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds); + q.where().not(idIn); + } + + return server.findIds(q, t); + } + + /** + * Add a where clause to the query for a given list of parent Id's. + */ + public void addWhereParentIdIn(SpiQuery query, List parentIds) { + + String tableAlias = manyToMany ? "int_." : "t0."; + if (manyToMany) { + query.setIncludeTableJoin(inverseJoin); + } + String rawWhere = deriveWhereParentIdSql(true, tableAlias); + String expr = descriptor.getParentIdInExpr(parentIds.size(), rawWhere); + + // Flatten the bind values if needed (embeddedId) + List bindValues = getBindParentIds(parentIds); + + query.where().raw(expr, bindValues.toArray()); + } + + private List findIdsByParentIdList(List parentIdist, Transaction t, ArrayList excludeDetailIds) { + + String rawWhere = deriveWhereParentIdSql(true, ""); + String inClause = buildInClauseBinding(parentIdist.size(), exportedPropertyBindProto); + + String expr = rawWhere + inClause; + + EbeanServer server = getBeanDescriptor().getEbeanServer(); + Query q = server.find(getPropertyType()).where().raw(expr).query(); + + int pos = 1; + for (int i = 0; i < parentIdist.size(); i++) { + pos = bindWhereParendId(pos, q, parentIdist.get(i)); + } + + if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { + Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds); + q.where().not(idIn); + } + + return server.findIds(q, t); + } + + private SqlUpdate deleteByParentIdList(List parentIdist) { + + StringBuilder sb = new StringBuilder(100); + sb.append(deleteByParentIdInSql); + + String inClause = buildInClauseBinding(parentIdist.size(), exportedPropertyBindProto); + sb.append(inClause); + + DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); + for (int i = 0; i < parentIdist.size(); i++) { + bindWhereParendId(delete, parentIdist.get(i)); + } + + return delete; + } + + private String deriveExportedPropertyBindProto() { + if (exportedProperties.length == 1) { + return "?"; + } + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (int i = 0; i < exportedProperties.length; i++) { + if (i > 0) { + sb.append(","); + } + sb.append("?"); + } + sb.append(")"); + return sb.toString(); + } + + private String buildInClauseBinding(int size, String bindProto) { + + StringBuilder sb = new StringBuilder(10 + (size * (bindProto.length() + 1))); + sb.append(" in"); + + sb.append(" ("); + for (int i = 0; i < size; i++) { + if (i > 0) { + sb.append(","); + } + sb.append(bindProto); + } + sb.append(") "); + return sb.toString(); + } + + /** + * Set the lazy load server to help create reference collections (that lazy + * load on demand). + */ + public void setLoader(BeanCollectionLoader loader) { + if (help != null) { + help.setLoader(loader); + } + } + + /** + * Return the mode for listening to modifications to collections for this + * association. + */ + public ModifyListenMode getModifyListenMode() { + return modifyListenMode; + } + + @Override + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + } + + @Override + public void loadIgnore(DbReadContext ctx) { + // nothing to ignore for Many + } + + @Override + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + sqlBeanLoad.loadAssocMany(this); + } + + @Override + public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { + return null; + } + + @Override + public Object read(DbReadContext ctx) throws SQLException { + return null; + } + + public void add(BeanCollection collection, EntityBean bean) { + help.add(collection, bean); + } + + /** + * Refresh the appropriate list set or map. + */ + public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { + help.refresh(server, query, t, parentBean); + } + + /** + * Apply the refreshed BeanCollection to the property of the parentBean. + */ + public void refresh(BeanCollection bc, EntityBean parentBean) { + help.refresh(bc, parentBean); + } + + /** + * 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 true; + } + + @Override + public boolean isAssocProperty() { + return true; + } + + /** + * Returns true. + */ + @Override + public boolean containsMany() { + return true; + } + + /** + * Return the many type. + */ + public ManyType getManyType() { + return manyType; + } + + /** + * Return true if this is many to many. + */ + public boolean isManyToMany() { + return manyToMany; + } + + /** + * ManyToMany only, join from local table to intersection table. + */ + public TableJoin getIntersectionTableJoin() { + return intersectionJoin; + } + + /** + * Set the join properties from the parent bean to the child bean. + * This is only valid for OneToMany and NOT valid for ManyToMany. + */ + public void setJoinValuesToChild(EntityBean parent, EntityBean child, Object mapKeyValue) { + + if (mapKeyProperty != null) { + mapKeyProperty.setValue(child, mapKeyValue); + } + + if (!manyToMany && childMasterProperty != null) { + // bidirectional in the sense that the 'master' property + // exists on the 'detail' bean + childMasterProperty.setValue(child, parent); + } + } + + /** + * Return the order by clause used to order the fetching of the data for + * this list, set or map. + */ + public String getFetchOrderBy() { + return fetchOrderBy; + } + + /** + * Return the order by for use when lazy loading the associated collection. + */ + public String getLazyFetchOrderBy() { + return lazyFetchOrderBy; + } + + /** + * Return the default mapKey when returning a Map. + */ + public String getMapKey() { + return mapKey; + } + + public BeanCollection createReferenceIfNull(EntityBean parentBean) { + + Object v = getValue(parentBean); + if (v instanceof BeanCollection) { + BeanCollection bc = (BeanCollection) v; + return bc.isReference() ? bc : null; + } else { + return createReference(parentBean); + } + } + + public BeanCollection createReference(EntityBean parentBean) { + + BeanCollection ref = help.createReference(parentBean); + setValue(parentBean, ref); + return ref; + } + + public BeanCollection createEmpty(EntityBean parentBean) { + return help.createEmpty(parentBean); + } + + public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { + return help.getBeanCollectionAdd(bc, mapKey); + } + + public Object getParentId(EntityBean parentBean) { + return descriptor.getId(parentBean); + } + + public List getBindParentIds(List parentIds) { + if (exportedProperties.length == 1) { + return parentIds; + } + List expandedList = new ArrayList(parentIds.size() * exportedProperties.length); + for (int i = 0; i < parentIds.size(); i++) { + for (int y = 0; y < exportedProperties.length; y++) { + Object compId = parentIds.get(i); + expandedList.add(exportedProperties[y].getValue((EntityBean) compId)); + } + } + return expandedList; + } + + private void bindWhereParendId(DefaultSqlUpdate sqlUpd, Object parentId) { + + if (exportedProperties.length == 1) { + sqlUpd.addParameter(parentId); + return; + } + EntityBean parent = (EntityBean) parentId; + for (int i = 0; i < exportedProperties.length; i++) { + Object embVal = exportedProperties[i].getValue(parent); + sqlUpd.addParameter(embVal); + } + } + + private int bindWhereParendId(int pos, Query q, Object parentId) { + + if (exportedProperties.length == 1) { + q.setParameter(pos++, parentId); + + } else { + + EntityBean parent = (EntityBean) parentId; + for (int i = 0; i < exportedProperties.length; i++) { + Object embVal = exportedProperties[i].getValue(parent); + q.setParameter(pos++, embVal); + } + } + return pos; + } + + public void addSelectExported(DbSqlContext ctx, String tableAlias) { + + String alias = manyToMany ? "int_" : tableAlias; + if (alias == null) { + alias = "t0"; + } + for (int i = 0; i < exportedProperties.length; i++) { + ctx.appendColumn(alias, exportedProperties[i].getForeignDbColumn()); + } + } + + private String deriveWhereParentIdSql(boolean inClause, String tableAlias) { + + StringBuilder sb = new StringBuilder(); + + if (inClause) { + sb.append("("); + } + 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(tableAlias).append(fkColumn); + if (!inClause) { + sb.append("=? "); + } + } + if (inClause) { + sb.append(")"); + } + return sb.toString(); + } + +// public void setPredicates(SpiQuery query, EntityBean parentBean) { +// +// if (manyToMany){ +// // for ManyToMany lazy loading we need to include a +// // join to the intersection table. The predicate column +// // is not on the 'destination many table'. +// query.setIncludeTableJoin(inverseJoin); +// } +// +// if (embeddedExportedProperties) { +// // use the EmbeddedId object instead of the parentBean +// BeanProperty idProp = descriptor.getIdProperty(); +// parentBean = (EntityBean)idProp.getValue(parentBean); +// } +// +// for (int i = 0; i < exportedProperties.length; i++) { +// Object val = exportedProperties[i].getValue(parentBean); +// String fkColumn = exportedProperties[i].getForeignDbColumn(); +// if (!manyToMany){ +// fkColumn = targetDescriptor.getBaseTableAlias()+"."+fkColumn; +// } else { +// // use hard coded alias for intersection table +// fkColumn = "int_."+fkColumn; +// } +// query.where().eq(fkColumn, val); +// } +// +// if (extraWhere != null){ +// // replace the table alias place holder +// String ta = targetDescriptor.getBaseTableAlias(); +// String where = StringHelper.replaceString(extraWhere, "${ta}", ta); +// query.where().raw(where); +// } +// +// if (fetchOrderBy != null){ +// query.order(fetchOrderBy); +// } +// } + + /** + * 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 + logger.error("Could not find a exported property?", e); + } + + } 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 embedded, BeanProperty prop) { + + String matchColumn = prop.getDbColumn(); + + String searchTable; + TableJoinColumn[] columns; + if (manyToMany) { + // look for column going to intersection + columns = intersectionJoin.columns(); + searchTable = intersectionJoin.getTable(); + + } else { + columns = tableJoin.columns(); + searchTable = tableJoin.getTable(); + } + 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(embedded, 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); + } + + /** + * Return the child property that links back to the master bean. + *

+ * Note that childMasterProperty will be null if a field is used instead of + * a ManyToOne bean association. + *

+ */ + private BeanPropertyAssocOne initChildMasterProperty() { + + if (unidirectional) { + return null; + } + + // search for the property, to see if it exists + Class beanType = descriptor.getBeanType(); + BeanDescriptor targetDesc = getTargetDescriptor(); + + BeanPropertyAssocOne[] ones = targetDesc.propertiesOne(); + for (int i = 0; i < ones.length; i++) { + BeanPropertyAssocOne prop = ones[i]; + if (mappedBy != null) { + // match using mappedBy as property name + if (mappedBy.equalsIgnoreCase(prop.getName())) { + return prop; + } + } else { + // assume only one property that matches parent object type + if (prop.getTargetType().equals(beanType)) { + // found it, stop search + return prop; + } + } + } + + throw new RuntimeException("Can not find Master [" + beanType + "] in Child[" + targetDesc + "]"); + } + + /** + * Search for and return the mapKey property. + */ + private BeanProperty initMapKeyProperty() { + + // search for the property + BeanDescriptor targetDesc = getTargetDescriptor(); + for (BeanProperty prop : targetDesc.propertiesAll()) { + if (mapKey.equalsIgnoreCase(prop.getName())) { + return prop; + } + } + + String from = descriptor.getFullName(); + String to = targetDesc.getFullName(); + throw new PersistenceException(from + ": Could not find mapKey property [" + mapKey + "] on [" + to + "]"); + } + + public IntersectionRow buildManyDeleteChildren(EntityBean parentBean, ArrayList excludeDetailIds) { + + IntersectionRow row = new IntersectionRow(tableJoin.getTable()); + if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { + row.setExcludeIds(excludeDetailIds, getTargetDescriptor()); + } + buildExport(row, parentBean); + return row; + } + + public IntersectionRow buildManyToManyDeleteChildren(EntityBean parentBean) { + + IntersectionRow row = new IntersectionRow(intersectionJoin.getTable()); + buildExport(row, parentBean); + return row; + } + + public IntersectionRow buildManyToManyMapBean(EntityBean parent, EntityBean other) { + + IntersectionRow row = new IntersectionRow(intersectionJoin.getTable()); + + buildExport(row, parent); + buildImport(row, other); + return row; + } + + private void buildExport(IntersectionRow row, EntityBean parentBean) { + + if (embeddedExportedProperties) { + BeanProperty idProp = descriptor.getIdProperty(); + parentBean = (EntityBean) idProp.getValue(parentBean); + } + for (int i = 0; i < exportedProperties.length; i++) { + Object val = exportedProperties[i].getValue(parentBean); + String fkColumn = exportedProperties[i].getForeignDbColumn(); + + row.put(fkColumn, val); + } + } + + /** + * Set the predicates for lazy loading of the association. + * Handles predicates for both OneToMany and ManyToMany. + */ + private void buildImport(IntersectionRow row, EntityBean otherBean) { + + importedId.buildImport(row, otherBean); + } + + /** + * Return true if the otherBean has an Id value. + */ + public boolean hasImportedId(EntityBean otherBean) { + + return null != targetDescriptor.getId(otherBean); + } + + public void jsonWrite(WriteJson ctx, EntityBean bean) throws IOException { + if (!this.jsonSerialize) { + return; + } + Boolean include = ctx.includeMany(name); + if (Boolean.FALSE.equals(include)) { + return; + } + + Object value = getValueIntercept(bean); + if (value != null) { + ctx.pushParentBeanMany(bean); + if (help != null) { + help.jsonWrite(ctx, name, value, include != null); + } else { + ctx.toJson(name, (Collection) value); + } + ctx.popParentBeanMany(); + } + } + + public void jsonRead(JsonParser parser, EntityBean parentBean) throws IOException { + jsonHelp.jsonRead(parser, parentBean); + } +} 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 aa41d3c10..452e1a55d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -1,871 +1,871 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.io.IOException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.Query; -import com.avaje.ebean.SqlUpdate; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.server.cache.CachedBeanData; -import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; -import com.avaje.ebeaninternal.server.deploy.id.IdBinder; -import com.avaje.ebeaninternal.server.deploy.id.ImportedId; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.query.SplitName; -import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.query.SqlJoinType; -import com.avaje.ebeaninternal.server.text.json.WriteJson; -import com.fasterxml.jackson.core.JsonParser; - -/** - * Property mapped to a joined bean. - */ -public class BeanPropertyAssocOne extends BeanPropertyAssoc { - - private final boolean oneToOne; - - private final boolean oneToOneExported; - - private final boolean importedPrimaryKey; - - private final LocalHelp localHelp; - - 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; - - /** - * 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(); - } - - 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; - } - - public void buildSelectExpressionChain(String prefix, List selectChain) { - - prefix = SplitName.add(prefix, name); - - if (!embedded){ - targetIdBinder.buildSelectExpressionChain(prefix, selectChain); - - } else { - for (int i = 0; i < embeddedProps.length; i++) { - embeddedProps[i].buildSelectExpressionChain(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); - } - } 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 - 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); - } - } - } - - /** - * For imported reference - this is the common case. - */ - private final class Reference extends LocalHelp { - - //private final BeanPropertyAssocOne beanProp; - - Reference(BeanPropertyAssocOne beanProp) { -// this.beanProp = beanProp; - } - - 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; - } - - /** - * 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); - } - } - - /** - * For OneToOne exported reference - not so common. - */ - private final class ReferenceExported extends LocalHelp { - - @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); - } - } - - @Override - public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException { - - if (!jsonSerialize) { - return; - } - - Object value = getValueIntercept(bean); - if (value == null) { - writeJson.writeNull(name); - - } else { - if (writeJson.isParentBean(value)) { - // bi-directional and already rendered parent - - } else { - // Hmmm, not writing complex non-entity bean - if (value instanceof EntityBean) { - writeJson.beginAssocOne(name, bean); - BeanDescriptor refDesc = descriptor.getBeanDescriptor(value.getClass()); - refDesc.jsonWrite(writeJson, (EntityBean) value, name); - writeJson.endAssocOne(); - } - } - } - } - - @Override - public void jsonRead(JsonParser parser, EntityBean bean) throws IOException { - if (jsonDeserialize && targetDescriptor != null) { - T assocBean = targetDescriptor.jsonRead(parser, name); - setValue(bean, assocBean); - } - } - - 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) { - - 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); - } - } - } -} +package com.avaje.ebeaninternal.server.deploy; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Query; +import com.avaje.ebean.SqlUpdate; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.server.cache.CachedBeanData; +import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; +import com.avaje.ebeaninternal.server.deploy.id.IdBinder; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.query.SplitName; +import com.avaje.ebeaninternal.server.query.SqlBeanLoad; +import com.avaje.ebeaninternal.server.query.SqlJoinType; +import com.avaje.ebeaninternal.server.text.json.WriteJson; +import com.fasterxml.jackson.core.JsonParser; + +/** + * Property mapped to a joined bean. + */ +public class BeanPropertyAssocOne extends BeanPropertyAssoc { + + private final boolean oneToOne; + + private final boolean oneToOneExported; + + private final boolean importedPrimaryKey; + + private final LocalHelp localHelp; + + 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; + + /** + * 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(); + } + + 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; + } + + public void buildSelectExpressionChain(String prefix, List selectChain) { + + prefix = SplitName.add(prefix, name); + + if (!embedded){ + targetIdBinder.buildSelectExpressionChain(prefix, selectChain); + + } else { + for (int i = 0; i < embeddedProps.length; i++) { + embeddedProps[i].buildSelectExpressionChain(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); + } + } 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 + 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); + } + } + } + + /** + * For imported reference - this is the common case. + */ + private final class Reference extends LocalHelp { + + //private final BeanPropertyAssocOne beanProp; + + Reference(BeanPropertyAssocOne beanProp) { +// this.beanProp = beanProp; + } + + 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; + } + + /** + * 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); + } + } + + /** + * For OneToOne exported reference - not so common. + */ + private final class ReferenceExported extends LocalHelp { + + @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); + } + } + + @Override + public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException { + + if (!jsonSerialize) { + return; + } + + Object value = getValueIntercept(bean); + if (value == null) { + writeJson.writeNull(name); + + } else { + if (writeJson.isParentBean(value)) { + // bi-directional and already rendered parent + + } else { + // Hmmm, not writing complex non-entity bean + if (value instanceof EntityBean) { + writeJson.beginAssocOne(name, bean); + BeanDescriptor refDesc = descriptor.getBeanDescriptor(value.getClass()); + refDesc.jsonWrite(writeJson, (EntityBean) value, name); + writeJson.endAssocOne(); + } + } + } + } + + @Override + public void jsonRead(JsonParser parser, EntityBean bean) throws IOException { + if (jsonDeserialize && targetDescriptor != null) { + T assocBean = targetDescriptor.jsonRead(parser, name); + setValue(bean, assocBean); + } + } + + 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) { + + 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/BeanPropertyCompound.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java index a080a8035..d73f74fde 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java @@ -1,198 +1,198 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.io.IOException; -import java.sql.SQLException; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.config.ScalarTypeConverter; -import com.avaje.ebean.text.json.EJson; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; -import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.text.json.WriteJson; -import com.avaje.ebeaninternal.server.type.CtCompoundProperty; -import com.avaje.ebeaninternal.server.type.CtCompoundPropertyElAdapter; -import com.avaje.ebeaninternal.server.type.CtCompoundType; -import com.fasterxml.jackson.core.JsonParser; - -/** - * Property mapped to an Immutable Compound Value Object. - *

- * An Immutable Compound Value Object is similar to an Embedded bean but it - * doesn't require enhancement and MUST be treated as an Immutable type. - *

- */ -public class BeanPropertyCompound extends BeanProperty { - - private final CtCompoundType compoundType; - - /** - * Type Converter for scala.Option and similar type wrapping. - */ - @SuppressWarnings("rawtypes") - private final ScalarTypeConverter typeConverter; - - private final BeanProperty[] scalarProperties; - - private final LinkedHashMap propertyMap = new LinkedHashMap(); - - private final LinkedHashMap nonScalarMap = new LinkedHashMap(); - - private final BeanPropertyCompoundRoot root; - - /** - * Create the property. - */ - public BeanPropertyCompound(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertyCompound deploy) { - - super(owner, descriptor, deploy); - - this.compoundType = deploy.getCompoundType(); - this.typeConverter = deploy.getTypeConverter(); - - this.root = deploy.getFlatProperties(owner, descriptor); - - this.scalarProperties = root.getScalarProperties(); - - for (int i = 0; i < scalarProperties.length; i++) { - propertyMap.put(scalarProperties[i].getName(), scalarProperties[i]); - } - - List nonScalarPropsList = root.getNonScalarProperties(); - - for (int i = 0; i < nonScalarPropsList.size(); i++) { - CtCompoundProperty ctProp = nonScalarPropsList.get(i); - CtCompoundPropertyElAdapter adapter = new CtCompoundPropertyElAdapter(ctProp); - nonScalarMap.put(ctProp.getRelativeName(), adapter); - } - - } - - @Override - public void initialise() { - // do nothing for normal BeanProperty - if (!isTransient && compoundType == null) { - String msg = "No cvoInternalType assigned to " + descriptor.getFullName() + "." + getName(); - throw new RuntimeException(msg); - } - } - - @Override - public void setDeployOrder(int deployOrder) { - this.deployOrder = deployOrder; - for (CtCompoundPropertyElAdapter adapter : nonScalarMap.values()) { - adapter.setDeployOrder(deployOrder); - } - } - - public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { - - if (chain == null) { - chain = new ElPropertyChainBuilder(true, propName); - } - - // first add this property - chain.add(this); - - // handle all the rest of the chain handled by the - // BeanProperty (all depth for nested compound type) - BeanProperty p = propertyMap.get(remainder); - if (p != null) { - return chain.add(p).build(); - } - CtCompoundPropertyElAdapter elAdapter = nonScalarMap.get(remainder); - if (elAdapter == null) { - throw new RuntimeException("property [" + remainder + "] not found in " + getFullBeanName()); - } - return chain.add(elAdapter).build(); - } - - @Override - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - if (!isTransient) { - for (int i = 0; i < scalarProperties.length; i++) { - scalarProperties[i].appendSelect(ctx, subQuery); - } - } - } - - public BeanProperty[] getScalarProperties() { - return scalarProperties; - } - - @Override - public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { - - boolean assignable = (type == null || owningType.isAssignableFrom(type)); - - Object v = compoundType.read(ctx.getDataReader()); - if (assignable) { - setValue(bean, v); - } - - return v; - } - - /** - * Read the data from the resultSet effectively ignoring it and returning - * null. - */ - @SuppressWarnings("unchecked") - @Override - public Object read(DbReadContext ctx) throws SQLException { - - Object v = compoundType.read(ctx.getDataReader()); - if (typeConverter != null){ - v = typeConverter.wrapValue(v); - } - return v; - } - - @Override - public void loadIgnore(DbReadContext ctx) { - compoundType.loadIgnore(ctx.getDataReader()); - } - - @Override - public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { - sqlBeanLoad.load(this); - } - - @Override - public Object elGetReference(EntityBean bean) { - return bean; - } - - public void jsonWrite(WriteJson ctx, EntityBean bean) throws IOException { - if (!jsonSerialize) { - return; - } - Object value = getValueIntercept(bean); - if (value == null) { - ctx.writeNull(name); - } else { - compoundType.jsonWrite(ctx, value, name); - } - } - - public void jsonRead(JsonParser ctx, EntityBean bean) throws IOException { - - if (!jsonDeserialize) { - return; - } - - Object value = EJson.parse(ctx); - if (value == null) { - setValue(bean, null); - } else { - @SuppressWarnings("unchecked") - Map map = (Map)value; - Object objValue = compoundType.jsonConvert(map); - setValue(bean, objValue); - } - } -} +package com.avaje.ebeaninternal.server.deploy; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.config.ScalarTypeConverter; +import com.avaje.ebean.text.json.EJson; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; +import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.query.SqlBeanLoad; +import com.avaje.ebeaninternal.server.text.json.WriteJson; +import com.avaje.ebeaninternal.server.type.CtCompoundProperty; +import com.avaje.ebeaninternal.server.type.CtCompoundPropertyElAdapter; +import com.avaje.ebeaninternal.server.type.CtCompoundType; +import com.fasterxml.jackson.core.JsonParser; + +/** + * Property mapped to an Immutable Compound Value Object. + *

+ * An Immutable Compound Value Object is similar to an Embedded bean but it + * doesn't require enhancement and MUST be treated as an Immutable type. + *

+ */ +public class BeanPropertyCompound extends BeanProperty { + + private final CtCompoundType compoundType; + + /** + * Type Converter for scala.Option and similar type wrapping. + */ + @SuppressWarnings("rawtypes") + private final ScalarTypeConverter typeConverter; + + private final BeanProperty[] scalarProperties; + + private final LinkedHashMap propertyMap = new LinkedHashMap(); + + private final LinkedHashMap nonScalarMap = new LinkedHashMap(); + + private final BeanPropertyCompoundRoot root; + + /** + * Create the property. + */ + public BeanPropertyCompound(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertyCompound deploy) { + + super(owner, descriptor, deploy); + + this.compoundType = deploy.getCompoundType(); + this.typeConverter = deploy.getTypeConverter(); + + this.root = deploy.getFlatProperties(owner, descriptor); + + this.scalarProperties = root.getScalarProperties(); + + for (int i = 0; i < scalarProperties.length; i++) { + propertyMap.put(scalarProperties[i].getName(), scalarProperties[i]); + } + + List nonScalarPropsList = root.getNonScalarProperties(); + + for (int i = 0; i < nonScalarPropsList.size(); i++) { + CtCompoundProperty ctProp = nonScalarPropsList.get(i); + CtCompoundPropertyElAdapter adapter = new CtCompoundPropertyElAdapter(ctProp); + nonScalarMap.put(ctProp.getRelativeName(), adapter); + } + + } + + @Override + public void initialise() { + // do nothing for normal BeanProperty + if (!isTransient && compoundType == null) { + String msg = "No cvoInternalType assigned to " + descriptor.getFullName() + "." + getName(); + throw new RuntimeException(msg); + } + } + + @Override + public void setDeployOrder(int deployOrder) { + this.deployOrder = deployOrder; + for (CtCompoundPropertyElAdapter adapter : nonScalarMap.values()) { + adapter.setDeployOrder(deployOrder); + } + } + + public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { + + if (chain == null) { + chain = new ElPropertyChainBuilder(true, propName); + } + + // first add this property + chain.add(this); + + // handle all the rest of the chain handled by the + // BeanProperty (all depth for nested compound type) + BeanProperty p = propertyMap.get(remainder); + if (p != null) { + return chain.add(p).build(); + } + CtCompoundPropertyElAdapter elAdapter = nonScalarMap.get(remainder); + if (elAdapter == null) { + throw new RuntimeException("property [" + remainder + "] not found in " + getFullBeanName()); + } + return chain.add(elAdapter).build(); + } + + @Override + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + if (!isTransient) { + for (int i = 0; i < scalarProperties.length; i++) { + scalarProperties[i].appendSelect(ctx, subQuery); + } + } + } + + public BeanProperty[] getScalarProperties() { + return scalarProperties; + } + + @Override + public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { + + boolean assignable = (type == null || owningType.isAssignableFrom(type)); + + Object v = compoundType.read(ctx.getDataReader()); + if (assignable) { + setValue(bean, v); + } + + return v; + } + + /** + * Read the data from the resultSet effectively ignoring it and returning + * null. + */ + @SuppressWarnings("unchecked") + @Override + public Object read(DbReadContext ctx) throws SQLException { + + Object v = compoundType.read(ctx.getDataReader()); + if (typeConverter != null){ + v = typeConverter.wrapValue(v); + } + return v; + } + + @Override + public void loadIgnore(DbReadContext ctx) { + compoundType.loadIgnore(ctx.getDataReader()); + } + + @Override + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + sqlBeanLoad.load(this); + } + + @Override + public Object elGetReference(EntityBean bean) { + return bean; + } + + public void jsonWrite(WriteJson ctx, EntityBean bean) throws IOException { + if (!jsonSerialize) { + return; + } + Object value = getValueIntercept(bean); + if (value == null) { + ctx.writeNull(name); + } else { + compoundType.jsonWrite(ctx, value, name); + } + } + + public void jsonRead(JsonParser ctx, EntityBean bean) throws IOException { + + if (!jsonDeserialize) { + return; + } + + Object value = EJson.parse(ctx); + if (value == null) { + setValue(bean, null); + } else { + @SuppressWarnings("unchecked") + Map map = (Map)value; + Object objValue = compoundType.jsonConvert(map); + setValue(bean, objValue); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundRoot.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundRoot.java index 018feedd0..d496c7563 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundRoot.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundRoot.java @@ -1,110 +1,110 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.properties.BeanPropertySetter; -import com.avaje.ebeaninternal.server.type.CtCompoundProperty; - -/** - * Represents the root BeanProperty for properties of a compound type. - *

- * Holds all the scalar and non-scalar properties of the compound type. The - * scalar properties match to DB columns and the non-scalar ones are here solely - * to support EL expression language for nested compound types. - *

- * - * @author rbygrave - */ -public class BeanPropertyCompoundRoot { - - private final BeanPropertySetter setter; - - /** - * The method used to write the property. - */ - private final Method writeMethod; - - private final String name; - private final String fullBeanName; - - private final LinkedHashMap propMap; - - private final ArrayList propList; - - private List nonScalarProperties; - - public BeanPropertyCompoundRoot(DeployBeanProperty deploy) { - this.fullBeanName = deploy.getFullBeanName(); - this.name = deploy.getName(); - this.setter = deploy.getSetter(); - this.writeMethod = deploy.getWriteMethod(); - this.propList = new ArrayList(); - this.propMap = new LinkedHashMap(); - } - - public BeanProperty[] getScalarProperties() { - - return propList.toArray(new BeanProperty[propList.size()]); - } - - public void register(BeanPropertyCompoundScalar prop) { - propList.add(prop); - propMap.put(prop.getName(), prop); - } - - public BeanPropertyCompoundScalar getCompoundScalarProperty(String propName) { - return propMap.get(propName); - } - - public List getNonScalarProperties() { - return nonScalarProperties; - } - - public void setNonScalarProperties(List nonScalarProperties) { - this.nonScalarProperties = nonScalarProperties; - } - - /** - * Set the value of the property without interception or - * PropertyChangeSupport. - */ - public void setRootValue(EntityBean bean, Object value) { - try { - if (bean instanceof EntityBean) { - setter.set(bean, value); - } else { - Object[] args = new Object[1]; - args[0] = value; - writeMethod.invoke(bean, args); - } - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "set " + name + " with arg[" + value + "] on ["+fullBeanName+"] with type[" + beanType + "] threw error"; - throw new RuntimeException(msg, ex); - } - } - - /** - * Set the value of the property. - */ - public void setRootValueIntercept(EntityBean bean, Object value) { - try { - if (bean instanceof EntityBean) { - setter.setIntercept(bean, value); - } else { - Object[] args = new Object[1]; - args[0] = value; - writeMethod.invoke(bean, args); - } - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "setIntercept " + name + " arg[" + value + "] on ["+fullBeanName+"] with type[" + beanType + "] threw error"; - throw new RuntimeException(msg, ex); - } - } -} +package com.avaje.ebeaninternal.server.deploy; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.properties.BeanPropertySetter; +import com.avaje.ebeaninternal.server.type.CtCompoundProperty; + +/** + * Represents the root BeanProperty for properties of a compound type. + *

+ * Holds all the scalar and non-scalar properties of the compound type. The + * scalar properties match to DB columns and the non-scalar ones are here solely + * to support EL expression language for nested compound types. + *

+ * + * @author rbygrave + */ +public class BeanPropertyCompoundRoot { + + private final BeanPropertySetter setter; + + /** + * The method used to write the property. + */ + private final Method writeMethod; + + private final String name; + private final String fullBeanName; + + private final LinkedHashMap propMap; + + private final ArrayList propList; + + private List nonScalarProperties; + + public BeanPropertyCompoundRoot(DeployBeanProperty deploy) { + this.fullBeanName = deploy.getFullBeanName(); + this.name = deploy.getName(); + this.setter = deploy.getSetter(); + this.writeMethod = deploy.getWriteMethod(); + this.propList = new ArrayList(); + this.propMap = new LinkedHashMap(); + } + + public BeanProperty[] getScalarProperties() { + + return propList.toArray(new BeanProperty[propList.size()]); + } + + public void register(BeanPropertyCompoundScalar prop) { + propList.add(prop); + propMap.put(prop.getName(), prop); + } + + public BeanPropertyCompoundScalar getCompoundScalarProperty(String propName) { + return propMap.get(propName); + } + + public List getNonScalarProperties() { + return nonScalarProperties; + } + + public void setNonScalarProperties(List nonScalarProperties) { + this.nonScalarProperties = nonScalarProperties; + } + + /** + * Set the value of the property without interception or + * PropertyChangeSupport. + */ + public void setRootValue(EntityBean bean, Object value) { + try { + if (bean instanceof EntityBean) { + setter.set(bean, value); + } else { + Object[] args = new Object[1]; + args[0] = value; + writeMethod.invoke(bean, args); + } + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "set " + name + " with arg[" + value + "] on ["+fullBeanName+"] with type[" + beanType + "] threw error"; + throw new RuntimeException(msg, ex); + } + } + + /** + * Set the value of the property. + */ + public void setRootValueIntercept(EntityBean bean, Object value) { + try { + if (bean instanceof EntityBean) { + setter.setIntercept(bean, value); + } else { + Object[] args = new Object[1]; + args[0] = value; + writeMethod.invoke(bean, args); + } + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "setIntercept " + name + " arg[" + value + "] on ["+fullBeanName+"] with type[" + beanType + "] threw error"; + throw new RuntimeException(msg, ex); + } + } +} 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 d794e306c..4a85f35ab 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundScalar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundScalar.java @@ -1,105 +1,105 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.config.ScalarTypeConverter; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.type.CtCompoundProperty; - -/** - * A BeanProperty owned by a Compound value object that maps to - * a real scalar type. - */ -public class BeanPropertyCompoundScalar extends BeanProperty { - - private final BeanPropertyCompoundRoot rootProperty; - - private final CtCompoundProperty ctProperty; - - @SuppressWarnings("rawtypes") - private final ScalarTypeConverter typeConverter; - - public BeanPropertyCompoundScalar(BeanPropertyCompoundRoot rootProperty, DeployBeanProperty scalarDeploy, - CtCompoundProperty ctProperty, ScalarTypeConverter typeConverter) { - - super(scalarDeploy); - this.rootProperty = rootProperty; - this.ctProperty = ctProperty; - this.typeConverter = typeConverter; - } - - /** - * Return one of the scalar values from a compound type. - */ - public Object getValueObject(Object compoundValue) { - if (typeConverter != null) { - compoundValue = typeConverter.unwrapValue(compoundValue); - } - return ctProperty.getValue(compoundValue); - } - - @SuppressWarnings("unchecked") - @Override - public Object getValue(EntityBean valueObject) { - Object val = valueObject; - if (typeConverter != null) { - val = typeConverter.unwrapValue(val); - } - return ctProperty.getValue(val); - } - - @Override - public void setValue(EntityBean bean, Object value) { - setValueInCompound(bean, value, false); - } - - @SuppressWarnings("unchecked") - public void setValueInCompound(EntityBean bean, Object value, boolean intercept) { - - Object compoundValue = ctProperty.setValue(bean, value); - - if (compoundValue != null) { - if (typeConverter != null) { - compoundValue = typeConverter.wrapValue(compoundValue); - } - // we are at the top level and we have a compound value - // that we can set using the root property - if (intercept) { - rootProperty.setRootValueIntercept(bean, compoundValue); - } else { - rootProperty.setRootValue(bean, compoundValue); - } - } - } - - /** - * No interception on embedded scalar values inside a CVO. - */ - @Override - public void setValueIntercept(EntityBean bean, Object value) { - setValueInCompound(bean, value, true); - } - - /** - * No interception on embedded scalar values inside a CVO. - */ - @Override - public Object getValueIntercept(EntityBean bean) { - return getValue(bean); - } - - @Override - public Object elGetReference(EntityBean bean) { - return getValue(bean); - } - - @Override - public Object elGetValue(EntityBean bean) { - return getValue(bean); - } - - @Override - public void elSetValue(EntityBean bean, Object value, boolean populate) {//, boolean reference) { - super.elSetValue(bean, value, populate); - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.config.ScalarTypeConverter; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.type.CtCompoundProperty; + +/** + * A BeanProperty owned by a Compound value object that maps to + * a real scalar type. + */ +public class BeanPropertyCompoundScalar extends BeanProperty { + + private final BeanPropertyCompoundRoot rootProperty; + + private final CtCompoundProperty ctProperty; + + @SuppressWarnings("rawtypes") + private final ScalarTypeConverter typeConverter; + + public BeanPropertyCompoundScalar(BeanPropertyCompoundRoot rootProperty, DeployBeanProperty scalarDeploy, + CtCompoundProperty ctProperty, ScalarTypeConverter typeConverter) { + + super(scalarDeploy); + this.rootProperty = rootProperty; + this.ctProperty = ctProperty; + this.typeConverter = typeConverter; + } + + /** + * Return one of the scalar values from a compound type. + */ + public Object getValueObject(Object compoundValue) { + if (typeConverter != null) { + compoundValue = typeConverter.unwrapValue(compoundValue); + } + return ctProperty.getValue(compoundValue); + } + + @SuppressWarnings("unchecked") + @Override + public Object getValue(EntityBean valueObject) { + Object val = valueObject; + if (typeConverter != null) { + val = typeConverter.unwrapValue(val); + } + return ctProperty.getValue(val); + } + + @Override + public void setValue(EntityBean bean, Object value) { + setValueInCompound(bean, value, false); + } + + @SuppressWarnings("unchecked") + public void setValueInCompound(EntityBean bean, Object value, boolean intercept) { + + Object compoundValue = ctProperty.setValue(bean, value); + + if (compoundValue != null) { + if (typeConverter != null) { + compoundValue = typeConverter.wrapValue(compoundValue); + } + // we are at the top level and we have a compound value + // that we can set using the root property + if (intercept) { + rootProperty.setRootValueIntercept(bean, compoundValue); + } else { + rootProperty.setRootValue(bean, compoundValue); + } + } + } + + /** + * No interception on embedded scalar values inside a CVO. + */ + @Override + public void setValueIntercept(EntityBean bean, Object value) { + setValueInCompound(bean, value, true); + } + + /** + * No interception on embedded scalar values inside a CVO. + */ + @Override + public Object getValueIntercept(EntityBean bean) { + return getValue(bean); + } + + @Override + public Object elGetReference(EntityBean bean) { + return getValue(bean); + } + + @Override + public Object elGetValue(EntityBean bean) { + return getValue(bean); + } + + @Override + 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/BeanPropertyOverride.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyOverride.java index 17924df4a..7791f4ad9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyOverride.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyOverride.java @@ -1,45 +1,45 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; - -/** - * Used hold meta data when a bean property is overridden. - *

- * Typically this is for Embedded Beans. - *

- */ -public class BeanPropertyOverride { - - private final String dbColumn; - - private final String sqlFormulaSelect; - - private final String sqlFormulaJoin; - - public BeanPropertyOverride(String dbColumn) { - this(dbColumn, null, null); - } - - public BeanPropertyOverride(String dbColumn, String sqlFormulaSelect, String sqlFormulaJoin) { - this.dbColumn = InternString.intern(dbColumn); - this.sqlFormulaSelect = InternString.intern(sqlFormulaSelect); - this.sqlFormulaJoin = InternString.intern(sqlFormulaJoin); - } - - public String getDbColumn() { - return dbColumn; - } - - public String getSqlFormulaSelect() { - return sqlFormulaSelect; - } - - public String getSqlFormulaJoin() { - return sqlFormulaJoin; - } - - public String replace(String src, String srcDbColumn){ - return StringHelper.replaceString(src, srcDbColumn, dbColumn); - } -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; + +/** + * Used hold meta data when a bean property is overridden. + *

+ * Typically this is for Embedded Beans. + *

+ */ +public class BeanPropertyOverride { + + private final String dbColumn; + + private final String sqlFormulaSelect; + + private final String sqlFormulaJoin; + + public BeanPropertyOverride(String dbColumn) { + this(dbColumn, null, null); + } + + public BeanPropertyOverride(String dbColumn, String sqlFormulaSelect, String sqlFormulaJoin) { + this.dbColumn = InternString.intern(dbColumn); + this.sqlFormulaSelect = InternString.intern(sqlFormulaSelect); + this.sqlFormulaJoin = InternString.intern(sqlFormulaJoin); + } + + public String getDbColumn() { + return dbColumn; + } + + public String getSqlFormulaSelect() { + return sqlFormulaSelect; + } + + public String getSqlFormulaJoin() { + return sqlFormulaJoin; + } + + public String replace(String src, String srcDbColumn){ + return StringHelper.replaceString(src, srcDbColumn, dbColumn); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertySimpleCollection.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertySimpleCollection.java index 5572c58cc..91eda0fb2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertySimpleCollection.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertySimpleCollection.java @@ -1,15 +1,15 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection; - -public class BeanPropertySimpleCollection extends BeanPropertyAssocMany { - - public BeanPropertySimpleCollection(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertySimpleCollection deploy) { - super(owner, descriptor, deploy); - } - - public void initialise() { - super.initialise(); - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection; + +public class BeanPropertySimpleCollection extends BeanPropertyAssocMany { + + public BeanPropertySimpleCollection(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertySimpleCollection deploy) { + super(owner, descriptor, deploy); + } + + public void initialise() { + super.initialise(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanQueryAdapterManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanQueryAdapterManager.java index d8b5c95c8..3ba40998e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanQueryAdapterManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanQueryAdapterManager.java @@ -1,43 +1,43 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.util.List; - -import com.avaje.ebean.event.BeanQueryAdapter; -import com.avaje.ebeaninternal.server.core.BootupClasses; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Default implementation for creating BeanControllers. - */ -public class BeanQueryAdapterManager { - - private static final Logger logger = LoggerFactory.getLogger(BeanQueryAdapterManager.class); - - private final List list; - - public BeanQueryAdapterManager(BootupClasses bootupClasses){ - - list = bootupClasses.getBeanQueryAdapters(); - } - - public int getRegisterCount() { - return list.size(); - } - - /** - * Return the BeanPersistController for a given entity type. - */ - public void addQueryAdapter(DeployBeanDescriptor deployDesc){ - - for (int i = 0; i < list.size(); i++) { - BeanQueryAdapter c = list.get(i); - if (c.isRegisterFor(deployDesc.getBeanType())){ - logger.debug("BeanQueryAdapter on[" + deployDesc.getFullName() + "] " + c.getClass().getName()); - deployDesc.addQueryAdapter(c); - } - } - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import java.util.List; + +import com.avaje.ebean.event.BeanQueryAdapter; +import com.avaje.ebeaninternal.server.core.BootupClasses; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Default implementation for creating BeanControllers. + */ +public class BeanQueryAdapterManager { + + private static final Logger logger = LoggerFactory.getLogger(BeanQueryAdapterManager.class); + + private final List list; + + public BeanQueryAdapterManager(BootupClasses bootupClasses){ + + list = bootupClasses.getBeanQueryAdapters(); + } + + public int getRegisterCount() { + return list.size(); + } + + /** + * Return the BeanPersistController for a given entity type. + */ + public void addQueryAdapter(DeployBeanDescriptor deployDesc){ + + for (int i = 0; i < list.size(); i++) { + BeanQueryAdapter c = list.get(i); + if (c.isRegisterFor(deployDesc.getBeanType())){ + logger.debug("BeanQueryAdapter on[" + deployDesc.getFullName() + "] " + c.getClass().getName()); + deployDesc.addQueryAdapter(c); + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java index b7c9ee7be..f7cd0e67b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanSetHelp.java @@ -1,149 +1,149 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.io.IOException; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.Set; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.Query; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.BeanCollectionAdd; -import com.avaje.ebean.bean.BeanCollectionLoader; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.common.BeanSet; -import com.avaje.ebeaninternal.server.text.json.WriteJson; - -/** - * Helper specifically for dealing with Sets. - */ -public final class BeanSetHelp implements BeanCollectionHelp { - - private final BeanPropertyAssocMany many; - private final BeanDescriptor targetDescriptor; - private final String propertyName; - private BeanCollectionLoader loader; - - /** - * When attached to a specific many property. - */ - public BeanSetHelp(BeanPropertyAssocMany many) { - this.many = many; - this.targetDescriptor = many.getTargetDescriptor(); - this.propertyName = many.getName(); - } - - /** - * For a query that returns a set. - */ - public BeanSetHelp() { - this.many = null; - this.targetDescriptor = null; - this.propertyName = null; - } - - @Override - public void setLoader(BeanCollectionLoader loader) { - this.loader = loader; - } - - @Override - public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { - if (bc instanceof BeanSet) { - BeanSet beanSet = (BeanSet) bc; - if (beanSet.getActualSet() == null) { - beanSet.setActualSet(new LinkedHashSet()); - } - return beanSet; - - } else { - throw new RuntimeException("Unhandled type " + bc); - } - } - - public void add(BeanCollection collection, EntityBean bean) { - collection.internalAdd(bean); - } - - @Override - public BeanCollection createEmptyNoParent() { - return new BeanSet(); - } - - @Override - public BeanCollection createEmpty(EntityBean ownerBean) { - BeanSet beanSet = new BeanSet(loader, ownerBean, propertyName); - if (many != null) { - beanSet.setModifyListening(many.getModifyListenMode()); - } - return beanSet; - } - - @Override - public BeanCollection createReference(EntityBean parentBean) { - - BeanSet beanSet = new BeanSet(loader, parentBean, propertyName); - beanSet.setModifyListening(many.getModifyListenMode()); - return beanSet; - } - - @Override - public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { - - BeanSet newBeanSet = (BeanSet) server.findSet(query, t); - refresh(newBeanSet, parentBean); - } - - @Override - public void refresh(BeanCollection bc, EntityBean parentBean) { - - BeanSet newBeanSet = (BeanSet) bc; - - Set current = (Set) many.getValue(parentBean); - - newBeanSet.setModifyListening(many.getModifyListenMode()); - if (current == null) { - // the currentList is null? Not really expecting this... - many.setValue(parentBean, newBeanSet); - - } else if (current instanceof BeanSet) { - // normally this case, replace just the underlying list - BeanSet currentBeanSet = (BeanSet) current; - currentBeanSet.setActualSet(newBeanSet.getActualSet()); - currentBeanSet.setModifyListening(many.getModifyListenMode()); - - } else { - // replace the entire set - many.setValue(parentBean, newBeanSet); - } - } - - @Override - public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException { - - Set set; - if (collection instanceof BeanCollection) { - BeanSet bc = (BeanSet) collection; - if (!bc.isPopulated()) { - if (explicitInclude) { - // invoke lazy loading as collection - // is explicitly included in the output - bc.size(); - } else { - return; - } - } - set = bc.getActualSet(); - } else { - set = (Set) collection; - } - - ctx.writeStartArray(name); - Iterator it = set.iterator(); - while (it.hasNext()) { - targetDescriptor.jsonWrite(ctx, (EntityBean) it.next()); - } - ctx.writeEndArray(); - } -} +package com.avaje.ebeaninternal.server.deploy; + +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Query; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.BeanCollectionAdd; +import com.avaje.ebean.bean.BeanCollectionLoader; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.common.BeanSet; +import com.avaje.ebeaninternal.server.text.json.WriteJson; + +/** + * Helper specifically for dealing with Sets. + */ +public final class BeanSetHelp implements BeanCollectionHelp { + + private final BeanPropertyAssocMany many; + private final BeanDescriptor targetDescriptor; + private final String propertyName; + private BeanCollectionLoader loader; + + /** + * When attached to a specific many property. + */ + public BeanSetHelp(BeanPropertyAssocMany many) { + this.many = many; + this.targetDescriptor = many.getTargetDescriptor(); + this.propertyName = many.getName(); + } + + /** + * For a query that returns a set. + */ + public BeanSetHelp() { + this.many = null; + this.targetDescriptor = null; + this.propertyName = null; + } + + @Override + public void setLoader(BeanCollectionLoader loader) { + this.loader = loader; + } + + @Override + public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { + if (bc instanceof BeanSet) { + BeanSet beanSet = (BeanSet) bc; + if (beanSet.getActualSet() == null) { + beanSet.setActualSet(new LinkedHashSet()); + } + return beanSet; + + } else { + throw new RuntimeException("Unhandled type " + bc); + } + } + + public void add(BeanCollection collection, EntityBean bean) { + collection.internalAdd(bean); + } + + @Override + public BeanCollection createEmptyNoParent() { + return new BeanSet(); + } + + @Override + public BeanCollection createEmpty(EntityBean ownerBean) { + BeanSet beanSet = new BeanSet(loader, ownerBean, propertyName); + if (many != null) { + beanSet.setModifyListening(many.getModifyListenMode()); + } + return beanSet; + } + + @Override + public BeanCollection createReference(EntityBean parentBean) { + + BeanSet beanSet = new BeanSet(loader, parentBean, propertyName); + beanSet.setModifyListening(many.getModifyListenMode()); + return beanSet; + } + + @Override + public void refresh(EbeanServer server, Query query, Transaction t, EntityBean parentBean) { + + BeanSet newBeanSet = (BeanSet) server.findSet(query, t); + refresh(newBeanSet, parentBean); + } + + @Override + public void refresh(BeanCollection bc, EntityBean parentBean) { + + BeanSet newBeanSet = (BeanSet) bc; + + Set current = (Set) many.getValue(parentBean); + + newBeanSet.setModifyListening(many.getModifyListenMode()); + if (current == null) { + // the currentList is null? Not really expecting this... + many.setValue(parentBean, newBeanSet); + + } else if (current instanceof BeanSet) { + // normally this case, replace just the underlying list + BeanSet currentBeanSet = (BeanSet) current; + currentBeanSet.setActualSet(newBeanSet.getActualSet()); + currentBeanSet.setModifyListening(many.getModifyListenMode()); + + } else { + // replace the entire set + many.setValue(parentBean, newBeanSet); + } + } + + @Override + public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException { + + Set set; + if (collection instanceof BeanCollection) { + BeanSet bc = (BeanSet) collection; + if (!bc.isPopulated()) { + if (explicitInclude) { + // invoke lazy loading as collection + // is explicitly included in the output + bc.size(); + } else { + return; + } + } + set = bc.getActualSet(); + } else { + set = (Set) collection; + } + + ctx.writeStartArray(name); + Iterator it = set.iterator(); + while (it.hasNext()) { + targetDescriptor.jsonWrite(ctx, (EntityBean) it.next()); + } + ctx.writeEndArray(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanTable.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanTable.java index 0d55eb9dd..f52116158 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanTable.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanTable.java @@ -1,117 +1,117 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * Used for associated beans in place of a BeanDescriptor. This is done to avoid - * recursion issues due to the potentially bi-directional and circular - * relationships between beans. - *

- * It holds the main deployment information and not all the detail that is held - * in a BeanDescriptor. - *

- */ -public class BeanTable { - - private static final Logger logger = LoggerFactory.getLogger(BeanTable.class); - - private final Class beanType; - - /** - * The base table. - */ - private final String baseTable; - - private final BeanProperty[] idProperties; - - /** - * Create the BeanTable. - */ - public BeanTable(DeployBeanTable mutable, BeanDescriptorMap owner) { - this.beanType = mutable.getBeanType(); - this.baseTable = InternString.intern(mutable.getBaseTable()); - this.idProperties = mutable.createIdProperties(owner); - } - - public String toString(){ - return baseTable; - } - - /** - * Return the base table for this BeanTable. - * This is used to determine the join information - * for associations. - */ - public String getBaseTable() { - return baseTable; - } - - /** - * Gets the unqualified base table. - * - * @return the unqualified base table - */ - public String getUnqualifiedBaseTable(){ - final String[] chunks = baseTable.split("\\."); - return chunks.length == 2 ? chunks[1] :chunks[0]; - } - - /** - * Return the Id properties. - */ - public BeanProperty[] getIdProperties() { - return idProperties; - } - - /** - * Return the class for this beanTable. - */ - public Class getBeanType() { - return beanType; - } - - public void createJoinColumn(String foreignKeyPrefix, DeployTableJoin join, boolean reverse) { - - boolean complexKey = false; - BeanProperty[] props = idProperties; - - if (idProperties.length == 1){ - if (idProperties[0] instanceof BeanPropertyAssocOne) { - BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne)idProperties[0]; - props = assocOne.getProperties(); - complexKey = true; - } - } - - for (int i = 0; i < props.length; i++) { - - String lc = props[i].getDbColumn(); - String fk = lc; - if (foreignKeyPrefix != null){ - fk = foreignKeyPrefix+"_"+fk; - } - - if (complexKey){ - // just to copy the column name rather than prefix with the foreignKeyPrefix. - // I think that with complex keys this is the more common approach. - String msg = "On table["+baseTable+"] foreign key column ["+lc+"]"; - logger.debug(msg); - fk = lc; - } - - DeployTableJoinColumn joinCol = new DeployTableJoinColumn(lc, fk); - if (reverse){ - joinCol = joinCol.reverse(); - } - join.addJoinColumn(joinCol); - } - - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Used for associated beans in place of a BeanDescriptor. This is done to avoid + * recursion issues due to the potentially bi-directional and circular + * relationships between beans. + *

+ * It holds the main deployment information and not all the detail that is held + * in a BeanDescriptor. + *

+ */ +public class BeanTable { + + private static final Logger logger = LoggerFactory.getLogger(BeanTable.class); + + private final Class beanType; + + /** + * The base table. + */ + private final String baseTable; + + private final BeanProperty[] idProperties; + + /** + * Create the BeanTable. + */ + public BeanTable(DeployBeanTable mutable, BeanDescriptorMap owner) { + this.beanType = mutable.getBeanType(); + this.baseTable = InternString.intern(mutable.getBaseTable()); + this.idProperties = mutable.createIdProperties(owner); + } + + public String toString(){ + return baseTable; + } + + /** + * Return the base table for this BeanTable. + * This is used to determine the join information + * for associations. + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Gets the unqualified base table. + * + * @return the unqualified base table + */ + public String getUnqualifiedBaseTable(){ + final String[] chunks = baseTable.split("\\."); + return chunks.length == 2 ? chunks[1] :chunks[0]; + } + + /** + * Return the Id properties. + */ + public BeanProperty[] getIdProperties() { + return idProperties; + } + + /** + * Return the class for this beanTable. + */ + public Class getBeanType() { + return beanType; + } + + public void createJoinColumn(String foreignKeyPrefix, DeployTableJoin join, boolean reverse) { + + boolean complexKey = false; + BeanProperty[] props = idProperties; + + if (idProperties.length == 1){ + if (idProperties[0] instanceof BeanPropertyAssocOne) { + BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne)idProperties[0]; + props = assocOne.getProperties(); + complexKey = true; + } + } + + for (int i = 0; i < props.length; i++) { + + String lc = props[i].getDbColumn(); + String fk = lc; + if (foreignKeyPrefix != null){ + fk = foreignKeyPrefix+"_"+fk; + } + + if (complexKey){ + // just to copy the column name rather than prefix with the foreignKeyPrefix. + // I think that with complex keys this is the more common approach. + String msg = "On table["+baseTable+"] foreign key column ["+lc+"]"; + logger.debug(msg); + fk = lc; + } + + DeployTableJoinColumn joinCol = new DeployTableJoinColumn(lc, fk); + if (reverse){ + joinCol = joinCol.reverse(); + } + join.addJoinColumn(joinCol); + } + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueContraint.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueContraint.java index 04a5e5819..a38ee9c57 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueContraint.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueContraint.java @@ -1,21 +1,21 @@ -package com.avaje.ebeaninternal.server.deploy; - -/** - * Holds multiple column unique constraints defined for an entity. - */ -public class CompoundUniqueContraint { - - private final String[] columns; - - public CompoundUniqueContraint(String[] columns) { - this.columns = columns; - } - - /** - * Return the columns that make up this unique constraint. - */ - public String[] getColumns() { - return columns; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +/** + * Holds multiple column unique constraints defined for an entity. + */ +public class CompoundUniqueContraint { + + private final String[] columns; + + public CompoundUniqueContraint(String[] columns) { + this.columns = columns; + } + + /** + * Return the columns that make up this unique constraint. + */ + public String[] getColumns() { + return columns; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/CopyContext.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/CopyContext.java index 72d4296c4..2bf41fad7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/CopyContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/CopyContext.java @@ -1,64 +1,64 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; - -/** - * Provides context when performing a bean copy. - * - * @author rbygrave - */ -public class CopyContext { - - private final boolean vanillaMode; - - private final boolean sharing; - - private final PersistenceContext pc; - - public CopyContext(boolean vanillaMode, boolean sharing) { - this.vanillaMode = vanillaMode; - this.sharing = sharing; - this.pc = new DefaultPersistenceContext(); - } - - public CopyContext(boolean vanillaMode) { - this(vanillaMode, false); - } - - /** - * Return true if the copy should be a vanilla bean. - */ - public boolean isVanillaMode() { - return vanillaMode; - } - - /** - * Return true if the copy should be safe for sharing. - */ - public boolean isSharing() { - return sharing; - } - - /** - * Return the persistence context used during the copy. - */ - public PersistenceContext getPersistenceContext() { - return pc; - } - - /** - * Put the bean if absent into the persistence context. - */ - public Object putIfAbsent(Object id, Object bean){ - return pc.putIfAbsent(id, bean); - } - - /** - * Return the bean for the given type and id from the persistence context. - */ - public Object get(Class beanType, Object beanId){ - return pc.get(beanType, beanId); - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; + +/** + * Provides context when performing a bean copy. + * + * @author rbygrave + */ +public class CopyContext { + + private final boolean vanillaMode; + + private final boolean sharing; + + private final PersistenceContext pc; + + public CopyContext(boolean vanillaMode, boolean sharing) { + this.vanillaMode = vanillaMode; + this.sharing = sharing; + this.pc = new DefaultPersistenceContext(); + } + + public CopyContext(boolean vanillaMode) { + this(vanillaMode, false); + } + + /** + * Return true if the copy should be a vanilla bean. + */ + public boolean isVanillaMode() { + return vanillaMode; + } + + /** + * Return true if the copy should be safe for sharing. + */ + public boolean isSharing() { + return sharing; + } + + /** + * Return the persistence context used during the copy. + */ + public PersistenceContext getPersistenceContext() { + return pc; + } + + /** + * Put the bean if absent into the persistence context. + */ + public Object putIfAbsent(Object id, Object bean){ + return pc.putIfAbsent(id, bean); + } + + /** + * Return the bean for the given type and id from the persistence context. + */ + public Object get(Class beanType, Object beanId){ + return pc.get(beanType, beanId); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlColumnInfo.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlColumnInfo.java index 02b735c2e..f83945284 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlColumnInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlColumnInfo.java @@ -1,39 +1,39 @@ -package com.avaje.ebeaninternal.server.deploy; - -public class DRawSqlColumnInfo { - - final String name; - - final String label; - - final String propertyName; - - final boolean scalarProperty; - - public DRawSqlColumnInfo(String name, String label, String propertyName, boolean scalarProperty) { - this.name = name; - this.label = label; - this.propertyName = propertyName; - this.scalarProperty = scalarProperty; - } - - public String getName() { - return name; - } - - public String getLabel() { - return label; - } - - public String getPropertyName() { - return propertyName; - } - - public boolean isScalarProperty() { - return scalarProperty; - } - - public String toString() { - return "name:" + name + " label:" + label + " prop:" + propertyName; - } +package com.avaje.ebeaninternal.server.deploy; + +public class DRawSqlColumnInfo { + + final String name; + + final String label; + + final String propertyName; + + final boolean scalarProperty; + + public DRawSqlColumnInfo(String name, String label, String propertyName, boolean scalarProperty) { + this.name = name; + this.label = label; + this.propertyName = propertyName; + this.scalarProperty = scalarProperty; + } + + public String getName() { + return name; + } + + public String getLabel() { + return label; + } + + public String getPropertyName() { + return propertyName; + } + + public boolean isScalarProperty() { + return scalarProperty; + } + + public String toString() { + return "name:" + name + " label:" + label + " prop:" + propertyName; + } } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DefaultBeanFinderManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DefaultBeanFinderManager.java index 067e245b5..169441f76 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DefaultBeanFinderManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DefaultBeanFinderManager.java @@ -1,61 +1,61 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.util.HashMap; -import java.util.List; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.event.BeanFinder; - -/** - * Default implementation for BeanFinderFactory. - */ -public class DefaultBeanFinderManager implements BeanFinderManager { - - HashMap, BeanFinder> registerFor = new HashMap, BeanFinder>(); - - public int createBeanFinders(List> finderClassList) { - - for (Class cls : finderClassList) { - Class entityType = getEntityClass(cls); - try { - BeanFinder beanFinder = (BeanFinder) cls.newInstance(); - registerFor.put(entityType, beanFinder); - - } catch (Exception ex) { - throw new PersistenceException(ex); - } - } - - return registerFor.size(); - } - - public int getRegisterCount() { - return registerFor.size(); - } - - /** - * Return the BeanFinder for a given entity type. - */ - @SuppressWarnings("unchecked") - public BeanFinder getBeanFinder(Class entityType) { - return (BeanFinder)registerFor.get(entityType); - } - - /** - * Find the entity class given the controller class. - *

- * This uses reflection to find the generics parameter type. - *

- */ - private Class getEntityClass(Class controller){ - - Class cls = ParamTypeUtil.findParamType(controller, BeanFinder.class); - - if (cls == null){ - String msg = "Could not determine the entity class (generics parameter type) from "+controller+" using reflection."; - throw new PersistenceException(msg); - } - return cls; - } -} +package com.avaje.ebeaninternal.server.deploy; + +import java.util.HashMap; +import java.util.List; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.event.BeanFinder; + +/** + * Default implementation for BeanFinderFactory. + */ +public class DefaultBeanFinderManager implements BeanFinderManager { + + HashMap, BeanFinder> registerFor = new HashMap, BeanFinder>(); + + public int createBeanFinders(List> finderClassList) { + + for (Class cls : finderClassList) { + Class entityType = getEntityClass(cls); + try { + BeanFinder beanFinder = (BeanFinder) cls.newInstance(); + registerFor.put(entityType, beanFinder); + + } catch (Exception ex) { + throw new PersistenceException(ex); + } + } + + return registerFor.size(); + } + + public int getRegisterCount() { + return registerFor.size(); + } + + /** + * Return the BeanFinder for a given entity type. + */ + @SuppressWarnings("unchecked") + public BeanFinder getBeanFinder(Class entityType) { + return (BeanFinder)registerFor.get(entityType); + } + + /** + * Find the entity class given the controller class. + *

+ * This uses reflection to find the generics parameter type. + *

+ */ + private Class getEntityClass(Class controller){ + + Class cls = ParamTypeUtil.findParamType(controller, BeanFinder.class); + + if (cls == null){ + String msg = "Could not determine the entity class (generics parameter type) from "+controller+" using reflection."; + throw new PersistenceException(msg); + } + return cls; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DeployOrmXml.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DeployOrmXml.java index 31a2ba175..496f0b7dd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DeployOrmXml.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DeployOrmXml.java @@ -1,171 +1,171 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import com.avaje.ebeaninternal.server.lib.resource.ResourceContent; -import com.avaje.ebeaninternal.server.lib.resource.ResourceSource; -import com.avaje.ebeaninternal.server.lib.util.Dnode; -import com.avaje.ebeaninternal.server.lib.util.DnodeReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Controls the creation and caching of BeanManager's, BeanDescriptors, - * BeanTable etc for both beans and tables(MapBeans). - *

- * Also supports some other deployment features such as type conversion. - *

- */ -public class DeployOrmXml { - - private static final Logger logger = LoggerFactory.getLogger(DeployOrmXml.class); - - private final HashMap nativeQueryCache; - - private final ArrayList ormXmlList; - - private final ResourceSource resSource; - - public DeployOrmXml(ResourceSource resSource) { - - this.resSource = resSource; - this.nativeQueryCache = new HashMap(); - this.ormXmlList = findAllOrmXml(); - - - initialiseNativeQueries(); - } - - /** - * Register all the native queries in ALL orm xml deployment. - */ - private void initialiseNativeQueries() { - for (Dnode ormXml : ormXmlList) { - initialiseNativeQueries(ormXml); - } - } - - /** - * Register the native queries in this particular orm xml deployment. - */ - private void initialiseNativeQueries(Dnode ormXml) { - - Dnode entityMappings = ormXml.find("entity-mappings"); - if (entityMappings != null) { - List nq = entityMappings.findAll("named-native-query", 1); - for (int i = 0; i < nq.size(); i++) { - Dnode nqNode = nq.get(i); - Dnode nqQueryNode = nqNode.find("query"); - if (nqQueryNode != null) { - String queryContent = nqQueryNode.getNodeContent(); - String queryName = (String) nqNode.getAttribute("name"); - - if (queryName != null && queryContent != null) { - DNativeQuery query = new DNativeQuery(queryContent); - nativeQueryCache.put(queryName, query); - } - } - } - } - } - - /** - * Return a native named query. - *

- * These are loaded from the orm.xml deployment file. - *

- */ - public DNativeQuery getNativeQuery(String name) { - return nativeQueryCache.get(name); - } - - private ArrayList findAllOrmXml() { - - ArrayList ormXmlList = new ArrayList(); - - - String defaultFile = "orm.xml"; - readOrmXml(defaultFile, ormXmlList); - - if (!ormXmlList.isEmpty()) { - StringBuilder sb = new StringBuilder(); - for (Dnode ox : ormXmlList) { - sb.append(", ").append(ox.getAttribute("ebean.filename")); - } - String loadedFiles = sb.toString().substring(2); - logger.info("Deployment xml [" + loadedFiles + "] loaded."); - } - - return ormXmlList; - } - - private boolean readOrmXml(String ormXmlName, ArrayList ormXmlList) { - - try { - Dnode ormXml = null; - ResourceContent content = resSource.getContent(ormXmlName); - if (content != null) { - // servlet resource or file system... - ormXml = readOrmXml(content.getInputStream()); - - } else { - // try the classpath... - ormXml = readOrmXmlFromClasspath(ormXmlName); - } - - if (ormXml != null) { - ormXml.setAttribute("ebean.filename", ormXmlName); - ormXmlList.add(ormXml); - return true; - - } else { - return false; - } - } catch (IOException e) { - logger.error("error reading orm xml deployment " + ormXmlName, e); - return false; - } - } - - private Dnode readOrmXmlFromClasspath(String ormXmlName) throws IOException { - InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(ormXmlName); - if (is == null) { - return null; - } else { - return readOrmXml(is); - } - } - - private Dnode readOrmXml(InputStream in) throws IOException { - DnodeReader reader = new DnodeReader(); - Dnode ormXml = reader.parseXml(in); - in.close(); - return ormXml; - } - - /** - * Find the deployment xml for a given entity. This will return null if no - * matching deployment xml is found for this entity. - *

- * This searches all the ormXml files and returns the first match. - *

- */ - public Dnode findEntityDeploymentXml(String className) { - - for (Dnode ormXml : ormXmlList) { - Dnode entityMappings = ormXml.find("entity-mappings"); - - List entities = entityMappings.findAll("entity", "class", className, 1); - if (entities.size() == 1) { - return entities.get(0); - } - } - - return null; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import com.avaje.ebeaninternal.server.lib.resource.ResourceContent; +import com.avaje.ebeaninternal.server.lib.resource.ResourceSource; +import com.avaje.ebeaninternal.server.lib.util.Dnode; +import com.avaje.ebeaninternal.server.lib.util.DnodeReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Controls the creation and caching of BeanManager's, BeanDescriptors, + * BeanTable etc for both beans and tables(MapBeans). + *

+ * Also supports some other deployment features such as type conversion. + *

+ */ +public class DeployOrmXml { + + private static final Logger logger = LoggerFactory.getLogger(DeployOrmXml.class); + + private final HashMap nativeQueryCache; + + private final ArrayList ormXmlList; + + private final ResourceSource resSource; + + public DeployOrmXml(ResourceSource resSource) { + + this.resSource = resSource; + this.nativeQueryCache = new HashMap(); + this.ormXmlList = findAllOrmXml(); + + + initialiseNativeQueries(); + } + + /** + * Register all the native queries in ALL orm xml deployment. + */ + private void initialiseNativeQueries() { + for (Dnode ormXml : ormXmlList) { + initialiseNativeQueries(ormXml); + } + } + + /** + * Register the native queries in this particular orm xml deployment. + */ + private void initialiseNativeQueries(Dnode ormXml) { + + Dnode entityMappings = ormXml.find("entity-mappings"); + if (entityMappings != null) { + List nq = entityMappings.findAll("named-native-query", 1); + for (int i = 0; i < nq.size(); i++) { + Dnode nqNode = nq.get(i); + Dnode nqQueryNode = nqNode.find("query"); + if (nqQueryNode != null) { + String queryContent = nqQueryNode.getNodeContent(); + String queryName = (String) nqNode.getAttribute("name"); + + if (queryName != null && queryContent != null) { + DNativeQuery query = new DNativeQuery(queryContent); + nativeQueryCache.put(queryName, query); + } + } + } + } + } + + /** + * Return a native named query. + *

+ * These are loaded from the orm.xml deployment file. + *

+ */ + public DNativeQuery getNativeQuery(String name) { + return nativeQueryCache.get(name); + } + + private ArrayList findAllOrmXml() { + + ArrayList ormXmlList = new ArrayList(); + + + String defaultFile = "orm.xml"; + readOrmXml(defaultFile, ormXmlList); + + if (!ormXmlList.isEmpty()) { + StringBuilder sb = new StringBuilder(); + for (Dnode ox : ormXmlList) { + sb.append(", ").append(ox.getAttribute("ebean.filename")); + } + String loadedFiles = sb.toString().substring(2); + logger.info("Deployment xml [" + loadedFiles + "] loaded."); + } + + return ormXmlList; + } + + private boolean readOrmXml(String ormXmlName, ArrayList ormXmlList) { + + try { + Dnode ormXml = null; + ResourceContent content = resSource.getContent(ormXmlName); + if (content != null) { + // servlet resource or file system... + ormXml = readOrmXml(content.getInputStream()); + + } else { + // try the classpath... + ormXml = readOrmXmlFromClasspath(ormXmlName); + } + + if (ormXml != null) { + ormXml.setAttribute("ebean.filename", ormXmlName); + ormXmlList.add(ormXml); + return true; + + } else { + return false; + } + } catch (IOException e) { + logger.error("error reading orm xml deployment " + ormXmlName, e); + return false; + } + } + + private Dnode readOrmXmlFromClasspath(String ormXmlName) throws IOException { + InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(ormXmlName); + if (is == null) { + return null; + } else { + return readOrmXml(is); + } + } + + private Dnode readOrmXml(InputStream in) throws IOException { + DnodeReader reader = new DnodeReader(); + Dnode ormXml = reader.parseXml(in); + in.close(); + return ormXml; + } + + /** + * Find the deployment xml for a given entity. This will return null if no + * matching deployment xml is found for this entity. + *

+ * This searches all the ormXml files and returns the first match. + *

+ */ + public Dnode findEntityDeploymentXml(String className) { + + for (Dnode ormXml : ormXmlList) { + Dnode entityMappings = ormXml.find("entity-mappings"); + + List entities = entityMappings.findAll("entity", "class", className, 1); + if (entities.size() == 1) { + return entities.get(0); + } + } + + return null; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DetermineManyType.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DetermineManyType.java index 6fe93ae59..212c407ae 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DetermineManyType.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DetermineManyType.java @@ -1,27 +1,27 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Determine the Many Type for a property. - */ -public class DetermineManyType { - - public DetermineManyType() { - } - - public ManyType getManyType(Class type) { - if (type.equals(List.class)) { - return ManyType.JAVA_LIST; - } - if (type.equals(Set.class)) { - return ManyType.JAVA_SET; - } - if (type.equals(Map.class)) { - return ManyType.JAVA_MAP; - } - return null; - } -} +package com.avaje.ebeaninternal.server.deploy; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Determine the Many Type for a property. + */ +public class DetermineManyType { + + public DetermineManyType() { + } + + public ManyType getManyType(Class type) { + if (type.equals(List.class)) { + return ManyType.JAVA_LIST; + } + if (type.equals(Set.class)) { + return ManyType.JAVA_SET; + } + if (type.equals(Map.class)) { + return ManyType.JAVA_MAP; + } + return null; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ExportedProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ExportedProperty.java index 2f21c1497..9cf7fdf1f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ExportedProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ExportedProperty.java @@ -1,53 +1,53 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.core.InternString; - -/** - * The Exported foreign key and property. - *

- * Used to for Assoc Manys to create references etc. - *

- */ -public class ExportedProperty { - - private final String foreignDbColumn; - - private final BeanProperty property; - - private final boolean embedded; - - public ExportedProperty(boolean embedded, String foreignDbColumn, BeanProperty property) { - this.embedded = embedded; - this.foreignDbColumn = InternString.intern(foreignDbColumn); - this.property = property; - } - - /** - * Return true if this is part of an embedded concatinated key. - */ - public boolean isEmbedded() { - return embedded; - } - - /** - * Return the property value from the bean. - */ - public Object getValue(EntityBean bean){ - return property.getValue(bean); - } - - /** - * Return the foreign database column matching this property. - *

- * We use this foreign database column in the query predicates - * in preference to a parentProperty.idProperty = value. - * Just using the foreign database column avoids triggering - * a join to the 'parent' table. - *

- */ - public String getForeignDbColumn() { - return foreignDbColumn; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.core.InternString; + +/** + * The Exported foreign key and property. + *

+ * Used to for Assoc Manys to create references etc. + *

+ */ +public class ExportedProperty { + + private final String foreignDbColumn; + + private final BeanProperty property; + + private final boolean embedded; + + public ExportedProperty(boolean embedded, String foreignDbColumn, BeanProperty property) { + this.embedded = embedded; + this.foreignDbColumn = InternString.intern(foreignDbColumn); + this.property = property; + } + + /** + * Return true if this is part of an embedded concatinated key. + */ + public boolean isEmbedded() { + return embedded; + } + + /** + * Return the property value from the bean. + */ + public Object getValue(EntityBean bean){ + return property.getValue(bean); + } + + /** + * Return the foreign database column matching this property. + *

+ * We use this foreign database column in the query predicates + * in preference to a parentProperty.idProperty = value. + * Just using the foreign database column avoids triggering + * a join to the 'parent' table. + *

+ */ + public String getForeignDbColumn() { + return foreignDbColumn; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/InheritInfo.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/InheritInfo.java index c2e429403..a38608da0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/InheritInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/InheritInfo.java @@ -1,344 +1,344 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; - -import javax.persistence.PersistenceException; - -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.parse.DeployInheritInfo; -import com.avaje.ebeaninternal.server.query.SqlTreeProperties; - -/** - * Represents a node in the Inheritance tree. Holds information regarding Super Subclass support. - */ -public class InheritInfo { - - private final String discriminatorStringValue; - private final Object discriminatorValue; - - private final String discriminatorColumn; - - private final int discriminatorType; - - private final int discriminatorLength; - - private final String where; - - private final Class type; - - private final ArrayList children = new ArrayList(); - - /** - * Map of discriminator values to InheritInfo. - */ - private final HashMap discMap; - - /** - * Map of class types to InheritInfo (taking into account subclass proxy classes). - */ - private final HashMap typeMap; - - private final InheritInfo parent; - - private final InheritInfo root; - - private BeanDescriptor descriptor; - - public InheritInfo(InheritInfo r, InheritInfo parent, DeployInheritInfo deploy) { - - this.parent = parent; - this.type = deploy.getType(); - this.discriminatorColumn = InternString.intern(deploy.getDiscriminatorColumn(parent)); - this.discriminatorValue = deploy.getDiscriminatorObjectValue(); - this.discriminatorStringValue = deploy.getDiscriminatorStringValue(); - - this.discriminatorType = deploy.getDiscriminatorType(parent); - this.discriminatorLength = deploy.getDiscriminatorLength(parent); - this.where = InternString.intern(deploy.getWhere()); - - if (r == null) { - // this is a root node - root = this; - discMap = new HashMap(); - typeMap = new HashMap(); - registerWithRoot(this); - - } else { - this.root = r; - // register with the root node... - discMap = null; - typeMap = null; - root.registerWithRoot(this); - } - } - - /** - * Visit all the children in the inheritance tree. - */ - public void visitChildren(InheritInfoVisitor visitor) { - - for (int i = 0; i < children.size(); i++) { - InheritInfo child = children.get(i); - visitor.visit(child); - child.visitChildren(visitor); - } - } - - /** - * return true if anything in the inheritance hierarchy has a relationship with a save cascade on - * it. - */ - public boolean isSaveRecurseSkippable() { - return root.isNodeSaveRecurseSkippable(); - } - - private boolean isNodeSaveRecurseSkippable() { - if (!descriptor.isSaveRecurseSkippable()) { - return false; - } - for (int i = 0; i < children.size(); i++) { - InheritInfo child = children.get(i); - if (!child.isNodeSaveRecurseSkippable()) { - return false; - } - } - return true; - } - - /** - * return true if anything in the inheritance hierarchy has a relationship with a delete cascade - * on it. - */ - public boolean isDeleteRecurseSkippable() { - return root.isNodeDeleteRecurseSkippable(); - } - - private boolean isNodeDeleteRecurseSkippable() { - if (!descriptor.isDeleteRecurseSkippable()) { - return false; - } - for (int i = 0; i < children.size(); i++) { - InheritInfo child = children.get(i); - if (!child.isNodeDeleteRecurseSkippable()) { - return false; - } - } - return true; - } - - /** - * Set the descriptor for this node. - */ - public void setDescriptor(BeanDescriptor descriptor) { - - this.descriptor = descriptor; - } - - /** - * Return the associated BeanDescriptor for this node. - */ - public BeanDescriptor getBeanDescriptor() { - return descriptor; - } - - /** - * Get the bean property additionally looking in the sub types. - */ - public BeanProperty findSubTypeProperty(String propertyName) { - - BeanProperty prop = null; - - for (int i = 0, x = children.size(); i < x; i++) { - InheritInfo childInfo = children.get(i); - - // recursively search this child bean descriptor - prop = childInfo.getBeanDescriptor().findBeanProperty(propertyName); - - if (prop != null) { - return prop; - } - } - - return null; - } - - /** - * Add the local properties for each sub class below this one. - */ - public void addChildrenProperties(SqlTreeProperties selectProps) { - - for (int i = 0, x = children.size(); i < x; i++) { - InheritInfo childInfo = children.get(i); - selectProps.add(childInfo.descriptor.propertiesLocal()); - - childInfo.addChildrenProperties(selectProps); - } - } - - /** - * Return the associated InheritInfo for this DB row read. - */ - public InheritInfo readType(DbReadContext ctx) throws SQLException { - - String discValue = ctx.getDataReader().getString(); - return readType(discValue); - } - - /** - * Return the associated InheritInfo for this discriminator value. - */ - public InheritInfo readType(String discValue) { - - if (discValue == null) { - return null; - } - - InheritInfo typeInfo = root.getType(discValue); - if (typeInfo == null) { - throw new PersistenceException("Inheritance type for discriminator value [" + discValue + "] was not found?"); - } - return typeInfo; - } - - /** - * Return the associated InheritInfo for this bean type. - */ - public InheritInfo readType(Class beanType) { - - InheritInfo typeInfo = root.getTypeByClass(beanType); - if (typeInfo == null) { - throw new PersistenceException("Inheritance type for bean type [" + beanType.getName() + "] was not found?"); - } - return typeInfo; - } - - /** - * Create an EntityBean for this type. - */ - public EntityBean createEntityBean() { - return descriptor.createEntityBean(); - } - - /** - * Return the IdBinder for this type. - */ - public IdBinder getIdBinder() { - return descriptor.getIdBinder(); - } - - /** - * return the type. - */ - public Class getType() { - return type; - } - - /** - * Return the root node of the tree. - *

- * The root has a map of discriminator values to types. - *

- */ - public InheritInfo getRoot() { - return root; - } - - /** - * Return the parent node. - */ - public InheritInfo getParent() { - return parent; - } - - /** - * Return true if this is abstract node. - */ - public boolean isAbstract() { - return (discriminatorValue == null); - } - - /** - * Return true if this is the root node. - */ - public boolean isRoot() { - return parent == null; - } - - /** - * For a discriminator get the inheritance information for this tree. - */ - public InheritInfo getType(String discValue) { - return discMap.get(discValue); - } - - /** - * Return the InheritInfo for the given bean type. - */ - private InheritInfo getTypeByClass(Class beanType) { - return typeMap.get(beanType.getName()); - } - - private void registerWithRoot(InheritInfo info) { - if (info.getDiscriminatorStringValue() != null) { - String stringDiscValue = info.getDiscriminatorStringValue(); - discMap.put(stringDiscValue, info); - } - typeMap.put(info.getType().getName(), info); - } - - /** - * Add a child node. - */ - public void addChild(InheritInfo childInfo) { - children.add(childInfo); - } - - /** - * Return the derived where for the discriminator. - */ - public String getWhere() { - - return where; - } - - /** - * Return the column name of the discriminator. - */ - public String getDiscriminatorColumn() { - return discriminatorColumn; - } - - /** - * Return the sql type of the discriminator value. - */ - public int getDiscriminatorType() { - return discriminatorType; - } - - /** - * Return the length of the discriminator column. - */ - public int getDiscriminatorLength() { - return discriminatorLength; - } - - /** - * Return the discriminator value for this node. - */ - public String getDiscriminatorStringValue() { - return discriminatorStringValue; - } - - public Object getDiscriminatorValue() { - return discriminatorValue; - } - - public String toString() { - return "InheritInfo[" + type.getName() + "] disc[" + discriminatorStringValue + "]"; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; + +import javax.persistence.PersistenceException; + +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.parse.DeployInheritInfo; +import com.avaje.ebeaninternal.server.query.SqlTreeProperties; + +/** + * Represents a node in the Inheritance tree. Holds information regarding Super Subclass support. + */ +public class InheritInfo { + + private final String discriminatorStringValue; + private final Object discriminatorValue; + + private final String discriminatorColumn; + + private final int discriminatorType; + + private final int discriminatorLength; + + private final String where; + + private final Class type; + + private final ArrayList children = new ArrayList(); + + /** + * Map of discriminator values to InheritInfo. + */ + private final HashMap discMap; + + /** + * Map of class types to InheritInfo (taking into account subclass proxy classes). + */ + private final HashMap typeMap; + + private final InheritInfo parent; + + private final InheritInfo root; + + private BeanDescriptor descriptor; + + public InheritInfo(InheritInfo r, InheritInfo parent, DeployInheritInfo deploy) { + + this.parent = parent; + this.type = deploy.getType(); + this.discriminatorColumn = InternString.intern(deploy.getDiscriminatorColumn(parent)); + this.discriminatorValue = deploy.getDiscriminatorObjectValue(); + this.discriminatorStringValue = deploy.getDiscriminatorStringValue(); + + this.discriminatorType = deploy.getDiscriminatorType(parent); + this.discriminatorLength = deploy.getDiscriminatorLength(parent); + this.where = InternString.intern(deploy.getWhere()); + + if (r == null) { + // this is a root node + root = this; + discMap = new HashMap(); + typeMap = new HashMap(); + registerWithRoot(this); + + } else { + this.root = r; + // register with the root node... + discMap = null; + typeMap = null; + root.registerWithRoot(this); + } + } + + /** + * Visit all the children in the inheritance tree. + */ + public void visitChildren(InheritInfoVisitor visitor) { + + for (int i = 0; i < children.size(); i++) { + InheritInfo child = children.get(i); + visitor.visit(child); + child.visitChildren(visitor); + } + } + + /** + * return true if anything in the inheritance hierarchy has a relationship with a save cascade on + * it. + */ + public boolean isSaveRecurseSkippable() { + return root.isNodeSaveRecurseSkippable(); + } + + private boolean isNodeSaveRecurseSkippable() { + if (!descriptor.isSaveRecurseSkippable()) { + return false; + } + for (int i = 0; i < children.size(); i++) { + InheritInfo child = children.get(i); + if (!child.isNodeSaveRecurseSkippable()) { + return false; + } + } + return true; + } + + /** + * return true if anything in the inheritance hierarchy has a relationship with a delete cascade + * on it. + */ + public boolean isDeleteRecurseSkippable() { + return root.isNodeDeleteRecurseSkippable(); + } + + private boolean isNodeDeleteRecurseSkippable() { + if (!descriptor.isDeleteRecurseSkippable()) { + return false; + } + for (int i = 0; i < children.size(); i++) { + InheritInfo child = children.get(i); + if (!child.isNodeDeleteRecurseSkippable()) { + return false; + } + } + return true; + } + + /** + * Set the descriptor for this node. + */ + public void setDescriptor(BeanDescriptor descriptor) { + + this.descriptor = descriptor; + } + + /** + * Return the associated BeanDescriptor for this node. + */ + public BeanDescriptor getBeanDescriptor() { + return descriptor; + } + + /** + * Get the bean property additionally looking in the sub types. + */ + public BeanProperty findSubTypeProperty(String propertyName) { + + BeanProperty prop = null; + + for (int i = 0, x = children.size(); i < x; i++) { + InheritInfo childInfo = children.get(i); + + // recursively search this child bean descriptor + prop = childInfo.getBeanDescriptor().findBeanProperty(propertyName); + + if (prop != null) { + return prop; + } + } + + return null; + } + + /** + * Add the local properties for each sub class below this one. + */ + public void addChildrenProperties(SqlTreeProperties selectProps) { + + for (int i = 0, x = children.size(); i < x; i++) { + InheritInfo childInfo = children.get(i); + selectProps.add(childInfo.descriptor.propertiesLocal()); + + childInfo.addChildrenProperties(selectProps); + } + } + + /** + * Return the associated InheritInfo for this DB row read. + */ + public InheritInfo readType(DbReadContext ctx) throws SQLException { + + String discValue = ctx.getDataReader().getString(); + return readType(discValue); + } + + /** + * Return the associated InheritInfo for this discriminator value. + */ + public InheritInfo readType(String discValue) { + + if (discValue == null) { + return null; + } + + InheritInfo typeInfo = root.getType(discValue); + if (typeInfo == null) { + throw new PersistenceException("Inheritance type for discriminator value [" + discValue + "] was not found?"); + } + return typeInfo; + } + + /** + * Return the associated InheritInfo for this bean type. + */ + public InheritInfo readType(Class beanType) { + + InheritInfo typeInfo = root.getTypeByClass(beanType); + if (typeInfo == null) { + throw new PersistenceException("Inheritance type for bean type [" + beanType.getName() + "] was not found?"); + } + return typeInfo; + } + + /** + * Create an EntityBean for this type. + */ + public EntityBean createEntityBean() { + return descriptor.createEntityBean(); + } + + /** + * Return the IdBinder for this type. + */ + public IdBinder getIdBinder() { + return descriptor.getIdBinder(); + } + + /** + * return the type. + */ + public Class getType() { + return type; + } + + /** + * Return the root node of the tree. + *

+ * The root has a map of discriminator values to types. + *

+ */ + public InheritInfo getRoot() { + return root; + } + + /** + * Return the parent node. + */ + public InheritInfo getParent() { + return parent; + } + + /** + * Return true if this is abstract node. + */ + public boolean isAbstract() { + return (discriminatorValue == null); + } + + /** + * Return true if this is the root node. + */ + public boolean isRoot() { + return parent == null; + } + + /** + * For a discriminator get the inheritance information for this tree. + */ + public InheritInfo getType(String discValue) { + return discMap.get(discValue); + } + + /** + * Return the InheritInfo for the given bean type. + */ + private InheritInfo getTypeByClass(Class beanType) { + return typeMap.get(beanType.getName()); + } + + private void registerWithRoot(InheritInfo info) { + if (info.getDiscriminatorStringValue() != null) { + String stringDiscValue = info.getDiscriminatorStringValue(); + discMap.put(stringDiscValue, info); + } + typeMap.put(info.getType().getName(), info); + } + + /** + * Add a child node. + */ + public void addChild(InheritInfo childInfo) { + children.add(childInfo); + } + + /** + * Return the derived where for the discriminator. + */ + public String getWhere() { + + return where; + } + + /** + * Return the column name of the discriminator. + */ + public String getDiscriminatorColumn() { + return discriminatorColumn; + } + + /** + * Return the sql type of the discriminator value. + */ + public int getDiscriminatorType() { + return discriminatorType; + } + + /** + * Return the length of the discriminator column. + */ + public int getDiscriminatorLength() { + return discriminatorLength; + } + + /** + * Return the discriminator value for this node. + */ + public String getDiscriminatorStringValue() { + return discriminatorStringValue; + } + + public Object getDiscriminatorValue() { + return discriminatorValue; + } + + public String toString() { + return "InheritInfo[" + type.getName() + "] disc[" + discriminatorStringValue + "]"; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ManyType.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ManyType.java index d1dd4843b..21801efd8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ManyType.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ManyType.java @@ -1,59 +1,59 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebeaninternal.api.SpiQuery; - -/** - * Represents the type of a OneToMany or ManyToMany property. - */ -public class ManyType { - - public static final ManyType JAVA_LIST = new ManyType(Underlying.LIST); - public static final ManyType JAVA_SET = new ManyType(Underlying.SET); - public static final ManyType JAVA_MAP = new ManyType(Underlying.MAP); - - public enum Underlying { - LIST, - SET, - MAP - } - - private final SpiQuery.Type queryType; - - private final Underlying underlying; - - - public ManyType(Underlying underlying) { - this.underlying = underlying; - switch (underlying) { - case LIST: - queryType = SpiQuery.Type.LIST; - break; - case SET: - queryType = SpiQuery.Type.SET; - break; - - default: - queryType = SpiQuery.Type.MAP; - break; - } - } - - public boolean isMap() { - return Underlying.MAP.equals(underlying); - } - - /** - * Return the matching Query type. - */ - public SpiQuery.Type getQueryType() { - return queryType; - } - - /** - * Return the underlying type. - */ - public Underlying getUnderlying() { - return underlying; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebeaninternal.api.SpiQuery; + +/** + * Represents the type of a OneToMany or ManyToMany property. + */ +public class ManyType { + + public static final ManyType JAVA_LIST = new ManyType(Underlying.LIST); + public static final ManyType JAVA_SET = new ManyType(Underlying.SET); + public static final ManyType JAVA_MAP = new ManyType(Underlying.MAP); + + public enum Underlying { + LIST, + SET, + MAP + } + + private final SpiQuery.Type queryType; + + private final Underlying underlying; + + + public ManyType(Underlying underlying) { + this.underlying = underlying; + switch (underlying) { + case LIST: + queryType = SpiQuery.Type.LIST; + break; + case SET: + queryType = SpiQuery.Type.SET; + break; + + default: + queryType = SpiQuery.Type.MAP; + break; + } + } + + public boolean isMap() { + return Underlying.MAP.equals(underlying); + } + + /** + * Return the matching Query type. + */ + public SpiQuery.Type getQueryType() { + return queryType; + } + + /** + * Return the underlying type. + */ + public Underlying getUnderlying() { + return underlying; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistControllerManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistControllerManager.java index d90677907..32f2e28e2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistControllerManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistControllerManager.java @@ -1,43 +1,43 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.util.List; - -import com.avaje.ebean.event.BeanPersistController; -import com.avaje.ebeaninternal.server.core.BootupClasses; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Default implementation for creating BeanControllers. - */ -public class PersistControllerManager { - - private static final Logger logger = LoggerFactory.getLogger(PersistControllerManager.class); - - private final List list; - - public PersistControllerManager(BootupClasses bootupClasses){ - - list = bootupClasses.getBeanPersistControllers(); - } - - public int getRegisterCount() { - return list.size(); - } - - /** - * Return the BeanPersistController for a given entity type. - */ - public void addPersistControllers(DeployBeanDescriptor deployDesc){ - - for (int i = 0; i < list.size(); i++) { - BeanPersistController c = list.get(i); - if (c.isRegisterFor(deployDesc.getBeanType())){ - logger.debug("BeanPersistController on[" + deployDesc.getFullName() + "] " + c.getClass().getName()); - deployDesc.addPersistController(c); - } - } - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import java.util.List; + +import com.avaje.ebean.event.BeanPersistController; +import com.avaje.ebeaninternal.server.core.BootupClasses; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Default implementation for creating BeanControllers. + */ +public class PersistControllerManager { + + private static final Logger logger = LoggerFactory.getLogger(PersistControllerManager.class); + + private final List list; + + public PersistControllerManager(BootupClasses bootupClasses){ + + list = bootupClasses.getBeanPersistControllers(); + } + + public int getRegisterCount() { + return list.size(); + } + + /** + * Return the BeanPersistController for a given entity type. + */ + public void addPersistControllers(DeployBeanDescriptor deployDesc){ + + for (int i = 0; i < list.size(); i++) { + BeanPersistController c = list.get(i); + if (c.isRegisterFor(deployDesc.getBeanType())){ + logger.debug("BeanPersistController on[" + deployDesc.getFullName() + "] " + c.getClass().getName()); + deployDesc.addPersistController(c); + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistListenerManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistListenerManager.java index 97c794116..bbca13e48 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistListenerManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistListenerManager.java @@ -1,45 +1,45 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.util.List; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.event.BeanPersistListener; -import com.avaje.ebeaninternal.server.core.BootupClasses; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Manages the assignment/registration of BeanPersistListener with their - * respective DeployBeanDescriptor's. - */ -public class PersistListenerManager { - - private static final Logger logger = LoggerFactory.getLogger(PersistListenerManager.class); - - private final List list; - - public PersistListenerManager(BootupClasses bootupClasses) { - list = bootupClasses.getBeanPersistListeners(); - } - - public int getRegisterCount() { - return list.size(); - } - - /** - * Return the BeanPersistController for a given entity type. - */ - public void addPersistListeners(DeployBeanDescriptor deployDesc) { - - for (int i = 0; i < list.size(); i++) { - BeanPersistListener listener = list.get(i); - if (listener.isRegisterFor(deployDesc.getBeanType())) { - logger.debug("BeanPersistListener on[{}] {}", deployDesc.getFullName(), listener.getClass().getName()); - deployDesc.addPersistListener(listener); - } - } - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import java.util.List; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.event.BeanPersistListener; +import com.avaje.ebeaninternal.server.core.BootupClasses; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Manages the assignment/registration of BeanPersistListener with their + * respective DeployBeanDescriptor's. + */ +public class PersistListenerManager { + + private static final Logger logger = LoggerFactory.getLogger(PersistListenerManager.class); + + private final List list; + + public PersistListenerManager(BootupClasses bootupClasses) { + list = bootupClasses.getBeanPersistListeners(); + } + + public int getRegisterCount() { + return list.size(); + } + + /** + * Return the BeanPersistController for a given entity type. + */ + public void addPersistListeners(DeployBeanDescriptor deployDesc) { + + for (int i = 0; i < list.size(); i++) { + BeanPersistListener listener = list.get(i); + if (listener.isRegisterFor(deployDesc.getBeanType())) { + logger.debug("BeanPersistListener on[{}] {}", deployDesc.getFullName(), listener.getClass().getName()); + deployDesc.addPersistListener(listener); + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoin.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoin.java index d71b8a50f..88d7aef56 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoin.java @@ -1,203 +1,203 @@ -package com.avaje.ebeaninternal.server.deploy; - -import java.sql.SQLException; -import java.util.LinkedHashMap; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; -import com.avaje.ebeaninternal.server.query.SplitName; -import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.query.SqlJoinType; - -/** - * Represents a join to another table. - */ -public final class TableJoin { - - /** - * Flag set when the imported key maps to the primary key. This occurs for - * intersection tables (ManyToMany). - */ - private final boolean importedPrimaryKey; - - /** - * The joined table. - */ - private final String table; - - /** - * The type of join as per deployment (cardinality and optionality). - */ - private final SqlJoinType type; - - /** - * The persist cascade info. - */ - private final BeanCascadeInfo cascadeInfo; - - private final InheritInfo inheritInfo; - - /** - * Properties as an array. - */ - private final BeanProperty[] properties; - - /** - * Columns as an array. - */ - private final TableJoinColumn[] columns; - - /** - * A hash that can be used with the query plan. - */ - private final int queryHash; - - /** - * Create a TableJoin. - */ - public TableJoin(DeployTableJoin deploy, LinkedHashMap propMap) { - - this.importedPrimaryKey = deploy.isImportedPrimaryKey(); - this.table = InternString.intern(deploy.getTable()); - this.type = deploy.getType(); - this.cascadeInfo = deploy.getCascadeInfo(); - this.inheritInfo = deploy.getInheritInfo(); - - DeployTableJoinColumn[] deployCols = deploy.columns(); - this.columns = new TableJoinColumn[deployCols.length]; - for (int i = 0; i < deployCols.length; i++) { - this.columns[i] = new TableJoinColumn(deployCols[i]); - } - - DeployBeanProperty[] deployProps = deploy.properties(); - if (deployProps.length > 0 && propMap == null) { - throw new NullPointerException("propMap is null?"); - } - - this.properties = new BeanProperty[deployProps.length]; - for (int i = 0; i < deployProps.length; i++) { - BeanProperty prop = propMap.get(deployProps[i].getName()); - this.properties[i] = prop; - } - this.queryHash = calcQueryHash(); - } - - /** - * Calculate a hash value for adding to a query plan. - */ - private int calcQueryHash() { - int hc = type.hashCode(); - hc = hc * 31 + (table == null ? 0 : table.hashCode()); - for (int i = 0; i < columns.length; i++) { - hc = hc * 31 + columns[i].queryHash(); - } - return hc; - } - - /** - * Return a hash value for adding to a query plan. - */ - public int queryHash() { - return queryHash; - } - - public String toString() { - StringBuilder sb = new StringBuilder(30); - sb.append(type).append(" ").append(table).append(" "); - for (int i = 0; i < columns.length; i++) { - sb.append(columns[i]).append(" "); - } - return sb.toString(); - } - - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - for (int i = 0, x = properties.length; i < x; i++) { - properties[i].appendSelect(ctx, subQuery); - } - } - - public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { - for (int i = 0, x = properties.length; i < x; i++) { - properties[i].load(sqlBeanLoad); - } - } - - public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { - for (int i = 0, x = properties.length; i < x; i++) { - properties[i].readSet(ctx, bean, type); - } - return null; - } - - /** - * Return true if the imported foreign key maps to the primary key. - */ - public boolean isImportedPrimaryKey() { - return importedPrimaryKey; - } - - /** - * Return the persist info. - */ - public BeanCascadeInfo getCascadeInfo() { - return cascadeInfo; - } - - /** - * Return the join columns. - */ - public TableJoinColumn[] columns() { - return columns; - } - - /** - * For secondary table joins returns the properties mapped to that table. - */ - public BeanProperty[] properties() { - return properties; - } - - /** - * Return the joined table name. - */ - public String getTable() { - return table; - } - - /** - * Return the type of join. LEFT OUTER JOIN etc. - */ - public SqlJoinType getType() { - return type; - } - - /** - * Return true if this join is a left outer join. - */ - public boolean isOuterJoin() { - return type == SqlJoinType.OUTER; - } - - public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) { - - String[] names = SplitName.split(prefix); - String a1 = ctx.getTableAlias(names[0]); - String a2 = ctx.getTableAlias(prefix); - - return addJoin(joinType, a1, a2, ctx); - } - - public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) { - - String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null; - - String joinLiteral = joinType.getLiteral(type); - ctx.addJoin(joinLiteral, table, columns(), a1, a2, inheritance); - - return joinType.autoToOuter(type); - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import java.sql.SQLException; +import java.util.LinkedHashMap; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; +import com.avaje.ebeaninternal.server.query.SplitName; +import com.avaje.ebeaninternal.server.query.SqlBeanLoad; +import com.avaje.ebeaninternal.server.query.SqlJoinType; + +/** + * Represents a join to another table. + */ +public final class TableJoin { + + /** + * Flag set when the imported key maps to the primary key. This occurs for + * intersection tables (ManyToMany). + */ + private final boolean importedPrimaryKey; + + /** + * The joined table. + */ + private final String table; + + /** + * The type of join as per deployment (cardinality and optionality). + */ + private final SqlJoinType type; + + /** + * The persist cascade info. + */ + private final BeanCascadeInfo cascadeInfo; + + private final InheritInfo inheritInfo; + + /** + * Properties as an array. + */ + private final BeanProperty[] properties; + + /** + * Columns as an array. + */ + private final TableJoinColumn[] columns; + + /** + * A hash that can be used with the query plan. + */ + private final int queryHash; + + /** + * Create a TableJoin. + */ + public TableJoin(DeployTableJoin deploy, LinkedHashMap propMap) { + + this.importedPrimaryKey = deploy.isImportedPrimaryKey(); + this.table = InternString.intern(deploy.getTable()); + this.type = deploy.getType(); + this.cascadeInfo = deploy.getCascadeInfo(); + this.inheritInfo = deploy.getInheritInfo(); + + DeployTableJoinColumn[] deployCols = deploy.columns(); + this.columns = new TableJoinColumn[deployCols.length]; + for (int i = 0; i < deployCols.length; i++) { + this.columns[i] = new TableJoinColumn(deployCols[i]); + } + + DeployBeanProperty[] deployProps = deploy.properties(); + if (deployProps.length > 0 && propMap == null) { + throw new NullPointerException("propMap is null?"); + } + + this.properties = new BeanProperty[deployProps.length]; + for (int i = 0; i < deployProps.length; i++) { + BeanProperty prop = propMap.get(deployProps[i].getName()); + this.properties[i] = prop; + } + this.queryHash = calcQueryHash(); + } + + /** + * Calculate a hash value for adding to a query plan. + */ + private int calcQueryHash() { + int hc = type.hashCode(); + hc = hc * 31 + (table == null ? 0 : table.hashCode()); + for (int i = 0; i < columns.length; i++) { + hc = hc * 31 + columns[i].queryHash(); + } + return hc; + } + + /** + * Return a hash value for adding to a query plan. + */ + public int queryHash() { + return queryHash; + } + + public String toString() { + StringBuilder sb = new StringBuilder(30); + sb.append(type).append(" ").append(table).append(" "); + for (int i = 0; i < columns.length; i++) { + sb.append(columns[i]).append(" "); + } + return sb.toString(); + } + + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + for (int i = 0, x = properties.length; i < x; i++) { + properties[i].appendSelect(ctx, subQuery); + } + } + + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + for (int i = 0, x = properties.length; i < x; i++) { + properties[i].load(sqlBeanLoad); + } + } + + public Object readSet(DbReadContext ctx, EntityBean bean, Class type) throws SQLException { + for (int i = 0, x = properties.length; i < x; i++) { + properties[i].readSet(ctx, bean, type); + } + return null; + } + + /** + * Return true if the imported foreign key maps to the primary key. + */ + public boolean isImportedPrimaryKey() { + return importedPrimaryKey; + } + + /** + * Return the persist info. + */ + public BeanCascadeInfo getCascadeInfo() { + return cascadeInfo; + } + + /** + * Return the join columns. + */ + public TableJoinColumn[] columns() { + return columns; + } + + /** + * For secondary table joins returns the properties mapped to that table. + */ + public BeanProperty[] properties() { + return properties; + } + + /** + * Return the joined table name. + */ + public String getTable() { + return table; + } + + /** + * Return the type of join. LEFT OUTER JOIN etc. + */ + public SqlJoinType getType() { + return type; + } + + /** + * Return true if this join is a left outer join. + */ + public boolean isOuterJoin() { + return type == SqlJoinType.OUTER; + } + + public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) { + + String[] names = SplitName.split(prefix); + String a1 = ctx.getTableAlias(names[0]); + String a2 = ctx.getTableAlias(prefix); + + return addJoin(joinType, a1, a2, ctx); + } + + public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) { + + String inheritance = inheritInfo != null ? inheritInfo.getWhere() : null; + + String joinLiteral = joinType.getLiteral(type); + ctx.addJoin(joinLiteral, table, columns(), a1, a2, inheritance); + + return joinType.autoToOuter(type); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoinColumn.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoinColumn.java index 0014fcd9c..0ee61b6cc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoinColumn.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoinColumn.java @@ -1,83 +1,83 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; - -/** - * A join pair of local and foreign properties. - */ -public class TableJoinColumn { - - /** - * The local database column name. - */ - private final String localDbColumn; - - /** - * The foreign database column name. - */ - private final String foreignDbColumn; - - private final boolean insertable; - - private final boolean updateable; - - /** - * Hash for including in a query plan - */ - private final int queryHash; - - /** - * Create the pair. - */ - public TableJoinColumn(DeployTableJoinColumn deploy) { - this.localDbColumn = InternString.intern(deploy.getLocalDbColumn()); - this.foreignDbColumn = InternString.intern(deploy.getForeignDbColumn()); - this.insertable = deploy.isInsertable(); - this.updateable = deploy.isUpdateable(); - this.queryHash = hashOf(localDbColumn) * 31 + hashOf(foreignDbColumn); - } - - private int hashOf(String value) { - return (value == null) ? 0 : value.hashCode(); - } - - public String toString() { - return localDbColumn+" = "+foreignDbColumn; - } - - /** - * Return a hash for including in a query plan. - */ - public int queryHash() { - return queryHash; - } - - /** - * Return the foreign database column name. - */ - public String getForeignDbColumn() { - return foreignDbColumn; - } - - /** - * Return the local database column name. - */ - public String getLocalDbColumn() { - return localDbColumn; - } - - /** - * Return true if this column should be insertable. - */ - public boolean isInsertable() { - return insertable; - } - - /** - * Return true if this column should be updateable. - */ - public boolean isUpdateable() { - return updateable; - } -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; + +/** + * A join pair of local and foreign properties. + */ +public class TableJoinColumn { + + /** + * The local database column name. + */ + private final String localDbColumn; + + /** + * The foreign database column name. + */ + private final String foreignDbColumn; + + private final boolean insertable; + + private final boolean updateable; + + /** + * Hash for including in a query plan + */ + private final int queryHash; + + /** + * Create the pair. + */ + public TableJoinColumn(DeployTableJoinColumn deploy) { + this.localDbColumn = InternString.intern(deploy.getLocalDbColumn()); + this.foreignDbColumn = InternString.intern(deploy.getForeignDbColumn()); + this.insertable = deploy.isInsertable(); + this.updateable = deploy.isUpdateable(); + this.queryHash = hashOf(localDbColumn) * 31 + hashOf(foreignDbColumn); + } + + private int hashOf(String value) { + return (value == null) ? 0 : value.hashCode(); + } + + public String toString() { + return localDbColumn+" = "+foreignDbColumn; + } + + /** + * Return a hash for including in a query plan. + */ + public int queryHash() { + return queryHash; + } + + /** + * Return the foreign database column name. + */ + public String getForeignDbColumn() { + return foreignDbColumn; + } + + /** + * Return the local database column name. + */ + public String getLocalDbColumn() { + return localDbColumn; + } + + /** + * Return true if this column should be insertable. + */ + public boolean isInsertable() { + return insertable; + } + + /** + * Return true if this column should be updateable. + */ + public boolean isUpdateable() { + return updateable; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/CounterFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/CounterFactory.java index b8bf187d1..437dec665 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/CounterFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/CounterFactory.java @@ -1,64 +1,64 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.sql.Types; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; - -/** - * Creates "Counter" GeneratedProperty for various types of number. - *

- * Aka, Integer, Long, Short etc. - *

- */ -public class CounterFactory { - - final GeneratedCounterInteger integerCounter = new GeneratedCounterInteger(); - - final GeneratedCounterLong longCounter = new GeneratedCounterLong(); - - public void setCounter(DeployBeanProperty property) { - - property.setGeneratedProperty(createCounter(property)); - } - - /** - * Create the GeneratedProperty based on the property type. - */ - private GeneratedProperty createCounter(DeployBeanProperty property) { - - Class propType = property.getPropertyType(); - if (propType.equals(Integer.class) || propType.equals(int.class)) { - return integerCounter; - } - if (propType.equals(Long.class) || propType.equals(long.class)) { - return longCounter; - } - - int type = getType(propType); - return new GeneratedCounter(type); - } - - private int getType(Class propType){ - if (propType.equals(Short.class) || propType.equals(short.class)){ - return Types.TINYINT; - } - if (propType.equals(BigDecimal.class)){ - return Types.DECIMAL; - } - if (propType.equals(Double.class) || propType.equals(double.class)){ - return Types.DOUBLE; - } - if (propType.equals(Float.class) || propType.equals(float.class)){ - return Types.REAL; - } - if (propType.equals(BigInteger.class)){ - return Types.BIGINT; - } - String msg = "Can not support Counter for type "+propType.getName(); - throw new PersistenceException(msg); - } -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Types; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +/** + * Creates "Counter" GeneratedProperty for various types of number. + *

+ * Aka, Integer, Long, Short etc. + *

+ */ +public class CounterFactory { + + final GeneratedCounterInteger integerCounter = new GeneratedCounterInteger(); + + final GeneratedCounterLong longCounter = new GeneratedCounterLong(); + + public void setCounter(DeployBeanProperty property) { + + property.setGeneratedProperty(createCounter(property)); + } + + /** + * Create the GeneratedProperty based on the property type. + */ + private GeneratedProperty createCounter(DeployBeanProperty property) { + + Class propType = property.getPropertyType(); + if (propType.equals(Integer.class) || propType.equals(int.class)) { + return integerCounter; + } + if (propType.equals(Long.class) || propType.equals(long.class)) { + return longCounter; + } + + int type = getType(propType); + return new GeneratedCounter(type); + } + + private int getType(Class propType){ + if (propType.equals(Short.class) || propType.equals(short.class)){ + return Types.TINYINT; + } + if (propType.equals(BigDecimal.class)){ + return Types.DECIMAL; + } + if (propType.equals(Double.class) || propType.equals(double.class)){ + return Types.DOUBLE; + } + if (propType.equals(Float.class) || propType.equals(float.class)){ + return Types.REAL; + } + if (propType.equals(BigInteger.class)){ + return Types.BIGINT; + } + String msg = "Can not support Counter for type "+propType.getName(); + throw new PersistenceException(msg); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounter.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounter.java index 55c143fbc..0a2a1d5b9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounter.java @@ -1,58 +1,58 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * A general number counter for various number types. - */ -public class GeneratedCounter implements GeneratedProperty { - - final int numberType; - - public GeneratedCounter(int numberType) { - this.numberType = numberType; - } - - /** - * Always returns a 1. - */ - public Object getInsertValue(BeanProperty prop, EntityBean bean) { - Integer i = Integer.valueOf(1); - return BasicTypeConverter.convert(i, numberType); - } - - /** - * Increments the current value by one. - */ - public Object getUpdateValue(BeanProperty prop, EntityBean bean) { - Number currVal = (Number) prop.getValue(bean); - Integer nextVal = Integer.valueOf(currVal.intValue() + 1); - return BasicTypeConverter.convert(nextVal, numberType); - } - - /** - * Include this in every update. - */ - public boolean includeInUpdate() { - return true; - } - - @Override - public boolean includeInAllUpdates() { - return false; - } - - /** - * Include this in every insert setting initial counter value to 1. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * A general number counter for various number types. + */ +public class GeneratedCounter implements GeneratedProperty { + + final int numberType; + + public GeneratedCounter(int numberType) { + this.numberType = numberType; + } + + /** + * Always returns a 1. + */ + public Object getInsertValue(BeanProperty prop, EntityBean bean) { + Integer i = Integer.valueOf(1); + return BasicTypeConverter.convert(i, numberType); + } + + /** + * Increments the current value by one. + */ + public Object getUpdateValue(BeanProperty prop, EntityBean bean) { + Number currVal = (Number) prop.getValue(bean); + Integer nextVal = Integer.valueOf(currVal.intValue() + 1); + return BasicTypeConverter.convert(nextVal, numberType); + } + + /** + * Include this in every update. + */ + public boolean includeInUpdate() { + return true; + } + + @Override + public boolean includeInAllUpdates() { + return false; + } + + /** + * Include this in every insert setting initial counter value to 1. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterInteger.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterInteger.java index e94413eb7..4f3343e68 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterInteger.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterInteger.java @@ -1,53 +1,53 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Used to create a counter version column for Integer. - */ -public class GeneratedCounterInteger implements GeneratedProperty { - - public GeneratedCounterInteger() { - - } - - /** - * Always returns a 1. - */ - public Object getInsertValue(BeanProperty prop, EntityBean bean) { - return Integer.valueOf(1); - } - - /** - * Increments the current value by one. - */ - public Object getUpdateValue(BeanProperty prop, EntityBean bean) { - Integer i = (Integer) prop.getValue(bean); - return Integer.valueOf(i.intValue() + 1); - } - - /** - * Include this in every update. - */ - public boolean includeInUpdate() { - return true; - } - - @Override - public boolean includeInAllUpdates() { - return false; - } - - /** - * Include this in every insert setting initial counter value to 1. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to create a counter version column for Integer. + */ +public class GeneratedCounterInteger implements GeneratedProperty { + + public GeneratedCounterInteger() { + + } + + /** + * Always returns a 1. + */ + public Object getInsertValue(BeanProperty prop, EntityBean bean) { + return Integer.valueOf(1); + } + + /** + * Increments the current value by one. + */ + public Object getUpdateValue(BeanProperty prop, EntityBean bean) { + Integer i = (Integer) prop.getValue(bean); + return Integer.valueOf(i.intValue() + 1); + } + + /** + * Include this in every update. + */ + public boolean includeInUpdate() { + return true; + } + + @Override + public boolean includeInAllUpdates() { + return false; + } + + /** + * Include this in every insert setting initial counter value to 1. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterLong.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterLong.java index cf40be89d..1d17c71d7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterLong.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterLong.java @@ -1,53 +1,53 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Used to create a counter version column for Long. - */ -public class GeneratedCounterLong implements GeneratedProperty { - - public GeneratedCounterLong() { - - } - - /** - * Always returns a 1. - */ - public Object getInsertValue(BeanProperty prop, EntityBean bean) { - return Long.valueOf(1); - } - - /** - * Increments the current value by one. - */ - public Object getUpdateValue(BeanProperty prop, EntityBean bean) { - Long i = (Long) prop.getValue(bean); - return Long.valueOf(i.longValue() + 1); - } - - /** - * Include this in every update. - */ - public boolean includeInUpdate() { - return true; - } - - @Override - public boolean includeInAllUpdates() { - return false; - } - - /** - * Include this in every insert setting initial counter value to 1. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to create a counter version column for Long. + */ +public class GeneratedCounterLong implements GeneratedProperty { + + public GeneratedCounterLong() { + + } + + /** + * Always returns a 1. + */ + public Object getInsertValue(BeanProperty prop, EntityBean bean) { + return Long.valueOf(1); + } + + /** + * Increments the current value by one. + */ + public Object getUpdateValue(BeanProperty prop, EntityBean bean) { + Long i = (Long) prop.getValue(bean); + return Long.valueOf(i.longValue() + 1); + } + + /** + * Include this in every update. + */ + public boolean includeInUpdate() { + return true; + } + + @Override + public boolean includeInAllUpdates() { + return false; + } + + /** + * Include this in every insert setting initial counter value to 1. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertDate.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertDate.java index 08b367f7f..098a7dc6e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertDate.java @@ -1,50 +1,50 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import java.util.Date; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Used to generate a (java.util.Date) timestamp when a bean is inserted. - */ -public class GeneratedInsertDate implements GeneratedProperty { - - /** - * Return the current time as a Timestamp. - */ - public Object getInsertValue(BeanProperty prop, EntityBean bean) { - return new Date(System.currentTimeMillis()); - } - - /** - * Just returns the beans original insert timestamp value. - */ - public Object getUpdateValue(BeanProperty prop, EntityBean bean) { - return prop.getValue(bean); - } - - /** - * Return false. - */ - public boolean includeInUpdate() { - return false; - } - - @Override - public boolean includeInAllUpdates() { - return false; - } - - /** - * Return true. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.util.Date; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to generate a (java.util.Date) timestamp when a bean is inserted. + */ +public class GeneratedInsertDate implements GeneratedProperty { + + /** + * Return the current time as a Timestamp. + */ + public Object getInsertValue(BeanProperty prop, EntityBean bean) { + return new Date(System.currentTimeMillis()); + } + + /** + * Just returns the beans original insert timestamp value. + */ + public Object getUpdateValue(BeanProperty prop, EntityBean bean) { + return prop.getValue(bean); + } + + /** + * Return false. + */ + public boolean includeInUpdate() { + return false; + } + + @Override + public boolean includeInAllUpdates() { + return false; + } + + /** + * Return true. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertLong.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertLong.java index 52c3fa280..9883916c0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertLong.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertLong.java @@ -1,48 +1,48 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Used to generate a (Long) timestamp when a bean is inserted. - */ -public class GeneratedInsertLong implements GeneratedProperty { - - /** - * Return the current time as a Timestamp. - */ - public Object getInsertValue(BeanProperty prop, EntityBean bean) { - return Long.valueOf(System.currentTimeMillis()); - } - - /** - * Just returns the beans original insert timestamp value. - */ - public Object getUpdateValue(BeanProperty prop, EntityBean bean) { - return prop.getValue(bean); - } - - /** - * Return false. - */ - public boolean includeInUpdate() { - return false; - } - - @Override - public boolean includeInAllUpdates() { - return false; - } - - /** - * Return true. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to generate a (Long) timestamp when a bean is inserted. + */ +public class GeneratedInsertLong implements GeneratedProperty { + + /** + * Return the current time as a Timestamp. + */ + public Object getInsertValue(BeanProperty prop, EntityBean bean) { + return Long.valueOf(System.currentTimeMillis()); + } + + /** + * Just returns the beans original insert timestamp value. + */ + public Object getUpdateValue(BeanProperty prop, EntityBean bean) { + return prop.getValue(bean); + } + + /** + * Return false. + */ + public boolean includeInUpdate() { + return false; + } + + @Override + public boolean includeInAllUpdates() { + return false; + } + + /** + * Return true. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertTimestamp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertTimestamp.java index 17c6e29ed..27daf7b1c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertTimestamp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertTimestamp.java @@ -1,50 +1,50 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import java.sql.Timestamp; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Used to generate a timestamp when a bean is inserted. - */ -public class GeneratedInsertTimestamp implements GeneratedProperty { - - /** - * Return the current time as a Timestamp. - */ - public Object getInsertValue(BeanProperty prop, EntityBean bean) { - return new Timestamp(System.currentTimeMillis()); - } - - /** - * Just returns the beans original insert timestamp value. - */ - public Object getUpdateValue(BeanProperty prop, EntityBean bean) { - return prop.getValue(bean); - } - - /** - * Return false. - */ - public boolean includeInUpdate() { - return false; - } - - @Override - public boolean includeInAllUpdates() { - return false; - } - - /** - * Return true. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.sql.Timestamp; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to generate a timestamp when a bean is inserted. + */ +public class GeneratedInsertTimestamp implements GeneratedProperty { + + /** + * Return the current time as a Timestamp. + */ + public Object getInsertValue(BeanProperty prop, EntityBean bean) { + return new Timestamp(System.currentTimeMillis()); + } + + /** + * Just returns the beans original insert timestamp value. + */ + public Object getUpdateValue(BeanProperty prop, EntityBean bean) { + return prop.getValue(bean); + } + + /** + * Return false. + */ + public boolean includeInUpdate() { + return false; + } + + @Override + public boolean includeInAllUpdates() { + return false; + } + + /** + * Return true. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedProperty.java index 1d54e9a05..c30686450 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedProperty.java @@ -1,47 +1,47 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Used to generate values for a property rather than have then set by the user. - * For example generate the update timestamp when a bean is updated. - */ -public interface GeneratedProperty { - - /** - * Get the generated insert value for a specific property of a bean. - */ - public Object getInsertValue(BeanProperty prop, EntityBean bean); - - /** - * Get the generated update value for a specific property of a bean. - */ - public Object getUpdateValue(BeanProperty prop, EntityBean bean); - - /** - * Return true if this should always be includes in an update statement. - *

- * Used to include GeneratedUpdateTimestamp in dynamic table updates. - *

- */ - public boolean includeInUpdate(); - - /** - * Return true if the property should be included in an update even if - * it is not loaded (ie. Last Updated Timestamp). - */ - public boolean includeInAllUpdates(); - - /** - * Return true if this should be included in insert statements. - */ - public boolean includeInInsert(); - - /** - * Return true if the GeneratedProperty implies the DDL to create the DB - * column should have a not null constraint. - */ - public boolean isDDLNotNullable(); - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to generate values for a property rather than have then set by the user. + * For example generate the update timestamp when a bean is updated. + */ +public interface GeneratedProperty { + + /** + * Get the generated insert value for a specific property of a bean. + */ + public Object getInsertValue(BeanProperty prop, EntityBean bean); + + /** + * Get the generated update value for a specific property of a bean. + */ + public Object getUpdateValue(BeanProperty prop, EntityBean bean); + + /** + * Return true if this should always be includes in an update statement. + *

+ * Used to include GeneratedUpdateTimestamp in dynamic table updates. + *

+ */ + public boolean includeInUpdate(); + + /** + * Return true if the property should be included in an update even if + * it is not loaded (ie. Last Updated Timestamp). + */ + public boolean includeInAllUpdates(); + + /** + * Return true if this should be included in insert statements. + */ + public boolean includeInInsert(); + + /** + * Return true if the GeneratedProperty implies the DDL to create the DB + * column should have a not null constraint. + */ + public boolean isDDLNotNullable(); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedPropertyFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedPropertyFactory.java index 3d58d972d..30e6a8be1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedPropertyFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedPropertyFactory.java @@ -1,65 +1,65 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import java.math.BigDecimal; -import java.util.HashSet; - -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; - -/** - * Default implementation of GeneratedPropertyFactory. - */ -public class GeneratedPropertyFactory { - - CounterFactory counterFactory; - - InsertTimestampFactory insertFactory; - - UpdateTimestampFactory updateFactory; - - HashSet numberTypes = new HashSet(); - - public GeneratedPropertyFactory() { - counterFactory = new CounterFactory(); - insertFactory = new InsertTimestampFactory(); - updateFactory = new UpdateTimestampFactory(); - - - numberTypes.add(Integer.class.getName()); - numberTypes.add(int.class.getName()); - numberTypes.add(Long.class.getName()); - numberTypes.add(long.class.getName()); - numberTypes.add(Short.class.getName()); - numberTypes.add(short.class.getName()); - numberTypes.add(Double.class.getName()); - numberTypes.add(double.class.getName()); - numberTypes.add(BigDecimal.class.getName()); - } - - private boolean isNumberType(String typeClassName) { - return numberTypes.contains(typeClassName); - } - - public void setVersion(DeployBeanProperty property) { - if (isNumberType(property.getPropertyType().getName())) { - setCounter(property); - } else { - setUpdateTimestamp(property); - } - } - - public void setCounter(DeployBeanProperty property) { - - counterFactory.setCounter(property); - } - - public void setInsertTimestamp(DeployBeanProperty property) { - - insertFactory.setInsertTimestamp(property); - } - - public void setUpdateTimestamp(DeployBeanProperty property) { - - updateFactory.setUpdateTimestamp(property); - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.math.BigDecimal; +import java.util.HashSet; + +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +/** + * Default implementation of GeneratedPropertyFactory. + */ +public class GeneratedPropertyFactory { + + CounterFactory counterFactory; + + InsertTimestampFactory insertFactory; + + UpdateTimestampFactory updateFactory; + + HashSet numberTypes = new HashSet(); + + public GeneratedPropertyFactory() { + counterFactory = new CounterFactory(); + insertFactory = new InsertTimestampFactory(); + updateFactory = new UpdateTimestampFactory(); + + + numberTypes.add(Integer.class.getName()); + numberTypes.add(int.class.getName()); + numberTypes.add(Long.class.getName()); + numberTypes.add(long.class.getName()); + numberTypes.add(Short.class.getName()); + numberTypes.add(short.class.getName()); + numberTypes.add(Double.class.getName()); + numberTypes.add(double.class.getName()); + numberTypes.add(BigDecimal.class.getName()); + } + + private boolean isNumberType(String typeClassName) { + return numberTypes.contains(typeClassName); + } + + public void setVersion(DeployBeanProperty property) { + if (isNumberType(property.getPropertyType().getName())) { + setCounter(property); + } else { + setUpdateTimestamp(property); + } + } + + public void setCounter(DeployBeanProperty property) { + + counterFactory.setCounter(property); + } + + public void setInsertTimestamp(DeployBeanProperty property) { + + insertFactory.setInsertTimestamp(property); + } + + public void setUpdateTimestamp(DeployBeanProperty property) { + + updateFactory.setUpdateTimestamp(property); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateDate.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateDate.java index b25dcdaf7..1602102d7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateDate.java @@ -1,51 +1,51 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import java.util.Date; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Generate a (java.util.Date) Timestamp whenever the bean is inserted or - * updated. - */ -public class GeneratedUpdateDate implements GeneratedProperty { - - /** - * Return now as a Timestamp. - */ - public Object getInsertValue(BeanProperty prop, EntityBean bean) { - return new Date(System.currentTimeMillis()); - } - - /** - * Return now as a Timestamp. - */ - public Object getUpdateValue(BeanProperty prop, EntityBean bean) { - return new Date(System.currentTimeMillis()); - } - - /** - * For dynamic table updates make sure this is included. - */ - public boolean includeInUpdate() { - return true; - } - - @Override - public boolean includeInAllUpdates() { - return true; - } - - /** - * Include this in every insert. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.util.Date; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Generate a (java.util.Date) Timestamp whenever the bean is inserted or + * updated. + */ +public class GeneratedUpdateDate implements GeneratedProperty { + + /** + * Return now as a Timestamp. + */ + public Object getInsertValue(BeanProperty prop, EntityBean bean) { + return new Date(System.currentTimeMillis()); + } + + /** + * Return now as a Timestamp. + */ + public Object getUpdateValue(BeanProperty prop, EntityBean bean) { + return new Date(System.currentTimeMillis()); + } + + /** + * For dynamic table updates make sure this is included. + */ + public boolean includeInUpdate() { + return true; + } + + @Override + public boolean includeInAllUpdates() { + return true; + } + + /** + * Include this in every insert. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateLong.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateLong.java index a0648f249..64fe22663 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateLong.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateLong.java @@ -1,48 +1,48 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Generate a (Long) Timestamp whenever the bean is inserted or updated. - */ -public class GeneratedUpdateLong implements GeneratedProperty { - - /** - * Return now as a Timestamp. - */ - public Object getInsertValue(BeanProperty prop, EntityBean bean) { - return Long.valueOf(System.currentTimeMillis()); - } - - /** - * Return now as a Timestamp. - */ - public Object getUpdateValue(BeanProperty prop, EntityBean bean) { - return Long.valueOf(System.currentTimeMillis()); - } - - /** - * For dynamic table updates make sure this is included. - */ - public boolean includeInUpdate() { - return true; - } - - @Override - public boolean includeInAllUpdates() { - return true; - } - - /** - * Include this in every insert. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Generate a (Long) Timestamp whenever the bean is inserted or updated. + */ +public class GeneratedUpdateLong implements GeneratedProperty { + + /** + * Return now as a Timestamp. + */ + public Object getInsertValue(BeanProperty prop, EntityBean bean) { + return Long.valueOf(System.currentTimeMillis()); + } + + /** + * Return now as a Timestamp. + */ + public Object getUpdateValue(BeanProperty prop, EntityBean bean) { + return Long.valueOf(System.currentTimeMillis()); + } + + /** + * For dynamic table updates make sure this is included. + */ + public boolean includeInUpdate() { + return true; + } + + @Override + public boolean includeInAllUpdates() { + return true; + } + + /** + * Include this in every insert. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateTimestamp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateTimestamp.java index 744e9d422..e20857233 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateTimestamp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateTimestamp.java @@ -1,50 +1,50 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import java.sql.Timestamp; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Generate a Timestamp whenever the bean is inserted or updated. - */ -public class GeneratedUpdateTimestamp implements GeneratedProperty { - - /** - * Return now as a Timestamp. - */ - public Object getInsertValue(BeanProperty prop, EntityBean bean) { - return new Timestamp(System.currentTimeMillis()); - } - - /** - * Return now as a Timestamp. - */ - public Object getUpdateValue(BeanProperty prop, EntityBean bean) { - return new Timestamp(System.currentTimeMillis()); - } - - /** - * For dynamic table updates make sure this is included. - */ - public boolean includeInUpdate() { - return true; - } - - @Override - public boolean includeInAllUpdates() { - return true; - } - - /** - * Include this in every insert. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.sql.Timestamp; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Generate a Timestamp whenever the bean is inserted or updated. + */ +public class GeneratedUpdateTimestamp implements GeneratedProperty { + + /** + * Return now as a Timestamp. + */ + public Object getInsertValue(BeanProperty prop, EntityBean bean) { + return new Timestamp(System.currentTimeMillis()); + } + + /** + * Return now as a Timestamp. + */ + public Object getUpdateValue(BeanProperty prop, EntityBean bean) { + return new Timestamp(System.currentTimeMillis()); + } + + /** + * For dynamic table updates make sure this is included. + */ + public boolean includeInUpdate() { + return true; + } + + @Override + public boolean includeInAllUpdates() { + return true; + } + + /** + * Include this in every insert. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/InsertTimestampFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/InsertTimestampFactory.java index a8649b6ce..841ec21e2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/InsertTimestampFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/InsertTimestampFactory.java @@ -1,61 +1,61 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import java.sql.Timestamp; -import java.time.LocalDateTime; -import java.time.OffsetDateTime; -import java.time.ZonedDateTime; -import java.util.HashMap; -import java.util.Map; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.api.ClassUtil; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; - -/** - * Helper for creating Insert timestamp GeneratedProperty objects. - */ -public class InsertTimestampFactory { - - final GeneratedInsertLong longTime = new GeneratedInsertLong(); - - Map, GeneratedProperty> map = new HashMap, GeneratedProperty>(); - - public InsertTimestampFactory() { - map.put(Timestamp.class, new GeneratedInsertTimestamp()); - map.put(java.util.Date.class, new GeneratedInsertDate()); - map.put(Long.class, longTime); - map.put(long.class, longTime); - - if (ClassUtil.isPresent("java.time.LocalDate", this.getClass())) { - map.put(LocalDateTime.class, new GeneratedInsertJavaTime.LocalDT()); - map.put(OffsetDateTime.class, new GeneratedInsertJavaTime.OffsetDT()); - map.put(ZonedDateTime.class, new GeneratedInsertJavaTime.ZonedDT()); - } - if (ClassUtil.isPresent("org.joda.time.LocalDateTime", this.getClass())) { - map.put(org.joda.time.LocalDateTime.class, new GeneratedInsertJodaTime.LocalDT()); - map.put(org.joda.time.DateTime.class, new GeneratedInsertJodaTime.DateTimeDT()); - } - - } - - public void setInsertTimestamp(DeployBeanProperty property) { - - property.setGeneratedProperty(createInsertTimestamp(property)); - } - - /** - * Create the insert GeneratedProperty depending on the property type. - */ - public GeneratedProperty createInsertTimestamp(DeployBeanProperty property) { - - Class propType = property.getPropertyType(); - GeneratedProperty generatedProperty = map.get(propType); - if (generatedProperty != null) { - return generatedProperty; - } - - throw new PersistenceException("Generated Insert Timestamp not supported on "+propType.getName()); - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.api.ClassUtil; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +/** + * Helper for creating Insert timestamp GeneratedProperty objects. + */ +public class InsertTimestampFactory { + + final GeneratedInsertLong longTime = new GeneratedInsertLong(); + + Map, GeneratedProperty> map = new HashMap, GeneratedProperty>(); + + public InsertTimestampFactory() { + map.put(Timestamp.class, new GeneratedInsertTimestamp()); + map.put(java.util.Date.class, new GeneratedInsertDate()); + map.put(Long.class, longTime); + map.put(long.class, longTime); + + if (ClassUtil.isPresent("java.time.LocalDate", this.getClass())) { + map.put(LocalDateTime.class, new GeneratedInsertJavaTime.LocalDT()); + map.put(OffsetDateTime.class, new GeneratedInsertJavaTime.OffsetDT()); + map.put(ZonedDateTime.class, new GeneratedInsertJavaTime.ZonedDT()); + } + if (ClassUtil.isPresent("org.joda.time.LocalDateTime", this.getClass())) { + map.put(org.joda.time.LocalDateTime.class, new GeneratedInsertJodaTime.LocalDT()); + map.put(org.joda.time.DateTime.class, new GeneratedInsertJodaTime.DateTimeDT()); + } + + } + + public void setInsertTimestamp(DeployBeanProperty property) { + + property.setGeneratedProperty(createInsertTimestamp(property)); + } + + /** + * Create the insert GeneratedProperty depending on the property type. + */ + public GeneratedProperty createInsertTimestamp(DeployBeanProperty property) { + + Class propType = property.getPropertyType(); + GeneratedProperty generatedProperty = map.get(propType); + if (generatedProperty != null) { + return generatedProperty; + } + + throw new PersistenceException("Generated Insert Timestamp not supported on "+propType.getName()); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/UpdateTimestampFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/UpdateTimestampFactory.java index 59226e694..b9918844c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/UpdateTimestampFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/UpdateTimestampFactory.java @@ -1,60 +1,60 @@ -package com.avaje.ebeaninternal.server.deploy.generatedproperty; - -import java.sql.Timestamp; -import java.time.LocalDateTime; -import java.time.OffsetDateTime; -import java.time.ZonedDateTime; -import java.util.HashMap; -import java.util.Map; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.api.ClassUtil; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; - -/** - * Helper for creating Update timestamp GeneratedProperty objects. - */ -public class UpdateTimestampFactory { - - final GeneratedUpdateLong longTime = new GeneratedUpdateLong(); - - Map, GeneratedProperty> map = new HashMap, GeneratedProperty>(); - - public UpdateTimestampFactory() { - map.put(Timestamp.class, new GeneratedUpdateTimestamp()); - map.put(java.util.Date.class, new GeneratedUpdateDate()); - map.put(Long.class, longTime); - map.put(long.class, longTime); - - if (ClassUtil.isPresent("java.time.LocalDate", this.getClass())) { - map.put(LocalDateTime.class, new GeneratedUpdateJavaTime.LocalDT()); - map.put(OffsetDateTime.class, new GeneratedUpdateJavaTime.OffsetDT()); - map.put(ZonedDateTime.class, new GeneratedUpdateJavaTime.ZonedDT()); - } - if (ClassUtil.isPresent("org.joda.time.LocalDateTime", this.getClass())) { - map.put(org.joda.time.LocalDateTime.class, new GeneratedUpdateJodaTime.LocalDT()); - map.put(org.joda.time.DateTime.class, new GeneratedUpdateJodaTime.DateTimeDT()); - } - } - - public void setUpdateTimestamp(DeployBeanProperty property) { - - property.setGeneratedProperty(createUpdateTimestamp(property)); - } - - /** - * Create the update GeneratedProperty depending on the property type. - */ - protected GeneratedProperty createUpdateTimestamp(DeployBeanProperty property) { - - Class propType = property.getPropertyType(); - GeneratedProperty generatedProperty = map.get(propType); - if (generatedProperty != null) { - return generatedProperty; - } - - throw new PersistenceException("Generated update Timestamp not supported on "+propType.getName()); - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.api.ClassUtil; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +/** + * Helper for creating Update timestamp GeneratedProperty objects. + */ +public class UpdateTimestampFactory { + + final GeneratedUpdateLong longTime = new GeneratedUpdateLong(); + + Map, GeneratedProperty> map = new HashMap, GeneratedProperty>(); + + public UpdateTimestampFactory() { + map.put(Timestamp.class, new GeneratedUpdateTimestamp()); + map.put(java.util.Date.class, new GeneratedUpdateDate()); + map.put(Long.class, longTime); + map.put(long.class, longTime); + + if (ClassUtil.isPresent("java.time.LocalDate", this.getClass())) { + map.put(LocalDateTime.class, new GeneratedUpdateJavaTime.LocalDT()); + map.put(OffsetDateTime.class, new GeneratedUpdateJavaTime.OffsetDT()); + map.put(ZonedDateTime.class, new GeneratedUpdateJavaTime.ZonedDT()); + } + if (ClassUtil.isPresent("org.joda.time.LocalDateTime", this.getClass())) { + map.put(org.joda.time.LocalDateTime.class, new GeneratedUpdateJodaTime.LocalDT()); + map.put(org.joda.time.DateTime.class, new GeneratedUpdateJodaTime.DateTimeDT()); + } + } + + public void setUpdateTimestamp(DeployBeanProperty property) { + + property.setGeneratedProperty(createUpdateTimestamp(property)); + } + + /** + * Create the update GeneratedProperty depending on the property type. + */ + protected GeneratedProperty createUpdateTimestamp(DeployBeanProperty property) { + + Class propType = property.getPropertyType(); + GeneratedProperty generatedProperty = map.get(propType); + if (generatedProperty != null) { + return generatedProperty; + } + + throw new PersistenceException("Generated update Timestamp not supported on "+propType.getName()); + } + +} 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 1f17ad28b..20ee2787d 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 @@ -1,407 +1,407 @@ -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.List; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.api.SpiExpressionRequest; -import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.DbReadContext; -import com.avaje.ebeaninternal.server.deploy.DbSqlContext; -import com.avaje.ebeaninternal.server.query.SplitName; -import com.avaje.ebeaninternal.server.type.DataBind; - -/** - * Bind an Id that is an Embedded bean. - */ -public final class IdBinderEmbedded implements IdBinder { - - private final BeanPropertyAssocOne embIdProperty; - - private final boolean idInExpandedForm; - - private BeanProperty[] props; - - 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(); - } - - public boolean isIdInExpandedForm() { - return idInExpandedForm; - } - - private String idInExpanded() { - - StringBuilder sb = new StringBuilder(30); - sb.append("("); - for (int i = 0; i < props.length; i++) { - if (i > 0) { - sb.append(" and "); - } - sb.append(idDesc.getBaseTableAlias()); - sb.append("."); - sb.append(props[i].getDbColumn()); - 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()); - } - - 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.createEntityBean(); - 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; - } 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.createEntityBean(); - boolean notNull = true; - - 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; - } - } - - 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; - } -} +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.List; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.api.SpiExpressionRequest; +import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.DbReadContext; +import com.avaje.ebeaninternal.server.deploy.DbSqlContext; +import com.avaje.ebeaninternal.server.query.SplitName; +import com.avaje.ebeaninternal.server.type.DataBind; + +/** + * Bind an Id that is an Embedded bean. + */ +public final class IdBinderEmbedded implements IdBinder { + + private final BeanPropertyAssocOne embIdProperty; + + private final boolean idInExpandedForm; + + private BeanProperty[] props; + + 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(); + } + + public boolean isIdInExpandedForm() { + return idInExpandedForm; + } + + private String idInExpanded() { + + StringBuilder sb = new StringBuilder(30); + sb.append("("); + for (int i = 0; i < props.length; i++) { + if (i > 0) { + sb.append(" and "); + } + sb.append(idDesc.getBaseTableAlias()); + sb.append("."); + sb.append(props[i].getDbColumn()); + 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()); + } + + 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.createEntityBean(); + 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; + } 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.createEntityBean(); + boolean notNull = true; + + 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; + } + } + + 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; + } +} 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 502fdfff3..64f8543b7 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 @@ -1,36 +1,36 @@ -package com.avaje.ebeaninternal.server.deploy.id; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; - -/** - * Creates the appropriate IdConvertSet depending on the type of Id property(s). - */ -public class IdBinderFactory { - - private static final IdBinderEmpty EMPTY = new IdBinderEmpty(); - - private final boolean idInExpandedForm; - - public IdBinderFactory(boolean idInExpandedForm) { - this.idInExpandedForm = idInExpandedForm; - } - - /** - * Create the IdConvertSet for the given type of Id properties. - */ - public IdBinder createIdBinder(BeanProperty id) { - - if (id == null){ - // for report type beans that don't need an id - return EMPTY; - - } - if (id.isEmbedded()){ - return new IdBinderEmbedded(idInExpandedForm, (BeanPropertyAssocOne)id); - } else { - return new IdBinderSimple(id); - } - } - -} +package com.avaje.ebeaninternal.server.deploy.id; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; + +/** + * Creates the appropriate IdConvertSet depending on the type of Id property(s). + */ +public class IdBinderFactory { + + private static final IdBinderEmpty EMPTY = new IdBinderEmpty(); + + private final boolean idInExpandedForm; + + public IdBinderFactory(boolean idInExpandedForm) { + this.idInExpandedForm = idInExpandedForm; + } + + /** + * Create the IdConvertSet for the given type of Id properties. + */ + public IdBinder createIdBinder(BeanProperty id) { + + if (id == null){ + // for report type beans that don't need an id + return EMPTY; + + } + if (id.isEmbedded()){ + return new IdBinderEmbedded(idInExpandedForm, (BeanPropertyAssocOne)id); + } else { + return new IdBinderSimple(id); + } + } + +} 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 7120a680a..1dd79dd01 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 @@ -1,217 +1,217 @@ -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.List; - -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.type.DataBind; -import com.avaje.ebeaninternal.server.type.ScalarType; - -/** - * Bind an Id where the Id is made of a single property (not embedded). - */ -public final class IdBinderSimple implements IdBinder { - - private final BeanProperty idProperty; - - private final String bindIdSql; - - private final Class expectedType; - - @SuppressWarnings("rawtypes") - private final ScalarType scalarType; - - public IdBinderSimple(BeanProperty idProperty) { - this.idProperty = idProperty; - this.scalarType = idProperty.getScalarType(); - this.expectedType = idProperty.getPropertyType(); - bindIdSql = InternString.intern(idProperty.getDbColumn()+" = ? "); - } - - public void initialise(){ - // do nothing - } - - public boolean isIdInExpandedForm() { - return false; - } - - 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(); - } - - public void buildSelectExpressionChain(String prefix, List selectChain) { - - idProperty.buildSelectExpressionChain(prefix, selectChain); - } - - /** - * Returns 1. - */ - public int getPropertyCount() { - return 1; - } - - @Override - public BeanProperty getBeanProperty() { - return idProperty; - } - - public String getIdProperty() { - return idProperty.getName(); - } - - public BeanProperty findBeanProperty(String dbColumnName) { - if (dbColumnName.equalsIgnoreCase(idProperty.getDbColumn())){ - return idProperty; - } - return null; - } - - public boolean isComplexId(){ - return false; - } - - public String getDefaultOrderBy() { - return idProperty.getName(); - } - - 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 Object[] getIdValues(EntityBean bean) { - return new Object[] { idProperty.getValue(bean) }; - } - - 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); - } - - if (bean != null) { - // support PropertyChangeSupport - idProperty.setValueIntercept(bean, idValue); - } - - return idValue; - } -} +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.List; + +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.type.DataBind; +import com.avaje.ebeaninternal.server.type.ScalarType; + +/** + * Bind an Id where the Id is made of a single property (not embedded). + */ +public final class IdBinderSimple implements IdBinder { + + private final BeanProperty idProperty; + + private final String bindIdSql; + + private final Class expectedType; + + @SuppressWarnings("rawtypes") + private final ScalarType scalarType; + + public IdBinderSimple(BeanProperty idProperty) { + this.idProperty = idProperty; + this.scalarType = idProperty.getScalarType(); + this.expectedType = idProperty.getPropertyType(); + bindIdSql = InternString.intern(idProperty.getDbColumn()+" = ? "); + } + + public void initialise(){ + // do nothing + } + + public boolean isIdInExpandedForm() { + return false; + } + + 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(); + } + + public void buildSelectExpressionChain(String prefix, List selectChain) { + + idProperty.buildSelectExpressionChain(prefix, selectChain); + } + + /** + * Returns 1. + */ + public int getPropertyCount() { + return 1; + } + + @Override + public BeanProperty getBeanProperty() { + return idProperty; + } + + public String getIdProperty() { + return idProperty.getName(); + } + + public BeanProperty findBeanProperty(String dbColumnName) { + if (dbColumnName.equalsIgnoreCase(idProperty.getDbColumn())){ + return idProperty; + } + return null; + } + + public boolean isComplexId(){ + return false; + } + + public String getDefaultOrderBy() { + return idProperty.getName(); + } + + 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 Object[] getIdValues(EntityBean bean) { + return new Object[] { idProperty.getValue(bean) }; + } + + 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); + } + + if (bean != null) { + // support PropertyChangeSupport + idProperty.setValueIntercept(bean, idValue); + } + + return idValue; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index 4ad63d1e9..b992a954f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -1,913 +1,913 @@ -package com.avaje.ebeaninternal.server.deploy.meta; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.persistence.Entity; -import javax.persistence.MappedSuperclass; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.avaje.ebean.annotation.ConcurrencyMode; -import com.avaje.ebean.config.TableName; -import com.avaje.ebean.config.dbplatform.IdGenerator; -import com.avaje.ebean.config.dbplatform.IdType; -import com.avaje.ebean.event.BeanFinder; -import com.avaje.ebean.event.BeanPersistController; -import com.avaje.ebean.event.BeanPersistListener; -import com.avaje.ebean.event.BeanQueryAdapter; -import com.avaje.ebeaninternal.server.core.CacheOptions; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController; -import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistListener; -import com.avaje.ebeaninternal.server.deploy.ChainedBeanQueryAdapter; -import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; -import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta; -import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; -import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; -import com.avaje.ebeaninternal.server.deploy.InheritInfo; -import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo; - -/** - * Describes Beans including their deployment information. - */ -public class DeployBeanDescriptor { - - static class PropOrder implements Comparator { - - public int compare(DeployBeanProperty o1, DeployBeanProperty o2) { - - int v2 = o1.getSortOrder(); - int v1 = o2.getSortOrder(); - return (v1 < v2 ? -1 : (v1 == v2 ? 0 : 1)); - } - } - - private static final PropOrder PROP_ORDER = new PropOrder(); - - private static final String I_SCALAOBJECT = "scala.ScalaObject"; - - private static final Logger logger = LoggerFactory.getLogger(DeployBeanDescriptor.class); - - /** - * Map of BeanProperty Linked so as to preserve order. - */ - private LinkedHashMap propMap = new LinkedHashMap(); - - private EntityType entityType; - - private final Map namedQueries = new LinkedHashMap(); - - private final Map namedUpdates = new LinkedHashMap(); - - private final Map rawSqlMetas = new LinkedHashMap(); - - private DeployBeanPropertyAssocOne unidirectional; - - /** - * Type of Identity generation strategy used. - */ - private IdType idType; - - /** - * The name of an IdGenerator (optional). - */ - private String idGeneratorName; - - private IdGenerator idGenerator; - - /** - * The database sequence name (optional). - */ - private String sequenceName; - - private int sequenceInitialValue; - - private int sequenceAllocationSize; - - /** - * Used with Identity columns but no getGeneratedKeys support. - */ - private String selectLastInsertedId; - - private String lazyFetchIncludes; - - /** - * The concurrency mode for beans of this type. - */ - private ConcurrencyMode concurrencyMode; - - private boolean updateChangesOnly; - - /** - * The tables this bean is dependent on. - */ - private String[] dependantTables; - - private List compoundUniqueConstraints; - - /** - * The base database table. - */ - private String baseTable; - private TableName baseTableFull; - - /** - * Used to provide mechanism to new EntityBean instances. Generated code - * faster than reflection at this stage. - */ - private BeanPropertyInfo beanReflect; - private String[] properties; - - /** - * The EntityBean type used to create new EntityBeans. - */ - private Class beanType; - - private List persistControllers = new ArrayList(2); - private List persistListeners = new ArrayList(2); - private List queryAdapters = new ArrayList(2); - - private CacheOptions cacheOptions = new CacheOptions(); - - /** - * If set overrides the find implementation. Server side only. - */ - private BeanFinder beanFinder; - - /** - * The table joins for this bean. Server side only. - */ - private ArrayList tableJoinList = new ArrayList(2); - - /** - * Inheritance information. Server side only. - */ - private InheritInfo inheritInfo; - - private String name; - - private boolean processedRawSqlExtend; - - /** - * Construct the BeanDescriptor. - */ - public DeployBeanDescriptor(Class beanType) { - this.beanType = beanType; - } - - /** - * Return true if this beanType is an abstract class. - */ - public boolean isAbstract() { - return Modifier.isAbstract(beanType.getModifiers()); - } - - public boolean isScalaObject() { - Class[] interfaces = beanType.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - String iname = interfaces[i].getName(); - if (I_SCALAOBJECT.equals(iname)) { - return true; - } - } - return false; - } - - public Collection getRawSqlMeta() { - if (!processedRawSqlExtend) { - rawSqlProcessExtend(); - processedRawSqlExtend = true; - } - return rawSqlMetas.values(); - } - - /** - * Process the "extend" attributes of raw SQL. Aka inherit the query and - * column mapping. - */ - private void rawSqlProcessExtend() { - - for (DRawSqlMeta rawSqlMeta : rawSqlMetas.values()) { - String extend = rawSqlMeta.getExtend(); - if (extend != null) { - DRawSqlMeta parentQuery = rawSqlMetas.get(extend); - if (parentQuery == null) { - throw new RuntimeException("parent query [" + extend + "] not found for sql-select " + rawSqlMeta.getName()); - } - rawSqlMeta.extend(parentQuery); - } - } - } - - public DeployBeanTable createDeployBeanTable() { - - DeployBeanTable beanTable = new DeployBeanTable(getBeanType()); - beanTable.setBaseTable(baseTable); - beanTable.setIdProperties(propertiesId()); - - return beanTable; - } - - /** - * Check all the properties to see if they all have read and write methods - * (required if using "subclassing" but not for "enhancement"). - */ - public boolean checkReadAndWriteMethods() { - - boolean missingMethods = false; - - for (DeployBeanProperty prop : propMap.values()) { - if (!prop.isTransient()) { - String m = ""; - if (prop.getReadMethod() == null) { - m += " missing readMethod "; - } - if (prop.getWriteMethod() == null) { - m += " missing writeMethod "; - } - if (!"".equals(m)) { - m += ". Should it be transient?"; - String msg = "Bean property " + getFullName() + "." + prop.getName() + " has " + m; - logger.error(msg); - missingMethods = true; - } - } - } - return !missingMethods; - } - - public void setEntityType(EntityType entityType) { - this.entityType = entityType; - } - - public boolean isEmbedded() { - return EntityType.EMBEDDED.equals(entityType); - } - - public boolean isBaseTableType() { - EntityType et = getEntityType(); - return EntityType.ORM.equals(et); - } - - public EntityType getEntityType() { - if (entityType == null) { - entityType = EntityType.ORM; - } - return entityType; - } - - public void setSequenceInitialValue(int sequenceInitialValue) { - this.sequenceInitialValue = sequenceInitialValue; - } - - public void setSequenceAllocationSize(int sequenceAllocationSize) { - this.sequenceAllocationSize = sequenceAllocationSize; - } - - public int getSequenceInitialValue() { - return sequenceInitialValue; - } - - public int getSequenceAllocationSize() { - return sequenceAllocationSize; - } - - public void add(DRawSqlMeta rawSqlMeta) { - rawSqlMetas.put(rawSqlMeta.getName(), rawSqlMeta); - if ("default".equals(rawSqlMeta.getName())) { - setEntityType(EntityType.SQL); - } - } - - public void add(DeployNamedUpdate namedUpdate) { - namedUpdates.put(namedUpdate.getName(), namedUpdate); - } - - public void add(DeployNamedQuery namedQuery) { - namedQueries.put(namedQuery.getName(), namedQuery); - if ("default".equals(namedQuery.getName())) { - setEntityType(EntityType.SQL); - } - } - - public Map getNamedQueries() { - return namedQueries; - } - - public Map getNamedUpdates() { - return namedUpdates; - } - - public String[] getProperties() { - return properties; - } - - public void setProperties(String[] props) { - this.properties = props; - } - - public BeanPropertyInfo getBeanReflect() { - return beanReflect; - } - - /** - * Return the class type this BeanDescriptor describes. - */ - public Class getBeanType() { - return beanType; - } - - /** - * Set the BeanReflect used to create new instances of an EntityBean. This - * could use reflection or code generation to do this. - */ - public void setBeanReflect(BeanPropertyInfo beanReflect) { - this.beanReflect = beanReflect; - } - - /** - * Returns the Inheritance mapping information. This will be null if this type - * of bean is not involved in any ORM inheritance mapping. - */ - public InheritInfo getInheritInfo() { - return inheritInfo; - } - - /** - * Set the ORM inheritance mapping information. - */ - public void setInheritInfo(InheritInfo inheritInfo) { - this.inheritInfo = inheritInfo; - } - - /** - * Return the cache options. - */ - public CacheOptions getCacheOptions() { - return cacheOptions; - } - - public boolean isNaturalKeyProperty(String name) { - return name.equals(cacheOptions.getNaturalKey()); - } - - public DeployBeanPropertyAssocOne getUnidirectional() { - return unidirectional; - } - - public void setUnidirectional(DeployBeanPropertyAssocOne unidirectional) { - this.unidirectional = unidirectional; - } - - /** - * Return the concurrency mode used for beans of this type. - */ - public ConcurrencyMode getConcurrencyMode() { - return concurrencyMode; - } - - /** - * Set the concurrency mode used for beans of this type. - */ - public void setConcurrencyMode(ConcurrencyMode concurrencyMode) { - this.concurrencyMode = concurrencyMode; - } - - public boolean isUpdateChangesOnly() { - return updateChangesOnly; - } - - public void setUpdateChangesOnly(boolean updateChangesOnly) { - this.updateChangesOnly = updateChangesOnly; - } - - /** - * Return the tables this bean is dependant on. This implies that if any of - * these tables are modified then cached beans may be invalidated. - */ - public String[] getDependantTables() { - return dependantTables; - } - - /** - * Add a compound unique constraint. - */ - public void addCompoundUniqueConstraint(CompoundUniqueContraint c) { - if (compoundUniqueConstraints == null) { - compoundUniqueConstraints = new ArrayList(); - } - compoundUniqueConstraints.add(c); - } - - /** - * Return the compound unique constraints (can be null). - */ - public CompoundUniqueContraint[] getCompoundUniqueConstraints() { - if (compoundUniqueConstraints == null) { - return null; - } else { - return compoundUniqueConstraints.toArray(new CompoundUniqueContraint[compoundUniqueConstraints.size()]); - } - } - - /** - * Set the tables this bean is dependant on. This implies that if any of these - * tables are modified then cached beans may be invalidated. - */ - public void setDependantTables(String[] dependantTables) { - this.dependantTables = dependantTables; - } - - /** - * Return the beanFinder. Usually null unless overriding the finder. - */ - public BeanFinder getBeanFinder() { - return beanFinder; - } - - /** - * Set the BeanFinder to use for beans of this type. This is set to override - * the finding from the default. - */ - public void setBeanFinder(BeanFinder beanFinder) { - this.beanFinder = beanFinder; - } - - /** - * Return the BeanPersistController (could be a chain of them, 1 or null). - */ - public BeanPersistController getPersistController() { - if (persistControllers.size() == 0) { - return null; - } else if (persistControllers.size() == 1) { - return persistControllers.get(0); - } else { - return new ChainedBeanPersistController(persistControllers); - } - } - - /** - * Return the BeanPersistListener (could be a chain of them, 1 or null). - */ - public BeanPersistListener getPersistListener() { - if (persistListeners.size() == 0) { - return null; - } else if (persistListeners.size() == 1) { - return persistListeners.get(0); - } else { - return new ChainedBeanPersistListener(persistListeners); - } - } - - public BeanQueryAdapter getQueryAdapter() { - if (queryAdapters.size() == 0) { - return null; - } else if (queryAdapters.size() == 1) { - return queryAdapters.get(0); - } else { - return new ChainedBeanQueryAdapter(queryAdapters); - } - } - - /** - * Set the Controller. - */ - public void addPersistController(BeanPersistController controller) { - persistControllers.add(controller); - } - - public void addPersistListener(BeanPersistListener listener) { - persistListeners.add(listener); - } - - public void addQueryAdapter(BeanQueryAdapter queryAdapter) { - queryAdapters.add(queryAdapter); - } - - /** - * Return true if this bean type should use IdGeneration. - *

- * If this is false and the Id is null it is assumed that a database auto - * increment feature is being used to populate the id. - *

- */ - public boolean isUseIdGenerator() { - return idType == IdType.GENERATOR; - } - - /** - * Return the base table. Only properties mapped to the base table are by - * default persisted. - */ - public String getBaseTable() { - return baseTable; - } - - /** - * Return the base table with full structure. - */ - public TableName getBaseTableFull() { - return baseTableFull; - } - - /** - * Set the base table. Only properties mapped to the base table are by default - * persisted. - */ - public void setBaseTable(TableName baseTableFull) { - this.baseTableFull = baseTableFull; - this.baseTable = baseTableFull == null ? null : baseTableFull.getQualifiedName(); - } - - public void sortProperties() { - - ArrayList list = new ArrayList(); - list.addAll(propMap.values()); - - Collections.sort(list, PROP_ORDER); - - propMap = new LinkedHashMap(list.size()); - for (int i = 0; i < list.size(); i++) { - addBeanProperty(list.get(i)); - } - } - - /** - * Add a bean property. - */ - public DeployBeanProperty addBeanProperty(DeployBeanProperty prop) { - return propMap.put(prop.getName(), prop); - } - - /** - * Get a BeanProperty by its name. - */ - public DeployBeanProperty getBeanProperty(String propName) { - return propMap.get(propName); - } - - /** - * Return the bean class name this descriptor is used for. - *

- * If this BeanDescriptor is for a table then this returns the table name - * instead. - *

- */ - public String getFullName() { - return beanType.getName(); - } - - /** - * Return the bean short name. - */ - public String getName() { - return name; - } - - /** - * Set the bean shortName. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Return the identity generation type. - */ - public IdType getIdType() { - return idType; - } - - /** - * Set the identity generation type. - */ - public void setIdType(IdType idType) { - this.idType = idType; - } - - /** - * Return the DB sequence name (can be null). - */ - public String getSequenceName() { - return sequenceName; - } - - /** - * Set the DB sequence name. - */ - public void setSequenceName(String sequenceName) { - this.sequenceName = sequenceName; - } - - /** - * Return the SQL used to return the last inserted Id. - *

- * Used with Identity columns where getGeneratedKeys is not supported. - *

- */ - public String getSelectLastInsertedId() { - return selectLastInsertedId; - } - - /** - * Set the SQL used to return the last inserted Id. - */ - public void setSelectLastInsertedId(String selectLastInsertedId) { - this.selectLastInsertedId = selectLastInsertedId; - } - - /** - * Return the name of the IdGenerator that should be used with this type of - * bean. A null value could be used to specify the 'default' IdGenerator. - */ - public String getIdGeneratorName() { - return idGeneratorName; - } - - /** - * Set the name of the IdGenerator that should be used with this type of bean. - */ - public void setIdGeneratorName(String idGeneratorName) { - this.idGeneratorName = idGeneratorName; - } - - /** - * Return the actual IdGenerator for this bean type (can be null). - */ - public IdGenerator getIdGenerator() { - return idGenerator; - } - - /** - * Set the actual IdGenerator for this bean type. - */ - public void setIdGenerator(IdGenerator idGenerator) { - this.idGenerator = idGenerator; - if (idGenerator != null && idGenerator.isDbSequence()) { - setSequenceName(idGenerator.getName()); - } - } - - /** - * Return the includes for getReference(). - */ - public String getLazyFetchIncludes() { - return lazyFetchIncludes; - } - - /** - * Set includes to use for lazy loading by getReference(). Note queries also - * build references and includes on the actual association are used for those - * references. - */ - public void setLazyFetchIncludes(String lazyFetchIncludes) { - if (lazyFetchIncludes != null && lazyFetchIncludes.length() > 0) { - this.lazyFetchIncludes = lazyFetchIncludes; - } - } - - /** - * Summary description. - */ - public String toString() { - return getFullName(); - } - - /** - * Add a TableJoin to this type of bean. For Secondary table properties. - */ - public void addTableJoin(DeployTableJoin join) { - tableJoinList.add(join); - } - - public List getTableJoins() { - return tableJoinList; - } - - /** - * Return a collection of all BeanProperty deployment information. - */ - public Collection propertiesAll() { - return propMap.values(); - } - - /** - * Return the defaultSelectClause using FetchType.LAZY and FetchType.EAGER. - */ - public String getDefaultSelectClause() { - - StringBuilder sb = new StringBuilder(); - - boolean hasLazyFetch = false; - - for (DeployBeanProperty prop : propMap.values()) { - if (prop.isTransient()) { - // ignore transient props etc - } else if (prop instanceof DeployBeanPropertyAssocMany) { - // ignore the associated many properties - } else { - if (prop.isFetchEager()) { - sb.append(prop.getName()).append(","); - } else { - hasLazyFetch = true; - } - } - } - - if (!hasLazyFetch) { - return null; - } - String selectClause = sb.toString(); - if (selectClause.length() == 0) { - throw new IllegalStateException("Bean " + getFullName() + " has no properties?"); - } - return selectClause.substring(0, selectClause.length() - 1); - } - - /** - * Parse the include separating by comma or semicolon. - */ - public Set parseDefaultSelectClause(String rawList) { - - if (rawList == null) { - return null; - } - - String[] res = rawList.split(","); - - LinkedHashSet set = new LinkedHashSet(res.length + 3); - - String temp = null; - for (int i = 0; i < res.length; i++) { - temp = res[i].trim(); - if (temp.length() > 0) { - set.add(temp); - } - } - return Collections.unmodifiableSet(set); - } - - /** - * Return the Primary Key column assuming it is a single column (not - * compound). This is for the purpose of defining a sequence name. - */ - public String getSinglePrimaryKeyColumn() { - List ids = propertiesId(); - if (ids.size() == 1) { - DeployBeanProperty p = ids.get(0); - if (p instanceof DeployBeanPropertyAssoc) { - // its a compound primary key - return null; - } else { - return p.getDbColumn(); - } - } - return null; - } - - /** - * 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 List propertiesId() { - - ArrayList list = new ArrayList(2); - - for (DeployBeanProperty prop : propMap.values()) { - if (prop.isId()) { - list.add(prop); - } - } - - return list; - } - - public DeployBeanPropertyAssocOne findJoinToTable(String tableName) { - - List> assocOne = propertiesAssocOne(); - for (DeployBeanPropertyAssocOne prop : assocOne) { - DeployTableJoin tableJoin = prop.getTableJoin(); - if (tableJoin != null && tableJoin.getTable().equalsIgnoreCase(tableName)) { - return prop; - } - } - return null; - } - - /** - * Return an Iterator of BeanPropertyAssocOne that are not embedded. These are - * effectively joined beans. For ManyToOne and OneToOne associations. - */ - public List> propertiesAssocOne() { - - ArrayList> list = new ArrayList>(); - - for (DeployBeanProperty prop : propMap.values()) { - if (prop instanceof DeployBeanPropertyAssocOne) { - if (!prop.isEmbedded()) { - list.add((DeployBeanPropertyAssocOne) prop); - } - } - } - - return list; - - } - - /** - * Return BeanPropertyAssocMany for this descriptor. - */ - public List> propertiesAssocMany() { - - ArrayList> list = new ArrayList>(); - - for (DeployBeanProperty prop : propMap.values()) { - if (prop instanceof DeployBeanPropertyAssocMany) { - list.add((DeployBeanPropertyAssocMany) prop); - } - } - - return list; - } - - /** - * Returns 'Version' properties on this bean. These are 'Counter' or 'Update - * Timestamp' type properties. Note version properties can also be on embedded - * beans rather than on the bean itself. - */ - public List propertiesVersion() { - - ArrayList list = new ArrayList(); - - for (DeployBeanProperty prop : propMap.values()) { - if (prop instanceof DeployBeanPropertyAssoc == false) { - if (!prop.isId() && prop.isVersionColumn()) { - list.add(prop); - } - } - } - - return list; - } - - /** - * base properties without the unique id properties. - */ - public List propertiesBase() { - - ArrayList list = new ArrayList(); - - for (DeployBeanProperty prop : propMap.values()) { - if (prop instanceof DeployBeanPropertyAssoc == false) { - if (!prop.isId()) { - list.add(prop); - } - } - } - - return list; - } - - /** - * Check the mapping for class inheritance - */ - public void checkInheritanceMapping() { - if (inheritInfo == null) { - checkInheritance(getBeanType()); - } - } - - /** - * Check valid mapping annotations on the class hierarchy. - */ - private void checkInheritance(Class beanType) { - - Class parent = beanType.getSuperclass(); - if (parent == null || Object.class.equals(parent)) { - // all good - return; - } - if (parent.isAnnotationPresent(Entity.class)) { - String msg = "Checking "+getBeanType()+" and found "+parent+" that has @Entity annotation rather than MappedSuperclass?"; - throw new IllegalStateException(msg); - } - if (parent.isAnnotationPresent(MappedSuperclass.class)) { - // continue checking - checkInheritance(parent); - } - } -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.persistence.Entity; +import javax.persistence.MappedSuperclass; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebean.annotation.ConcurrencyMode; +import com.avaje.ebean.config.TableName; +import com.avaje.ebean.config.dbplatform.IdGenerator; +import com.avaje.ebean.config.dbplatform.IdType; +import com.avaje.ebean.event.BeanFinder; +import com.avaje.ebean.event.BeanPersistController; +import com.avaje.ebean.event.BeanPersistListener; +import com.avaje.ebean.event.BeanQueryAdapter; +import com.avaje.ebeaninternal.server.core.CacheOptions; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController; +import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistListener; +import com.avaje.ebeaninternal.server.deploy.ChainedBeanQueryAdapter; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta; +import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; +import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.properties.BeanPropertyInfo; + +/** + * Describes Beans including their deployment information. + */ +public class DeployBeanDescriptor { + + static class PropOrder implements Comparator { + + public int compare(DeployBeanProperty o1, DeployBeanProperty o2) { + + int v2 = o1.getSortOrder(); + int v1 = o2.getSortOrder(); + return (v1 < v2 ? -1 : (v1 == v2 ? 0 : 1)); + } + } + + private static final PropOrder PROP_ORDER = new PropOrder(); + + private static final String I_SCALAOBJECT = "scala.ScalaObject"; + + private static final Logger logger = LoggerFactory.getLogger(DeployBeanDescriptor.class); + + /** + * Map of BeanProperty Linked so as to preserve order. + */ + private LinkedHashMap propMap = new LinkedHashMap(); + + private EntityType entityType; + + private final Map namedQueries = new LinkedHashMap(); + + private final Map namedUpdates = new LinkedHashMap(); + + private final Map rawSqlMetas = new LinkedHashMap(); + + private DeployBeanPropertyAssocOne unidirectional; + + /** + * Type of Identity generation strategy used. + */ + private IdType idType; + + /** + * The name of an IdGenerator (optional). + */ + private String idGeneratorName; + + private IdGenerator idGenerator; + + /** + * The database sequence name (optional). + */ + private String sequenceName; + + private int sequenceInitialValue; + + private int sequenceAllocationSize; + + /** + * Used with Identity columns but no getGeneratedKeys support. + */ + private String selectLastInsertedId; + + private String lazyFetchIncludes; + + /** + * The concurrency mode for beans of this type. + */ + private ConcurrencyMode concurrencyMode; + + private boolean updateChangesOnly; + + /** + * The tables this bean is dependent on. + */ + private String[] dependantTables; + + private List compoundUniqueConstraints; + + /** + * The base database table. + */ + private String baseTable; + private TableName baseTableFull; + + /** + * Used to provide mechanism to new EntityBean instances. Generated code + * faster than reflection at this stage. + */ + private BeanPropertyInfo beanReflect; + private String[] properties; + + /** + * The EntityBean type used to create new EntityBeans. + */ + private Class beanType; + + private List persistControllers = new ArrayList(2); + private List persistListeners = new ArrayList(2); + private List queryAdapters = new ArrayList(2); + + private CacheOptions cacheOptions = new CacheOptions(); + + /** + * If set overrides the find implementation. Server side only. + */ + private BeanFinder beanFinder; + + /** + * The table joins for this bean. Server side only. + */ + private ArrayList tableJoinList = new ArrayList(2); + + /** + * Inheritance information. Server side only. + */ + private InheritInfo inheritInfo; + + private String name; + + private boolean processedRawSqlExtend; + + /** + * Construct the BeanDescriptor. + */ + public DeployBeanDescriptor(Class beanType) { + this.beanType = beanType; + } + + /** + * Return true if this beanType is an abstract class. + */ + public boolean isAbstract() { + return Modifier.isAbstract(beanType.getModifiers()); + } + + public boolean isScalaObject() { + Class[] interfaces = beanType.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + String iname = interfaces[i].getName(); + if (I_SCALAOBJECT.equals(iname)) { + return true; + } + } + return false; + } + + public Collection getRawSqlMeta() { + if (!processedRawSqlExtend) { + rawSqlProcessExtend(); + processedRawSqlExtend = true; + } + return rawSqlMetas.values(); + } + + /** + * Process the "extend" attributes of raw SQL. Aka inherit the query and + * column mapping. + */ + private void rawSqlProcessExtend() { + + for (DRawSqlMeta rawSqlMeta : rawSqlMetas.values()) { + String extend = rawSqlMeta.getExtend(); + if (extend != null) { + DRawSqlMeta parentQuery = rawSqlMetas.get(extend); + if (parentQuery == null) { + throw new RuntimeException("parent query [" + extend + "] not found for sql-select " + rawSqlMeta.getName()); + } + rawSqlMeta.extend(parentQuery); + } + } + } + + public DeployBeanTable createDeployBeanTable() { + + DeployBeanTable beanTable = new DeployBeanTable(getBeanType()); + beanTable.setBaseTable(baseTable); + beanTable.setIdProperties(propertiesId()); + + return beanTable; + } + + /** + * Check all the properties to see if they all have read and write methods + * (required if using "subclassing" but not for "enhancement"). + */ + public boolean checkReadAndWriteMethods() { + + boolean missingMethods = false; + + for (DeployBeanProperty prop : propMap.values()) { + if (!prop.isTransient()) { + String m = ""; + if (prop.getReadMethod() == null) { + m += " missing readMethod "; + } + if (prop.getWriteMethod() == null) { + m += " missing writeMethod "; + } + if (!"".equals(m)) { + m += ". Should it be transient?"; + String msg = "Bean property " + getFullName() + "." + prop.getName() + " has " + m; + logger.error(msg); + missingMethods = true; + } + } + } + return !missingMethods; + } + + public void setEntityType(EntityType entityType) { + this.entityType = entityType; + } + + public boolean isEmbedded() { + return EntityType.EMBEDDED.equals(entityType); + } + + public boolean isBaseTableType() { + EntityType et = getEntityType(); + return EntityType.ORM.equals(et); + } + + public EntityType getEntityType() { + if (entityType == null) { + entityType = EntityType.ORM; + } + return entityType; + } + + public void setSequenceInitialValue(int sequenceInitialValue) { + this.sequenceInitialValue = sequenceInitialValue; + } + + public void setSequenceAllocationSize(int sequenceAllocationSize) { + this.sequenceAllocationSize = sequenceAllocationSize; + } + + public int getSequenceInitialValue() { + return sequenceInitialValue; + } + + public int getSequenceAllocationSize() { + return sequenceAllocationSize; + } + + public void add(DRawSqlMeta rawSqlMeta) { + rawSqlMetas.put(rawSqlMeta.getName(), rawSqlMeta); + if ("default".equals(rawSqlMeta.getName())) { + setEntityType(EntityType.SQL); + } + } + + public void add(DeployNamedUpdate namedUpdate) { + namedUpdates.put(namedUpdate.getName(), namedUpdate); + } + + public void add(DeployNamedQuery namedQuery) { + namedQueries.put(namedQuery.getName(), namedQuery); + if ("default".equals(namedQuery.getName())) { + setEntityType(EntityType.SQL); + } + } + + public Map getNamedQueries() { + return namedQueries; + } + + public Map getNamedUpdates() { + return namedUpdates; + } + + public String[] getProperties() { + return properties; + } + + public void setProperties(String[] props) { + this.properties = props; + } + + public BeanPropertyInfo getBeanReflect() { + return beanReflect; + } + + /** + * Return the class type this BeanDescriptor describes. + */ + public Class getBeanType() { + return beanType; + } + + /** + * Set the BeanReflect used to create new instances of an EntityBean. This + * could use reflection or code generation to do this. + */ + public void setBeanReflect(BeanPropertyInfo beanReflect) { + this.beanReflect = beanReflect; + } + + /** + * Returns the Inheritance mapping information. This will be null if this type + * of bean is not involved in any ORM inheritance mapping. + */ + public InheritInfo getInheritInfo() { + return inheritInfo; + } + + /** + * Set the ORM inheritance mapping information. + */ + public void setInheritInfo(InheritInfo inheritInfo) { + this.inheritInfo = inheritInfo; + } + + /** + * Return the cache options. + */ + public CacheOptions getCacheOptions() { + return cacheOptions; + } + + public boolean isNaturalKeyProperty(String name) { + return name.equals(cacheOptions.getNaturalKey()); + } + + public DeployBeanPropertyAssocOne getUnidirectional() { + return unidirectional; + } + + public void setUnidirectional(DeployBeanPropertyAssocOne unidirectional) { + this.unidirectional = unidirectional; + } + + /** + * Return the concurrency mode used for beans of this type. + */ + public ConcurrencyMode getConcurrencyMode() { + return concurrencyMode; + } + + /** + * Set the concurrency mode used for beans of this type. + */ + public void setConcurrencyMode(ConcurrencyMode concurrencyMode) { + this.concurrencyMode = concurrencyMode; + } + + public boolean isUpdateChangesOnly() { + return updateChangesOnly; + } + + public void setUpdateChangesOnly(boolean updateChangesOnly) { + this.updateChangesOnly = updateChangesOnly; + } + + /** + * Return the tables this bean is dependant on. This implies that if any of + * these tables are modified then cached beans may be invalidated. + */ + public String[] getDependantTables() { + return dependantTables; + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(CompoundUniqueContraint c) { + if (compoundUniqueConstraints == null) { + compoundUniqueConstraints = new ArrayList(); + } + compoundUniqueConstraints.add(c); + } + + /** + * Return the compound unique constraints (can be null). + */ + public CompoundUniqueContraint[] getCompoundUniqueConstraints() { + if (compoundUniqueConstraints == null) { + return null; + } else { + return compoundUniqueConstraints.toArray(new CompoundUniqueContraint[compoundUniqueConstraints.size()]); + } + } + + /** + * Set the tables this bean is dependant on. This implies that if any of these + * tables are modified then cached beans may be invalidated. + */ + public void setDependantTables(String[] dependantTables) { + this.dependantTables = dependantTables; + } + + /** + * Return the beanFinder. Usually null unless overriding the finder. + */ + public BeanFinder getBeanFinder() { + return beanFinder; + } + + /** + * Set the BeanFinder to use for beans of this type. This is set to override + * the finding from the default. + */ + public void setBeanFinder(BeanFinder beanFinder) { + this.beanFinder = beanFinder; + } + + /** + * Return the BeanPersistController (could be a chain of them, 1 or null). + */ + public BeanPersistController getPersistController() { + if (persistControllers.size() == 0) { + return null; + } else if (persistControllers.size() == 1) { + return persistControllers.get(0); + } else { + return new ChainedBeanPersistController(persistControllers); + } + } + + /** + * Return the BeanPersistListener (could be a chain of them, 1 or null). + */ + public BeanPersistListener getPersistListener() { + if (persistListeners.size() == 0) { + return null; + } else if (persistListeners.size() == 1) { + return persistListeners.get(0); + } else { + return new ChainedBeanPersistListener(persistListeners); + } + } + + public BeanQueryAdapter getQueryAdapter() { + if (queryAdapters.size() == 0) { + return null; + } else if (queryAdapters.size() == 1) { + return queryAdapters.get(0); + } else { + return new ChainedBeanQueryAdapter(queryAdapters); + } + } + + /** + * Set the Controller. + */ + public void addPersistController(BeanPersistController controller) { + persistControllers.add(controller); + } + + public void addPersistListener(BeanPersistListener listener) { + persistListeners.add(listener); + } + + public void addQueryAdapter(BeanQueryAdapter queryAdapter) { + queryAdapters.add(queryAdapter); + } + + /** + * Return true if this bean type should use IdGeneration. + *

+ * If this is false and the Id is null it is assumed that a database auto + * increment feature is being used to populate the id. + *

+ */ + public boolean isUseIdGenerator() { + return idType == IdType.GENERATOR; + } + + /** + * Return the base table. Only properties mapped to the base table are by + * default persisted. + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Return the base table with full structure. + */ + public TableName getBaseTableFull() { + return baseTableFull; + } + + /** + * Set the base table. Only properties mapped to the base table are by default + * persisted. + */ + public void setBaseTable(TableName baseTableFull) { + this.baseTableFull = baseTableFull; + this.baseTable = baseTableFull == null ? null : baseTableFull.getQualifiedName(); + } + + public void sortProperties() { + + ArrayList list = new ArrayList(); + list.addAll(propMap.values()); + + Collections.sort(list, PROP_ORDER); + + propMap = new LinkedHashMap(list.size()); + for (int i = 0; i < list.size(); i++) { + addBeanProperty(list.get(i)); + } + } + + /** + * Add a bean property. + */ + public DeployBeanProperty addBeanProperty(DeployBeanProperty prop) { + return propMap.put(prop.getName(), prop); + } + + /** + * Get a BeanProperty by its name. + */ + public DeployBeanProperty getBeanProperty(String propName) { + return propMap.get(propName); + } + + /** + * Return the bean class name this descriptor is used for. + *

+ * If this BeanDescriptor is for a table then this returns the table name + * instead. + *

+ */ + public String getFullName() { + return beanType.getName(); + } + + /** + * Return the bean short name. + */ + public String getName() { + return name; + } + + /** + * Set the bean shortName. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Return the identity generation type. + */ + public IdType getIdType() { + return idType; + } + + /** + * Set the identity generation type. + */ + public void setIdType(IdType idType) { + this.idType = idType; + } + + /** + * Return the DB sequence name (can be null). + */ + public String getSequenceName() { + return sequenceName; + } + + /** + * Set the DB sequence name. + */ + public void setSequenceName(String sequenceName) { + this.sequenceName = sequenceName; + } + + /** + * Return the SQL used to return the last inserted Id. + *

+ * Used with Identity columns where getGeneratedKeys is not supported. + *

+ */ + public String getSelectLastInsertedId() { + return selectLastInsertedId; + } + + /** + * Set the SQL used to return the last inserted Id. + */ + public void setSelectLastInsertedId(String selectLastInsertedId) { + this.selectLastInsertedId = selectLastInsertedId; + } + + /** + * Return the name of the IdGenerator that should be used with this type of + * bean. A null value could be used to specify the 'default' IdGenerator. + */ + public String getIdGeneratorName() { + return idGeneratorName; + } + + /** + * Set the name of the IdGenerator that should be used with this type of bean. + */ + public void setIdGeneratorName(String idGeneratorName) { + this.idGeneratorName = idGeneratorName; + } + + /** + * Return the actual IdGenerator for this bean type (can be null). + */ + public IdGenerator getIdGenerator() { + return idGenerator; + } + + /** + * Set the actual IdGenerator for this bean type. + */ + public void setIdGenerator(IdGenerator idGenerator) { + this.idGenerator = idGenerator; + if (idGenerator != null && idGenerator.isDbSequence()) { + setSequenceName(idGenerator.getName()); + } + } + + /** + * Return the includes for getReference(). + */ + public String getLazyFetchIncludes() { + return lazyFetchIncludes; + } + + /** + * Set includes to use for lazy loading by getReference(). Note queries also + * build references and includes on the actual association are used for those + * references. + */ + public void setLazyFetchIncludes(String lazyFetchIncludes) { + if (lazyFetchIncludes != null && lazyFetchIncludes.length() > 0) { + this.lazyFetchIncludes = lazyFetchIncludes; + } + } + + /** + * Summary description. + */ + public String toString() { + return getFullName(); + } + + /** + * Add a TableJoin to this type of bean. For Secondary table properties. + */ + public void addTableJoin(DeployTableJoin join) { + tableJoinList.add(join); + } + + public List getTableJoins() { + return tableJoinList; + } + + /** + * Return a collection of all BeanProperty deployment information. + */ + public Collection propertiesAll() { + return propMap.values(); + } + + /** + * Return the defaultSelectClause using FetchType.LAZY and FetchType.EAGER. + */ + public String getDefaultSelectClause() { + + StringBuilder sb = new StringBuilder(); + + boolean hasLazyFetch = false; + + for (DeployBeanProperty prop : propMap.values()) { + if (prop.isTransient()) { + // ignore transient props etc + } else if (prop instanceof DeployBeanPropertyAssocMany) { + // ignore the associated many properties + } else { + if (prop.isFetchEager()) { + sb.append(prop.getName()).append(","); + } else { + hasLazyFetch = true; + } + } + } + + if (!hasLazyFetch) { + return null; + } + String selectClause = sb.toString(); + if (selectClause.length() == 0) { + throw new IllegalStateException("Bean " + getFullName() + " has no properties?"); + } + return selectClause.substring(0, selectClause.length() - 1); + } + + /** + * Parse the include separating by comma or semicolon. + */ + public Set parseDefaultSelectClause(String rawList) { + + if (rawList == null) { + return null; + } + + String[] res = rawList.split(","); + + LinkedHashSet set = new LinkedHashSet(res.length + 3); + + String temp = null; + for (int i = 0; i < res.length; i++) { + temp = res[i].trim(); + if (temp.length() > 0) { + set.add(temp); + } + } + return Collections.unmodifiableSet(set); + } + + /** + * Return the Primary Key column assuming it is a single column (not + * compound). This is for the purpose of defining a sequence name. + */ + public String getSinglePrimaryKeyColumn() { + List ids = propertiesId(); + if (ids.size() == 1) { + DeployBeanProperty p = ids.get(0); + if (p instanceof DeployBeanPropertyAssoc) { + // its a compound primary key + return null; + } else { + return p.getDbColumn(); + } + } + return null; + } + + /** + * 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 List propertiesId() { + + ArrayList list = new ArrayList(2); + + for (DeployBeanProperty prop : propMap.values()) { + if (prop.isId()) { + list.add(prop); + } + } + + return list; + } + + public DeployBeanPropertyAssocOne findJoinToTable(String tableName) { + + List> assocOne = propertiesAssocOne(); + for (DeployBeanPropertyAssocOne prop : assocOne) { + DeployTableJoin tableJoin = prop.getTableJoin(); + if (tableJoin != null && tableJoin.getTable().equalsIgnoreCase(tableName)) { + return prop; + } + } + return null; + } + + /** + * Return an Iterator of BeanPropertyAssocOne that are not embedded. These are + * effectively joined beans. For ManyToOne and OneToOne associations. + */ + public List> propertiesAssocOne() { + + ArrayList> list = new ArrayList>(); + + for (DeployBeanProperty prop : propMap.values()) { + if (prop instanceof DeployBeanPropertyAssocOne) { + if (!prop.isEmbedded()) { + list.add((DeployBeanPropertyAssocOne) prop); + } + } + } + + return list; + + } + + /** + * Return BeanPropertyAssocMany for this descriptor. + */ + public List> propertiesAssocMany() { + + ArrayList> list = new ArrayList>(); + + for (DeployBeanProperty prop : propMap.values()) { + if (prop instanceof DeployBeanPropertyAssocMany) { + list.add((DeployBeanPropertyAssocMany) prop); + } + } + + return list; + } + + /** + * Returns 'Version' properties on this bean. These are 'Counter' or 'Update + * Timestamp' type properties. Note version properties can also be on embedded + * beans rather than on the bean itself. + */ + public List propertiesVersion() { + + ArrayList list = new ArrayList(); + + for (DeployBeanProperty prop : propMap.values()) { + if (prop instanceof DeployBeanPropertyAssoc == false) { + if (!prop.isId() && prop.isVersionColumn()) { + list.add(prop); + } + } + } + + return list; + } + + /** + * base properties without the unique id properties. + */ + public List propertiesBase() { + + ArrayList list = new ArrayList(); + + for (DeployBeanProperty prop : propMap.values()) { + if (prop instanceof DeployBeanPropertyAssoc == false) { + if (!prop.isId()) { + list.add(prop); + } + } + } + + return list; + } + + /** + * Check the mapping for class inheritance + */ + public void checkInheritanceMapping() { + if (inheritInfo == null) { + checkInheritance(getBeanType()); + } + } + + /** + * Check valid mapping annotations on the class hierarchy. + */ + private void checkInheritance(Class beanType) { + + Class parent = beanType.getSuperclass(); + if (parent == null || Object.class.equals(parent)) { + // all good + return; + } + if (parent.isAnnotationPresent(Entity.class)) { + String msg = "Checking "+getBeanType()+" and found "+parent+" that has @Entity annotation rather than MappedSuperclass?"; + throw new IllegalStateException(msg); + } + if (parent.isAnnotationPresent(MappedSuperclass.class)) { + // continue checking + checkInheritance(parent); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanEmbedded.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanEmbedded.java index 913f2c4f3..8410c9c87 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanEmbedded.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanEmbedded.java @@ -1,42 +1,42 @@ -package com.avaje.ebeaninternal.server.deploy.meta; - -import java.util.HashMap; -import java.util.Map; - -/** - * Collects Deployment information on Embedded beans. - *

- * Typically collects the overridden column names mapped - * to the Embedded bean. - *

- */ -public class DeployBeanEmbedded { - - /** - * A map of property names to dbColumns. - */ - Map propMap = new HashMap(); - - /** - * Set a property name to use a specific dbColumn. - */ - public void put(String propertyName, String dbCoumn){ - propMap.put(propertyName, dbCoumn); - } - - /** - * Set a Map of property names to dbColumns. - */ - public void putAll(Map propertyColumnMap){ - propMap.putAll(propertyColumnMap); - } - - /** - * Return a map of property names to dbColumns. - */ - public Map getPropertyColumnMap() { - return propMap; - } - - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.util.HashMap; +import java.util.Map; + +/** + * Collects Deployment information on Embedded beans. + *

+ * Typically collects the overridden column names mapped + * to the Embedded bean. + *

+ */ +public class DeployBeanEmbedded { + + /** + * A map of property names to dbColumns. + */ + Map propMap = new HashMap(); + + /** + * Set a property name to use a specific dbColumn. + */ + public void put(String propertyName, String dbCoumn){ + propMap.put(propertyName, dbCoumn); + } + + /** + * Set a Map of property names to dbColumns. + */ + public void putAll(Map propertyColumnMap){ + propMap.putAll(propertyColumnMap); + } + + /** + * Return a map of property names to dbColumns. + */ + public Map getPropertyColumnMap() { + return propMap; + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index 39303268f..ba07c55bc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -1,951 +1,951 @@ -package com.avaje.ebeaninternal.server.deploy.meta; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.sql.Types; -import java.util.HashMap; -import java.util.Map; - -import javax.persistence.EmbeddedId; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.Version; - -import com.avaje.ebean.annotation.CreatedTimestamp; -import com.avaje.ebean.annotation.UpdatedTimestamp; -import com.avaje.ebean.config.ScalarTypeConverter; -import com.avaje.ebean.config.dbplatform.DbEncrypt; -import com.avaje.ebean.config.dbplatform.DbEncryptFunction; -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.properties.BeanPropertyGetter; -import com.avaje.ebeaninternal.server.properties.BeanPropertySetter; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.server.type.ScalarTypeEnum; -import com.avaje.ebeaninternal.server.type.ScalarTypeWrapper; - -/** - * Description of a property of a bean. Includes its deployment information such - * as database column mapping information. - */ -public class DeployBeanProperty { - - private static final int ID_ORDER = 1000000; - private static final int UNIDIRECTIONAL_ORDER = 100000; - private static final int AUDITCOLUMN_ORDER = -1000000; - private static final int VERSIONCOLUMN_ORDER = -1000000; - - /** - * Advanced bean deployment. To exclude this property from update where - * clause. - */ - public static final String EXCLUDE_FROM_UPDATE_WHERE = "EXCLUDE_FROM_UPDATE_WHERE"; - - /** - * Advanced bean deployment. To exclude this property from delete where - * clause. - */ - public static final String EXCLUDE_FROM_DELETE_WHERE = "EXCLUDE_FROM_DELETE_WHERE"; - - /** - * Advanced bean deployment. To exclude this property from insert. - */ - public static final String EXCLUDE_FROM_INSERT = "EXCLUDE_FROM_INSERT"; - - /** - * Advanced bean deployment. To exclude this property from update set - * clause. - */ - public static final String EXCLUDE_FROM_UPDATE = "EXCLUDE_FROM_UPDATE"; - - /** - * Flag to mark this at part of the unique id. - */ - private boolean id; - - /** - * Flag to mark the property as embedded. This could be on - * BeanPropertyAssocOne rather than here. Put it here for checking Id type - * (embedded or not). - */ - private boolean embedded; - - /** - * Flag indicating if this the version property. - */ - private boolean versionColumn; - - private boolean fetchEager = true; - - /** - * Set if this property is nullable. - */ - private boolean nullable = true; - - private boolean unique; - - private boolean discriminator; - - /** - * The length or precision of the DB column. - */ - private int dbLength; - - private int dbScale; - - private String dbColumnDefn; - - private boolean isTransient; - - private boolean localEncrypted; - - private boolean exposeSerialize = true; - private boolean exposeDeserialize = true; - - private boolean dbEncrypted; - private DbEncryptFunction dbEncryptFunction; - - private int dbEncryptedType; - - private String dbBind = "?"; - - /** - * Is this property include in database resultSet. - */ - private boolean dbRead; - - /** - * Include this in DB insert. - */ - private boolean dbInsertable; - - /** - * Include this in a DB update. - */ - private boolean dbUpdateable; - - private DeployTableJoin secondaryTableJoin; - - private String secondaryTableJoinPrefix; - - /** - * Set to true if this property is based on a secondary table. - */ - private String secondaryTable; - - /** - * The type that owns this property. - */ - private Class owningType; - - /** - * True if the property is a Clob, Blob LongVarchar or LongVarbinary. - */ - private boolean lob; - - private boolean naturalKey; - - /** - * The logical bean property name. - */ - private String name; - - /** - * The reflected field. - */ - private Field field; - - /** - * The bean type. - */ - private Class propertyType; - - /** - * Set for Non-JDBC types to provide logical to db type conversion. - */ - private ScalarType scalarType; - - /** - * The database column. This can include quoted identifiers. - */ - private String dbColumn; - - private String sqlFormulaSelect; - private String sqlFormulaJoin; - - /** - * The jdbc data type this maps to. - */ - private int dbType; - - /** - * The default value to insert if null. - */ - private Object defaultValue; - - /** - * Extra deployment parameters. - */ - private HashMap extraAttributeMap = new HashMap(); - - /** - * The method used to read the property. - */ - private Method readMethod; - - /** - * The method used to write the property. - */ - private Method writeMethod; - - private int propertyIndex; - - private BeanPropertyGetter getter; - - private BeanPropertySetter setter; - - /** - * Generator for insert or update timestamp etc. - */ - private GeneratedProperty generatedProperty; - - private final DeployBeanDescriptor desc; - - private boolean undirectionalShadow; - - private int sortOrder; - - private boolean indexed; - private String indexName; - - public DeployBeanProperty(DeployBeanDescriptor desc, Class propertyType, ScalarType scalarType, ScalarTypeConverter typeConverter) { - this.desc = desc; - this.propertyType = propertyType; - this.scalarType = wrapScalarType(propertyType, scalarType, typeConverter); - } - - /** - * Wrap the ScalarType using a ScalarTypeConverter. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - private ScalarType wrapScalarType(Class propertyType, ScalarType scalarType, ScalarTypeConverter typeConverter) { - if (typeConverter == null){ - return scalarType; - } - return new ScalarTypeWrapper(propertyType, scalarType, typeConverter); - } - - public int getSortOverride() { - if (field == null) { - return 0; - } - if (field.getAnnotation(Id.class) != null) { - return ID_ORDER; - } else if (field.getAnnotation(EmbeddedId.class) != null) { - return ID_ORDER; - } else if (undirectionalShadow){ - return UNIDIRECTIONAL_ORDER; - } else if (field.getAnnotation(CreatedTimestamp.class) != null) { - return AUDITCOLUMN_ORDER; - } else if (field.getAnnotation(UpdatedTimestamp.class) != null) { - return AUDITCOLUMN_ORDER; - } else if (field.getAnnotation(Version.class) != null) { - return VERSIONCOLUMN_ORDER; - } - return 0; - } - - /** - * Return true is this is a simple scalar property. - */ - public boolean isScalar() { - return true; - } - - public String getFullBeanName() { - return desc.getFullName() + "." + name; - } - - /** - * Return true if this is a primitive type with a nullable DB column. - *

- * This should log a WARNING as primitive types can't be null. - *

- */ - public boolean isNullablePrimitive() { - if (nullable && propertyType.isPrimitive()) { - return true; - } - return false; - } - - /** - * Return the DB column length for character columns. - *

- * Note if there is no length explicitly defined then the scalarType is - * checked to see if that has one (primarily to support putting a length on - * Enum types). - *

- */ - public int getDbLength() { - if (dbLength == 0 && scalarType != null) { - return scalarType.getLength(); - } - - return dbLength; - } - - public boolean isExposeSerialize() { - return exposeSerialize; - } - - public void setExposeSerialize(boolean exposeSerialize) { - this.exposeSerialize = exposeSerialize; - } - - public boolean isExposeDeserialize() { - return exposeDeserialize; - } - - public void setExposeDeserialize(boolean exposeDeserialize) { - this.exposeDeserialize = exposeDeserialize; - } - - /** - * Return the sortOrder for the properties. - */ - public int getSortOrder() { - return sortOrder; - } - - /** - * Set the sortOrder for the properties. - */ - public void setSortOrder(int sortOrder) { - this.sortOrder = sortOrder; - } - - /** - * Return true if this is a placeholder property for a unidirectional relationship. - */ - public boolean isUndirectionalShadow() { - return undirectionalShadow; - } - - /** - * Mark this property as a placeholder for a unidirectional relationship. - */ - public void setUndirectionalShadow(boolean undirectionalShadow) { - this.undirectionalShadow = undirectionalShadow; - } - - /** - * Mark this property as mapping to the discriminator column. - */ - public void setDiscriminator(boolean discriminator) { - this.discriminator = discriminator; - } - - /** - * Return true if this property maps to the inheritance discriminator column.s - */ - public boolean isDiscriminator() { - return discriminator; - } - - /** - * Return true if the property is encrypted in java rather than in the DB. - */ - public boolean isLocalEncrypted() { - return localEncrypted; - } - - /** - * Set to true when the property is encrypted in java rather than in the DB. - */ - public void setLocalEncrypted(boolean localEncrypted) { - this.localEncrypted = localEncrypted; - } - - /** - * Set the DB column length for character columns. - */ - public void setDbLength(int dbLength) { - this.dbLength = dbLength; - } - - /** - * Return the Db scale for numeric columns. - */ - public int getDbScale() { - return dbScale; - } - - /** - * Set the Db scale for numeric columns. - */ - public void setDbScale(int dbScale) { - this.dbScale = dbScale; - } - - /** - * Return the DB column definition if defined. - */ - public String getDbColumnDefn() { - return dbColumnDefn; - } - - /** - * Set a specific DB column definition. - */ - public void setDbColumnDefn(String dbColumnDefn) { - if (dbColumnDefn == null || dbColumnDefn.trim().length() == 0) { - this.dbColumnDefn = null; - } else { - this.dbColumnDefn = InternString.intern(dbColumnDefn); - } - } - - public String getDbConstraintExpression() { - if (scalarType instanceof ScalarTypeEnum) { - // create a check constraint for the enum - ScalarTypeEnum etype = (ScalarTypeEnum) scalarType; - - // check dbColName IN ('A', 'I', 'D') - return "check (" + dbColumn + " in " + etype.getConstraintInValues() + ")"; - } - return null; - } - - /** - * Return the scalarType. This returns null for native JDBC types, otherwise - * it is used to convert between logical types and jdbc types. - */ - public ScalarType getScalarType() { - return scalarType; - } - - public void setScalarType(ScalarType scalarType) { - this.scalarType = scalarType; - } - - public int getPropertyIndex() { - return propertyIndex; - } - - public void setPropertyIndex(int propertyIndex) { - this.propertyIndex = propertyIndex; - } - - public BeanPropertyGetter getGetter() { - return getter; - } - - public BeanPropertySetter getSetter() { - return setter; - } - - /** - * Return the getter method. - */ - public Method getReadMethod() { - return readMethod; - } - - /** - * Return the setter method. - */ - public Method getWriteMethod() { - return writeMethod; - } - - /** - * Set to the owning type form a Inheritance heirarchy. - */ - public void setOwningType(Class owningType) { - this.owningType = owningType; - } - - public Class getOwningType() { - return owningType; - } - - /** - * Return true if this is local to this type - aka not from a super type. - */ - public boolean isLocal() { - return owningType == null || owningType.equals(desc.getBeanType()); - } - - /** - * Set the getter used to read the property value from a bean. - */ - public void setGetter(BeanPropertyGetter getter) { - this.getter = getter; - } - - /** - * Set the setter used to set the property value to a bean. - */ - public void setSetter(BeanPropertySetter setter) { - this.setter = setter; - } - - /** - * Return the name of the property. - */ - public String getName() { - return name; - } - - /** - * Set the name of the property. - */ - public void setName(String name) { - this.name = InternString.intern(name); - } - - /** - * Return the bean Field associated with this property. - */ - public Field getField() { - return field; - } - - /** - * Set the bean Field associated with this property. - */ - public void setField(Field field) { - this.field = field; - } - - public boolean isNaturalKey() { - return naturalKey; - } - - public void setNaturalKey(boolean naturalKey) { - this.naturalKey = naturalKey; - } - - /** - * Return true if this is a generated property like update timestamp and - * create timestamp. - */ - public boolean isGenerated() { - return generatedProperty != null; - } - - /** - * Return the GeneratedValue. Used to generate update timestamp etc. - */ - public GeneratedProperty getGeneratedProperty() { - return generatedProperty; - } - - /** - * Set the GeneratedValue. Used to generate update timestamp etc. - */ - public void setGeneratedProperty(GeneratedProperty generatedValue) { - this.generatedProperty = generatedValue; - } - - /** - * Return true if this property is mandatory. - */ - public boolean isNullable() { - return nullable; - } - - /** - * Set the not nullable of this property. - */ - public void setNullable(boolean isNullable) { - this.nullable = isNullable; - } - - /** - * Return true if the DB column is unique. - */ - public boolean isUnique() { - return unique; - } - - /** - * Set to true if the DB column is unique. - */ - public void setUnique(boolean unique) { - this.unique = unique; - } - - /** - * Return true if this is a version column used for concurrency checking. - */ - public boolean isVersionColumn() { - return versionColumn; - } - - /** - * Set if this is a version column used for concurrency checking. - */ - public void setVersionColumn(boolean isVersionColumn) { - this.versionColumn = isVersionColumn; - } - - /** - * Return true if this should be eager fetched by default. - */ - public boolean isFetchEager() { - return fetchEager; - } - - /** - * Set the default fetch type for this property. - */ - public void setFetchType(FetchType fetchType) { - this.fetchEager = FetchType.EAGER.equals(fetchType); - } - - /** - * Return the formula this property is based on. - */ - public String getSqlFormulaSelect() { - return sqlFormulaSelect; - } - - public String getSqlFormulaJoin() { - return sqlFormulaJoin; - } - - /** - * The property is based on a formula. - */ - public void setSqlFormula(String formulaSelect, String formulaJoin) { - this.sqlFormulaSelect = formulaSelect; - this.sqlFormulaJoin = formulaJoin.equals("") ? null : formulaJoin; - this.dbRead = true; - this.dbInsertable = false; - this.dbUpdateable = false; - } - - public String getElPlaceHolder(EntityType et) { - if (sqlFormulaSelect != null) { - return sqlFormulaSelect; - } else { - if (secondaryTableJoinPrefix != null){ - return "${"+secondaryTableJoinPrefix+"}"+getDbColumn(); - } - // prepend table alias placeholder - return ElPropertyValue.ROOT_ELPREFIX + getDbColumn(); - } - } - - /** - * The database column name this is mapped to. - */ - public String getDbColumn() { - if (sqlFormulaSelect != null) { - return sqlFormulaSelect; - } - return dbColumn; - } - - /** - * Set the database column name this is mapped to. - */ - public void setDbColumn(String dbColumn) { - this.dbColumn = InternString.intern(dbColumn); - } - - /** - * Return the database jdbc data type this is mapped to. - */ - public int getDbType() { - return dbType; - } - - /** - * Set the database jdbc data type this is mapped to. - */ - public void setDbType(int dbType) { - this.dbType = dbType; - this.lob = isLobType(dbType); - } - - /** - * Return true if this is mapped to a Clob Blob LongVarchar or - * LongVarbinary. - */ - public boolean isLob() { - return lob; - } - - private boolean isLobType(int type) { - switch (type) { - case Types.CLOB: - return true; - case Types.BLOB: - return true; - case Types.LONGVARBINARY: - return true; - case Types.LONGVARCHAR: - return true; - - default: - return false; - } - } - - /** - * Return true if this property is based on a secondary table. - */ - public boolean isSecondaryTable() { - return secondaryTable != null; - } - - /** - * Return the secondary table this property is associated with. - */ - public String getSecondaryTable() { - return secondaryTable; - } - - /** - * Set to true if this property is included in persisting. - */ - public void setSecondaryTable(String secondaryTable) { - this.secondaryTable = secondaryTable; - this.dbInsertable = false; - this.dbUpdateable = false; - } - - /** - * - */ - public String getSecondaryTableJoinPrefix() { - return secondaryTableJoinPrefix; - } - - public DeployTableJoin getSecondaryTableJoin() { - return secondaryTableJoin; - } - - public void setSecondaryTableJoin(DeployTableJoin secondaryTableJoin, String prefix) { - this.secondaryTableJoin = secondaryTableJoin; - this.secondaryTableJoinPrefix = prefix; - } - - /** - * Return the DB Bind parameter. Typically is "?" but can be different for - * encrypted bind. - */ - public String getDbBind() { - return dbBind; - } - - /** - * Set the DB bind parameter (if different from "?"). - */ - public void setDbBind(String dbBind) { - this.dbBind = dbBind; - } - - /** - * Return true if this property is encrypted in the DB. - */ - public boolean isDbEncrypted() { - return dbEncrypted; - } - -// /** -// * Set true if this property should be encrypted in the DB. -// */ -// public void setDbEncrypted(boolean dbEncrypted) { -// this.dbEncrypted = dbEncrypted; -// } - - public DbEncryptFunction getDbEncryptFunction() { - return dbEncryptFunction; - } - - public void setDbEncryptFunction(DbEncryptFunction dbEncryptFunction, DbEncrypt dbEncrypt, int dbLen) { - this.dbEncryptFunction = dbEncryptFunction; - this.dbEncrypted = true; - this.dbBind = dbEncryptFunction.getEncryptBindSql(); - - this.dbEncryptedType = isLob() ? Types.BLOB : dbEncrypt.getEncryptDbType(); - if (dbLen > 0){ - setDbLength(dbLen); - } - } - - /** - * Return the DB type for the encrypted property. This can differ from the - * logical type (String encrypted and stored in a VARBINARY) - */ - public int getDbEncryptedType() { - return dbEncryptedType; - } - - /** - * Set the DB type used to store the encrypted value. - */ - public void setDbEncryptedType(int dbEncryptedType) { - this.dbEncryptedType = dbEncryptedType; - } - - /** - * Return true if this property is included in database queries. - */ - public boolean isDbRead() { - return dbRead; - } - - /** - * Set to true if this property is included in database queries. - */ - public void setDbRead(boolean isDBRead) { - this.dbRead = isDBRead; - } - - public boolean isDbInsertable() { - return dbInsertable; - } - - public void setDbInsertable(boolean insertable) { - this.dbInsertable = insertable; - } - - public boolean isDbUpdateable() { - return dbUpdateable; - } - - public void setDbUpdateable(boolean updateable) { - this.dbUpdateable = updateable; - } - - /** - * Return true if the property is transient. - */ - public boolean isTransient() { - return isTransient; - } - - /** - * Mark the property explicitly as a transient property. - */ - public void setTransient(boolean isTransient) { - this.isTransient = isTransient; - } - - /** - * Set the bean read method. - *

- * NB: That a BeanReflectGetter is used to actually perform the getting of - * property values from a bean. This is due to performance considerations. - *

- */ - public void setReadMethod(Method readMethod) { - this.readMethod = readMethod; - } - - /** - * Set the bean write method. - *

- * NB: That a BeanReflectSetter is used to actually perform the setting of - * property values to a bean. This is due to performance considerations. - *

- */ - public void setWriteMethod(Method writeMethod) { - this.writeMethod = writeMethod; - } - - /** - * Return the property type. - */ - public Class getPropertyType() { - return propertyType; - } - - /** - * Return true if this is included in the unique id. - */ - public boolean isId() { - return id; - } - - /** - * Set to true if this is included in the unique id. - */ - public void setId(boolean id) { - this.id = id; - } - - /** - * Return true if this is an Embedded property. In this case it shares the - * table and pk of its owner object. - */ - public boolean isEmbedded() { - return embedded; - } - - /** - * Set to true if this is an embedded property. - */ - public void setEmbedded(boolean embedded) { - this.embedded = embedded; - } - - public Map getExtraAttributeMap() { - return extraAttributeMap; - } - - /** - * Return an extra attribute set on this property. - */ - public String getExtraAttribute(String key) { - return (String) extraAttributeMap.get(key); - } - - /** - * Set an extra attribute set on this property. - */ - public void setExtraAttribute(String key, String value) { - extraAttributeMap.put(key, value); - } - - /** - * Return the default value. - */ - public Object getDefaultValue() { - return defaultValue; - } - - /** - * Set the default value. Inserted if the value is null. - */ - public void setDefaultValue(Object defaultValue) { - this.defaultValue = defaultValue; - } - - public String toString() { - return desc.getFullName() + "." + name; - } - - public boolean isIndexed() { - return indexed; - } - - public void setIndexed(boolean indexed) { - this.indexed = indexed; - } - - public String getIndexName() { - return indexName; - } - - public void setIndexName(String indexName) { - this.indexName = indexName; - } -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.sql.Types; +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.EmbeddedId; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.Version; + +import com.avaje.ebean.annotation.CreatedTimestamp; +import com.avaje.ebean.annotation.UpdatedTimestamp; +import com.avaje.ebean.config.ScalarTypeConverter; +import com.avaje.ebean.config.dbplatform.DbEncrypt; +import com.avaje.ebean.config.dbplatform.DbEncryptFunction; +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.properties.BeanPropertyGetter; +import com.avaje.ebeaninternal.server.properties.BeanPropertySetter; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.avaje.ebeaninternal.server.type.ScalarTypeEnum; +import com.avaje.ebeaninternal.server.type.ScalarTypeWrapper; + +/** + * Description of a property of a bean. Includes its deployment information such + * as database column mapping information. + */ +public class DeployBeanProperty { + + private static final int ID_ORDER = 1000000; + private static final int UNIDIRECTIONAL_ORDER = 100000; + private static final int AUDITCOLUMN_ORDER = -1000000; + private static final int VERSIONCOLUMN_ORDER = -1000000; + + /** + * Advanced bean deployment. To exclude this property from update where + * clause. + */ + public static final String EXCLUDE_FROM_UPDATE_WHERE = "EXCLUDE_FROM_UPDATE_WHERE"; + + /** + * Advanced bean deployment. To exclude this property from delete where + * clause. + */ + public static final String EXCLUDE_FROM_DELETE_WHERE = "EXCLUDE_FROM_DELETE_WHERE"; + + /** + * Advanced bean deployment. To exclude this property from insert. + */ + public static final String EXCLUDE_FROM_INSERT = "EXCLUDE_FROM_INSERT"; + + /** + * Advanced bean deployment. To exclude this property from update set + * clause. + */ + public static final String EXCLUDE_FROM_UPDATE = "EXCLUDE_FROM_UPDATE"; + + /** + * Flag to mark this at part of the unique id. + */ + private boolean id; + + /** + * Flag to mark the property as embedded. This could be on + * BeanPropertyAssocOne rather than here. Put it here for checking Id type + * (embedded or not). + */ + private boolean embedded; + + /** + * Flag indicating if this the version property. + */ + private boolean versionColumn; + + private boolean fetchEager = true; + + /** + * Set if this property is nullable. + */ + private boolean nullable = true; + + private boolean unique; + + private boolean discriminator; + + /** + * The length or precision of the DB column. + */ + private int dbLength; + + private int dbScale; + + private String dbColumnDefn; + + private boolean isTransient; + + private boolean localEncrypted; + + private boolean exposeSerialize = true; + private boolean exposeDeserialize = true; + + private boolean dbEncrypted; + private DbEncryptFunction dbEncryptFunction; + + private int dbEncryptedType; + + private String dbBind = "?"; + + /** + * Is this property include in database resultSet. + */ + private boolean dbRead; + + /** + * Include this in DB insert. + */ + private boolean dbInsertable; + + /** + * Include this in a DB update. + */ + private boolean dbUpdateable; + + private DeployTableJoin secondaryTableJoin; + + private String secondaryTableJoinPrefix; + + /** + * Set to true if this property is based on a secondary table. + */ + private String secondaryTable; + + /** + * The type that owns this property. + */ + private Class owningType; + + /** + * True if the property is a Clob, Blob LongVarchar or LongVarbinary. + */ + private boolean lob; + + private boolean naturalKey; + + /** + * The logical bean property name. + */ + private String name; + + /** + * The reflected field. + */ + private Field field; + + /** + * The bean type. + */ + private Class propertyType; + + /** + * Set for Non-JDBC types to provide logical to db type conversion. + */ + private ScalarType scalarType; + + /** + * The database column. This can include quoted identifiers. + */ + private String dbColumn; + + private String sqlFormulaSelect; + private String sqlFormulaJoin; + + /** + * The jdbc data type this maps to. + */ + private int dbType; + + /** + * The default value to insert if null. + */ + private Object defaultValue; + + /** + * Extra deployment parameters. + */ + private HashMap extraAttributeMap = new HashMap(); + + /** + * The method used to read the property. + */ + private Method readMethod; + + /** + * The method used to write the property. + */ + private Method writeMethod; + + private int propertyIndex; + + private BeanPropertyGetter getter; + + private BeanPropertySetter setter; + + /** + * Generator for insert or update timestamp etc. + */ + private GeneratedProperty generatedProperty; + + private final DeployBeanDescriptor desc; + + private boolean undirectionalShadow; + + private int sortOrder; + + private boolean indexed; + private String indexName; + + public DeployBeanProperty(DeployBeanDescriptor desc, Class propertyType, ScalarType scalarType, ScalarTypeConverter typeConverter) { + this.desc = desc; + this.propertyType = propertyType; + this.scalarType = wrapScalarType(propertyType, scalarType, typeConverter); + } + + /** + * Wrap the ScalarType using a ScalarTypeConverter. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + private ScalarType wrapScalarType(Class propertyType, ScalarType scalarType, ScalarTypeConverter typeConverter) { + if (typeConverter == null){ + return scalarType; + } + return new ScalarTypeWrapper(propertyType, scalarType, typeConverter); + } + + public int getSortOverride() { + if (field == null) { + return 0; + } + if (field.getAnnotation(Id.class) != null) { + return ID_ORDER; + } else if (field.getAnnotation(EmbeddedId.class) != null) { + return ID_ORDER; + } else if (undirectionalShadow){ + return UNIDIRECTIONAL_ORDER; + } else if (field.getAnnotation(CreatedTimestamp.class) != null) { + return AUDITCOLUMN_ORDER; + } else if (field.getAnnotation(UpdatedTimestamp.class) != null) { + return AUDITCOLUMN_ORDER; + } else if (field.getAnnotation(Version.class) != null) { + return VERSIONCOLUMN_ORDER; + } + return 0; + } + + /** + * Return true is this is a simple scalar property. + */ + public boolean isScalar() { + return true; + } + + public String getFullBeanName() { + return desc.getFullName() + "." + name; + } + + /** + * Return true if this is a primitive type with a nullable DB column. + *

+ * This should log a WARNING as primitive types can't be null. + *

+ */ + public boolean isNullablePrimitive() { + if (nullable && propertyType.isPrimitive()) { + return true; + } + return false; + } + + /** + * Return the DB column length for character columns. + *

+ * Note if there is no length explicitly defined then the scalarType is + * checked to see if that has one (primarily to support putting a length on + * Enum types). + *

+ */ + public int getDbLength() { + if (dbLength == 0 && scalarType != null) { + return scalarType.getLength(); + } + + return dbLength; + } + + public boolean isExposeSerialize() { + return exposeSerialize; + } + + public void setExposeSerialize(boolean exposeSerialize) { + this.exposeSerialize = exposeSerialize; + } + + public boolean isExposeDeserialize() { + return exposeDeserialize; + } + + public void setExposeDeserialize(boolean exposeDeserialize) { + this.exposeDeserialize = exposeDeserialize; + } + + /** + * Return the sortOrder for the properties. + */ + public int getSortOrder() { + return sortOrder; + } + + /** + * Set the sortOrder for the properties. + */ + public void setSortOrder(int sortOrder) { + this.sortOrder = sortOrder; + } + + /** + * Return true if this is a placeholder property for a unidirectional relationship. + */ + public boolean isUndirectionalShadow() { + return undirectionalShadow; + } + + /** + * Mark this property as a placeholder for a unidirectional relationship. + */ + public void setUndirectionalShadow(boolean undirectionalShadow) { + this.undirectionalShadow = undirectionalShadow; + } + + /** + * Mark this property as mapping to the discriminator column. + */ + public void setDiscriminator(boolean discriminator) { + this.discriminator = discriminator; + } + + /** + * Return true if this property maps to the inheritance discriminator column.s + */ + public boolean isDiscriminator() { + return discriminator; + } + + /** + * Return true if the property is encrypted in java rather than in the DB. + */ + public boolean isLocalEncrypted() { + return localEncrypted; + } + + /** + * Set to true when the property is encrypted in java rather than in the DB. + */ + public void setLocalEncrypted(boolean localEncrypted) { + this.localEncrypted = localEncrypted; + } + + /** + * Set the DB column length for character columns. + */ + public void setDbLength(int dbLength) { + this.dbLength = dbLength; + } + + /** + * Return the Db scale for numeric columns. + */ + public int getDbScale() { + return dbScale; + } + + /** + * Set the Db scale for numeric columns. + */ + public void setDbScale(int dbScale) { + this.dbScale = dbScale; + } + + /** + * Return the DB column definition if defined. + */ + public String getDbColumnDefn() { + return dbColumnDefn; + } + + /** + * Set a specific DB column definition. + */ + public void setDbColumnDefn(String dbColumnDefn) { + if (dbColumnDefn == null || dbColumnDefn.trim().length() == 0) { + this.dbColumnDefn = null; + } else { + this.dbColumnDefn = InternString.intern(dbColumnDefn); + } + } + + public String getDbConstraintExpression() { + if (scalarType instanceof ScalarTypeEnum) { + // create a check constraint for the enum + ScalarTypeEnum etype = (ScalarTypeEnum) scalarType; + + // check dbColName IN ('A', 'I', 'D') + return "check (" + dbColumn + " in " + etype.getConstraintInValues() + ")"; + } + return null; + } + + /** + * Return the scalarType. This returns null for native JDBC types, otherwise + * it is used to convert between logical types and jdbc types. + */ + public ScalarType getScalarType() { + return scalarType; + } + + public void setScalarType(ScalarType scalarType) { + this.scalarType = scalarType; + } + + public int getPropertyIndex() { + return propertyIndex; + } + + public void setPropertyIndex(int propertyIndex) { + this.propertyIndex = propertyIndex; + } + + public BeanPropertyGetter getGetter() { + return getter; + } + + public BeanPropertySetter getSetter() { + return setter; + } + + /** + * Return the getter method. + */ + public Method getReadMethod() { + return readMethod; + } + + /** + * Return the setter method. + */ + public Method getWriteMethod() { + return writeMethod; + } + + /** + * Set to the owning type form a Inheritance heirarchy. + */ + public void setOwningType(Class owningType) { + this.owningType = owningType; + } + + public Class getOwningType() { + return owningType; + } + + /** + * Return true if this is local to this type - aka not from a super type. + */ + public boolean isLocal() { + return owningType == null || owningType.equals(desc.getBeanType()); + } + + /** + * Set the getter used to read the property value from a bean. + */ + public void setGetter(BeanPropertyGetter getter) { + this.getter = getter; + } + + /** + * Set the setter used to set the property value to a bean. + */ + public void setSetter(BeanPropertySetter setter) { + this.setter = setter; + } + + /** + * Return the name of the property. + */ + public String getName() { + return name; + } + + /** + * Set the name of the property. + */ + public void setName(String name) { + this.name = InternString.intern(name); + } + + /** + * Return the bean Field associated with this property. + */ + public Field getField() { + return field; + } + + /** + * Set the bean Field associated with this property. + */ + public void setField(Field field) { + this.field = field; + } + + public boolean isNaturalKey() { + return naturalKey; + } + + public void setNaturalKey(boolean naturalKey) { + this.naturalKey = naturalKey; + } + + /** + * Return true if this is a generated property like update timestamp and + * create timestamp. + */ + public boolean isGenerated() { + return generatedProperty != null; + } + + /** + * Return the GeneratedValue. Used to generate update timestamp etc. + */ + public GeneratedProperty getGeneratedProperty() { + return generatedProperty; + } + + /** + * Set the GeneratedValue. Used to generate update timestamp etc. + */ + public void setGeneratedProperty(GeneratedProperty generatedValue) { + this.generatedProperty = generatedValue; + } + + /** + * Return true if this property is mandatory. + */ + public boolean isNullable() { + return nullable; + } + + /** + * Set the not nullable of this property. + */ + public void setNullable(boolean isNullable) { + this.nullable = isNullable; + } + + /** + * Return true if the DB column is unique. + */ + public boolean isUnique() { + return unique; + } + + /** + * Set to true if the DB column is unique. + */ + public void setUnique(boolean unique) { + this.unique = unique; + } + + /** + * Return true if this is a version column used for concurrency checking. + */ + public boolean isVersionColumn() { + return versionColumn; + } + + /** + * Set if this is a version column used for concurrency checking. + */ + public void setVersionColumn(boolean isVersionColumn) { + this.versionColumn = isVersionColumn; + } + + /** + * Return true if this should be eager fetched by default. + */ + public boolean isFetchEager() { + return fetchEager; + } + + /** + * Set the default fetch type for this property. + */ + public void setFetchType(FetchType fetchType) { + this.fetchEager = FetchType.EAGER.equals(fetchType); + } + + /** + * Return the formula this property is based on. + */ + public String getSqlFormulaSelect() { + return sqlFormulaSelect; + } + + public String getSqlFormulaJoin() { + return sqlFormulaJoin; + } + + /** + * The property is based on a formula. + */ + public void setSqlFormula(String formulaSelect, String formulaJoin) { + this.sqlFormulaSelect = formulaSelect; + this.sqlFormulaJoin = formulaJoin.equals("") ? null : formulaJoin; + this.dbRead = true; + this.dbInsertable = false; + this.dbUpdateable = false; + } + + public String getElPlaceHolder(EntityType et) { + if (sqlFormulaSelect != null) { + return sqlFormulaSelect; + } else { + if (secondaryTableJoinPrefix != null){ + return "${"+secondaryTableJoinPrefix+"}"+getDbColumn(); + } + // prepend table alias placeholder + return ElPropertyValue.ROOT_ELPREFIX + getDbColumn(); + } + } + + /** + * The database column name this is mapped to. + */ + public String getDbColumn() { + if (sqlFormulaSelect != null) { + return sqlFormulaSelect; + } + return dbColumn; + } + + /** + * Set the database column name this is mapped to. + */ + public void setDbColumn(String dbColumn) { + this.dbColumn = InternString.intern(dbColumn); + } + + /** + * Return the database jdbc data type this is mapped to. + */ + public int getDbType() { + return dbType; + } + + /** + * Set the database jdbc data type this is mapped to. + */ + public void setDbType(int dbType) { + this.dbType = dbType; + this.lob = isLobType(dbType); + } + + /** + * Return true if this is mapped to a Clob Blob LongVarchar or + * LongVarbinary. + */ + public boolean isLob() { + return lob; + } + + private boolean isLobType(int type) { + switch (type) { + case Types.CLOB: + return true; + case Types.BLOB: + return true; + case Types.LONGVARBINARY: + return true; + case Types.LONGVARCHAR: + return true; + + default: + return false; + } + } + + /** + * Return true if this property is based on a secondary table. + */ + public boolean isSecondaryTable() { + return secondaryTable != null; + } + + /** + * Return the secondary table this property is associated with. + */ + public String getSecondaryTable() { + return secondaryTable; + } + + /** + * Set to true if this property is included in persisting. + */ + public void setSecondaryTable(String secondaryTable) { + this.secondaryTable = secondaryTable; + this.dbInsertable = false; + this.dbUpdateable = false; + } + + /** + * + */ + public String getSecondaryTableJoinPrefix() { + return secondaryTableJoinPrefix; + } + + public DeployTableJoin getSecondaryTableJoin() { + return secondaryTableJoin; + } + + public void setSecondaryTableJoin(DeployTableJoin secondaryTableJoin, String prefix) { + this.secondaryTableJoin = secondaryTableJoin; + this.secondaryTableJoinPrefix = prefix; + } + + /** + * Return the DB Bind parameter. Typically is "?" but can be different for + * encrypted bind. + */ + public String getDbBind() { + return dbBind; + } + + /** + * Set the DB bind parameter (if different from "?"). + */ + public void setDbBind(String dbBind) { + this.dbBind = dbBind; + } + + /** + * Return true if this property is encrypted in the DB. + */ + public boolean isDbEncrypted() { + return dbEncrypted; + } + +// /** +// * Set true if this property should be encrypted in the DB. +// */ +// public void setDbEncrypted(boolean dbEncrypted) { +// this.dbEncrypted = dbEncrypted; +// } + + public DbEncryptFunction getDbEncryptFunction() { + return dbEncryptFunction; + } + + public void setDbEncryptFunction(DbEncryptFunction dbEncryptFunction, DbEncrypt dbEncrypt, int dbLen) { + this.dbEncryptFunction = dbEncryptFunction; + this.dbEncrypted = true; + this.dbBind = dbEncryptFunction.getEncryptBindSql(); + + this.dbEncryptedType = isLob() ? Types.BLOB : dbEncrypt.getEncryptDbType(); + if (dbLen > 0){ + setDbLength(dbLen); + } + } + + /** + * Return the DB type for the encrypted property. This can differ from the + * logical type (String encrypted and stored in a VARBINARY) + */ + public int getDbEncryptedType() { + return dbEncryptedType; + } + + /** + * Set the DB type used to store the encrypted value. + */ + public void setDbEncryptedType(int dbEncryptedType) { + this.dbEncryptedType = dbEncryptedType; + } + + /** + * Return true if this property is included in database queries. + */ + public boolean isDbRead() { + return dbRead; + } + + /** + * Set to true if this property is included in database queries. + */ + public void setDbRead(boolean isDBRead) { + this.dbRead = isDBRead; + } + + public boolean isDbInsertable() { + return dbInsertable; + } + + public void setDbInsertable(boolean insertable) { + this.dbInsertable = insertable; + } + + public boolean isDbUpdateable() { + return dbUpdateable; + } + + public void setDbUpdateable(boolean updateable) { + this.dbUpdateable = updateable; + } + + /** + * Return true if the property is transient. + */ + public boolean isTransient() { + return isTransient; + } + + /** + * Mark the property explicitly as a transient property. + */ + public void setTransient(boolean isTransient) { + this.isTransient = isTransient; + } + + /** + * Set the bean read method. + *

+ * NB: That a BeanReflectGetter is used to actually perform the getting of + * property values from a bean. This is due to performance considerations. + *

+ */ + public void setReadMethod(Method readMethod) { + this.readMethod = readMethod; + } + + /** + * Set the bean write method. + *

+ * NB: That a BeanReflectSetter is used to actually perform the setting of + * property values to a bean. This is due to performance considerations. + *

+ */ + public void setWriteMethod(Method writeMethod) { + this.writeMethod = writeMethod; + } + + /** + * Return the property type. + */ + public Class getPropertyType() { + return propertyType; + } + + /** + * Return true if this is included in the unique id. + */ + public boolean isId() { + return id; + } + + /** + * Set to true if this is included in the unique id. + */ + public void setId(boolean id) { + this.id = id; + } + + /** + * Return true if this is an Embedded property. In this case it shares the + * table and pk of its owner object. + */ + public boolean isEmbedded() { + return embedded; + } + + /** + * Set to true if this is an embedded property. + */ + public void setEmbedded(boolean embedded) { + this.embedded = embedded; + } + + public Map getExtraAttributeMap() { + return extraAttributeMap; + } + + /** + * Return an extra attribute set on this property. + */ + public String getExtraAttribute(String key) { + return (String) extraAttributeMap.get(key); + } + + /** + * Set an extra attribute set on this property. + */ + public void setExtraAttribute(String key, String value) { + extraAttributeMap.put(key, value); + } + + /** + * Return the default value. + */ + public Object getDefaultValue() { + return defaultValue; + } + + /** + * Set the default value. Inserted if the value is null. + */ + public void setDefaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + } + + public String toString() { + return desc.getFullName() + "." + name; + } + + public boolean isIndexed() { + return indexed; + } + + public void setIndexed(boolean indexed) { + this.indexed = indexed; + } + + public String getIndexName() { + return indexName; + } + + public void setIndexName(String indexName) { + this.indexName = indexName; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java index f06be9264..5e7f1c059 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java @@ -1,156 +1,156 @@ -package com.avaje.ebeaninternal.server.deploy.meta; - -import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo; -import com.avaje.ebeaninternal.server.deploy.BeanTable; - -/** - * Abstract base for properties mapped to an associated bean, list, set or map. - */ -public abstract class DeployBeanPropertyAssoc extends DeployBeanProperty { - - /** - * The type of the joined bean. - */ - Class targetType; - - /** - * Persist settings. - */ - BeanCascadeInfo cascadeInfo = new BeanCascadeInfo(); - - /** - * The join table information. - */ - BeanTable beanTable; - - /** - * Join between the beans. - */ - DeployTableJoin tableJoin = new DeployTableJoin(); - - /** - * Whether the associated join type should be an outer join. - */ - boolean isOuterJoin = false; - - /** - * Literal added to where clause of lazy loading query. - */ - String extraWhere; - - /** - * From the deployment mappedBy attribute. - */ - String mappedBy; - - /** - * Construct the property. - */ - public DeployBeanPropertyAssoc(DeployBeanDescriptor desc, Class targetType) { - super(desc, targetType, null, null); - this.targetType = targetType; - } - - /** - * Return false. - */ - @Override - public boolean isScalar() { - return false; - } - - /** - * Return the type of the target. - *

- * This is the class of the associated bean, or beans contained in a list, - * set or map. - *

- */ - public Class getTargetType() { - return targetType; - } - - /** - * Return if this association should use an Outer join. - */ - public boolean isOuterJoin() { - return isOuterJoin; - } - - /** - * Specify that this bean should use an outer join. - */ - public void setOuterJoin(boolean isOuterJoin) { - this.isOuterJoin = isOuterJoin; - } - - /** - * Return a literal expression that is added to the query that lazy loads - * the collection. - */ - public String getExtraWhere() { - return extraWhere; - } - - /** - * Set a literal expression to add to the query that lazy loads the - * collection. - */ - public void setExtraWhere(String extraWhere) { - this.extraWhere = extraWhere; - } - - /** - * return the join to use for the bean. - */ - public DeployTableJoin getTableJoin() { - return tableJoin; - } - - /** - * Return the BeanTable for this association. - *

- * This has the table name which is used to determine the relationship for - * this association. - *

- */ - public BeanTable getBeanTable() { - return beanTable; - } - - /** - * Set the bean table. - */ - public void setBeanTable(BeanTable beanTable) { - this.beanTable = beanTable; - getTableJoin().setTable(beanTable.getBaseTable()); - } - - /** - * Get the persist info. - */ - public BeanCascadeInfo getCascadeInfo() { - return cascadeInfo; - } - - - /** - * Return the mappedBy deployment attribute. - *

- * This is the name of the property in the 'detail' bean that maps back to - * this 'master' bean. - *

- */ - public String getMappedBy() { - return mappedBy; - } - - /** - * Set mappedBy deployment attribute. - */ - public void setMappedBy(String mappedBy) { - if (!"".equals(mappedBy)) { - this.mappedBy = mappedBy; - } - } -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo; +import com.avaje.ebeaninternal.server.deploy.BeanTable; + +/** + * Abstract base for properties mapped to an associated bean, list, set or map. + */ +public abstract class DeployBeanPropertyAssoc extends DeployBeanProperty { + + /** + * The type of the joined bean. + */ + Class targetType; + + /** + * Persist settings. + */ + BeanCascadeInfo cascadeInfo = new BeanCascadeInfo(); + + /** + * The join table information. + */ + BeanTable beanTable; + + /** + * Join between the beans. + */ + DeployTableJoin tableJoin = new DeployTableJoin(); + + /** + * Whether the associated join type should be an outer join. + */ + boolean isOuterJoin = false; + + /** + * Literal added to where clause of lazy loading query. + */ + String extraWhere; + + /** + * From the deployment mappedBy attribute. + */ + String mappedBy; + + /** + * Construct the property. + */ + public DeployBeanPropertyAssoc(DeployBeanDescriptor desc, Class targetType) { + super(desc, targetType, null, null); + this.targetType = targetType; + } + + /** + * Return false. + */ + @Override + public boolean isScalar() { + return false; + } + + /** + * Return the type of the target. + *

+ * This is the class of the associated bean, or beans contained in a list, + * set or map. + *

+ */ + public Class getTargetType() { + return targetType; + } + + /** + * Return if this association should use an Outer join. + */ + public boolean isOuterJoin() { + return isOuterJoin; + } + + /** + * Specify that this bean should use an outer join. + */ + public void setOuterJoin(boolean isOuterJoin) { + this.isOuterJoin = isOuterJoin; + } + + /** + * Return a literal expression that is added to the query that lazy loads + * the collection. + */ + public String getExtraWhere() { + return extraWhere; + } + + /** + * Set a literal expression to add to the query that lazy loads the + * collection. + */ + public void setExtraWhere(String extraWhere) { + this.extraWhere = extraWhere; + } + + /** + * return the join to use for the bean. + */ + public DeployTableJoin getTableJoin() { + return tableJoin; + } + + /** + * Return the BeanTable for this association. + *

+ * This has the table name which is used to determine the relationship for + * this association. + *

+ */ + public BeanTable getBeanTable() { + return beanTable; + } + + /** + * Set the bean table. + */ + public void setBeanTable(BeanTable beanTable) { + this.beanTable = beanTable; + getTableJoin().setTable(beanTable.getBaseTable()); + } + + /** + * Get the persist info. + */ + public BeanCascadeInfo getCascadeInfo() { + return cascadeInfo; + } + + + /** + * Return the mappedBy deployment attribute. + *

+ * This is the name of the property in the 'detail' bean that maps back to + * this 'master' bean. + *

+ */ + public String getMappedBy() { + return mappedBy; + } + + /** + * Set mappedBy deployment attribute. + */ + public void setMappedBy(String mappedBy) { + if (!"".equals(mappedBy)) { + this.mappedBy = mappedBy; + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocMany.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocMany.java index 711683ca6..2fd7245ea 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocMany.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocMany.java @@ -1,194 +1,194 @@ -package com.avaje.ebeaninternal.server.deploy.meta; - -import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; -import com.avaje.ebeaninternal.server.deploy.ManyType; -import com.avaje.ebeaninternal.server.deploy.TableJoin; - -/** - * Property mapped to a List Set or Map. - */ -public class DeployBeanPropertyAssocMany extends DeployBeanPropertyAssoc { - - ModifyListenMode modifyListenMode = ModifyListenMode.NONE; - - /** - * Flag to indicate manyToMany relationship. - */ - boolean manyToMany; - - /** - * Flag to indicate this is a unidirectional relationship. - */ - boolean unidirectional; - - /** - * Join for manyToMany intersection table. - */ - DeployTableJoin intersectionJoin; - - /** - * For ManyToMany this is the Inverse join used to build reference queries. - */ - DeployTableJoin inverseJoin; - - String fetchOrderBy; - - String mapKey; - - /** - * The type of the many, set, list or map. - */ - ManyType manyType; - - /** - * Create this property. - */ - public DeployBeanPropertyAssocMany(DeployBeanDescriptor desc, Class targetType, ManyType manyType) { - super(desc, targetType); - this.manyType = manyType; - } - - /** - * When generics is not used for manyType you can specify via annotations. - *

- * Really only expect this for Scala due to a Scala compiler bug at the moment. - * Otherwise I'd probably not bother support this. - *

- */ - @SuppressWarnings("unchecked") - public void setTargetType(Class cls){ - this.targetType = (Class)cls; - } - - - /** - * Return the many type. - */ - public ManyType getManyType() { - return manyType; - } - - /** - * Return true if this is many to many. - */ - public boolean isManyToMany() { - return manyToMany; - } - - /** - * Set to true if this is a many to many. - */ - public void setManyToMany(boolean isManyToMany) { - this.manyToMany = isManyToMany; - } - - /** - * Return the mode for listening to changes to the List Set or Map. - */ - public ModifyListenMode getModifyListenMode() { - return modifyListenMode; - } - - /** - * Set the mode for listening to changes to the List Set or Map. - */ - public void setModifyListenMode(ModifyListenMode modifyListenMode) { - this.modifyListenMode = modifyListenMode; - } - - /** - * Return true if this is a unidirectional relationship. - */ - public boolean isUnidirectional() { - return unidirectional; - } - - /** - * Set to true if this is a unidirectional relationship. - */ - public void setUnidirectional(boolean unidirectional) { - this.unidirectional = unidirectional; - } - - /** - * Create the immutable version of the intersection join. - */ - public TableJoin createIntersectionTableJoin() { - if (intersectionJoin != null){ - return new TableJoin(intersectionJoin, null); - } else { - return null; - } - } - - /** - * Create the immutable version of the inverse join. - */ - public TableJoin createInverseTableJoin() { - if (inverseJoin != null){ - return new TableJoin(inverseJoin, null); - } else { - return null; - } - } - - /** - * ManyToMany only, join from local table to intersection table. - */ - public DeployTableJoin getIntersectionJoin() { - return intersectionJoin; - } - - public DeployTableJoin getInverseJoin() { - return inverseJoin; - } - - /** - * ManyToMany only, join from local table to intersection table. - */ - public void setIntersectionJoin(DeployTableJoin intersectionJoin) { - this.intersectionJoin = intersectionJoin; - } - - /** - * ManyToMany only, join from foreign table to intersection table. - */ - public void setInverseJoin(DeployTableJoin inverseJoin) { - this.inverseJoin = inverseJoin; - } - - /** - * Return the order by clause used to order the fetching of the data for - * this list, set or map. - */ - public String getFetchOrderBy() { - return fetchOrderBy; - } - - /** - * Return the default mapKey when returning a Map. - */ - public String getMapKey() { - return mapKey; - } - - /** - * Set the default mapKey to use when returning a Map. - */ - public void setMapKey(String mapKey) { - if (mapKey != null && mapKey.length() > 0) { - this.mapKey = mapKey; - } - } - - /** - * Set the order by clause used to order the fetching or the data for this - * list, set or map. - */ - public void setFetchOrderBy(String orderBy) { - if (orderBy != null && orderBy.length() > 0) { - fetchOrderBy = orderBy; - } - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; +import com.avaje.ebeaninternal.server.deploy.ManyType; +import com.avaje.ebeaninternal.server.deploy.TableJoin; + +/** + * Property mapped to a List Set or Map. + */ +public class DeployBeanPropertyAssocMany extends DeployBeanPropertyAssoc { + + ModifyListenMode modifyListenMode = ModifyListenMode.NONE; + + /** + * Flag to indicate manyToMany relationship. + */ + boolean manyToMany; + + /** + * Flag to indicate this is a unidirectional relationship. + */ + boolean unidirectional; + + /** + * Join for manyToMany intersection table. + */ + DeployTableJoin intersectionJoin; + + /** + * For ManyToMany this is the Inverse join used to build reference queries. + */ + DeployTableJoin inverseJoin; + + String fetchOrderBy; + + String mapKey; + + /** + * The type of the many, set, list or map. + */ + ManyType manyType; + + /** + * Create this property. + */ + public DeployBeanPropertyAssocMany(DeployBeanDescriptor desc, Class targetType, ManyType manyType) { + super(desc, targetType); + this.manyType = manyType; + } + + /** + * When generics is not used for manyType you can specify via annotations. + *

+ * Really only expect this for Scala due to a Scala compiler bug at the moment. + * Otherwise I'd probably not bother support this. + *

+ */ + @SuppressWarnings("unchecked") + public void setTargetType(Class cls){ + this.targetType = (Class)cls; + } + + + /** + * Return the many type. + */ + public ManyType getManyType() { + return manyType; + } + + /** + * Return true if this is many to many. + */ + public boolean isManyToMany() { + return manyToMany; + } + + /** + * Set to true if this is a many to many. + */ + public void setManyToMany(boolean isManyToMany) { + this.manyToMany = isManyToMany; + } + + /** + * Return the mode for listening to changes to the List Set or Map. + */ + public ModifyListenMode getModifyListenMode() { + return modifyListenMode; + } + + /** + * Set the mode for listening to changes to the List Set or Map. + */ + public void setModifyListenMode(ModifyListenMode modifyListenMode) { + this.modifyListenMode = modifyListenMode; + } + + /** + * Return true if this is a unidirectional relationship. + */ + public boolean isUnidirectional() { + return unidirectional; + } + + /** + * Set to true if this is a unidirectional relationship. + */ + public void setUnidirectional(boolean unidirectional) { + this.unidirectional = unidirectional; + } + + /** + * Create the immutable version of the intersection join. + */ + public TableJoin createIntersectionTableJoin() { + if (intersectionJoin != null){ + return new TableJoin(intersectionJoin, null); + } else { + return null; + } + } + + /** + * Create the immutable version of the inverse join. + */ + public TableJoin createInverseTableJoin() { + if (inverseJoin != null){ + return new TableJoin(inverseJoin, null); + } else { + return null; + } + } + + /** + * ManyToMany only, join from local table to intersection table. + */ + public DeployTableJoin getIntersectionJoin() { + return intersectionJoin; + } + + public DeployTableJoin getInverseJoin() { + return inverseJoin; + } + + /** + * ManyToMany only, join from local table to intersection table. + */ + public void setIntersectionJoin(DeployTableJoin intersectionJoin) { + this.intersectionJoin = intersectionJoin; + } + + /** + * ManyToMany only, join from foreign table to intersection table. + */ + public void setInverseJoin(DeployTableJoin inverseJoin) { + this.inverseJoin = inverseJoin; + } + + /** + * Return the order by clause used to order the fetching of the data for + * this list, set or map. + */ + public String getFetchOrderBy() { + return fetchOrderBy; + } + + /** + * Return the default mapKey when returning a Map. + */ + public String getMapKey() { + return mapKey; + } + + /** + * Set the default mapKey to use when returning a Map. + */ + public void setMapKey(String mapKey) { + if (mapKey != null && mapKey.length() > 0) { + this.mapKey = mapKey; + } + } + + /** + * Set the order by clause used to order the fetching or the data for this + * list, set or map. + */ + public void setFetchOrderBy(String orderBy) { + if (orderBy != null && orderBy.length() > 0) { + fetchOrderBy = orderBy; + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocOne.java index 524cc7b7c..06a100adc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocOne.java @@ -1,95 +1,95 @@ -package com.avaje.ebeaninternal.server.deploy.meta; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; - - -/** - * Property mapped to a joined bean. - */ -public class DeployBeanPropertyAssocOne extends DeployBeanPropertyAssoc { - - boolean oneToOne; - - boolean oneToOneExported; - - boolean importedPrimaryKey; - - DeployBeanEmbedded deployEmbedded; - - /** - * Create the property. - */ - public DeployBeanPropertyAssocOne(DeployBeanDescriptor desc, Class targetType) { - super(desc, targetType); - } - - /** - * Return the deploy information specifically for the deployment - * of Embedded beans. - */ - public DeployBeanEmbedded getDeployEmbedded() { - // deployment should be single threaded - if (deployEmbedded == null){ - deployEmbedded = new DeployBeanEmbedded(); - } - return deployEmbedded; - } - - @Override - public String getDbColumn() { - DeployTableJoinColumn[] columns = tableJoin.columns(); - if (columns.length == 1){ - return columns[0].getLocalDbColumn(); - } - return super.getDbColumn(); - } - - @Override - public String getElPlaceHolder(EntityType et) { - return super.getElPlaceHolder(et); - } - - /** - * Return true if this a OneToOne property. Otherwise assumed ManyToOne. - */ - public boolean isOneToOne() { - return oneToOne; - } - - /** - * Set to true if this is a OneToOne. - */ - public void setOneToOne(boolean oneToOne) { - this.oneToOne = oneToOne; - } - - /** - * Return true if this is the exported side of a OneToOne. - */ - public boolean isOneToOneExported() { - return oneToOneExported; - } - - /** - * Set to true if this is the exported side of a OneToOne. This means - * it doesn't 'own' the foreign key column. A OneToMany without the many. - */ - public void setOneToOneExported(boolean oneToOneExported) { - this.oneToOneExported = oneToOneExported; - } - - /** - * If true this bean maps to the primary key. - */ - public boolean isImportedPrimaryKey() { - return importedPrimaryKey; - } - - /** - * Set to true if the bean maps to the primary key. - */ - public void setImportedPrimaryKey(boolean importedPrimaryKey) { - this.importedPrimaryKey = importedPrimaryKey; - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; + + +/** + * Property mapped to a joined bean. + */ +public class DeployBeanPropertyAssocOne extends DeployBeanPropertyAssoc { + + boolean oneToOne; + + boolean oneToOneExported; + + boolean importedPrimaryKey; + + DeployBeanEmbedded deployEmbedded; + + /** + * Create the property. + */ + public DeployBeanPropertyAssocOne(DeployBeanDescriptor desc, Class targetType) { + super(desc, targetType); + } + + /** + * Return the deploy information specifically for the deployment + * of Embedded beans. + */ + public DeployBeanEmbedded getDeployEmbedded() { + // deployment should be single threaded + if (deployEmbedded == null){ + deployEmbedded = new DeployBeanEmbedded(); + } + return deployEmbedded; + } + + @Override + public String getDbColumn() { + DeployTableJoinColumn[] columns = tableJoin.columns(); + if (columns.length == 1){ + return columns[0].getLocalDbColumn(); + } + return super.getDbColumn(); + } + + @Override + public String getElPlaceHolder(EntityType et) { + return super.getElPlaceHolder(et); + } + + /** + * Return true if this a OneToOne property. Otherwise assumed ManyToOne. + */ + public boolean isOneToOne() { + return oneToOne; + } + + /** + * Set to true if this is a OneToOne. + */ + public void setOneToOne(boolean oneToOne) { + this.oneToOne = oneToOne; + } + + /** + * Return true if this is the exported side of a OneToOne. + */ + public boolean isOneToOneExported() { + return oneToOneExported; + } + + /** + * Set to true if this is the exported side of a OneToOne. This means + * it doesn't 'own' the foreign key column. A OneToMany without the many. + */ + public void setOneToOneExported(boolean oneToOneExported) { + this.oneToOneExported = oneToOneExported; + } + + /** + * If true this bean maps to the primary key. + */ + public boolean isImportedPrimaryKey() { + return importedPrimaryKey; + } + + /** + * Set to true if the bean maps to the primary key. + */ + public void setImportedPrimaryKey(boolean importedPrimaryKey) { + this.importedPrimaryKey = importedPrimaryKey; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyCompound.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyCompound.java index ebdd90acd..cfdc2c8f7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyCompound.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyCompound.java @@ -1,119 +1,119 @@ -package com.avaje.ebeaninternal.server.deploy.meta; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map.Entry; - -import com.avaje.ebean.config.ScalarTypeConverter; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompoundRoot; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompoundScalar; -import com.avaje.ebeaninternal.server.type.CtCompoundProperty; -import com.avaje.ebeaninternal.server.type.CtCompoundType; -import com.avaje.ebeaninternal.server.type.CtCompoundTypeScalarList; -import com.avaje.ebeaninternal.server.type.ScalarType; - - -/** - * Property mapped to a joined bean. - */ -public class DeployBeanPropertyCompound extends DeployBeanProperty { - - final CtCompoundType compoundType; - - final ScalarTypeConverter typeConverter; - - DeployBeanEmbedded deployEmbedded; - - /** - * Create the property. - */ - public DeployBeanPropertyCompound(DeployBeanDescriptor desc, Class targetType, - CtCompoundType compoundType, ScalarTypeConverter typeConverter) { - - super(desc, targetType, null, null); - this.compoundType = compoundType; - this.typeConverter = typeConverter; - } - - public BeanPropertyCompoundRoot getFlatProperties(BeanDescriptorMap owner, BeanDescriptor descriptor) { - - // get a 'flat' list of all the scalar types, their relative property names - // and also set their matching dbColumn - - // represents the root property - BeanPropertyCompoundRoot rootProperty = new BeanPropertyCompoundRoot(this); - - // Walk the tree of a compound type collecting the - // scalar types and non-scalar properties - CtCompoundTypeScalarList ctMeta = new CtCompoundTypeScalarList(); - - compoundType.accumulateScalarTypes(null, ctMeta); - - List beanPropertyList = new ArrayList(); - - - // for each of the scalar types inside a compound value object - // build a BeanPropertyCompoundScalar with appropriate deployment - // information. - - for (Entry> entry : ctMeta.entries()) { - - String relativePropertyName = entry.getKey(); - ScalarType scalarType = entry.getValue(); - - CtCompoundProperty ctProp = ctMeta.getCompoundType(relativePropertyName); - - - String dbColumn = (getName() + "." + relativePropertyName).replace(".", "_"); - dbColumn = getDbColumn(relativePropertyName, dbColumn); - - DeployBeanProperty deploy = new DeployBeanProperty(null, scalarType.getType(), scalarType, null); - deploy.setScalarType(scalarType); - deploy.setDbColumn(dbColumn); - deploy.setName(relativePropertyName); - deploy.setDbInsertable(true); - deploy.setDbUpdateable(true); - deploy.setDbRead(true); - - BeanPropertyCompoundScalar bp = new BeanPropertyCompoundScalar(rootProperty, deploy, ctProp, typeConverter); - beanPropertyList.add(bp); - - rootProperty.register(bp); - } - - rootProperty.setNonScalarProperties(ctMeta.getNonScalarProperties()); - return rootProperty; - } - - private String getDbColumn(String propName, String defaultDbColumn) { - if (deployEmbedded == null) { - return defaultDbColumn; - } - String dbColumn = deployEmbedded.getPropertyColumnMap().get(propName); - return dbColumn == null ? defaultDbColumn : dbColumn; - } - - /** - * Return the deploy information specifically for the deployment - * of Embedded beans. - */ - public DeployBeanEmbedded getDeployEmbedded() { - // deployment should be single threaded - if (deployEmbedded == null) { - deployEmbedded = new DeployBeanEmbedded(); - } - return deployEmbedded; - } - - public ScalarTypeConverter getTypeConverter() { - return typeConverter; - } - - public CtCompoundType getCompoundType() { - return compoundType; - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +import com.avaje.ebean.config.ScalarTypeConverter; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompoundRoot; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompoundScalar; +import com.avaje.ebeaninternal.server.type.CtCompoundProperty; +import com.avaje.ebeaninternal.server.type.CtCompoundType; +import com.avaje.ebeaninternal.server.type.CtCompoundTypeScalarList; +import com.avaje.ebeaninternal.server.type.ScalarType; + + +/** + * Property mapped to a joined bean. + */ +public class DeployBeanPropertyCompound extends DeployBeanProperty { + + final CtCompoundType compoundType; + + final ScalarTypeConverter typeConverter; + + DeployBeanEmbedded deployEmbedded; + + /** + * Create the property. + */ + public DeployBeanPropertyCompound(DeployBeanDescriptor desc, Class targetType, + CtCompoundType compoundType, ScalarTypeConverter typeConverter) { + + super(desc, targetType, null, null); + this.compoundType = compoundType; + this.typeConverter = typeConverter; + } + + public BeanPropertyCompoundRoot getFlatProperties(BeanDescriptorMap owner, BeanDescriptor descriptor) { + + // get a 'flat' list of all the scalar types, their relative property names + // and also set their matching dbColumn + + // represents the root property + BeanPropertyCompoundRoot rootProperty = new BeanPropertyCompoundRoot(this); + + // Walk the tree of a compound type collecting the + // scalar types and non-scalar properties + CtCompoundTypeScalarList ctMeta = new CtCompoundTypeScalarList(); + + compoundType.accumulateScalarTypes(null, ctMeta); + + List beanPropertyList = new ArrayList(); + + + // for each of the scalar types inside a compound value object + // build a BeanPropertyCompoundScalar with appropriate deployment + // information. + + for (Entry> entry : ctMeta.entries()) { + + String relativePropertyName = entry.getKey(); + ScalarType scalarType = entry.getValue(); + + CtCompoundProperty ctProp = ctMeta.getCompoundType(relativePropertyName); + + + String dbColumn = (getName() + "." + relativePropertyName).replace(".", "_"); + dbColumn = getDbColumn(relativePropertyName, dbColumn); + + DeployBeanProperty deploy = new DeployBeanProperty(null, scalarType.getType(), scalarType, null); + deploy.setScalarType(scalarType); + deploy.setDbColumn(dbColumn); + deploy.setName(relativePropertyName); + deploy.setDbInsertable(true); + deploy.setDbUpdateable(true); + deploy.setDbRead(true); + + BeanPropertyCompoundScalar bp = new BeanPropertyCompoundScalar(rootProperty, deploy, ctProp, typeConverter); + beanPropertyList.add(bp); + + rootProperty.register(bp); + } + + rootProperty.setNonScalarProperties(ctMeta.getNonScalarProperties()); + return rootProperty; + } + + private String getDbColumn(String propName, String defaultDbColumn) { + if (deployEmbedded == null) { + return defaultDbColumn; + } + String dbColumn = deployEmbedded.getPropertyColumnMap().get(propName); + return dbColumn == null ? defaultDbColumn : dbColumn; + } + + /** + * Return the deploy information specifically for the deployment + * of Embedded beans. + */ + public DeployBeanEmbedded getDeployEmbedded() { + // deployment should be single threaded + if (deployEmbedded == null) { + deployEmbedded = new DeployBeanEmbedded(); + } + return deployEmbedded; + } + + public ScalarTypeConverter getTypeConverter() { + return typeConverter; + } + + public CtCompoundType getCompoundType() { + return compoundType; + } + +} 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 22ec56c41..da863416c 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 @@ -1,387 +1,387 @@ -package com.avaje.ebeaninternal.server.deploy.meta; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; -import com.avaje.ebeaninternal.server.deploy.BeanPropertySimpleCollection; -import com.avaje.ebeaninternal.server.deploy.InheritInfo; -import com.avaje.ebeaninternal.server.deploy.TableJoin; -import com.avaje.ebeaninternal.server.type.ScalarTypeString; - -/** - * Helper object to classify BeanProperties into appropriate lists. - */ -public class DeployBeanPropertyLists { - - private static final Logger logger = LoggerFactory.getLogger(DeployBeanPropertyLists.class); - - private BeanProperty versionProperty; - - private final BeanDescriptor desc; - - private final LinkedHashMap propertyMap; - - private final ArrayList ids = new ArrayList(); - - private final ArrayList local = new ArrayList(); - - private final ArrayList mutable = new ArrayList(); - - private final ArrayList manys = new ArrayList(); - - private final ArrayList nonManys = new ArrayList(); - - private final ArrayList ones = new ArrayList(); - - private final ArrayList onesExported = new ArrayList(); - - private final ArrayList onesImported = new ArrayList(); - - private final ArrayList embedded = new ArrayList(); - - private final ArrayList baseScalar = new ArrayList(); - - private final ArrayList baseCompound = new ArrayList(); - - private final ArrayList transients = new ArrayList(); - - private final ArrayList nonTransients = new ArrayList(); - - private final TableJoin[] tableJoins; - - private final BeanPropertyAssocOne unidirectional; - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor desc, DeployBeanDescriptor deploy) { - this.desc = desc; - - DeployBeanPropertyAssocOne deployUnidirectional = deploy.getUnidirectional(); - if (deployUnidirectional == null) { - unidirectional = null; - } else { - unidirectional = new BeanPropertyAssocOne(owner, desc, deployUnidirectional); - } - - this.propertyMap = new LinkedHashMap(); - - for (DeployBeanProperty prop : deploy.propertiesAll()) { - BeanProperty beanProp = createBeanProperty(owner, prop); - propertyMap.put(beanProp.getName(), beanProp); - } - - int order = 0; - for (BeanProperty prop : propertyMap.values()) { - prop.setDeployOrder(order++); - allocateToList(prop); - } - - InheritInfo inheritInfo = deploy.getInheritInfo(); - if (inheritInfo != null) { - // Create a BeanProperty for the discriminator column to support - // using RawSql queries with inheritance - String discriminatorColumn = inheritInfo.getDiscriminatorColumn(); - DeployBeanProperty discDeployProp = new DeployBeanProperty(deploy, String.class, new ScalarTypeString(), null); - discDeployProp.setDiscriminator(true); - discDeployProp.setName(discriminatorColumn); - discDeployProp.setDbColumn(discriminatorColumn); - - // create the discriminator BeanProperty and only register it in the propertyMap - BeanProperty dprop = new BeanProperty(owner, desc, discDeployProp); - propertyMap.put(dprop.getName(), dprop); - } - - List deployTableJoins = deploy.getTableJoins(); - tableJoins = new TableJoin[deployTableJoins.size()]; - for (int i = 0; i < deployTableJoins.size(); i++) { - tableJoins[i] = new TableJoin(deployTableJoins.get(i), propertyMap); - } - - } - - /** - * Return the unidirectional. - */ - public BeanPropertyAssocOne getUnidirectional() { - return unidirectional; - } - - /** - * Allocate the property to a list. - */ - private void allocateToList(BeanProperty prop) { - if (prop.isTransient()) { - transients.add(prop); - return; - } - if (prop.isId()) { - ids.add(prop); - return; - } else { - nonTransients.add(prop); - } - - if (prop.isMutableScalarType()) { - mutable.add(prop); - } - - if (desc.getInheritInfo() != null && prop.isLocal()) { - local.add(prop); - } - - if (prop instanceof BeanPropertyAssocMany) { - manys.add(prop); - - } else { - nonManys.add(prop); - if (prop instanceof BeanPropertyAssocOne) { - if (prop.isEmbedded()) { - embedded.add(prop); - } else { - ones.add(prop); - BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne) prop; - if (assocOne.isOneToOneExported()) { - onesExported.add(prop); - } else { - onesImported.add(prop); - } - } - } else { - // its a "base" property... - if (prop.isVersion()) { - if (versionProperty == null) { - versionProperty = prop; - } else { - logger.warn("Multiple @Version properties - property " + prop.getFullBeanName() - + " not treated as a version property"); - } - } - if (prop instanceof BeanPropertyCompound) { - baseCompound.add((BeanPropertyCompound) prop); - } else { - baseScalar.add(prop); - } - } - } - } - - public LinkedHashMap getPropertyMap() { - return propertyMap; - } - - public TableJoin[] getTableJoin() { - return tableJoins; - } - - /** - * Return the base scalar properties (excludes Id and secondary table - * properties). - */ - public BeanProperty[] getBaseScalar() { - return (BeanProperty[]) baseScalar.toArray(new BeanProperty[baseScalar.size()]); - } - - public BeanPropertyCompound[] getBaseCompound() { - return (BeanPropertyCompound[]) baseCompound.toArray(new BeanPropertyCompound[baseCompound.size()]); - } - - public BeanProperty getId() { - if (ids.size() > 1) { - String msg = "Issue with bean "+desc+". 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() { - return (BeanProperty[]) nonTransients.toArray(new BeanProperty[nonTransients.size()]); - } - - public BeanProperty[] getTransients() { - return (BeanProperty[]) transients.toArray(new BeanProperty[transients.size()]); - } - - public BeanProperty getVersionProperty() { - return versionProperty; - } - - public BeanProperty[] getLocal() { - return (BeanProperty[]) local.toArray(new BeanProperty[local.size()]); - } - - public BeanProperty[] getMutable() { - return (BeanProperty[]) mutable.toArray(new BeanProperty[mutable.size()]); - } - - public BeanPropertyAssocOne[] getEmbedded() { - return (BeanPropertyAssocOne[]) embedded.toArray(new BeanPropertyAssocOne[embedded.size()]); - } - - public BeanPropertyAssocOne[] getOneExported() { - return (BeanPropertyAssocOne[]) onesExported.toArray(new BeanPropertyAssocOne[onesExported.size()]); - } - - public BeanPropertyAssocOne[] getOneImported() { - return (BeanPropertyAssocOne[]) onesImported.toArray(new BeanPropertyAssocOne[onesImported.size()]); - } - - public BeanPropertyAssocOne[] getOnes() { - return (BeanPropertyAssocOne[]) ones.toArray(new BeanPropertyAssocOne[ones.size()]); - } - - public BeanPropertyAssocOne[] getOneExportedSave() { - return getOne(false, Mode.Save); - } - - public BeanPropertyAssocOne[] getOneExportedDelete() { - return getOne(false, Mode.Delete); - } - - public BeanPropertyAssocOne[] getOneImportedSave() { - return getOne(true, Mode.Save); - } - - public BeanPropertyAssocOne[] getOneImportedDelete() { - return getOne(true, Mode.Delete); - } - - public BeanProperty[] getNonMany() { - return (BeanProperty[]) nonManys.toArray(new BeanProperty[nonManys.size()]); - } - - public BeanPropertyAssocMany[] getMany() { - return (BeanPropertyAssocMany[]) manys.toArray(new BeanPropertyAssocMany[manys.size()]); - } - - public BeanPropertyAssocMany[] getManySave() { - return getMany(Mode.Save); - } - - public BeanPropertyAssocMany[] getManyDelete() { - return getMany(Mode.Delete); - } - - public BeanPropertyAssocMany[] getManyToMany() { - return getMany2Many(); - } - - /** - * Mode used to determine which BeanPropertyAssoc to include. - */ - private enum Mode { - Save, Delete, Validate; - } - - private BeanPropertyAssocOne[] getOne(boolean imported, Mode mode) { - ArrayList> list = new ArrayList>(); - for (int i = 0; i < ones.size(); i++) { - BeanPropertyAssocOne prop = (BeanPropertyAssocOne) ones.get(i); - if (imported != prop.isOneToOneExported()) { - switch (mode) { - case Save: - if (prop.getCascadeInfo().isSave()) { - list.add(prop); - } - break; - case Delete: - if (prop.getCascadeInfo().isDelete()) { - list.add(prop); - } - break; - case Validate: - if (prop.getCascadeInfo().isValidate()) { - list.add(prop); - } - break; - default: - break; - } - } - } - - return (BeanPropertyAssocOne[]) list.toArray(new BeanPropertyAssocOne[list.size()]); - } - - private BeanPropertyAssocMany[] getMany2Many() { - ArrayList> list = new ArrayList>(); - for (int i = 0; i < manys.size(); i++) { - BeanPropertyAssocMany prop = (BeanPropertyAssocMany) manys.get(i); - if (prop.isManyToMany()) { - list.add(prop); - } - } - - return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[list.size()]); - } - - private BeanPropertyAssocMany[] getMany(Mode mode) { - ArrayList> list = new ArrayList>(); - for (int i = 0; i < manys.size(); i++) { - BeanPropertyAssocMany prop = (BeanPropertyAssocMany) manys.get(i); - - switch (mode) { - case Save: - if (prop.getCascadeInfo().isSave() || prop.isManyToMany() - || ModifyListenMode.REMOVALS.equals(prop.getModifyListenMode())) { - // Note ManyToMany always included as we always 'save' - // the relationship via insert/delete of intersection table - // REMOVALS means including PrivateOwned relationships - list.add(prop); - } - break; - case Delete: - if (prop.getCascadeInfo().isDelete() || ModifyListenMode.REMOVALS.equals(prop.getModifyListenMode())) { - // REMOVALS means including PrivateOwned relationships - list.add(prop); - } - break; - case Validate: - if (prop.getCascadeInfo().isValidate()) { - list.add(prop); - } - break; - default: - break; - } - - } - - return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[list.size()]); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private BeanProperty createBeanProperty(BeanDescriptorMap owner, DeployBeanProperty deployProp) { - - if (deployProp instanceof DeployBeanPropertyAssocOne) { - return new BeanPropertyAssocOne(owner, desc, (DeployBeanPropertyAssocOne) deployProp); - } - - if (deployProp instanceof DeployBeanPropertySimpleCollection) { - return new BeanPropertySimpleCollection(owner, desc, (DeployBeanPropertySimpleCollection) deployProp); - } - - if (deployProp instanceof DeployBeanPropertyAssocMany) { - return new BeanPropertyAssocMany(owner, desc, (DeployBeanPropertyAssocMany) deployProp); - } - - if (deployProp instanceof DeployBeanPropertyCompound) { - return new BeanPropertyCompound(owner, desc, (DeployBeanPropertyCompound) deployProp); - } - - return new BeanProperty(owner, desc, deployProp); - } -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.BeanPropertySimpleCollection; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.type.ScalarTypeString; + +/** + * Helper object to classify BeanProperties into appropriate lists. + */ +public class DeployBeanPropertyLists { + + private static final Logger logger = LoggerFactory.getLogger(DeployBeanPropertyLists.class); + + private BeanProperty versionProperty; + + private final BeanDescriptor desc; + + private final LinkedHashMap propertyMap; + + private final ArrayList ids = new ArrayList(); + + private final ArrayList local = new ArrayList(); + + private final ArrayList mutable = new ArrayList(); + + private final ArrayList manys = new ArrayList(); + + private final ArrayList nonManys = new ArrayList(); + + private final ArrayList ones = new ArrayList(); + + private final ArrayList onesExported = new ArrayList(); + + private final ArrayList onesImported = new ArrayList(); + + private final ArrayList embedded = new ArrayList(); + + private final ArrayList baseScalar = new ArrayList(); + + private final ArrayList baseCompound = new ArrayList(); + + private final ArrayList transients = new ArrayList(); + + private final ArrayList nonTransients = new ArrayList(); + + private final TableJoin[] tableJoins; + + private final BeanPropertyAssocOne unidirectional; + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor desc, DeployBeanDescriptor deploy) { + this.desc = desc; + + DeployBeanPropertyAssocOne deployUnidirectional = deploy.getUnidirectional(); + if (deployUnidirectional == null) { + unidirectional = null; + } else { + unidirectional = new BeanPropertyAssocOne(owner, desc, deployUnidirectional); + } + + this.propertyMap = new LinkedHashMap(); + + for (DeployBeanProperty prop : deploy.propertiesAll()) { + BeanProperty beanProp = createBeanProperty(owner, prop); + propertyMap.put(beanProp.getName(), beanProp); + } + + int order = 0; + for (BeanProperty prop : propertyMap.values()) { + prop.setDeployOrder(order++); + allocateToList(prop); + } + + InheritInfo inheritInfo = deploy.getInheritInfo(); + if (inheritInfo != null) { + // Create a BeanProperty for the discriminator column to support + // using RawSql queries with inheritance + String discriminatorColumn = inheritInfo.getDiscriminatorColumn(); + DeployBeanProperty discDeployProp = new DeployBeanProperty(deploy, String.class, new ScalarTypeString(), null); + discDeployProp.setDiscriminator(true); + discDeployProp.setName(discriminatorColumn); + discDeployProp.setDbColumn(discriminatorColumn); + + // create the discriminator BeanProperty and only register it in the propertyMap + BeanProperty dprop = new BeanProperty(owner, desc, discDeployProp); + propertyMap.put(dprop.getName(), dprop); + } + + List deployTableJoins = deploy.getTableJoins(); + tableJoins = new TableJoin[deployTableJoins.size()]; + for (int i = 0; i < deployTableJoins.size(); i++) { + tableJoins[i] = new TableJoin(deployTableJoins.get(i), propertyMap); + } + + } + + /** + * Return the unidirectional. + */ + public BeanPropertyAssocOne getUnidirectional() { + return unidirectional; + } + + /** + * Allocate the property to a list. + */ + private void allocateToList(BeanProperty prop) { + if (prop.isTransient()) { + transients.add(prop); + return; + } + if (prop.isId()) { + ids.add(prop); + return; + } else { + nonTransients.add(prop); + } + + if (prop.isMutableScalarType()) { + mutable.add(prop); + } + + if (desc.getInheritInfo() != null && prop.isLocal()) { + local.add(prop); + } + + if (prop instanceof BeanPropertyAssocMany) { + manys.add(prop); + + } else { + nonManys.add(prop); + if (prop instanceof BeanPropertyAssocOne) { + if (prop.isEmbedded()) { + embedded.add(prop); + } else { + ones.add(prop); + BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne) prop; + if (assocOne.isOneToOneExported()) { + onesExported.add(prop); + } else { + onesImported.add(prop); + } + } + } else { + // its a "base" property... + if (prop.isVersion()) { + if (versionProperty == null) { + versionProperty = prop; + } else { + logger.warn("Multiple @Version properties - property " + prop.getFullBeanName() + + " not treated as a version property"); + } + } + if (prop instanceof BeanPropertyCompound) { + baseCompound.add((BeanPropertyCompound) prop); + } else { + baseScalar.add(prop); + } + } + } + } + + public LinkedHashMap getPropertyMap() { + return propertyMap; + } + + public TableJoin[] getTableJoin() { + return tableJoins; + } + + /** + * Return the base scalar properties (excludes Id and secondary table + * properties). + */ + public BeanProperty[] getBaseScalar() { + return (BeanProperty[]) baseScalar.toArray(new BeanProperty[baseScalar.size()]); + } + + public BeanPropertyCompound[] getBaseCompound() { + return (BeanPropertyCompound[]) baseCompound.toArray(new BeanPropertyCompound[baseCompound.size()]); + } + + public BeanProperty getId() { + if (ids.size() > 1) { + String msg = "Issue with bean "+desc+". 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() { + return (BeanProperty[]) nonTransients.toArray(new BeanProperty[nonTransients.size()]); + } + + public BeanProperty[] getTransients() { + return (BeanProperty[]) transients.toArray(new BeanProperty[transients.size()]); + } + + public BeanProperty getVersionProperty() { + return versionProperty; + } + + public BeanProperty[] getLocal() { + return (BeanProperty[]) local.toArray(new BeanProperty[local.size()]); + } + + public BeanProperty[] getMutable() { + return (BeanProperty[]) mutable.toArray(new BeanProperty[mutable.size()]); + } + + public BeanPropertyAssocOne[] getEmbedded() { + return (BeanPropertyAssocOne[]) embedded.toArray(new BeanPropertyAssocOne[embedded.size()]); + } + + public BeanPropertyAssocOne[] getOneExported() { + return (BeanPropertyAssocOne[]) onesExported.toArray(new BeanPropertyAssocOne[onesExported.size()]); + } + + public BeanPropertyAssocOne[] getOneImported() { + return (BeanPropertyAssocOne[]) onesImported.toArray(new BeanPropertyAssocOne[onesImported.size()]); + } + + public BeanPropertyAssocOne[] getOnes() { + return (BeanPropertyAssocOne[]) ones.toArray(new BeanPropertyAssocOne[ones.size()]); + } + + public BeanPropertyAssocOne[] getOneExportedSave() { + return getOne(false, Mode.Save); + } + + public BeanPropertyAssocOne[] getOneExportedDelete() { + return getOne(false, Mode.Delete); + } + + public BeanPropertyAssocOne[] getOneImportedSave() { + return getOne(true, Mode.Save); + } + + public BeanPropertyAssocOne[] getOneImportedDelete() { + return getOne(true, Mode.Delete); + } + + public BeanProperty[] getNonMany() { + return (BeanProperty[]) nonManys.toArray(new BeanProperty[nonManys.size()]); + } + + public BeanPropertyAssocMany[] getMany() { + return (BeanPropertyAssocMany[]) manys.toArray(new BeanPropertyAssocMany[manys.size()]); + } + + public BeanPropertyAssocMany[] getManySave() { + return getMany(Mode.Save); + } + + public BeanPropertyAssocMany[] getManyDelete() { + return getMany(Mode.Delete); + } + + public BeanPropertyAssocMany[] getManyToMany() { + return getMany2Many(); + } + + /** + * Mode used to determine which BeanPropertyAssoc to include. + */ + private enum Mode { + Save, Delete, Validate; + } + + private BeanPropertyAssocOne[] getOne(boolean imported, Mode mode) { + ArrayList> list = new ArrayList>(); + for (int i = 0; i < ones.size(); i++) { + BeanPropertyAssocOne prop = (BeanPropertyAssocOne) ones.get(i); + if (imported != prop.isOneToOneExported()) { + switch (mode) { + case Save: + if (prop.getCascadeInfo().isSave()) { + list.add(prop); + } + break; + case Delete: + if (prop.getCascadeInfo().isDelete()) { + list.add(prop); + } + break; + case Validate: + if (prop.getCascadeInfo().isValidate()) { + list.add(prop); + } + break; + default: + break; + } + } + } + + return (BeanPropertyAssocOne[]) list.toArray(new BeanPropertyAssocOne[list.size()]); + } + + private BeanPropertyAssocMany[] getMany2Many() { + ArrayList> list = new ArrayList>(); + for (int i = 0; i < manys.size(); i++) { + BeanPropertyAssocMany prop = (BeanPropertyAssocMany) manys.get(i); + if (prop.isManyToMany()) { + list.add(prop); + } + } + + return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[list.size()]); + } + + private BeanPropertyAssocMany[] getMany(Mode mode) { + ArrayList> list = new ArrayList>(); + for (int i = 0; i < manys.size(); i++) { + BeanPropertyAssocMany prop = (BeanPropertyAssocMany) manys.get(i); + + switch (mode) { + case Save: + if (prop.getCascadeInfo().isSave() || prop.isManyToMany() + || ModifyListenMode.REMOVALS.equals(prop.getModifyListenMode())) { + // Note ManyToMany always included as we always 'save' + // the relationship via insert/delete of intersection table + // REMOVALS means including PrivateOwned relationships + list.add(prop); + } + break; + case Delete: + if (prop.getCascadeInfo().isDelete() || ModifyListenMode.REMOVALS.equals(prop.getModifyListenMode())) { + // REMOVALS means including PrivateOwned relationships + list.add(prop); + } + break; + case Validate: + if (prop.getCascadeInfo().isValidate()) { + list.add(prop); + } + break; + default: + break; + } + + } + + return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[list.size()]); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private BeanProperty createBeanProperty(BeanDescriptorMap owner, DeployBeanProperty deployProp) { + + if (deployProp instanceof DeployBeanPropertyAssocOne) { + return new BeanPropertyAssocOne(owner, desc, (DeployBeanPropertyAssocOne) deployProp); + } + + if (deployProp instanceof DeployBeanPropertySimpleCollection) { + return new BeanPropertySimpleCollection(owner, desc, (DeployBeanPropertySimpleCollection) deployProp); + } + + if (deployProp instanceof DeployBeanPropertyAssocMany) { + return new BeanPropertyAssocMany(owner, desc, (DeployBeanPropertyAssocMany) deployProp); + } + + if (deployProp instanceof DeployBeanPropertyCompound) { + return new BeanPropertyCompound(owner, desc, (DeployBeanPropertyCompound) deployProp); + } + + return new BeanProperty(owner, desc, deployProp); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertySimpleCollection.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertySimpleCollection.java index 9382e82a6..24df80a6d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertySimpleCollection.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertySimpleCollection.java @@ -1,29 +1,29 @@ -package com.avaje.ebeaninternal.server.deploy.meta; - -import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; -import com.avaje.ebeaninternal.server.deploy.ManyType; - -public class DeployBeanPropertySimpleCollection extends DeployBeanPropertyAssocMany { - - public DeployBeanPropertySimpleCollection(DeployBeanDescriptor desc, Class targetType, ManyType manyType) { - super(desc, targetType, manyType); - this.modifyListenMode = ModifyListenMode.ALL; - } - - /** - * Returns false as never a ManyToMany. - */ - @Override - public boolean isManyToMany() { - return false; - } - - /** - * Returns true as always Unidirectional. - */ - @Override - public boolean isUnidirectional() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; +import com.avaje.ebeaninternal.server.deploy.ManyType; + +public class DeployBeanPropertySimpleCollection extends DeployBeanPropertyAssocMany { + + public DeployBeanPropertySimpleCollection(DeployBeanDescriptor desc, Class targetType, ManyType manyType) { + super(desc, targetType, manyType); + this.modifyListenMode = ModifyListenMode.ALL; + } + + /** + * Returns false as never a ManyToMany. + */ + @Override + public boolean isManyToMany() { + return false; + } + + /** + * Returns true as always Unidirectional. + */ + @Override + public boolean isUnidirectional() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanTable.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanTable.java index c2de543ea..8510f46b4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanTable.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanTable.java @@ -1,90 +1,90 @@ -package com.avaje.ebeaninternal.server.deploy.meta; - -import java.util.List; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; - - -/** - * Used for associated beans in place of a BeanDescriptor. This is done to avoid - * recursion issues due to the potentially bi-directional and circular - * relationships between beans. - *

- * It holds the main deployment information and not all the detail that is held - * in a BeanDescriptor. - *

- */ -public class DeployBeanTable { - - private final Class beanType; - - /** - * The base table. - */ - private String baseTable; - - private List idProperties; - - /** - * Create the BeanTable. - */ - public DeployBeanTable(Class beanType) { - this.beanType = beanType; - } - - /** - * Return the base table for this BeanTable. - * This is used to determine the join information - * for associations. - */ - public String getBaseTable() { - return baseTable; - } - - /** - * Set the base table for this BeanTable. - */ - public void setBaseTable(String baseTable) { - this.baseTable = baseTable; - } - - /** - * Return the id properties. - */ - public BeanProperty[] createIdProperties(BeanDescriptorMap owner) { - BeanProperty[] props = new BeanProperty[idProperties.size()]; - for (int i = 0; i < idProperties.size(); i++) { - props[i] = createProperty(owner, idProperties.get(i)); - } - return props; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private BeanProperty createProperty(BeanDescriptorMap owner, DeployBeanProperty prop){ - - if (prop instanceof DeployBeanPropertyAssocOne){ - return new BeanPropertyAssocOne(owner, (DeployBeanPropertyAssocOne)prop); - - } else { - return new BeanProperty(prop); - } - - } - - /** - * Set the Id properties. - */ - public void setIdProperties(List idProperties) { - this.idProperties = idProperties; - } - - /** - * Return the class for this beanTable. - */ - public Class getBeanType() { - return beanType; - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.util.List; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; + + +/** + * Used for associated beans in place of a BeanDescriptor. This is done to avoid + * recursion issues due to the potentially bi-directional and circular + * relationships between beans. + *

+ * It holds the main deployment information and not all the detail that is held + * in a BeanDescriptor. + *

+ */ +public class DeployBeanTable { + + private final Class beanType; + + /** + * The base table. + */ + private String baseTable; + + private List idProperties; + + /** + * Create the BeanTable. + */ + public DeployBeanTable(Class beanType) { + this.beanType = beanType; + } + + /** + * Return the base table for this BeanTable. + * This is used to determine the join information + * for associations. + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Set the base table for this BeanTable. + */ + public void setBaseTable(String baseTable) { + this.baseTable = baseTable; + } + + /** + * Return the id properties. + */ + public BeanProperty[] createIdProperties(BeanDescriptorMap owner) { + BeanProperty[] props = new BeanProperty[idProperties.size()]; + for (int i = 0; i < idProperties.size(); i++) { + props[i] = createProperty(owner, idProperties.get(i)); + } + return props; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private BeanProperty createProperty(BeanDescriptorMap owner, DeployBeanProperty prop){ + + if (prop instanceof DeployBeanPropertyAssocOne){ + return new BeanPropertyAssocOne(owner, (DeployBeanPropertyAssocOne)prop); + + } else { + return new BeanProperty(prop); + } + + } + + /** + * Set the Id properties. + */ + public void setIdProperties(List idProperties) { + this.idProperties = idProperties; + } + + /** + * Return the class for this beanTable. + */ + public Class getBeanType() { + return beanType; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoin.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoin.java index acb36983e..722035528 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoin.java @@ -1,212 +1,212 @@ -package com.avaje.ebeaninternal.server.deploy.meta; - -import java.util.ArrayList; - -import javax.persistence.JoinColumn; - -import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo; -import com.avaje.ebeaninternal.server.deploy.BeanTable; -import com.avaje.ebeaninternal.server.deploy.InheritInfo; -import com.avaje.ebeaninternal.server.query.SqlJoinType; - -/** - * Represents a join to another table during deployment phase. - *

- * This gets converted into a immutable TableJoin when complete. - *

- */ -public class DeployTableJoin { - - /** - * Flag set when the imported key maps to the primary key. This occurs for intersection tables - * (ManyToMany). - */ - private boolean importedPrimaryKey; - - /** - * The joined table. - */ - private String table; - - /** - * The type of join. LEFT OUTER etc. - */ - private SqlJoinType type = SqlJoinType.INNER; - - /** - * The list of properties mapped to this joined table. - */ - private ArrayList properties = new ArrayList(); - - /** - * The list of join column pairs. Used to generate the on clause. - */ - private ArrayList columns = new ArrayList(4); - - /** - * The persist cascade info. - */ - private BeanCascadeInfo cascadeInfo = new BeanCascadeInfo(); - - private InheritInfo inheritInfo; - - /** - * Create a DeployTableJoin. - */ - public DeployTableJoin() { - } - - public String toString() { - return type + " " + table + " " + columns; - } - - /** - * Return true if the imported foreign key maps to the primary key. - */ - public boolean isImportedPrimaryKey() { - return importedPrimaryKey; - } - - /** - * Flag set when the imported key maps to the primary key. This occurs for intersection tables - * (ManyToMany). - */ - public void setImportedPrimaryKey(boolean importedPrimaryKey) { - this.importedPrimaryKey = importedPrimaryKey; - } - - /** - * Return true if the JoinOnPair have been set. - */ - public boolean hasJoinColumns() { - return columns.size() > 0; - } - - /** - * Return the persist info. - */ - public BeanCascadeInfo getCascadeInfo() { - return cascadeInfo; - } - - /** - * Copy all the columns to this join potentially reversing the columns. - */ - public void setColumns(DeployTableJoinColumn[] cols, boolean reverse) { - columns = new ArrayList(); - for (int i = 0; i < cols.length; i++) { - addJoinColumn(cols[i].copy(reverse)); - } - } - - /** - * Add a join pair - */ - public void addJoinColumn(DeployTableJoinColumn pair) { - columns.add(pair); - } - - /** - * Add a JoinColumn - *

- * The order is generally true for OneToMany and false for ManyToOne relationships. - *

- */ - public void addJoinColumn(boolean order, JoinColumn jc, BeanTable beanTable) { - if (!"".equals(jc.table())) { - setTable(jc.table()); - } - if (!"".equals(jc.name()) || !"".equals(jc.referencedColumnName())) { - // only add the join column details when name or referencedColumnName is specified - addJoinColumn(new DeployTableJoinColumn(order, jc, beanTable)); - } - } - - /** - * Add a JoinColumn array. - */ - public void addJoinColumn(boolean order, JoinColumn[] jcArray, BeanTable beanTable) { - for (int i = 0; i < jcArray.length; i++) { - addJoinColumn(order, jcArray[i], beanTable); - } - } - - /** - * Return the join columns. - */ - public DeployTableJoinColumn[] columns() { - return (DeployTableJoinColumn[]) columns.toArray(new DeployTableJoinColumn[columns.size()]); - } - - /** - * For secondary table joins returns the properties mapped to that table. - */ - public DeployBeanProperty[] properties() { - return (DeployBeanProperty[]) properties.toArray(new DeployBeanProperty[properties.size()]); - } - - /** - * Return the joined table name. - */ - public String getTable() { - return table; - } - - /** - * set the joined table name. - */ - public void setTable(String table) { - this.table = table; - } - - /** - * Return the type of join. LEFT OUTER JOIN etc. - */ - public SqlJoinType getType() { - return type; - } - - /** - * Return true if this join is a left outer join. - */ - public boolean isOuterJoin() { - return type == SqlJoinType.OUTER; - } - - public void setType(SqlJoinType type) { - this.type = type; - } - - public DeployTableJoin createInverse(String tableName) { - - DeployTableJoin inverse = new DeployTableJoin(); - return copyInternal(inverse, true, tableName, true); - } - - public DeployTableJoin copyTo(DeployTableJoin destJoin, boolean reverse, String tableName) { - return copyInternal(destJoin, reverse, tableName, true); - } - - public DeployTableJoin copyWithoutType(DeployTableJoin destJoin, boolean reverse, String tableName) { - return copyInternal(destJoin, reverse, tableName, false); - } - - private DeployTableJoin copyInternal(DeployTableJoin destJoin, boolean reverse, String tableName, boolean withType) { - - destJoin.setTable(tableName); - if (withType) { - destJoin.setType(type); - } - destJoin.setColumns(columns(), reverse); - - return destJoin; - } - - public InheritInfo getInheritInfo() { - return inheritInfo; - } - - public void setInheritInfo(InheritInfo inheritInfo) { - this.inheritInfo = inheritInfo; - } -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.util.ArrayList; + +import javax.persistence.JoinColumn; + +import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo; +import com.avaje.ebeaninternal.server.deploy.BeanTable; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.query.SqlJoinType; + +/** + * Represents a join to another table during deployment phase. + *

+ * This gets converted into a immutable TableJoin when complete. + *

+ */ +public class DeployTableJoin { + + /** + * Flag set when the imported key maps to the primary key. This occurs for intersection tables + * (ManyToMany). + */ + private boolean importedPrimaryKey; + + /** + * The joined table. + */ + private String table; + + /** + * The type of join. LEFT OUTER etc. + */ + private SqlJoinType type = SqlJoinType.INNER; + + /** + * The list of properties mapped to this joined table. + */ + private ArrayList properties = new ArrayList(); + + /** + * The list of join column pairs. Used to generate the on clause. + */ + private ArrayList columns = new ArrayList(4); + + /** + * The persist cascade info. + */ + private BeanCascadeInfo cascadeInfo = new BeanCascadeInfo(); + + private InheritInfo inheritInfo; + + /** + * Create a DeployTableJoin. + */ + public DeployTableJoin() { + } + + public String toString() { + return type + " " + table + " " + columns; + } + + /** + * Return true if the imported foreign key maps to the primary key. + */ + public boolean isImportedPrimaryKey() { + return importedPrimaryKey; + } + + /** + * Flag set when the imported key maps to the primary key. This occurs for intersection tables + * (ManyToMany). + */ + public void setImportedPrimaryKey(boolean importedPrimaryKey) { + this.importedPrimaryKey = importedPrimaryKey; + } + + /** + * Return true if the JoinOnPair have been set. + */ + public boolean hasJoinColumns() { + return columns.size() > 0; + } + + /** + * Return the persist info. + */ + public BeanCascadeInfo getCascadeInfo() { + return cascadeInfo; + } + + /** + * Copy all the columns to this join potentially reversing the columns. + */ + public void setColumns(DeployTableJoinColumn[] cols, boolean reverse) { + columns = new ArrayList(); + for (int i = 0; i < cols.length; i++) { + addJoinColumn(cols[i].copy(reverse)); + } + } + + /** + * Add a join pair + */ + public void addJoinColumn(DeployTableJoinColumn pair) { + columns.add(pair); + } + + /** + * Add a JoinColumn + *

+ * The order is generally true for OneToMany and false for ManyToOne relationships. + *

+ */ + public void addJoinColumn(boolean order, JoinColumn jc, BeanTable beanTable) { + if (!"".equals(jc.table())) { + setTable(jc.table()); + } + if (!"".equals(jc.name()) || !"".equals(jc.referencedColumnName())) { + // only add the join column details when name or referencedColumnName is specified + addJoinColumn(new DeployTableJoinColumn(order, jc, beanTable)); + } + } + + /** + * Add a JoinColumn array. + */ + public void addJoinColumn(boolean order, JoinColumn[] jcArray, BeanTable beanTable) { + for (int i = 0; i < jcArray.length; i++) { + addJoinColumn(order, jcArray[i], beanTable); + } + } + + /** + * Return the join columns. + */ + public DeployTableJoinColumn[] columns() { + return (DeployTableJoinColumn[]) columns.toArray(new DeployTableJoinColumn[columns.size()]); + } + + /** + * For secondary table joins returns the properties mapped to that table. + */ + public DeployBeanProperty[] properties() { + return (DeployBeanProperty[]) properties.toArray(new DeployBeanProperty[properties.size()]); + } + + /** + * Return the joined table name. + */ + public String getTable() { + return table; + } + + /** + * set the joined table name. + */ + public void setTable(String table) { + this.table = table; + } + + /** + * Return the type of join. LEFT OUTER JOIN etc. + */ + public SqlJoinType getType() { + return type; + } + + /** + * Return true if this join is a left outer join. + */ + public boolean isOuterJoin() { + return type == SqlJoinType.OUTER; + } + + public void setType(SqlJoinType type) { + this.type = type; + } + + public DeployTableJoin createInverse(String tableName) { + + DeployTableJoin inverse = new DeployTableJoin(); + return copyInternal(inverse, true, tableName, true); + } + + public DeployTableJoin copyTo(DeployTableJoin destJoin, boolean reverse, String tableName) { + return copyInternal(destJoin, reverse, tableName, true); + } + + public DeployTableJoin copyWithoutType(DeployTableJoin destJoin, boolean reverse, String tableName) { + return copyInternal(destJoin, reverse, tableName, false); + } + + private DeployTableJoin copyInternal(DeployTableJoin destJoin, boolean reverse, String tableName, boolean withType) { + + destJoin.setTable(tableName); + if (withType) { + destJoin.setType(type); + } + destJoin.setColumns(columns(), reverse); + + return destJoin; + } + + public InheritInfo getInheritInfo() { + return inheritInfo; + } + + public void setInheritInfo(InheritInfo inheritInfo) { + this.inheritInfo = inheritInfo; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoinColumn.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoinColumn.java index 8a9664605..e46d4dc49 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoinColumn.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoinColumn.java @@ -1,178 +1,178 @@ -package com.avaje.ebeaninternal.server.deploy.meta; - -import javax.persistence.JoinColumn; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanTable; - -/** - * A join pair of local and foreign properties. - */ -public class DeployTableJoinColumn { - - /** - * The local database column name. - */ - String localDbColumn; - - /** - * The foreign database column name. - */ - String foreignDbColumn; - - boolean insertable; - - boolean updateable; - - /** - * Construct when automatically determining the join. - *

- * Assume that we want the foreign key to be insertable and updateable. - *

- */ - public DeployTableJoinColumn(String localDbColumn, String foreignDbColumn) { - this(localDbColumn, foreignDbColumn, true, true); - } - - /** - * Construct with explicit insertable and updateable flags. - */ - public DeployTableJoinColumn(String localDbColumn, String foreignDbColumn, boolean insertable, boolean updateable) { - this.localDbColumn = nullEmptyString(localDbColumn); - this.foreignDbColumn = nullEmptyString(foreignDbColumn); - this.insertable = insertable; - this.updateable = updateable; - } - - public DeployTableJoinColumn(boolean order, JoinColumn jc, BeanTable beanTable) { - this(jc.referencedColumnName(), jc.name(), jc.insertable(), jc.updatable()); - setReferencedColumn(beanTable); - if (!order){ - reverse(); - } - } - - private void setReferencedColumn(BeanTable beanTable){ - if (localDbColumn == null){ - BeanProperty[] idProperties = beanTable.getIdProperties(); - if (idProperties.length == 1){ - localDbColumn = idProperties[0].getDbColumn(); - } - } - } - - /** - * Reverse the direction of the join. - */ - public DeployTableJoinColumn reverse() { - String temp = localDbColumn; - localDbColumn = foreignDbColumn; - foreignDbColumn = temp; - return this; - } - - /** - * Helper method to null out empty strings. - */ - private String nullEmptyString(String s){ - if ("".equals(s)){ - return null; - } - return s; - } - - - public DeployTableJoinColumn copy(boolean reverse) { - // Note that the insertable and updateable are just copied - // which may not always be the correct thing to do - // but will leave it like this for now - if (reverse){ - return new DeployTableJoinColumn(foreignDbColumn, localDbColumn, insertable, updateable); - - } else { - return new DeployTableJoinColumn(localDbColumn, foreignDbColumn, insertable, updateable); - } - } - - public String toString() { - return localDbColumn + " = " + foreignDbColumn; - } - - /** - * Return true if either the local or foreign column is null. - *

- * Both columns need to be defined. If one is null then typically it is - * derived as the primary key column. - *

- */ - public boolean hasNullColumn() { - return localDbColumn == null || foreignDbColumn == null; - } - - /** - * When only ONE column has been set by deployment information return that one. - *

- * Used with hasNullColumn() to set the foreignDbColumn for OneToMany joins. - *

- */ - public String getNonNullColumn() { - if (localDbColumn == null && foreignDbColumn == null) { - throw new IllegalStateException("expecting only one null column?"); - - } else if (localDbColumn != null && foreignDbColumn != null) { - throw new IllegalStateException("expecting one null column?"); - } - if (localDbColumn != null) { - return localDbColumn; - } else { - return foreignDbColumn; - } - } - - /** - * Return true if this column should be insertable. - */ - public boolean isInsertable() { - return insertable; - } - - /** - * Return true if this column should be updateable. - */ - public boolean isUpdateable() { - return updateable; - } - - /** - * Return the foreign database column name. - */ - public String getForeignDbColumn() { - return foreignDbColumn; - } - - /** - * Set the foreign database column name. - *

- * Used when this is derived from Primary Key and not set explicitly in the - * deployment information. - *

- */ - public void setForeignDbColumn(String foreignDbColumn) { - this.foreignDbColumn = foreignDbColumn; - } - - /** - * Return the local database column name. - */ - public String getLocalDbColumn() { - return localDbColumn; - } - - /** - * Set the local database column name. - */ - public void setLocalDbColumn(String localDbColumn) { - this.localDbColumn = localDbColumn; - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import javax.persistence.JoinColumn; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanTable; + +/** + * A join pair of local and foreign properties. + */ +public class DeployTableJoinColumn { + + /** + * The local database column name. + */ + String localDbColumn; + + /** + * The foreign database column name. + */ + String foreignDbColumn; + + boolean insertable; + + boolean updateable; + + /** + * Construct when automatically determining the join. + *

+ * Assume that we want the foreign key to be insertable and updateable. + *

+ */ + public DeployTableJoinColumn(String localDbColumn, String foreignDbColumn) { + this(localDbColumn, foreignDbColumn, true, true); + } + + /** + * Construct with explicit insertable and updateable flags. + */ + public DeployTableJoinColumn(String localDbColumn, String foreignDbColumn, boolean insertable, boolean updateable) { + this.localDbColumn = nullEmptyString(localDbColumn); + this.foreignDbColumn = nullEmptyString(foreignDbColumn); + this.insertable = insertable; + this.updateable = updateable; + } + + public DeployTableJoinColumn(boolean order, JoinColumn jc, BeanTable beanTable) { + this(jc.referencedColumnName(), jc.name(), jc.insertable(), jc.updatable()); + setReferencedColumn(beanTable); + if (!order){ + reverse(); + } + } + + private void setReferencedColumn(BeanTable beanTable){ + if (localDbColumn == null){ + BeanProperty[] idProperties = beanTable.getIdProperties(); + if (idProperties.length == 1){ + localDbColumn = idProperties[0].getDbColumn(); + } + } + } + + /** + * Reverse the direction of the join. + */ + public DeployTableJoinColumn reverse() { + String temp = localDbColumn; + localDbColumn = foreignDbColumn; + foreignDbColumn = temp; + return this; + } + + /** + * Helper method to null out empty strings. + */ + private String nullEmptyString(String s){ + if ("".equals(s)){ + return null; + } + return s; + } + + + public DeployTableJoinColumn copy(boolean reverse) { + // Note that the insertable and updateable are just copied + // which may not always be the correct thing to do + // but will leave it like this for now + if (reverse){ + return new DeployTableJoinColumn(foreignDbColumn, localDbColumn, insertable, updateable); + + } else { + return new DeployTableJoinColumn(localDbColumn, foreignDbColumn, insertable, updateable); + } + } + + public String toString() { + return localDbColumn + " = " + foreignDbColumn; + } + + /** + * Return true if either the local or foreign column is null. + *

+ * Both columns need to be defined. If one is null then typically it is + * derived as the primary key column. + *

+ */ + public boolean hasNullColumn() { + return localDbColumn == null || foreignDbColumn == null; + } + + /** + * When only ONE column has been set by deployment information return that one. + *

+ * Used with hasNullColumn() to set the foreignDbColumn for OneToMany joins. + *

+ */ + public String getNonNullColumn() { + if (localDbColumn == null && foreignDbColumn == null) { + throw new IllegalStateException("expecting only one null column?"); + + } else if (localDbColumn != null && foreignDbColumn != null) { + throw new IllegalStateException("expecting one null column?"); + } + if (localDbColumn != null) { + return localDbColumn; + } else { + return foreignDbColumn; + } + } + + /** + * Return true if this column should be insertable. + */ + public boolean isInsertable() { + return insertable; + } + + /** + * Return true if this column should be updateable. + */ + public boolean isUpdateable() { + return updateable; + } + + /** + * Return the foreign database column name. + */ + public String getForeignDbColumn() { + return foreignDbColumn; + } + + /** + * Set the foreign database column name. + *

+ * Used when this is derived from Primary Key and not set explicitly in the + * deployment information. + *

+ */ + public void setForeignDbColumn(String foreignDbColumn) { + this.foreignDbColumn = foreignDbColumn; + } + + /** + * Return the local database column name. + */ + public String getLocalDbColumn() { + return localDbColumn; + } + + /** + * Set the local database column name. + */ + public void setLocalDbColumn(String localDbColumn) { + this.localDbColumn = localDbColumn; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java index 891b69098..98f2d2b8b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java @@ -1,319 +1,319 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.JoinTable; -import javax.persistence.ManyToMany; -import javax.persistence.MapKey; -import javax.persistence.OneToMany; -import javax.persistence.OrderBy; - -import com.avaje.ebean.annotation.PrivateOwned; -import com.avaje.ebean.annotation.Where; -import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; -import com.avaje.ebean.config.NamingConvention; -import com.avaje.ebean.config.TableName; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanTable; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.query.SqlJoinType; - -/** - * Read the deployment annotation for Assoc Many beans. - */ -public class AnnotationAssocManys extends AnnotationParser { - - private final BeanDescriptorManager factory; - - /** - * Create with the DeployInfo. - */ - public AnnotationAssocManys(DeployBeanInfo info, BeanDescriptorManager factory) { - super(info); - this.factory = factory; - } - - /** - * Parse the annotations. - */ - public void parse() { - for (DeployBeanProperty prop : descriptor.propertiesAll()) { - if (prop instanceof DeployBeanPropertyAssocMany) { - read((DeployBeanPropertyAssocMany) prop); - } - } - } - - private void read(DeployBeanPropertyAssocMany prop) { - - OneToMany oneToMany = get(prop, OneToMany.class); - if (oneToMany != null) { - readToOne(oneToMany, prop); - PrivateOwned privateOwned = get(prop, PrivateOwned.class); - if (privateOwned != null){ - prop.setModifyListenMode(ModifyListenMode.REMOVALS); - prop.getCascadeInfo().setDelete(privateOwned.cascadeRemove()); - } - } - ManyToMany manyToMany = get(prop, ManyToMany.class); - if (manyToMany != null) { - readToMany(manyToMany, prop); - } - - OrderBy orderBy = get(prop, OrderBy.class); - if (orderBy != null) { - prop.setFetchOrderBy(orderBy.value()); - } - - MapKey mapKey = get(prop, MapKey.class); - if (mapKey != null) { - prop.setMapKey(mapKey.name()); - } - - Where where = get(prop, Where.class); - if (where != null) { - prop.setExtraWhere(where.clause()); - } - - // check for manually defined joins - BeanTable beanTable = prop.getBeanTable(); - JoinColumn joinColumn = get(prop, JoinColumn.class); - if (joinColumn != null) { - prop.getTableJoin().addJoinColumn(true, joinColumn, beanTable); - } - - JoinColumns joinColumns = get(prop, JoinColumns.class); - if (joinColumns != null) { - prop.getTableJoin().addJoinColumn(true, joinColumns.value(), beanTable); - } - - JoinTable joinTable = get(prop, JoinTable.class); - if (joinTable != null) { - if (prop.isManyToMany()){ - // expected this - readJoinTable(joinTable, prop); - - } else { - // OneToMany in theory - prop.getTableJoin().addJoinColumn(true, joinTable.joinColumns(), beanTable); - } - } - - if (prop.getMappedBy() != null){ - // the join is derived by reversing the join information - // from the mapped by property. - // Refer BeanDescriptorManager.readEntityRelationships() - return; - } - - if (prop.isManyToMany()){ - manyToManyDefaultJoins(prop); - return; - } - - - if (!prop.getTableJoin().hasJoinColumns() && beanTable != null){ - - // use naming convention to define join (based on the bean name for this side of relationship) - // A unidirectional OneToMany or OneToMany with no mappedBy property - - NamingConvention nc = factory.getNamingConvention(); - - String fkeyPrefix = null; - if (nc.isUseForeignKeyPrefix()){ - fkeyPrefix = nc.getColumnFromProperty(descriptor.getBeanType(), descriptor.getName()); - } - - // Use the owning bean table to define the join - BeanTable owningBeanTable = factory.getBeanTable(descriptor.getBeanType()); - owningBeanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), false); - } - } - - /** - * Define the joins for a ManyToMany relationship. - *

- * This includes joins to the intersection table and from the intersection table - * to the other side of the ManyToMany. - *

- */ - private void readJoinTable(JoinTable joinTable, DeployBeanPropertyAssocMany prop) { - - String intTableName = getFullTableName(joinTable); - // set the intersection table - DeployTableJoin intJoin = new DeployTableJoin(); - intJoin.setTable(intTableName); - - // add the source to intersection join columns - intJoin.addJoinColumn(true, joinTable.joinColumns(), prop.getBeanTable()); - - // set the intersection to dest table join columns - DeployTableJoin destJoin = prop.getTableJoin(); - destJoin.addJoinColumn(false, joinTable.inverseJoinColumns(), prop.getBeanTable()); - - intJoin.setType(SqlJoinType.OUTER); - - // reverse join from dest back to intersection - DeployTableJoin inverseDest = destJoin.createInverse(intTableName); - prop.setIntersectionJoin(intJoin); - prop.setInverseJoin(inverseDest); - } - - /** - * Return the full table name - * @param joinTable - * @return - */ - private String getFullTableName(JoinTable joinTable) { - StringBuilder sb = new StringBuilder(); - if (!StringHelper.isNull(joinTable.catalog())){ - sb.append(joinTable.catalog()).append("."); - } - if (!StringHelper.isNull(joinTable.schema())){ - sb.append(joinTable.schema()).append("."); - } - sb.append(joinTable.name()); - return sb.toString(); - } - - /** - * Define intersection table and foreign key columns for ManyToMany. - *

- * Some of these (maybe all) have been already defined via @JoinTable - * and @JoinColumns etc. - *

- */ - private void manyToManyDefaultJoins(DeployBeanPropertyAssocMany prop) { - - String intTableName = null; - - DeployTableJoin intJoin = prop.getIntersectionJoin(); - if (intJoin == null){ - intJoin = new DeployTableJoin(); - prop.setIntersectionJoin(intJoin); - } else { - // intersection table already defined (by @JoinTable) - intTableName = intJoin.getTable(); - } - - BeanTable localTable = factory.getBeanTable(descriptor.getBeanType()); - BeanTable otherTable = factory.getBeanTable(prop.getTargetType()); - - final String localTableName = localTable.getUnqualifiedBaseTable(); - final String otherTableName = otherTable.getUnqualifiedBaseTable(); - - if (intTableName == null){ - // define intersection table name - intTableName = getM2MJoinTableName(localTable, otherTable); - - intJoin.setTable(intTableName); - intJoin.setType(SqlJoinType.OUTER); - } - - DeployTableJoin destJoin = prop.getTableJoin(); - - - if (intJoin.hasJoinColumns() && destJoin.hasJoinColumns()){ - // already defined the foreign key columns etc - return; - } - if (!intJoin.hasJoinColumns()){ - // define foreign key columns - BeanProperty[] localIds = localTable.getIdProperties(); - for (int i = 0; i < localIds.length; i++) { - // add the source to intersection join columns - String fkCol = localTableName+"_"+localIds[i].getDbColumn(); - intJoin.addJoinColumn(new DeployTableJoinColumn(localIds[i].getDbColumn(), fkCol)); - } - } - - if (!destJoin.hasJoinColumns()){ - // define inverse foreign key columns - BeanProperty[] otherIds = otherTable.getIdProperties(); - for (int i = 0; i < otherIds.length; i++) { - // set the intersection to dest table join columns - final String fkCol = otherTableName+"_"+otherIds[i].getDbColumn(); - destJoin.addJoinColumn(new DeployTableJoinColumn(fkCol, otherIds[i].getDbColumn())); - } - } - - // reverse join from dest back to intersection - DeployTableJoin inverseDest = destJoin.createInverse(intTableName); - prop.setInverseJoin(inverseDest); - } - - - - private String errorMsgMissingBeanTable(Class type, String from) { - return "Error with association to ["+type+"] from ["+from+"]. Is "+type+" registered?"; - } - - private void readToMany(ManyToMany propAnn, DeployBeanPropertyAssocMany manyProp) { - - manyProp.setMappedBy(propAnn.mappedBy()); - manyProp.setFetchType(propAnn.fetch()); - - setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo()); - - Class targetType = propAnn.targetEntity(); - if (targetType.equals(void.class)) { - // via reflection of generics type - targetType = manyProp.getTargetType(); - } else { - manyProp.setTargetType(targetType); - } - - // find the other many table (not intersection) - BeanTable assoc = factory.getBeanTable(targetType); - if (assoc == null) { - String msg = errorMsgMissingBeanTable(targetType, manyProp.getFullBeanName()); - throw new RuntimeException(msg); - } - - manyProp.setManyToMany(true); - manyProp.setModifyListenMode(ModifyListenMode.ALL); - manyProp.setBeanTable(assoc); - manyProp.getTableJoin().setType(SqlJoinType.OUTER); - } - - private void readToOne(OneToMany propAnn, DeployBeanPropertyAssocMany manyProp) { - - manyProp.setMappedBy(propAnn.mappedBy()); - manyProp.setFetchType(propAnn.fetch()); - - setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo()); - - Class targetType = propAnn.targetEntity(); - if (targetType.equals(void.class)) { - // via reflection of generics type - targetType = manyProp.getTargetType(); - } else { - manyProp.setTargetType(targetType); - } - - BeanTable assoc = factory.getBeanTable(targetType); - if (assoc == null) { - String msg = errorMsgMissingBeanTable(targetType, manyProp.getFullBeanName()); - throw new RuntimeException(msg); - } - - manyProp.setBeanTable(assoc); - manyProp.getTableJoin().setType(SqlJoinType.OUTER); - } - - - private String getM2MJoinTableName(BeanTable lhsTable, BeanTable rhsTable){ - - TableName lhs = new TableName(lhsTable.getBaseTable()); - TableName rhs = new TableName(rhsTable.getBaseTable()); - - TableName joinTable = namingConvention.getM2MJoinTableName(lhs, rhs); - - return joinTable.getQualifiedName(); - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.MapKey; +import javax.persistence.OneToMany; +import javax.persistence.OrderBy; + +import com.avaje.ebean.annotation.PrivateOwned; +import com.avaje.ebean.annotation.Where; +import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; +import com.avaje.ebean.config.NamingConvention; +import com.avaje.ebean.config.TableName; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanTable; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.query.SqlJoinType; + +/** + * Read the deployment annotation for Assoc Many beans. + */ +public class AnnotationAssocManys extends AnnotationParser { + + private final BeanDescriptorManager factory; + + /** + * Create with the DeployInfo. + */ + public AnnotationAssocManys(DeployBeanInfo info, BeanDescriptorManager factory) { + super(info); + this.factory = factory; + } + + /** + * Parse the annotations. + */ + public void parse() { + for (DeployBeanProperty prop : descriptor.propertiesAll()) { + if (prop instanceof DeployBeanPropertyAssocMany) { + read((DeployBeanPropertyAssocMany) prop); + } + } + } + + private void read(DeployBeanPropertyAssocMany prop) { + + OneToMany oneToMany = get(prop, OneToMany.class); + if (oneToMany != null) { + readToOne(oneToMany, prop); + PrivateOwned privateOwned = get(prop, PrivateOwned.class); + if (privateOwned != null){ + prop.setModifyListenMode(ModifyListenMode.REMOVALS); + prop.getCascadeInfo().setDelete(privateOwned.cascadeRemove()); + } + } + ManyToMany manyToMany = get(prop, ManyToMany.class); + if (manyToMany != null) { + readToMany(manyToMany, prop); + } + + OrderBy orderBy = get(prop, OrderBy.class); + if (orderBy != null) { + prop.setFetchOrderBy(orderBy.value()); + } + + MapKey mapKey = get(prop, MapKey.class); + if (mapKey != null) { + prop.setMapKey(mapKey.name()); + } + + Where where = get(prop, Where.class); + if (where != null) { + prop.setExtraWhere(where.clause()); + } + + // check for manually defined joins + BeanTable beanTable = prop.getBeanTable(); + JoinColumn joinColumn = get(prop, JoinColumn.class); + if (joinColumn != null) { + prop.getTableJoin().addJoinColumn(true, joinColumn, beanTable); + } + + JoinColumns joinColumns = get(prop, JoinColumns.class); + if (joinColumns != null) { + prop.getTableJoin().addJoinColumn(true, joinColumns.value(), beanTable); + } + + JoinTable joinTable = get(prop, JoinTable.class); + if (joinTable != null) { + if (prop.isManyToMany()){ + // expected this + readJoinTable(joinTable, prop); + + } else { + // OneToMany in theory + prop.getTableJoin().addJoinColumn(true, joinTable.joinColumns(), beanTable); + } + } + + if (prop.getMappedBy() != null){ + // the join is derived by reversing the join information + // from the mapped by property. + // Refer BeanDescriptorManager.readEntityRelationships() + return; + } + + if (prop.isManyToMany()){ + manyToManyDefaultJoins(prop); + return; + } + + + if (!prop.getTableJoin().hasJoinColumns() && beanTable != null){ + + // use naming convention to define join (based on the bean name for this side of relationship) + // A unidirectional OneToMany or OneToMany with no mappedBy property + + NamingConvention nc = factory.getNamingConvention(); + + String fkeyPrefix = null; + if (nc.isUseForeignKeyPrefix()){ + fkeyPrefix = nc.getColumnFromProperty(descriptor.getBeanType(), descriptor.getName()); + } + + // Use the owning bean table to define the join + BeanTable owningBeanTable = factory.getBeanTable(descriptor.getBeanType()); + owningBeanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), false); + } + } + + /** + * Define the joins for a ManyToMany relationship. + *

+ * This includes joins to the intersection table and from the intersection table + * to the other side of the ManyToMany. + *

+ */ + private void readJoinTable(JoinTable joinTable, DeployBeanPropertyAssocMany prop) { + + String intTableName = getFullTableName(joinTable); + // set the intersection table + DeployTableJoin intJoin = new DeployTableJoin(); + intJoin.setTable(intTableName); + + // add the source to intersection join columns + intJoin.addJoinColumn(true, joinTable.joinColumns(), prop.getBeanTable()); + + // set the intersection to dest table join columns + DeployTableJoin destJoin = prop.getTableJoin(); + destJoin.addJoinColumn(false, joinTable.inverseJoinColumns(), prop.getBeanTable()); + + intJoin.setType(SqlJoinType.OUTER); + + // reverse join from dest back to intersection + DeployTableJoin inverseDest = destJoin.createInverse(intTableName); + prop.setIntersectionJoin(intJoin); + prop.setInverseJoin(inverseDest); + } + + /** + * Return the full table name + * @param joinTable + * @return + */ + private String getFullTableName(JoinTable joinTable) { + StringBuilder sb = new StringBuilder(); + if (!StringHelper.isNull(joinTable.catalog())){ + sb.append(joinTable.catalog()).append("."); + } + if (!StringHelper.isNull(joinTable.schema())){ + sb.append(joinTable.schema()).append("."); + } + sb.append(joinTable.name()); + return sb.toString(); + } + + /** + * Define intersection table and foreign key columns for ManyToMany. + *

+ * Some of these (maybe all) have been already defined via @JoinTable + * and @JoinColumns etc. + *

+ */ + private void manyToManyDefaultJoins(DeployBeanPropertyAssocMany prop) { + + String intTableName = null; + + DeployTableJoin intJoin = prop.getIntersectionJoin(); + if (intJoin == null){ + intJoin = new DeployTableJoin(); + prop.setIntersectionJoin(intJoin); + } else { + // intersection table already defined (by @JoinTable) + intTableName = intJoin.getTable(); + } + + BeanTable localTable = factory.getBeanTable(descriptor.getBeanType()); + BeanTable otherTable = factory.getBeanTable(prop.getTargetType()); + + final String localTableName = localTable.getUnqualifiedBaseTable(); + final String otherTableName = otherTable.getUnqualifiedBaseTable(); + + if (intTableName == null){ + // define intersection table name + intTableName = getM2MJoinTableName(localTable, otherTable); + + intJoin.setTable(intTableName); + intJoin.setType(SqlJoinType.OUTER); + } + + DeployTableJoin destJoin = prop.getTableJoin(); + + + if (intJoin.hasJoinColumns() && destJoin.hasJoinColumns()){ + // already defined the foreign key columns etc + return; + } + if (!intJoin.hasJoinColumns()){ + // define foreign key columns + BeanProperty[] localIds = localTable.getIdProperties(); + for (int i = 0; i < localIds.length; i++) { + // add the source to intersection join columns + String fkCol = localTableName+"_"+localIds[i].getDbColumn(); + intJoin.addJoinColumn(new DeployTableJoinColumn(localIds[i].getDbColumn(), fkCol)); + } + } + + if (!destJoin.hasJoinColumns()){ + // define inverse foreign key columns + BeanProperty[] otherIds = otherTable.getIdProperties(); + for (int i = 0; i < otherIds.length; i++) { + // set the intersection to dest table join columns + final String fkCol = otherTableName+"_"+otherIds[i].getDbColumn(); + destJoin.addJoinColumn(new DeployTableJoinColumn(fkCol, otherIds[i].getDbColumn())); + } + } + + // reverse join from dest back to intersection + DeployTableJoin inverseDest = destJoin.createInverse(intTableName); + prop.setInverseJoin(inverseDest); + } + + + + private String errorMsgMissingBeanTable(Class type, String from) { + return "Error with association to ["+type+"] from ["+from+"]. Is "+type+" registered?"; + } + + private void readToMany(ManyToMany propAnn, DeployBeanPropertyAssocMany manyProp) { + + manyProp.setMappedBy(propAnn.mappedBy()); + manyProp.setFetchType(propAnn.fetch()); + + setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo()); + + Class targetType = propAnn.targetEntity(); + if (targetType.equals(void.class)) { + // via reflection of generics type + targetType = manyProp.getTargetType(); + } else { + manyProp.setTargetType(targetType); + } + + // find the other many table (not intersection) + BeanTable assoc = factory.getBeanTable(targetType); + if (assoc == null) { + String msg = errorMsgMissingBeanTable(targetType, manyProp.getFullBeanName()); + throw new RuntimeException(msg); + } + + manyProp.setManyToMany(true); + manyProp.setModifyListenMode(ModifyListenMode.ALL); + manyProp.setBeanTable(assoc); + manyProp.getTableJoin().setType(SqlJoinType.OUTER); + } + + private void readToOne(OneToMany propAnn, DeployBeanPropertyAssocMany manyProp) { + + manyProp.setMappedBy(propAnn.mappedBy()); + manyProp.setFetchType(propAnn.fetch()); + + setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo()); + + Class targetType = propAnn.targetEntity(); + if (targetType.equals(void.class)) { + // via reflection of generics type + targetType = manyProp.getTargetType(); + } else { + manyProp.setTargetType(targetType); + } + + BeanTable assoc = factory.getBeanTable(targetType); + if (assoc == null) { + String msg = errorMsgMissingBeanTable(targetType, manyProp.getFullBeanName()); + throw new RuntimeException(msg); + } + + manyProp.setBeanTable(assoc); + manyProp.getTableJoin().setType(SqlJoinType.OUTER); + } + + + private String getM2MJoinTableName(BeanTable lhsTable, BeanTable rhsTable){ + + TableName lhs = new TableName(lhsTable.getBaseTable()); + TableName rhs = new TableName(rhsTable.getBaseTable()); + + TableName joinTable = namingConvention.getM2MJoinTableName(lhs, rhs); + + return joinTable.getQualifiedName(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java index 06be5c30b..5bbcfea64 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java @@ -1,213 +1,213 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import java.util.Map; - -import javax.persistence.Column; -import javax.persistence.Embedded; -import javax.persistence.EmbeddedId; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.JoinTable; -import javax.persistence.ManyToOne; -import javax.persistence.OneToOne; -import javax.validation.constraints.NotNull; - -import com.avaje.ebean.annotation.EmbeddedColumns; -import com.avaje.ebean.annotation.Where; -import com.avaje.ebean.config.NamingConvention; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; -import com.avaje.ebeaninternal.server.deploy.BeanTable; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.query.SqlJoinType; - -/** - * Read the deployment annotations for Associated One beans. - */ -public class AnnotationAssocOnes extends AnnotationParser { - - private final BeanDescriptorManager factory; - - /** - * Create with the deploy Info. - */ - public AnnotationAssocOnes(DeployBeanInfo info, BeanDescriptorManager factory) { - super(info); - this.factory = factory; - } - - /** - * Parse the annotation. - */ - public void parse() { - - for (DeployBeanProperty prop : descriptor.propertiesAll()) { - if (prop instanceof DeployBeanPropertyAssocOne) { - readAssocOne((DeployBeanPropertyAssocOne) prop); - } - } - } - - private void readAssocOne(DeployBeanPropertyAssocOne prop) { - - ManyToOne manyToOne = get(prop, ManyToOne.class); - if (manyToOne != null) { - readManyToOne(manyToOne, prop); - } - OneToOne oneToOne = get(prop, OneToOne.class); - if (oneToOne != null) { - readOneToOne(oneToOne, prop); - } - Embedded embedded = get(prop, Embedded.class); - if (embedded != null) { - readEmbedded(embedded, prop); - } - EmbeddedId emId = get(prop, EmbeddedId.class); - if (emId != null) { - prop.setEmbedded(true); - prop.setId(true); - prop.setNullable(false); - } - Column column = get(prop, Column.class); - if (column != null && !isEmpty(column.name())) { - // have this in for AssocOnes used on - // Sql based beans... - prop.setDbColumn(column.name()); - } - - // May as well check for Id. Makes sense to me. - Id id = get(prop, Id.class); - if (id != null) { - prop.setEmbedded(true); - prop.setId(true); - prop.setNullable(false); - } - - Where where = get(prop, Where.class); - if (where != null) { - // not expecting this to be used on assoc one properties - prop.setExtraWhere(where.clause()); - } - - if (validationAnnotations) { - NotNull notNull = get(prop, NotNull.class); - if (notNull != null) { - prop.setNullable(false); - // overrides optional attribute of ManyToOne etc - prop.getTableJoin().setType(SqlJoinType.INNER); - } - } - - // check for manually defined joins - BeanTable beanTable = prop.getBeanTable(); - JoinColumn joinColumn = get(prop, JoinColumn.class); - if (joinColumn != null) { - prop.getTableJoin().addJoinColumn(false, joinColumn, beanTable); - if (!joinColumn.updatable()) { - prop.setDbUpdateable(false); - } - if (!joinColumn.nullable()) { - prop.setNullable(false); - } - } - - JoinColumns joinColumns = get(prop, JoinColumns.class); - if (joinColumns != null) { - prop.getTableJoin().addJoinColumn(false, joinColumns.value(), beanTable); - } - - JoinTable joinTable = get(prop, JoinTable.class); - if (joinTable != null) { - prop.getTableJoin().addJoinColumn(false, joinTable.joinColumns(), beanTable); - } - - info.setBeanJoinType(prop, prop.isNullable()); - - if (!prop.getTableJoin().hasJoinColumns() && beanTable != null) { - - if (prop.getMappedBy() != null) { - // the join is derived by reversing the join information - // from the mapped by property. - // Refer BeanDescriptorManager.readEntityRelationships() - - } else { - // use naming convention to define join. - NamingConvention nc = factory.getNamingConvention(); - - String fkeyPrefix = null; - if (nc.isUseForeignKeyPrefix()){ - fkeyPrefix = nc.getColumnFromProperty(beanType, prop.getName()); - } - - beanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), true); - } - } - } - - private String errorMsgMissingBeanTable(Class type, String from) { - return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered?"; - } - - private void readManyToOne(ManyToOne propAnn, DeployBeanProperty prop) { - - DeployBeanPropertyAssocOne beanProp = (DeployBeanPropertyAssocOne) prop; - - setCascadeTypes(propAnn.cascade(), beanProp.getCascadeInfo()); - - BeanTable assoc = factory.getBeanTable(beanProp.getPropertyType()); - if (assoc == null) { - String msg = errorMsgMissingBeanTable(beanProp.getPropertyType(), prop.getFullBeanName()); - throw new RuntimeException(msg); - } - beanProp.setBeanTable(assoc); - beanProp.setDbInsertable(true); - beanProp.setDbUpdateable(true); - beanProp.setNullable(propAnn.optional()); - beanProp.setFetchType(propAnn.fetch()); - } - - private void readOneToOne(OneToOne propAnn, DeployBeanPropertyAssocOne prop) { - - prop.setOneToOne(true); - prop.setDbInsertable(true); - prop.setDbUpdateable(true); - prop.setNullable(propAnn.optional()); - prop.setFetchType(propAnn.fetch()); - prop.setMappedBy(propAnn.mappedBy()); - if (!"".equals(propAnn.mappedBy())) { - prop.setOneToOneExported(true); - } - - setCascadeTypes(propAnn.cascade(), prop.getCascadeInfo()); - - BeanTable assoc = factory.getBeanTable(prop.getPropertyType()); - if (assoc == null) { - String msg = errorMsgMissingBeanTable(prop.getPropertyType(), prop.getFullBeanName()); - throw new RuntimeException(msg); - } - - prop.setBeanTable(assoc); - } - - private void readEmbedded(Embedded propAnn, DeployBeanPropertyAssocOne prop) { - - prop.setEmbedded(true); - prop.setDbInsertable(true); - prop.setDbUpdateable(true); - - EmbeddedColumns columns = get(prop, EmbeddedColumns.class); - if (columns != null) { - - // convert into a Map - String propColumns = columns.columns(); - Map propMap = StringHelper.delimitedToMap(propColumns, ",", "="); - - prop.getDeployEmbedded().putAll(propMap); - } - - readEmbeddedAttributeOverrides(prop); - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.util.Map; + +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.EmbeddedId; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.JoinTable; +import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; +import javax.validation.constraints.NotNull; + +import com.avaje.ebean.annotation.EmbeddedColumns; +import com.avaje.ebean.annotation.Where; +import com.avaje.ebean.config.NamingConvention; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; +import com.avaje.ebeaninternal.server.deploy.BeanTable; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.query.SqlJoinType; + +/** + * Read the deployment annotations for Associated One beans. + */ +public class AnnotationAssocOnes extends AnnotationParser { + + private final BeanDescriptorManager factory; + + /** + * Create with the deploy Info. + */ + public AnnotationAssocOnes(DeployBeanInfo info, BeanDescriptorManager factory) { + super(info); + this.factory = factory; + } + + /** + * Parse the annotation. + */ + public void parse() { + + for (DeployBeanProperty prop : descriptor.propertiesAll()) { + if (prop instanceof DeployBeanPropertyAssocOne) { + readAssocOne((DeployBeanPropertyAssocOne) prop); + } + } + } + + private void readAssocOne(DeployBeanPropertyAssocOne prop) { + + ManyToOne manyToOne = get(prop, ManyToOne.class); + if (manyToOne != null) { + readManyToOne(manyToOne, prop); + } + OneToOne oneToOne = get(prop, OneToOne.class); + if (oneToOne != null) { + readOneToOne(oneToOne, prop); + } + Embedded embedded = get(prop, Embedded.class); + if (embedded != null) { + readEmbedded(embedded, prop); + } + EmbeddedId emId = get(prop, EmbeddedId.class); + if (emId != null) { + prop.setEmbedded(true); + prop.setId(true); + prop.setNullable(false); + } + Column column = get(prop, Column.class); + if (column != null && !isEmpty(column.name())) { + // have this in for AssocOnes used on + // Sql based beans... + prop.setDbColumn(column.name()); + } + + // May as well check for Id. Makes sense to me. + Id id = get(prop, Id.class); + if (id != null) { + prop.setEmbedded(true); + prop.setId(true); + prop.setNullable(false); + } + + Where where = get(prop, Where.class); + if (where != null) { + // not expecting this to be used on assoc one properties + prop.setExtraWhere(where.clause()); + } + + if (validationAnnotations) { + NotNull notNull = get(prop, NotNull.class); + if (notNull != null) { + prop.setNullable(false); + // overrides optional attribute of ManyToOne etc + prop.getTableJoin().setType(SqlJoinType.INNER); + } + } + + // check for manually defined joins + BeanTable beanTable = prop.getBeanTable(); + JoinColumn joinColumn = get(prop, JoinColumn.class); + if (joinColumn != null) { + prop.getTableJoin().addJoinColumn(false, joinColumn, beanTable); + if (!joinColumn.updatable()) { + prop.setDbUpdateable(false); + } + if (!joinColumn.nullable()) { + prop.setNullable(false); + } + } + + JoinColumns joinColumns = get(prop, JoinColumns.class); + if (joinColumns != null) { + prop.getTableJoin().addJoinColumn(false, joinColumns.value(), beanTable); + } + + JoinTable joinTable = get(prop, JoinTable.class); + if (joinTable != null) { + prop.getTableJoin().addJoinColumn(false, joinTable.joinColumns(), beanTable); + } + + info.setBeanJoinType(prop, prop.isNullable()); + + if (!prop.getTableJoin().hasJoinColumns() && beanTable != null) { + + if (prop.getMappedBy() != null) { + // the join is derived by reversing the join information + // from the mapped by property. + // Refer BeanDescriptorManager.readEntityRelationships() + + } else { + // use naming convention to define join. + NamingConvention nc = factory.getNamingConvention(); + + String fkeyPrefix = null; + if (nc.isUseForeignKeyPrefix()){ + fkeyPrefix = nc.getColumnFromProperty(beanType, prop.getName()); + } + + beanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), true); + } + } + } + + private String errorMsgMissingBeanTable(Class type, String from) { + return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered?"; + } + + private void readManyToOne(ManyToOne propAnn, DeployBeanProperty prop) { + + DeployBeanPropertyAssocOne beanProp = (DeployBeanPropertyAssocOne) prop; + + setCascadeTypes(propAnn.cascade(), beanProp.getCascadeInfo()); + + BeanTable assoc = factory.getBeanTable(beanProp.getPropertyType()); + if (assoc == null) { + String msg = errorMsgMissingBeanTable(beanProp.getPropertyType(), prop.getFullBeanName()); + throw new RuntimeException(msg); + } + beanProp.setBeanTable(assoc); + beanProp.setDbInsertable(true); + beanProp.setDbUpdateable(true); + beanProp.setNullable(propAnn.optional()); + beanProp.setFetchType(propAnn.fetch()); + } + + private void readOneToOne(OneToOne propAnn, DeployBeanPropertyAssocOne prop) { + + prop.setOneToOne(true); + prop.setDbInsertable(true); + prop.setDbUpdateable(true); + prop.setNullable(propAnn.optional()); + prop.setFetchType(propAnn.fetch()); + prop.setMappedBy(propAnn.mappedBy()); + if (!"".equals(propAnn.mappedBy())) { + prop.setOneToOneExported(true); + } + + setCascadeTypes(propAnn.cascade(), prop.getCascadeInfo()); + + BeanTable assoc = factory.getBeanTable(prop.getPropertyType()); + if (assoc == null) { + String msg = errorMsgMissingBeanTable(prop.getPropertyType(), prop.getFullBeanName()); + throw new RuntimeException(msg); + } + + prop.setBeanTable(assoc); + } + + private void readEmbedded(Embedded propAnn, DeployBeanPropertyAssocOne prop) { + + prop.setEmbedded(true); + prop.setDbInsertable(true); + prop.setDbUpdateable(true); + + EmbeddedColumns columns = get(prop, EmbeddedColumns.class); + if (columns != null) { + + // convert into a Map + String propColumns = columns.columns(); + Map propMap = StringHelper.delimitedToMap(propColumns, ",", "="); + + prop.getDeployEmbedded().putAll(propMap); + } + + readEmbeddedAttributeOverrides(prop); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBeanTable.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBeanTable.java index 1fe7494bd..491e5db30 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBeanTable.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBeanTable.java @@ -1,31 +1,31 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import com.avaje.ebean.config.TableName; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable; - -/** - * Read the annotations for BeanTable. - *

- * Refer to BeanTable but basically determining base table, table alias - * and the unique id properties. - *

- */ -public class AnnotationBeanTable extends AnnotationBase { - - final DeployBeanTable beanTable; - - public AnnotationBeanTable(DeployUtil util, DeployBeanTable beanTable){ - super(util); - this.beanTable = beanTable; - } - - /** - * Parse the annotations. - */ - public void parse() { - - TableName tableName = namingConvention.getTableName(beanTable.getBeanType()); - - beanTable.setBaseTable(tableName.getQualifiedName()); - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import com.avaje.ebean.config.TableName; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable; + +/** + * Read the annotations for BeanTable. + *

+ * Refer to BeanTable but basically determining base table, table alias + * and the unique id properties. + *

+ */ +public class AnnotationBeanTable extends AnnotationBase { + + final DeployBeanTable beanTable; + + public AnnotationBeanTable(DeployUtil util, DeployBeanTable beanTable){ + super(util); + this.beanTable = beanTable; + } + + /** + * Parse the annotations. + */ + public void parse() { + + TableName tableName = namingConvention.getTableName(beanTable.getBeanType()); + + beanTable.setBaseTable(tableName.getQualifiedName()); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java index 27ba7ce6e..9a114a67a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java @@ -1,169 +1,169 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; - -import com.avaje.ebean.annotation.CacheStrategy; -import com.avaje.ebean.annotation.CacheTuning; -import com.avaje.ebean.annotation.EntityConcurrencyMode; -import com.avaje.ebean.annotation.NamedUpdate; -import com.avaje.ebean.annotation.NamedUpdates; -import com.avaje.ebean.annotation.UpdateMode; -import com.avaje.ebean.config.TableName; -import com.avaje.ebeaninternal.server.core.CacheOptions; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; -import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; -import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; - -/** - * Read the class level deployment annotations. - */ -public class AnnotationClass extends AnnotationParser { - - public AnnotationClass(DeployBeanInfo info) { - super(info); - } - - /** - * Read the class level deployment annotations. - */ - public void parse() { - read(descriptor.getBeanType()); - setTableName(); - } - - /** - * Set the table name if it has not already been set. - */ - private void setTableName() { - - if (descriptor.isBaseTableType()) { - - // default the TableName using NamingConvention. - TableName tableName = namingConvention.getTableName(descriptor.getBeanType()); - - descriptor.setBaseTable(tableName); - } - } - - private void read(Class cls) { - - Entity entity = cls.getAnnotation(Entity.class); - if (entity != null) { - if (entity.name().equals("")) { - descriptor.setName(cls.getSimpleName()); - } else { - descriptor.setName(entity.name()); - } - } - - Embeddable embeddable = cls.getAnnotation(Embeddable.class); - if (embeddable != null) { - descriptor.setEntityType(EntityType.EMBEDDED); - descriptor.setName("Embeddable:" + cls.getSimpleName()); - } - - UniqueConstraint uc = cls.getAnnotation(UniqueConstraint.class); - if (uc != null) { - descriptor.addCompoundUniqueConstraint(new CompoundUniqueContraint(uc.columnNames())); - } - - Table table = cls.getAnnotation(Table.class); - if (table != null) { - UniqueConstraint[] uniqueConstraints = table.uniqueConstraints(); - if (uniqueConstraints != null) { - for (UniqueConstraint c : uniqueConstraints) { - descriptor.addCompoundUniqueConstraint(new CompoundUniqueContraint(c.columnNames())); - } - } - } - - UpdateMode updateMode = cls.getAnnotation(UpdateMode.class); - if (updateMode != null) { - descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly()); - } - - NamedQueries namedQueries = cls.getAnnotation(NamedQueries.class); - if (namedQueries != null) { - readNamedQueries(namedQueries); - } - NamedQuery namedQuery = cls.getAnnotation(NamedQuery.class); - if (namedQuery != null) { - readNamedQuery(namedQuery); - } - - NamedUpdates namedUpdates = cls.getAnnotation(NamedUpdates.class); - if (namedUpdates != null) { - readNamedUpdates(namedUpdates); - } - - NamedUpdate namedUpdate = cls.getAnnotation(NamedUpdate.class); - if (namedUpdate != null) { - readNamedUpdate(namedUpdate); - } - - CacheStrategy cacheStrategy = cls.getAnnotation(CacheStrategy.class); - CacheTuning cacheTuning = cls.getAnnotation(CacheTuning.class); - if (cacheStrategy != null || cacheTuning != null) { - readCacheStrategy(cacheStrategy, cacheTuning); - } - - EntityConcurrencyMode entityConcurrencyMode = cls.getAnnotation(EntityConcurrencyMode.class); - if (entityConcurrencyMode != null) { - descriptor.setConcurrencyMode(entityConcurrencyMode.value()); - } - } - - private void readCacheStrategy(CacheStrategy cacheStrategy, CacheTuning cacheTuning) { - - CacheOptions cacheOptions = descriptor.getCacheOptions(); - if (cacheTuning != null) { - cacheOptions.setMaxSecsToLive(cacheTuning.maxSecsToLive()); - cacheOptions.setMaxIdleSecs(cacheTuning.maxIdleSecs()); - } - if (cacheStrategy != null) { - cacheOptions.setUseCache(cacheStrategy.useBeanCache()); - cacheOptions.setReadOnly(cacheStrategy.readOnly()); - cacheOptions.setWarmingQuery(cacheStrategy.warmingQuery()); - if (cacheStrategy.naturalKey().length() > 0) { - String propName = cacheStrategy.naturalKey().trim(); - DeployBeanProperty beanProperty = descriptor.getBeanProperty(propName); - if (beanProperty != null) { - beanProperty.setNaturalKey(true); - cacheOptions.setNaturalKey(propName); - } - } - } - } - - private void readNamedQueries(NamedQueries namedQueries) { - NamedQuery[] queries = namedQueries.value(); - for (int i = 0; i < queries.length; i++) { - readNamedQuery(queries[i]); - } - } - - private void readNamedQuery(NamedQuery namedQuery) { - DeployNamedQuery q = new DeployNamedQuery(namedQuery); - descriptor.add(q); - } - - private void readNamedUpdates(NamedUpdates updates) { - NamedUpdate[] updateArray = updates.value(); - for (int i = 0; i < updateArray.length; i++) { - readNamedUpdate(updateArray[i]); - } - } - - private void readNamedUpdate(NamedUpdate update) { - DeployNamedUpdate upd = new DeployNamedUpdate(update); - descriptor.add(upd); - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +import com.avaje.ebean.annotation.CacheStrategy; +import com.avaje.ebean.annotation.CacheTuning; +import com.avaje.ebean.annotation.EntityConcurrencyMode; +import com.avaje.ebean.annotation.NamedUpdate; +import com.avaje.ebean.annotation.NamedUpdates; +import com.avaje.ebean.annotation.UpdateMode; +import com.avaje.ebean.config.TableName; +import com.avaje.ebeaninternal.server.core.CacheOptions; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; +import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +/** + * Read the class level deployment annotations. + */ +public class AnnotationClass extends AnnotationParser { + + public AnnotationClass(DeployBeanInfo info) { + super(info); + } + + /** + * Read the class level deployment annotations. + */ + public void parse() { + read(descriptor.getBeanType()); + setTableName(); + } + + /** + * Set the table name if it has not already been set. + */ + private void setTableName() { + + if (descriptor.isBaseTableType()) { + + // default the TableName using NamingConvention. + TableName tableName = namingConvention.getTableName(descriptor.getBeanType()); + + descriptor.setBaseTable(tableName); + } + } + + private void read(Class cls) { + + Entity entity = cls.getAnnotation(Entity.class); + if (entity != null) { + if (entity.name().equals("")) { + descriptor.setName(cls.getSimpleName()); + } else { + descriptor.setName(entity.name()); + } + } + + Embeddable embeddable = cls.getAnnotation(Embeddable.class); + if (embeddable != null) { + descriptor.setEntityType(EntityType.EMBEDDED); + descriptor.setName("Embeddable:" + cls.getSimpleName()); + } + + UniqueConstraint uc = cls.getAnnotation(UniqueConstraint.class); + if (uc != null) { + descriptor.addCompoundUniqueConstraint(new CompoundUniqueContraint(uc.columnNames())); + } + + Table table = cls.getAnnotation(Table.class); + if (table != null) { + UniqueConstraint[] uniqueConstraints = table.uniqueConstraints(); + if (uniqueConstraints != null) { + for (UniqueConstraint c : uniqueConstraints) { + descriptor.addCompoundUniqueConstraint(new CompoundUniqueContraint(c.columnNames())); + } + } + } + + UpdateMode updateMode = cls.getAnnotation(UpdateMode.class); + if (updateMode != null) { + descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly()); + } + + NamedQueries namedQueries = cls.getAnnotation(NamedQueries.class); + if (namedQueries != null) { + readNamedQueries(namedQueries); + } + NamedQuery namedQuery = cls.getAnnotation(NamedQuery.class); + if (namedQuery != null) { + readNamedQuery(namedQuery); + } + + NamedUpdates namedUpdates = cls.getAnnotation(NamedUpdates.class); + if (namedUpdates != null) { + readNamedUpdates(namedUpdates); + } + + NamedUpdate namedUpdate = cls.getAnnotation(NamedUpdate.class); + if (namedUpdate != null) { + readNamedUpdate(namedUpdate); + } + + CacheStrategy cacheStrategy = cls.getAnnotation(CacheStrategy.class); + CacheTuning cacheTuning = cls.getAnnotation(CacheTuning.class); + if (cacheStrategy != null || cacheTuning != null) { + readCacheStrategy(cacheStrategy, cacheTuning); + } + + EntityConcurrencyMode entityConcurrencyMode = cls.getAnnotation(EntityConcurrencyMode.class); + if (entityConcurrencyMode != null) { + descriptor.setConcurrencyMode(entityConcurrencyMode.value()); + } + } + + private void readCacheStrategy(CacheStrategy cacheStrategy, CacheTuning cacheTuning) { + + CacheOptions cacheOptions = descriptor.getCacheOptions(); + if (cacheTuning != null) { + cacheOptions.setMaxSecsToLive(cacheTuning.maxSecsToLive()); + cacheOptions.setMaxIdleSecs(cacheTuning.maxIdleSecs()); + } + if (cacheStrategy != null) { + cacheOptions.setUseCache(cacheStrategy.useBeanCache()); + cacheOptions.setReadOnly(cacheStrategy.readOnly()); + cacheOptions.setWarmingQuery(cacheStrategy.warmingQuery()); + if (cacheStrategy.naturalKey().length() > 0) { + String propName = cacheStrategy.naturalKey().trim(); + DeployBeanProperty beanProperty = descriptor.getBeanProperty(propName); + if (beanProperty != null) { + beanProperty.setNaturalKey(true); + cacheOptions.setNaturalKey(propName); + } + } + } + } + + private void readNamedQueries(NamedQueries namedQueries) { + NamedQuery[] queries = namedQueries.value(); + for (int i = 0; i < queries.length; i++) { + readNamedQuery(queries[i]); + } + } + + private void readNamedQuery(NamedQuery namedQuery) { + DeployNamedQuery q = new DeployNamedQuery(namedQuery); + descriptor.add(q); + } + + private void readNamedUpdates(NamedUpdates updates) { + NamedUpdate[] updateArray = updates.value(); + for (int i = 0; i < updateArray.length; i++) { + readNamedUpdate(updateArray[i]); + } + } + + private void readNamedUpdate(NamedUpdate update) { + DeployNamedUpdate upd = new DeployNamedUpdate(update); + descriptor.add(upd); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java index 7fff02f83..c868e6c18 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java @@ -1,427 +1,427 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import com.avaje.ebean.annotation.*; -import com.avaje.ebean.config.EncryptDeploy; -import com.avaje.ebean.config.EncryptDeploy.Mode; -import com.avaje.ebean.config.dbplatform.DbEncrypt; -import com.avaje.ebean.config.dbplatform.DbEncryptFunction; -import com.avaje.ebean.config.dbplatform.IdType; -import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; -import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.type.*; - -import javax.persistence.*; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; -import java.sql.Types; -import java.util.Map; -import java.util.UUID; - -/** - * Read the field level deployment annotations. - */ -public class AnnotationFields extends AnnotationParser { - - /** - * By default we lazy load Lob properties. - */ - private FetchType defaultLobFetchType = FetchType.LAZY; - - private GeneratedPropertyFactory generatedPropFactory = new GeneratedPropertyFactory(); - - public AnnotationFields(DeployBeanInfo info, boolean eagerFetchLobs) { - super(info); - - if (eagerFetchLobs) { - defaultLobFetchType = FetchType.EAGER; - } - } - - /** - * Read the field level deployment annotations. - */ - public void parse() { - - for (DeployBeanProperty prop : descriptor.propertiesAll()) { - if (prop instanceof DeployBeanPropertyAssoc) { - readAssocOne(prop); - } else { - readField(prop); - } - } - } - - /** - * Read the Id marker annotations on EmbeddedId properties. - */ - private void readAssocOne(DeployBeanProperty prop) { - - Id id = get(prop, Id.class); - if (id != null) { - prop.setId(true); - prop.setNullable(false); - } - - EmbeddedId embeddedId = get(prop, EmbeddedId.class); - if (embeddedId != null) { - prop.setId(true); - prop.setNullable(false); - prop.setEmbedded(true); - } - - if (prop instanceof DeployBeanPropertyAssocOne) { - if (prop.isId() && !prop.isEmbedded()) { - prop.setEmbedded(true); - } - readEmbeddedAttributeOverrides((DeployBeanPropertyAssocOne) prop); - } - } - - private void readField(DeployBeanProperty prop) { - - // all Enums will have a ScalarType assigned... - boolean isEnum = prop.getPropertyType().isEnum(); - Enumerated enumerated = get(prop, Enumerated.class); - if (isEnum || enumerated != null) { - util.setEnumScalarType(enumerated, prop); - } - - // its persistent and assumed to be on the base table - // rather than on a secondary table - prop.setDbRead(true); - prop.setDbInsertable(true); - prop.setDbUpdateable(true); - - Column column = get(prop, Column.class); - if (column != null) { - readColumn(column, prop); - } - Expose expose = get(prop, Expose.class); - if (expose != null) { - prop.setExposeSerialize(expose.serialize()); - prop.setExposeDeserialize(expose.deserialize()); - } - - if (prop.getDbColumn() == null) { - // No @Column annotation or @Column.name() not set - // Use the NamingConvention to set the DB column name - String dbColumn = namingConvention.getColumnFromProperty(beanType, prop.getName()); - prop.setDbColumn(dbColumn); - } - - GeneratedValue gen = get(prop, GeneratedValue.class); - if (gen != null) { - readGenValue(gen, prop); - } - - Id id = (Id) get(prop, Id.class); - if (id != null) { - readId(id, prop); - } - - // determine the JDBC type using Lob/Temporal - // otherwise based on the property Class - Lob lob = get(prop, Lob.class); - Temporal temporal = get(prop, Temporal.class); - if (temporal != null) { - readTemporal(temporal, prop); - - } else if (lob != null) { - util.setLobType(prop); - } - - Formula formula = get(prop, Formula.class); - if (formula != null) { - prop.setSqlFormula(formula.select(), formula.join()); - } - - Version version = get(prop, Version.class); - if (version != null) { - // explicitly specify a version column - prop.setVersionColumn(true); - generatedPropFactory.setVersion(prop); - } - - Basic basic = get(prop, Basic.class); - if (basic != null) { - prop.setFetchType(basic.fetch()); - if (!basic.optional()) { - prop.setNullable(false); - } - } else if (prop.isLob()) { - // use the default Lob fetchType - prop.setFetchType(defaultLobFetchType); - } - - CreatedTimestamp ct = get(prop, CreatedTimestamp.class); - if (ct != null) { - generatedPropFactory.setInsertTimestamp(prop); - } - - UpdatedTimestamp ut = get(prop, UpdatedTimestamp.class); - if (ut != null) { - generatedPropFactory.setUpdateTimestamp(prop); - } - - if (validationAnnotations) { - NotNull notNull = get(prop, NotNull.class); - if (notNull != null && isNotNullOnAllValidationGroups(notNull.groups())) { - // Not null on all validation groups so enable - // DDL generation of Not Null Constraint - prop.setNullable(false); - } - - Size size = get(prop, Size.class); - if (size != null) { - if (size.max() < Integer.MAX_VALUE) { - // explicitly specify a version column - prop.setDbLength(size.max()); - } - } - } - - EmbeddedColumns columns = get(prop, EmbeddedColumns.class); - if (columns != null) { - if (prop instanceof DeployBeanPropertyCompound) { - DeployBeanPropertyCompound p = (DeployBeanPropertyCompound) prop; - - // convert into a Map - String propColumns = columns.columns(); - Map propMap = StringHelper.delimitedToMap(propColumns, ",", "="); - - p.getDeployEmbedded().putAll(propMap); - - CtCompoundType compoundType = p.getCompoundType(); - if (compoundType == null) { - throw new RuntimeException("No registered CtCompoundType for " + p.getPropertyType()); - } - - } else { - throw new RuntimeException("Can't use EmbeddedColumns on ScalarType " - + prop.getFullBeanName()); - } - } - - // Want to process last so we can use with @Formula - Transient t = get(prop, Transient.class); - if (t != null) { - // it is not a persistent property. - prop.setDbRead(false); - prop.setDbInsertable(false); - prop.setDbUpdateable(false); - prop.setTransient(true); - } - - if (!prop.isTransient()) { - - EncryptDeploy encryptDeploy = util.getEncryptDeploy(info.getDescriptor().getBaseTableFull(), - prop.getDbColumn()); - if (encryptDeploy == null || encryptDeploy.getMode().equals(Mode.MODE_ANNOTATION)) { - Encrypted encrypted = get(prop, Encrypted.class); - if (encrypted != null) { - setEncryption(prop, encrypted.dbEncryption(), encrypted.dbLength()); - } - } else if (Mode.MODE_ENCRYPT.equals(encryptDeploy.getMode())) { - setEncryption(prop, encryptDeploy.isDbEncrypt(), encryptDeploy.getDbLength()); - } - } - - Index index = get(prop, Index.class); - if (index != null) { - if(hasRelationshipItem(prop)) { - throw new RuntimeException("Can't use Index on foreign key relationships."); - } - prop.setIndexed(true); - prop.setIndexName(index.value()); - } - } - - private boolean hasRelationshipItem(DeployBeanProperty prop) { - return get(prop, OneToMany.class) != null || - get(prop, ManyToOne.class) != null || - get(prop, OneToOne.class) != null; - } - - /** - * Return true if the validation is on all validation groups and hence - * can be applied to DDL generation. - */ - private boolean isNotNullOnAllValidationGroups(Class[] groups) { - if (groups.length == 0) { - return true; - } - if (groups.length == 1 && javax.validation.groups.Default.class.isAssignableFrom(groups[0])) { - return true; - } - return false; - } - - private void setEncryption(DeployBeanProperty prop, boolean dbEncString, int dbLen) { - - util.checkEncryptKeyManagerDefined(prop.getFullBeanName()); - - ScalarType st = prop.getScalarType(); - if (byte[].class.equals(st.getType())) { - // Always using Java client encryption rather than DB for encryption - // of binary data (partially as this is not supported on all db's etc) - // This could be reviewed at a later stage. - ScalarTypeBytesBase baseType = (ScalarTypeBytesBase) st; - DataEncryptSupport support = createDataEncryptSupport(prop); - ScalarTypeBytesEncrypted encryptedScalarType = new ScalarTypeBytesEncrypted(baseType, support); - prop.setScalarType(encryptedScalarType); - prop.setLocalEncrypted(true); - return; - - } - if (dbEncString) { - - DbEncrypt dbEncrypt = util.getDbPlatform().getDbEncrypt(); - - if (dbEncrypt != null) { - // check if we have a DB encryption function for this type - int jdbcType = prop.getScalarType().getJdbcType(); - DbEncryptFunction dbEncryptFunction = dbEncrypt.getDbEncryptFunction(jdbcType); - if (dbEncryptFunction != null) { - // Use DB functions to encrypt and decrypt - prop.setDbEncryptFunction(dbEncryptFunction, dbEncrypt, dbLen); - return; - } - } - } - - prop.setScalarType(createScalarType(prop, st)); - prop.setLocalEncrypted(true); - if (dbLen > 0) { - prop.setDbLength(dbLen); - } - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private ScalarTypeEncryptedWrapper createScalarType(DeployBeanProperty prop, ScalarType st) { - - // Use Java Encryptor wrapping the logical scalar type - DataEncryptSupport support = createDataEncryptSupport(prop); - ScalarTypeBytesBase byteType = getDbEncryptType(prop); - - return new ScalarTypeEncryptedWrapper(st, byteType, support); - } - - private ScalarTypeBytesBase getDbEncryptType(DeployBeanProperty prop) { - int dbType = prop.isLob() ? Types.BLOB : Types.VARBINARY; - return (ScalarTypeBytesBase) util.getTypeManager().getScalarType(dbType); - } - - private DataEncryptSupport createDataEncryptSupport(DeployBeanProperty prop) { - - String table = info.getDescriptor().getBaseTable(); - String column = prop.getDbColumn(); - - return util.createDataEncryptSupport(table, column); - } - - private void readId(Id id, DeployBeanProperty prop) { - - prop.setId(true); - prop.setNullable(false); - - if (prop.getPropertyType().equals(UUID.class)) { - // An Id of type UUID - if (descriptor.getIdGeneratorName() == null) { - // Without a generator explicitly specified - // so will use the default one AUTO_UUID - descriptor.setIdGeneratorName(UuidIdGenerator.AUTO_UUID); - descriptor.setIdType(IdType.GENERATOR); - } - } - } - - private void readGenValue(GeneratedValue gen, DeployBeanProperty prop) { - - String genName = gen.generator(); - - SequenceGenerator sequenceGenerator = find(prop, SequenceGenerator.class); - if (sequenceGenerator != null) { - if (sequenceGenerator.name().equals(genName)) { - genName = sequenceGenerator.sequenceName(); - } - descriptor.setSequenceInitialValue(sequenceGenerator.initialValue()); - descriptor.setSequenceAllocationSize(sequenceGenerator.allocationSize()); - } - - GenerationType strategy = gen.strategy(); - - if (strategy == GenerationType.IDENTITY) { - descriptor.setIdType(IdType.IDENTITY); - - } else if (strategy == GenerationType.SEQUENCE) { - descriptor.setIdType(IdType.SEQUENCE); - if (genName != null && genName.length() > 0) { - descriptor.setIdGeneratorName(genName); - } - - } else if (strategy == GenerationType.AUTO) { - if (prop.getPropertyType().equals(UUID.class)) { - descriptor.setIdGeneratorName(UuidIdGenerator.AUTO_UUID); - descriptor.setIdType(IdType.GENERATOR); - - } else { - // use DatabasePlatform defaults - } - } - } - - private void readTemporal(Temporal temporal, DeployBeanProperty prop) { - - TemporalType type = temporal.value(); - if (type.equals(TemporalType.DATE)) { - prop.setDbType(Types.DATE); - - } else if (type.equals(TemporalType.TIMESTAMP)) { - prop.setDbType(Types.TIMESTAMP); - - } else if (type.equals(TemporalType.TIME)) { - prop.setDbType(Types.TIME); - - } else { - throw new PersistenceException("Unhandled type " + type); - } - } - - private void readColumn(Column columnAnn, DeployBeanProperty prop) { - - if (!isEmpty(columnAnn.name())) { - String dbColumn = databasePlatform.convertQuotedIdentifiers(columnAnn.name()); - prop.setDbColumn(dbColumn); - } - - prop.setDbInsertable(columnAnn.insertable()); - prop.setDbUpdateable(columnAnn.updatable()); - prop.setNullable(columnAnn.nullable()); - prop.setUnique(columnAnn.unique()); - if (columnAnn.precision() > 0) { - prop.setDbLength(columnAnn.precision()); - } else if (columnAnn.length() != 255) { - // set default 255 on DbTypeMap - prop.setDbLength(columnAnn.length()); - } - prop.setDbScale(columnAnn.scale()); - prop.setDbColumnDefn(columnAnn.columnDefinition()); - - String baseTable = descriptor.getBaseTable(); - String tableName = columnAnn.table(); - if (tableName.equals("") || tableName.equalsIgnoreCase(baseTable)) { - // its a base table property... - } else { - // its on a secondary table... - prop.setSecondaryTable(tableName); - // DeployTableJoin tableJoin = info.getTableJoin(tableName); - // tableJoin.addProperty(prop); - } - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import com.avaje.ebean.annotation.*; +import com.avaje.ebean.config.EncryptDeploy; +import com.avaje.ebean.config.EncryptDeploy.Mode; +import com.avaje.ebean.config.dbplatform.DbEncrypt; +import com.avaje.ebean.config.dbplatform.DbEncryptFunction; +import com.avaje.ebean.config.dbplatform.IdType; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; +import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.type.*; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.sql.Types; +import java.util.Map; +import java.util.UUID; + +/** + * Read the field level deployment annotations. + */ +public class AnnotationFields extends AnnotationParser { + + /** + * By default we lazy load Lob properties. + */ + private FetchType defaultLobFetchType = FetchType.LAZY; + + private GeneratedPropertyFactory generatedPropFactory = new GeneratedPropertyFactory(); + + public AnnotationFields(DeployBeanInfo info, boolean eagerFetchLobs) { + super(info); + + if (eagerFetchLobs) { + defaultLobFetchType = FetchType.EAGER; + } + } + + /** + * Read the field level deployment annotations. + */ + public void parse() { + + for (DeployBeanProperty prop : descriptor.propertiesAll()) { + if (prop instanceof DeployBeanPropertyAssoc) { + readAssocOne(prop); + } else { + readField(prop); + } + } + } + + /** + * Read the Id marker annotations on EmbeddedId properties. + */ + private void readAssocOne(DeployBeanProperty prop) { + + Id id = get(prop, Id.class); + if (id != null) { + prop.setId(true); + prop.setNullable(false); + } + + EmbeddedId embeddedId = get(prop, EmbeddedId.class); + if (embeddedId != null) { + prop.setId(true); + prop.setNullable(false); + prop.setEmbedded(true); + } + + if (prop instanceof DeployBeanPropertyAssocOne) { + if (prop.isId() && !prop.isEmbedded()) { + prop.setEmbedded(true); + } + readEmbeddedAttributeOverrides((DeployBeanPropertyAssocOne) prop); + } + } + + private void readField(DeployBeanProperty prop) { + + // all Enums will have a ScalarType assigned... + boolean isEnum = prop.getPropertyType().isEnum(); + Enumerated enumerated = get(prop, Enumerated.class); + if (isEnum || enumerated != null) { + util.setEnumScalarType(enumerated, prop); + } + + // its persistent and assumed to be on the base table + // rather than on a secondary table + prop.setDbRead(true); + prop.setDbInsertable(true); + prop.setDbUpdateable(true); + + Column column = get(prop, Column.class); + if (column != null) { + readColumn(column, prop); + } + Expose expose = get(prop, Expose.class); + if (expose != null) { + prop.setExposeSerialize(expose.serialize()); + prop.setExposeDeserialize(expose.deserialize()); + } + + if (prop.getDbColumn() == null) { + // No @Column annotation or @Column.name() not set + // Use the NamingConvention to set the DB column name + String dbColumn = namingConvention.getColumnFromProperty(beanType, prop.getName()); + prop.setDbColumn(dbColumn); + } + + GeneratedValue gen = get(prop, GeneratedValue.class); + if (gen != null) { + readGenValue(gen, prop); + } + + Id id = (Id) get(prop, Id.class); + if (id != null) { + readId(id, prop); + } + + // determine the JDBC type using Lob/Temporal + // otherwise based on the property Class + Lob lob = get(prop, Lob.class); + Temporal temporal = get(prop, Temporal.class); + if (temporal != null) { + readTemporal(temporal, prop); + + } else if (lob != null) { + util.setLobType(prop); + } + + Formula formula = get(prop, Formula.class); + if (formula != null) { + prop.setSqlFormula(formula.select(), formula.join()); + } + + Version version = get(prop, Version.class); + if (version != null) { + // explicitly specify a version column + prop.setVersionColumn(true); + generatedPropFactory.setVersion(prop); + } + + Basic basic = get(prop, Basic.class); + if (basic != null) { + prop.setFetchType(basic.fetch()); + if (!basic.optional()) { + prop.setNullable(false); + } + } else if (prop.isLob()) { + // use the default Lob fetchType + prop.setFetchType(defaultLobFetchType); + } + + CreatedTimestamp ct = get(prop, CreatedTimestamp.class); + if (ct != null) { + generatedPropFactory.setInsertTimestamp(prop); + } + + UpdatedTimestamp ut = get(prop, UpdatedTimestamp.class); + if (ut != null) { + generatedPropFactory.setUpdateTimestamp(prop); + } + + if (validationAnnotations) { + NotNull notNull = get(prop, NotNull.class); + if (notNull != null && isNotNullOnAllValidationGroups(notNull.groups())) { + // Not null on all validation groups so enable + // DDL generation of Not Null Constraint + prop.setNullable(false); + } + + Size size = get(prop, Size.class); + if (size != null) { + if (size.max() < Integer.MAX_VALUE) { + // explicitly specify a version column + prop.setDbLength(size.max()); + } + } + } + + EmbeddedColumns columns = get(prop, EmbeddedColumns.class); + if (columns != null) { + if (prop instanceof DeployBeanPropertyCompound) { + DeployBeanPropertyCompound p = (DeployBeanPropertyCompound) prop; + + // convert into a Map + String propColumns = columns.columns(); + Map propMap = StringHelper.delimitedToMap(propColumns, ",", "="); + + p.getDeployEmbedded().putAll(propMap); + + CtCompoundType compoundType = p.getCompoundType(); + if (compoundType == null) { + throw new RuntimeException("No registered CtCompoundType for " + p.getPropertyType()); + } + + } else { + throw new RuntimeException("Can't use EmbeddedColumns on ScalarType " + + prop.getFullBeanName()); + } + } + + // Want to process last so we can use with @Formula + Transient t = get(prop, Transient.class); + if (t != null) { + // it is not a persistent property. + prop.setDbRead(false); + prop.setDbInsertable(false); + prop.setDbUpdateable(false); + prop.setTransient(true); + } + + if (!prop.isTransient()) { + + EncryptDeploy encryptDeploy = util.getEncryptDeploy(info.getDescriptor().getBaseTableFull(), + prop.getDbColumn()); + if (encryptDeploy == null || encryptDeploy.getMode().equals(Mode.MODE_ANNOTATION)) { + Encrypted encrypted = get(prop, Encrypted.class); + if (encrypted != null) { + setEncryption(prop, encrypted.dbEncryption(), encrypted.dbLength()); + } + } else if (Mode.MODE_ENCRYPT.equals(encryptDeploy.getMode())) { + setEncryption(prop, encryptDeploy.isDbEncrypt(), encryptDeploy.getDbLength()); + } + } + + Index index = get(prop, Index.class); + if (index != null) { + if(hasRelationshipItem(prop)) { + throw new RuntimeException("Can't use Index on foreign key relationships."); + } + prop.setIndexed(true); + prop.setIndexName(index.value()); + } + } + + private boolean hasRelationshipItem(DeployBeanProperty prop) { + return get(prop, OneToMany.class) != null || + get(prop, ManyToOne.class) != null || + get(prop, OneToOne.class) != null; + } + + /** + * Return true if the validation is on all validation groups and hence + * can be applied to DDL generation. + */ + private boolean isNotNullOnAllValidationGroups(Class[] groups) { + if (groups.length == 0) { + return true; + } + if (groups.length == 1 && javax.validation.groups.Default.class.isAssignableFrom(groups[0])) { + return true; + } + return false; + } + + private void setEncryption(DeployBeanProperty prop, boolean dbEncString, int dbLen) { + + util.checkEncryptKeyManagerDefined(prop.getFullBeanName()); + + ScalarType st = prop.getScalarType(); + if (byte[].class.equals(st.getType())) { + // Always using Java client encryption rather than DB for encryption + // of binary data (partially as this is not supported on all db's etc) + // This could be reviewed at a later stage. + ScalarTypeBytesBase baseType = (ScalarTypeBytesBase) st; + DataEncryptSupport support = createDataEncryptSupport(prop); + ScalarTypeBytesEncrypted encryptedScalarType = new ScalarTypeBytesEncrypted(baseType, support); + prop.setScalarType(encryptedScalarType); + prop.setLocalEncrypted(true); + return; + + } + if (dbEncString) { + + DbEncrypt dbEncrypt = util.getDbPlatform().getDbEncrypt(); + + if (dbEncrypt != null) { + // check if we have a DB encryption function for this type + int jdbcType = prop.getScalarType().getJdbcType(); + DbEncryptFunction dbEncryptFunction = dbEncrypt.getDbEncryptFunction(jdbcType); + if (dbEncryptFunction != null) { + // Use DB functions to encrypt and decrypt + prop.setDbEncryptFunction(dbEncryptFunction, dbEncrypt, dbLen); + return; + } + } + } + + prop.setScalarType(createScalarType(prop, st)); + prop.setLocalEncrypted(true); + if (dbLen > 0) { + prop.setDbLength(dbLen); + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private ScalarTypeEncryptedWrapper createScalarType(DeployBeanProperty prop, ScalarType st) { + + // Use Java Encryptor wrapping the logical scalar type + DataEncryptSupport support = createDataEncryptSupport(prop); + ScalarTypeBytesBase byteType = getDbEncryptType(prop); + + return new ScalarTypeEncryptedWrapper(st, byteType, support); + } + + private ScalarTypeBytesBase getDbEncryptType(DeployBeanProperty prop) { + int dbType = prop.isLob() ? Types.BLOB : Types.VARBINARY; + return (ScalarTypeBytesBase) util.getTypeManager().getScalarType(dbType); + } + + private DataEncryptSupport createDataEncryptSupport(DeployBeanProperty prop) { + + String table = info.getDescriptor().getBaseTable(); + String column = prop.getDbColumn(); + + return util.createDataEncryptSupport(table, column); + } + + private void readId(Id id, DeployBeanProperty prop) { + + prop.setId(true); + prop.setNullable(false); + + if (prop.getPropertyType().equals(UUID.class)) { + // An Id of type UUID + if (descriptor.getIdGeneratorName() == null) { + // Without a generator explicitly specified + // so will use the default one AUTO_UUID + descriptor.setIdGeneratorName(UuidIdGenerator.AUTO_UUID); + descriptor.setIdType(IdType.GENERATOR); + } + } + } + + private void readGenValue(GeneratedValue gen, DeployBeanProperty prop) { + + String genName = gen.generator(); + + SequenceGenerator sequenceGenerator = find(prop, SequenceGenerator.class); + if (sequenceGenerator != null) { + if (sequenceGenerator.name().equals(genName)) { + genName = sequenceGenerator.sequenceName(); + } + descriptor.setSequenceInitialValue(sequenceGenerator.initialValue()); + descriptor.setSequenceAllocationSize(sequenceGenerator.allocationSize()); + } + + GenerationType strategy = gen.strategy(); + + if (strategy == GenerationType.IDENTITY) { + descriptor.setIdType(IdType.IDENTITY); + + } else if (strategy == GenerationType.SEQUENCE) { + descriptor.setIdType(IdType.SEQUENCE); + if (genName != null && genName.length() > 0) { + descriptor.setIdGeneratorName(genName); + } + + } else if (strategy == GenerationType.AUTO) { + if (prop.getPropertyType().equals(UUID.class)) { + descriptor.setIdGeneratorName(UuidIdGenerator.AUTO_UUID); + descriptor.setIdType(IdType.GENERATOR); + + } else { + // use DatabasePlatform defaults + } + } + } + + private void readTemporal(Temporal temporal, DeployBeanProperty prop) { + + TemporalType type = temporal.value(); + if (type.equals(TemporalType.DATE)) { + prop.setDbType(Types.DATE); + + } else if (type.equals(TemporalType.TIMESTAMP)) { + prop.setDbType(Types.TIMESTAMP); + + } else if (type.equals(TemporalType.TIME)) { + prop.setDbType(Types.TIME); + + } else { + throw new PersistenceException("Unhandled type " + type); + } + } + + private void readColumn(Column columnAnn, DeployBeanProperty prop) { + + if (!isEmpty(columnAnn.name())) { + String dbColumn = databasePlatform.convertQuotedIdentifiers(columnAnn.name()); + prop.setDbColumn(dbColumn); + } + + prop.setDbInsertable(columnAnn.insertable()); + prop.setDbUpdateable(columnAnn.updatable()); + prop.setNullable(columnAnn.nullable()); + prop.setUnique(columnAnn.unique()); + if (columnAnn.precision() > 0) { + prop.setDbLength(columnAnn.precision()); + } else if (columnAnn.length() != 255) { + // set default 255 on DbTypeMap + prop.setDbLength(columnAnn.length()); + } + prop.setDbScale(columnAnn.scale()); + prop.setDbColumnDefn(columnAnn.columnDefinition()); + + String baseTable = descriptor.getBaseTable(); + String tableName = columnAnn.table(); + if (tableName.equals("") || tableName.equalsIgnoreCase(baseTable)) { + // its a base table property... + } else { + // its on a secondary table... + prop.setSecondaryTable(tableName); + // DeployTableJoin tableJoin = info.getTableJoin(tableName); + // tableJoin.addProperty(prop); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java index 2fce1d3a6..d36d17cc0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java @@ -1,77 +1,77 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import java.util.HashMap; - -import javax.persistence.AttributeOverride; -import javax.persistence.AttributeOverrides; -import javax.persistence.CascadeType; - -import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; - -/** - * Base class for reading deployment annotations. - */ -public abstract class AnnotationParser extends AnnotationBase { - - protected final DeployBeanInfo info; - - protected final DeployBeanDescriptor descriptor; - - protected final Class beanType; - - protected boolean validationAnnotations; - - public AnnotationParser(DeployBeanInfo info) { - super(info.getUtil()); - this.info = info; - this.beanType = info.getDescriptor().getBeanType(); - this.descriptor = info.getDescriptor(); - - try { - Class.forName("javax.validation.constraints.NotNull"); - validationAnnotations = true; - } catch (ClassNotFoundException e) { - // javax.validation not in the classpath so don't - // check for NotNull and Size - validationAnnotations = false; - } - } - - /** - * read the deployment annotations. - */ - public abstract void parse(); - - /** - * Helper method to set cascade types to the CascadeInfo on BeanProperty. - */ - protected void setCascadeTypes(CascadeType[] cascadeTypes, BeanCascadeInfo cascadeInfo) { - if (cascadeTypes != null && cascadeTypes.length > 0) { - cascadeInfo.setTypes(cascadeTypes); - } - } - - /** - * Read an AttributeOverrides if they exist for this embedded bean. - */ - protected void readEmbeddedAttributeOverrides(DeployBeanPropertyAssocOne prop) { - - AttributeOverrides attrOverrides = get(prop, AttributeOverrides.class); - if (attrOverrides != null) { - HashMap propMap = new HashMap(); - AttributeOverride[] aoArray = attrOverrides.value(); - for (int i = 0; i < aoArray.length; i++) { - String propName = aoArray[i].name(); - String columnName = aoArray[i].column().name(); - - propMap.put(propName, columnName); - } - - prop.getDeployEmbedded().putAll(propMap); - } - - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.util.HashMap; + +import javax.persistence.AttributeOverride; +import javax.persistence.AttributeOverrides; +import javax.persistence.CascadeType; + +import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; + +/** + * Base class for reading deployment annotations. + */ +public abstract class AnnotationParser extends AnnotationBase { + + protected final DeployBeanInfo info; + + protected final DeployBeanDescriptor descriptor; + + protected final Class beanType; + + protected boolean validationAnnotations; + + public AnnotationParser(DeployBeanInfo info) { + super(info.getUtil()); + this.info = info; + this.beanType = info.getDescriptor().getBeanType(); + this.descriptor = info.getDescriptor(); + + try { + Class.forName("javax.validation.constraints.NotNull"); + validationAnnotations = true; + } catch (ClassNotFoundException e) { + // javax.validation not in the classpath so don't + // check for NotNull and Size + validationAnnotations = false; + } + } + + /** + * read the deployment annotations. + */ + public abstract void parse(); + + /** + * Helper method to set cascade types to the CascadeInfo on BeanProperty. + */ + protected void setCascadeTypes(CascadeType[] cascadeTypes, BeanCascadeInfo cascadeInfo) { + if (cascadeTypes != null && cascadeTypes.length > 0) { + cascadeInfo.setTypes(cascadeTypes); + } + } + + /** + * Read an AttributeOverrides if they exist for this embedded bean. + */ + protected void readEmbeddedAttributeOverrides(DeployBeanPropertyAssocOne prop) { + + AttributeOverrides attrOverrides = get(prop, AttributeOverrides.class); + if (attrOverrides != null) { + HashMap propMap = new HashMap(); + AttributeOverride[] aoArray = attrOverrides.value(); + for (int i = 0; i < aoArray.length; i++) { + String propName = aoArray[i].name(); + String columnName = aoArray[i].column().name(); + + propMap.put(propName, columnName); + } + + prop.getDeployEmbedded().putAll(propMap); + } + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java index 555717898..f2191918e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java @@ -1,41 +1,41 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import com.avaje.ebean.annotation.Sql; -import com.avaje.ebean.annotation.SqlSelect; -import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta; - -/** - * Read the class level deployment annotations. - */ -public class AnnotationSql extends AnnotationParser { - - public AnnotationSql(DeployBeanInfo info) { - super(info); - } - - public void parse() { - Class cls = descriptor.getBeanType(); - Sql sql = cls.getAnnotation(Sql.class); - if (sql != null) { - setSql(sql); - } - - SqlSelect sqlSelect = cls.getAnnotation(SqlSelect.class); - if (sqlSelect != null) { - setSqlSelect(sqlSelect); - } - } - - private void setSql(Sql sql) { - SqlSelect[] select = sql.select(); - for (int i = 0; i < select.length; i++) { - setSqlSelect(select[i]); - } - } - - private void setSqlSelect(SqlSelect sqlSelect) { - - DRawSqlMeta rawSqlMeta = new DRawSqlMeta(sqlSelect); - descriptor.add(rawSqlMeta); - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import com.avaje.ebean.annotation.Sql; +import com.avaje.ebean.annotation.SqlSelect; +import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta; + +/** + * Read the class level deployment annotations. + */ +public class AnnotationSql extends AnnotationParser { + + public AnnotationSql(DeployBeanInfo info) { + super(info); + } + + public void parse() { + Class cls = descriptor.getBeanType(); + Sql sql = cls.getAnnotation(Sql.class); + if (sql != null) { + setSql(sql); + } + + SqlSelect sqlSelect = cls.getAnnotation(SqlSelect.class); + if (sqlSelect != null) { + setSqlSelect(sqlSelect); + } + } + + private void setSql(Sql sql) { + SqlSelect[] select = sql.select(); + for (int i = 0; i < select.length; i++) { + setSqlSelect(select[i]); + } + } + + private void setSqlSelect(SqlSelect sqlSelect) { + + DRawSqlMeta rawSqlMeta = new DRawSqlMeta(sqlSelect); + descriptor.add(rawSqlMeta); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployBeanInfo.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployBeanInfo.java index 7fad0685b..a85e48cbe 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployBeanInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployBeanInfo.java @@ -1,78 +1,78 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import java.util.HashMap; - -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; -import com.avaje.ebeaninternal.server.query.SqlJoinType; - -/** - * Wraps information about a bean during deployment parsing. - */ -public class DeployBeanInfo { - - /** - * Holds TableJoins for secondary table properties. - */ - private final HashMap tableJoinMap = new HashMap(); - - private final DeployUtil util; - - private final DeployBeanDescriptor descriptor; - - /** - * Create with a DeployUtil and BeanDescriptor. - */ - public DeployBeanInfo(DeployUtil util, DeployBeanDescriptor descriptor) { - this.util = util; - this.descriptor = descriptor; - } - - public String toString() { - return ""+descriptor; - } - - /** - * Return the BeanDescriptor currently being processed. - */ - public DeployBeanDescriptor getDescriptor() { - return descriptor; - } - - /** - * Return the DeployUtil we are using. - */ - public DeployUtil getUtil() { - return util; - } - - /** - * Appropriate TableJoin for a property mapped to a secondary table. - */ - public DeployTableJoin getTableJoin(String tableName) { - - String key = tableName.toLowerCase(); - - DeployTableJoin tableJoin = (DeployTableJoin) tableJoinMap.get(key); - if (tableJoin == null) { - tableJoin = new DeployTableJoin(); - tableJoin.setTable(tableName); - tableJoin.setType(SqlJoinType.INNER); - descriptor.addTableJoin(tableJoin); - - tableJoinMap.put(key, tableJoin); - } - return tableJoin; - } - - /** - * Set a the join alias for a assoc one property. - */ - public void setBeanJoinType(DeployBeanPropertyAssocOne beanProp, boolean outerJoin) { - - DeployTableJoin tableJoin = beanProp.getTableJoin(); - tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER); - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.util.HashMap; + +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; +import com.avaje.ebeaninternal.server.query.SqlJoinType; + +/** + * Wraps information about a bean during deployment parsing. + */ +public class DeployBeanInfo { + + /** + * Holds TableJoins for secondary table properties. + */ + private final HashMap tableJoinMap = new HashMap(); + + private final DeployUtil util; + + private final DeployBeanDescriptor descriptor; + + /** + * Create with a DeployUtil and BeanDescriptor. + */ + public DeployBeanInfo(DeployUtil util, DeployBeanDescriptor descriptor) { + this.util = util; + this.descriptor = descriptor; + } + + public String toString() { + return ""+descriptor; + } + + /** + * Return the BeanDescriptor currently being processed. + */ + public DeployBeanDescriptor getDescriptor() { + return descriptor; + } + + /** + * Return the DeployUtil we are using. + */ + public DeployUtil getUtil() { + return util; + } + + /** + * Appropriate TableJoin for a property mapped to a secondary table. + */ + public DeployTableJoin getTableJoin(String tableName) { + + String key = tableName.toLowerCase(); + + DeployTableJoin tableJoin = (DeployTableJoin) tableJoinMap.get(key); + if (tableJoin == null) { + tableJoin = new DeployTableJoin(); + tableJoin.setTable(tableName); + tableJoin.setType(SqlJoinType.INNER); + descriptor.addTableJoin(tableJoin); + + tableJoinMap.put(key, tableJoin); + } + return tableJoin; + } + + /** + * Set a the join alias for a assoc one property. + */ + public void setBeanJoinType(DeployBeanPropertyAssocOne beanProp, boolean outerJoin) { + + DeployTableJoin tableJoin = beanProp.getTableJoin(); + tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployCreateProperties.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployCreateProperties.java index 024f2bf66..a8438eb5a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployCreateProperties.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployCreateProperties.java @@ -1,427 +1,427 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; - -import javax.persistence.ManyToOne; -import javax.persistence.PersistenceException; -import javax.persistence.Transient; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.avaje.ebean.annotation.ColumnHstore; -import com.avaje.ebeaninternal.server.core.Message; -import com.avaje.ebeaninternal.server.deploy.DetermineManyType; -import com.avaje.ebeaninternal.server.deploy.ManyType; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection; -import com.avaje.ebeaninternal.server.type.CtCompoundType; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.server.type.ScalarTypePostgresHstore; -import com.avaje.ebeaninternal.server.type.TypeManager; -import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse; - -/** - * Create the properties for a bean. - *

- * This also needs to determine if the property is a associated many, associated - * one or normal scalar property. - *

- */ -public class DeployCreateProperties { - - private static final Logger logger = LoggerFactory.getLogger(DeployCreateProperties.class); - - private final DetermineManyType determineManyType; - - private final TypeManager typeManager; - - public DeployCreateProperties(TypeManager typeManager) { - this.typeManager = typeManager; - this.determineManyType = new DetermineManyType(); - } - - /** - * Create the appropriate properties for a bean. - */ - public void createProperties(DeployBeanDescriptor desc) { - - createProperties(desc, desc.getBeanType(), 0); - desc.sortProperties(); - - // check the transient properties... - for (DeployBeanProperty prop : desc.propertiesAll()) { - if (prop.isTransient()) { - if (prop.getWriteMethod() == null || prop.getReadMethod() == null) { - // Typically a helper method ... this is expected - logger.trace("... transient: " + prop.getFullBeanName()); - } else { - // dubious, possible error... - String msg = Message.msg("deploy.property.nofield", desc.getFullName(), prop.getName()); - logger.warn(msg); - } - } - } - } - - /** - * Return true if we should ignore this field. - *

- * We want to ignore ebean internal fields and some others as well. - *

- */ - private boolean ignoreFieldByName(String fieldName) { - if (fieldName.startsWith("_ebean_")) { - // ignore Ebean internal fields - return true; - } - if (fieldName.startsWith("ajc$instance$")) { - // ignore AspectJ internal fields - return true; - } - - // we are interested in this field - return false; - } - - /** - * properties the bean properties from Class. Some of these properties may not map to database - * columns. - */ - private void createProperties(DeployBeanDescriptor desc, Class beanType, int level) { - - boolean scalaObject = desc.isScalaObject(); - - try { - Method[] declaredMethods = beanType.getDeclaredMethods(); - Field[] fields = beanType.getDeclaredFields(); - - for (int i = 0; i < fields.length; i++) { - - Field field = fields[i]; - if (Modifier.isStatic(field.getModifiers())) { - // not interested in static fields - - } else if (Modifier.isTransient(field.getModifiers())) { - // not interested in transient fields - logger.trace("Skipping transient field " + field.getName() + " in " + beanType.getName()); - - } else if (ignoreFieldByName(field.getName())) { - // not interested this field (ebean or aspectJ field) - - } else { - - String fieldName = getFieldName(field, beanType); - String initFieldName = initCap(fieldName); - - Method getter = findGetter(field, initFieldName, declaredMethods, scalaObject); - Method setter = findSetter(field, initFieldName, declaredMethods, scalaObject); - - DeployBeanProperty prop = createProp(desc, field, beanType, getter, setter); - if (prop == null) { - // transient annotation on unsupported type - - } else { - // set a order that gives priority to inherited properties - // push Id/EmbeddedId up and CreatedTimestamp/UpdatedTimestamp down - int sortOverride = prop.getSortOverride(); - prop.setSortOrder((level * 10000 + 100 - i + sortOverride)); - - DeployBeanProperty replaced = desc.addBeanProperty(prop); - if (replaced != null) { - if (replaced.isTransient()) { - // expected for inheritance... - } else { - String msg = "Huh??? property " + prop.getFullBeanName() + " being defined twice"; - msg += " but replaced property was not transient? This is not expected?"; - logger.warn(msg); - } - } - } - } - } - - Class superClass = beanType.getSuperclass(); - - if (!superClass.equals(Object.class)) { - // recursively add any properties in the inheritance hierarchy - // up to the Object.class level... - createProperties(desc, superClass, level + 1); - } - - } catch (PersistenceException ex) { - throw ex; - - } catch (Exception ex) { - throw new PersistenceException(ex); - } - } - - /** - * Make the first letter of the string upper case. - */ - private String initCap(String str) { - if (str.length() > 1) { - return Character.toUpperCase(str.charAt(0)) + str.substring(1); - } else { - // only a single char - return str.toUpperCase(); - } - } - - /** - * Return the bean spec field name (trim of "is" from boolean types) - */ - private String getFieldName(Field field, Class beanType) { - - String name = field.getName(); - - if ((Boolean.class.equals(field.getType()) || boolean.class.equals(field.getType())) && name.startsWith("is") - && name.length() > 2) { - - // it is a boolean type field starting with "is" - char c = name.charAt(2); - if (Character.isUpperCase(c)) { - String msg = "trimming off 'is' from boolean field name " + name + " in class " + beanType.getName(); - logger.info(msg); - - return name.substring(2); - } - } - return name; - } - - /** - * Find a public non-static getter method that matches this field (according to bean-spec rules). - */ - private Method findGetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject) { - - String methGetName = "get" + initFieldName; - String methIsName = "is" + initFieldName; - String scalaGet = field.getName(); - - for (int i = 0; i < declaredMethods.length; i++) { - Method m = declaredMethods[i]; - if ((scalaObject && m.getName().equals(scalaGet)) || m.getName().equals(methGetName) - || m.getName().equals(methIsName)) { - - Class[] params = m.getParameterTypes(); - if (params.length == 0) { - if (field.getType().equals(m.getReturnType())) { - int modifiers = m.getModifiers(); - if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { - // we find it... - return m; - } - } - } - } - } - return null; - } - - /** - * Find a public non-static setter method that matches this field (according to bean-spec rules). - */ - private Method findSetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject) { - - String methSetName = "set" + initFieldName; - String scalaSetName = field.getName() + "_$eq"; - - for (int i = 0; i < declaredMethods.length; i++) { - Method m = declaredMethods[i]; - - if ((scalaObject && m.getName().equals(scalaSetName)) || m.getName().equals(methSetName)) { - - Class[] params = m.getParameterTypes(); - if (params.length == 1 && field.getType().equals(params[0])) { - if (void.class.equals(m.getReturnType())) { - int modifiers = m.getModifiers(); - if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { - return m; - } - } - } - } - } - return null; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private DeployBeanProperty createManyType(DeployBeanDescriptor desc, Class targetType, ManyType manyType) { - - try { - ScalarType scalarType = typeManager.getScalarType(targetType); - if (scalarType != null) { - return new DeployBeanPropertySimpleCollection(desc, targetType, manyType); - } - } catch (NullPointerException e) { - logger.debug("expected non-scalar type" + e.getMessage()); - } - // TODO: Handle Collection of CompoundType and Embedded Type - return new DeployBeanPropertyAssocMany(desc, targetType, manyType); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private DeployBeanProperty createProp(DeployBeanDescriptor desc, Field field) { - - Class propertyType = field.getType(); - - ManyToOne manyToOne = field.getAnnotation(ManyToOne.class); - - if (manyToOne != null){ - Class tt = manyToOne.targetEntity(); - - if (tt != null && !tt.equals(void.class)){ - propertyType = tt; - logger.debug("target type" + tt); - } - } - Class innerType = propertyType; - - String specialTypeKey = getSpecialScalarType(field); - if (specialTypeKey != null) { - ScalarType scalarType = typeManager.getScalarTypeFromKey(specialTypeKey); - if (scalarType == null) { - logger.error("Could not find ScalarType to match key ["+specialTypeKey+"]"); - } else { - return new DeployBeanProperty(desc, propertyType, scalarType, null); - } - } - - // check for Collection type (list, set or map) - ManyType manyType = determineManyType.getManyType(propertyType); - - if (manyType != null) { - // List, Set or Map based object - Class targetType = determineTargetType(field); - if (targetType == null) { - Transient transAnnotation = field.getAnnotation(Transient.class); - if (transAnnotation != null) { - // not supporting this field (generic type used) - return null; - } - logger.warn("Could not find parameter type (via reflection) on " + desc.getFullName() + " " + field.getName()); - } - return createManyType(desc, targetType, manyType); - } - - if (innerType.isEnum() || innerType.isPrimitive()) { - return new DeployBeanProperty(desc, propertyType, null, null); - } - - ScalarType scalarType = typeManager.getScalarType(innerType); - if (scalarType != null) { - return new DeployBeanProperty(desc, propertyType, scalarType, null); - } - - CtCompoundType compoundType = typeManager.getCompoundType(innerType); - if (compoundType != null) { - return new DeployBeanPropertyCompound(desc, propertyType, compoundType, null); - } - - if (isTransientField(field)) { - return null; - } - try { - CheckImmutableResponse checkImmutable = typeManager.checkImmutable(innerType); - if (checkImmutable.isImmutable()) { - if (checkImmutable.isCompoundType()) { - // use reflection to support compound immutable value objects - typeManager.recursiveCreateScalarDataReader(innerType); - compoundType = typeManager.getCompoundType(innerType); - if (compoundType != null) { - return new DeployBeanPropertyCompound(desc, propertyType, compoundType, null); - } - - } else { - // use reflection to support simple immutable value objects - scalarType = typeManager.recursiveCreateScalarTypes(innerType); - return new DeployBeanProperty(desc, propertyType, scalarType, null); - } - } - - return new DeployBeanPropertyAssocOne(desc, propertyType); - - } catch (Exception e) { - logger.error("Error with " + desc + " field:" + field.getName(), e); - return null; - } - } - - private String getSpecialScalarType(Field field) { - - if (field.getAnnotation(ColumnHstore.class) != null) { - return ScalarTypePostgresHstore.KEY; - } - - return null; - } - - private boolean isTransientField(Field field) { - - Transient t = field.getAnnotation(Transient.class); - return (t != null); - } - - private DeployBeanProperty createProp(DeployBeanDescriptor desc, Field field, Class beanType, - Method getter, Method setter) { - - DeployBeanProperty prop = createProp(desc, field); - if (prop == null) { - // transient annotation on unsupported type - return null; - } else { - prop.setOwningType(beanType); - prop.setName(field.getName()); - - // the getter or setter could be null if we are using - // javaagent type enhancement. If we are using subclass - // generation then we do need to find the getter and setter - prop.setReadMethod(getter); - prop.setWriteMethod(setter); - prop.setField(field); - return prop; - } - } - - /** - * Determine the type of the List,Set or Map. Not been set explicitly so determine this from - * ParameterizedType. - */ - private Class determineTargetType(Field field) { - - Type genType = field.getGenericType(); - if (genType instanceof ParameterizedType) { - ParameterizedType ptype = (ParameterizedType) genType; - - Type[] typeArgs = ptype.getActualTypeArguments(); - if (typeArgs.length == 1) { - // probably a Set or List - if (typeArgs[0] instanceof Class) { - return (Class) typeArgs[0]; - } - // throw new RuntimeException("Unexpected Parameterised Type? "+typeArgs[0]); - return null; - } - if (typeArgs.length == 2) { - // this is probably a Map - if (typeArgs[1] instanceof ParameterizedType) { - // not supporting ParameterizedType on Map. - return null; - } - return (Class) typeArgs[1]; - } - } - // if targetType is null, then must be set in annotations - return null; - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import javax.persistence.ManyToOne; +import javax.persistence.PersistenceException; +import javax.persistence.Transient; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebean.annotation.ColumnHstore; +import com.avaje.ebeaninternal.server.core.Message; +import com.avaje.ebeaninternal.server.deploy.DetermineManyType; +import com.avaje.ebeaninternal.server.deploy.ManyType; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection; +import com.avaje.ebeaninternal.server.type.CtCompoundType; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.avaje.ebeaninternal.server.type.ScalarTypePostgresHstore; +import com.avaje.ebeaninternal.server.type.TypeManager; +import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse; + +/** + * Create the properties for a bean. + *

+ * This also needs to determine if the property is a associated many, associated + * one or normal scalar property. + *

+ */ +public class DeployCreateProperties { + + private static final Logger logger = LoggerFactory.getLogger(DeployCreateProperties.class); + + private final DetermineManyType determineManyType; + + private final TypeManager typeManager; + + public DeployCreateProperties(TypeManager typeManager) { + this.typeManager = typeManager; + this.determineManyType = new DetermineManyType(); + } + + /** + * Create the appropriate properties for a bean. + */ + public void createProperties(DeployBeanDescriptor desc) { + + createProperties(desc, desc.getBeanType(), 0); + desc.sortProperties(); + + // check the transient properties... + for (DeployBeanProperty prop : desc.propertiesAll()) { + if (prop.isTransient()) { + if (prop.getWriteMethod() == null || prop.getReadMethod() == null) { + // Typically a helper method ... this is expected + logger.trace("... transient: " + prop.getFullBeanName()); + } else { + // dubious, possible error... + String msg = Message.msg("deploy.property.nofield", desc.getFullName(), prop.getName()); + logger.warn(msg); + } + } + } + } + + /** + * Return true if we should ignore this field. + *

+ * We want to ignore ebean internal fields and some others as well. + *

+ */ + private boolean ignoreFieldByName(String fieldName) { + if (fieldName.startsWith("_ebean_")) { + // ignore Ebean internal fields + return true; + } + if (fieldName.startsWith("ajc$instance$")) { + // ignore AspectJ internal fields + return true; + } + + // we are interested in this field + return false; + } + + /** + * properties the bean properties from Class. Some of these properties may not map to database + * columns. + */ + private void createProperties(DeployBeanDescriptor desc, Class beanType, int level) { + + boolean scalaObject = desc.isScalaObject(); + + try { + Method[] declaredMethods = beanType.getDeclaredMethods(); + Field[] fields = beanType.getDeclaredFields(); + + for (int i = 0; i < fields.length; i++) { + + Field field = fields[i]; + if (Modifier.isStatic(field.getModifiers())) { + // not interested in static fields + + } else if (Modifier.isTransient(field.getModifiers())) { + // not interested in transient fields + logger.trace("Skipping transient field " + field.getName() + " in " + beanType.getName()); + + } else if (ignoreFieldByName(field.getName())) { + // not interested this field (ebean or aspectJ field) + + } else { + + String fieldName = getFieldName(field, beanType); + String initFieldName = initCap(fieldName); + + Method getter = findGetter(field, initFieldName, declaredMethods, scalaObject); + Method setter = findSetter(field, initFieldName, declaredMethods, scalaObject); + + DeployBeanProperty prop = createProp(desc, field, beanType, getter, setter); + if (prop == null) { + // transient annotation on unsupported type + + } else { + // set a order that gives priority to inherited properties + // push Id/EmbeddedId up and CreatedTimestamp/UpdatedTimestamp down + int sortOverride = prop.getSortOverride(); + prop.setSortOrder((level * 10000 + 100 - i + sortOverride)); + + DeployBeanProperty replaced = desc.addBeanProperty(prop); + if (replaced != null) { + if (replaced.isTransient()) { + // expected for inheritance... + } else { + String msg = "Huh??? property " + prop.getFullBeanName() + " being defined twice"; + msg += " but replaced property was not transient? This is not expected?"; + logger.warn(msg); + } + } + } + } + } + + Class superClass = beanType.getSuperclass(); + + if (!superClass.equals(Object.class)) { + // recursively add any properties in the inheritance hierarchy + // up to the Object.class level... + createProperties(desc, superClass, level + 1); + } + + } catch (PersistenceException ex) { + throw ex; + + } catch (Exception ex) { + throw new PersistenceException(ex); + } + } + + /** + * Make the first letter of the string upper case. + */ + private String initCap(String str) { + if (str.length() > 1) { + return Character.toUpperCase(str.charAt(0)) + str.substring(1); + } else { + // only a single char + return str.toUpperCase(); + } + } + + /** + * Return the bean spec field name (trim of "is" from boolean types) + */ + private String getFieldName(Field field, Class beanType) { + + String name = field.getName(); + + if ((Boolean.class.equals(field.getType()) || boolean.class.equals(field.getType())) && name.startsWith("is") + && name.length() > 2) { + + // it is a boolean type field starting with "is" + char c = name.charAt(2); + if (Character.isUpperCase(c)) { + String msg = "trimming off 'is' from boolean field name " + name + " in class " + beanType.getName(); + logger.info(msg); + + return name.substring(2); + } + } + return name; + } + + /** + * Find a public non-static getter method that matches this field (according to bean-spec rules). + */ + private Method findGetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject) { + + String methGetName = "get" + initFieldName; + String methIsName = "is" + initFieldName; + String scalaGet = field.getName(); + + for (int i = 0; i < declaredMethods.length; i++) { + Method m = declaredMethods[i]; + if ((scalaObject && m.getName().equals(scalaGet)) || m.getName().equals(methGetName) + || m.getName().equals(methIsName)) { + + Class[] params = m.getParameterTypes(); + if (params.length == 0) { + if (field.getType().equals(m.getReturnType())) { + int modifiers = m.getModifiers(); + if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { + // we find it... + return m; + } + } + } + } + } + return null; + } + + /** + * Find a public non-static setter method that matches this field (according to bean-spec rules). + */ + private Method findSetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject) { + + String methSetName = "set" + initFieldName; + String scalaSetName = field.getName() + "_$eq"; + + for (int i = 0; i < declaredMethods.length; i++) { + Method m = declaredMethods[i]; + + if ((scalaObject && m.getName().equals(scalaSetName)) || m.getName().equals(methSetName)) { + + Class[] params = m.getParameterTypes(); + if (params.length == 1 && field.getType().equals(params[0])) { + if (void.class.equals(m.getReturnType())) { + int modifiers = m.getModifiers(); + if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { + return m; + } + } + } + } + } + return null; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private DeployBeanProperty createManyType(DeployBeanDescriptor desc, Class targetType, ManyType manyType) { + + try { + ScalarType scalarType = typeManager.getScalarType(targetType); + if (scalarType != null) { + return new DeployBeanPropertySimpleCollection(desc, targetType, manyType); + } + } catch (NullPointerException e) { + logger.debug("expected non-scalar type" + e.getMessage()); + } + // TODO: Handle Collection of CompoundType and Embedded Type + return new DeployBeanPropertyAssocMany(desc, targetType, manyType); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private DeployBeanProperty createProp(DeployBeanDescriptor desc, Field field) { + + Class propertyType = field.getType(); + + ManyToOne manyToOne = field.getAnnotation(ManyToOne.class); + + if (manyToOne != null){ + Class tt = manyToOne.targetEntity(); + + if (tt != null && !tt.equals(void.class)){ + propertyType = tt; + logger.debug("target type" + tt); + } + } + Class innerType = propertyType; + + String specialTypeKey = getSpecialScalarType(field); + if (specialTypeKey != null) { + ScalarType scalarType = typeManager.getScalarTypeFromKey(specialTypeKey); + if (scalarType == null) { + logger.error("Could not find ScalarType to match key ["+specialTypeKey+"]"); + } else { + return new DeployBeanProperty(desc, propertyType, scalarType, null); + } + } + + // check for Collection type (list, set or map) + ManyType manyType = determineManyType.getManyType(propertyType); + + if (manyType != null) { + // List, Set or Map based object + Class targetType = determineTargetType(field); + if (targetType == null) { + Transient transAnnotation = field.getAnnotation(Transient.class); + if (transAnnotation != null) { + // not supporting this field (generic type used) + return null; + } + logger.warn("Could not find parameter type (via reflection) on " + desc.getFullName() + " " + field.getName()); + } + return createManyType(desc, targetType, manyType); + } + + if (innerType.isEnum() || innerType.isPrimitive()) { + return new DeployBeanProperty(desc, propertyType, null, null); + } + + ScalarType scalarType = typeManager.getScalarType(innerType); + if (scalarType != null) { + return new DeployBeanProperty(desc, propertyType, scalarType, null); + } + + CtCompoundType compoundType = typeManager.getCompoundType(innerType); + if (compoundType != null) { + return new DeployBeanPropertyCompound(desc, propertyType, compoundType, null); + } + + if (isTransientField(field)) { + return null; + } + try { + CheckImmutableResponse checkImmutable = typeManager.checkImmutable(innerType); + if (checkImmutable.isImmutable()) { + if (checkImmutable.isCompoundType()) { + // use reflection to support compound immutable value objects + typeManager.recursiveCreateScalarDataReader(innerType); + compoundType = typeManager.getCompoundType(innerType); + if (compoundType != null) { + return new DeployBeanPropertyCompound(desc, propertyType, compoundType, null); + } + + } else { + // use reflection to support simple immutable value objects + scalarType = typeManager.recursiveCreateScalarTypes(innerType); + return new DeployBeanProperty(desc, propertyType, scalarType, null); + } + } + + return new DeployBeanPropertyAssocOne(desc, propertyType); + + } catch (Exception e) { + logger.error("Error with " + desc + " field:" + field.getName(), e); + return null; + } + } + + private String getSpecialScalarType(Field field) { + + if (field.getAnnotation(ColumnHstore.class) != null) { + return ScalarTypePostgresHstore.KEY; + } + + return null; + } + + private boolean isTransientField(Field field) { + + Transient t = field.getAnnotation(Transient.class); + return (t != null); + } + + private DeployBeanProperty createProp(DeployBeanDescriptor desc, Field field, Class beanType, + Method getter, Method setter) { + + DeployBeanProperty prop = createProp(desc, field); + if (prop == null) { + // transient annotation on unsupported type + return null; + } else { + prop.setOwningType(beanType); + prop.setName(field.getName()); + + // the getter or setter could be null if we are using + // javaagent type enhancement. If we are using subclass + // generation then we do need to find the getter and setter + prop.setReadMethod(getter); + prop.setWriteMethod(setter); + prop.setField(field); + return prop; + } + } + + /** + * Determine the type of the List,Set or Map. Not been set explicitly so determine this from + * ParameterizedType. + */ + private Class determineTargetType(Field field) { + + Type genType = field.getGenericType(); + if (genType instanceof ParameterizedType) { + ParameterizedType ptype = (ParameterizedType) genType; + + Type[] typeArgs = ptype.getActualTypeArguments(); + if (typeArgs.length == 1) { + // probably a Set or List + if (typeArgs[0] instanceof Class) { + return (Class) typeArgs[0]; + } + // throw new RuntimeException("Unexpected Parameterised Type? "+typeArgs[0]); + return null; + } + if (typeArgs.length == 2) { + // this is probably a Map + if (typeArgs[1] instanceof ParameterizedType) { + // not supporting ParameterizedType on Map. + return null; + } + return (Class) typeArgs[1]; + } + } + // if targetType is null, then must be set in annotations + return null; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInherit.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInherit.java index 1bc6af2b8..158866e0e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInherit.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInherit.java @@ -1,165 +1,165 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import java.lang.annotation.Annotation; -import java.sql.Types; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import javax.persistence.DiscriminatorColumn; -import javax.persistence.DiscriminatorType; -import javax.persistence.DiscriminatorValue; -import javax.persistence.Inheritance; - -import com.avaje.ebeaninternal.server.core.BootupClasses; -import com.avaje.ebeaninternal.server.deploy.InheritInfo; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; - -/** - * Builds the InheritInfo deployment information. - */ -public class DeployInherit { - - private final Map, DeployInheritInfo> deployMap = new LinkedHashMap, DeployInheritInfo>(); - - private final Map, InheritInfo> finalMap = new LinkedHashMap, InheritInfo>(); - - private final BootupClasses bootupClasses; - - /** - * Create the InheritInfoDeploy. - */ - public DeployInherit(BootupClasses bootupClasses) { - this.bootupClasses = bootupClasses; - initialise(); - } - - public void process(DeployBeanDescriptor desc) { - InheritInfo inheritInfo = finalMap.get(desc.getBeanType()); - desc.setInheritInfo(inheritInfo); - } - - private void initialise() { - List> entityList = bootupClasses.getEntities(); - - findInheritClasses(entityList); - buildDeployTree(); - buildFinalTree(); - } - - private void findInheritClasses(List> entityList) { - - // go through each class and initialise the info object... - for (Class cls : entityList) { - if (isInheritanceClass(cls)) { - DeployInheritInfo info = createInfo(cls); - deployMap.put(cls, info); - } - } - } - - private void buildDeployTree() { - - for (DeployInheritInfo info : deployMap.values()) { - if (!info.isRoot()) { - DeployInheritInfo parent = getInfo(info.getParent()); - parent.addChild(info); - } - } - } - - private void buildFinalTree() { - - for (DeployInheritInfo deploy : deployMap.values()) { - if (deploy.isRoot()) { - // build tree top down... - createFinalInfo(null, null, deploy); - } - } - } - - private InheritInfo createFinalInfo(InheritInfo root, InheritInfo parent, DeployInheritInfo deploy) { - - InheritInfo node = new InheritInfo(root, parent, deploy); - if (parent != null) { - parent.addChild(node); - } - finalMap.put(node.getType(), node); - - if (root == null) { - root = node; - } - - // buildFinalChildren(root, child, deploy); - for (DeployInheritInfo childDeploy : deploy.children()) { - createFinalInfo(root, node, childDeploy); - } - - return node; - } - - /** - * Build the InheritInfo for a given class. - */ - private DeployInheritInfo getInfo(Class cls) { - return deployMap.get(cls); - } - - private DeployInheritInfo createInfo(Class cls) { - - DeployInheritInfo info = new DeployInheritInfo(cls); - - Class parent = findParent(cls); - if (parent != null) { - info.setParent(parent); - } else { - // its the root of inheritance tree... - } - - Inheritance ia = (Inheritance) cls.getAnnotation(Inheritance.class); - if (ia != null) { - ia.strategy(); - } - DiscriminatorColumn da = (DiscriminatorColumn) cls.getAnnotation(DiscriminatorColumn.class); - if (da != null) { - // lowercase the discriminator column for RawSql and JSON - info.setDiscriminatorColumn(da.name().toLowerCase()); - DiscriminatorType discriminatorType = da.discriminatorType(); - if (discriminatorType.equals(DiscriminatorType.INTEGER)){ - info.setDiscriminatorType(Types.INTEGER); - } else { - info.setDiscriminatorType(Types.VARCHAR); - } - info.setDiscriminatorLength(da.length()); - } - - DiscriminatorValue dv = (DiscriminatorValue) cls.getAnnotation(DiscriminatorValue.class); - if (dv != null) { - info.setDiscriminatorValue(dv.value()); - } - - return info; - } - - private Class findParent(Class cls) { - Class superCls = cls.getSuperclass(); - if (isInheritanceClass(superCls)) { - return superCls; - } else { - return null; - } - } - - private boolean isInheritanceClass(Class cls) { - if (cls.equals(Object.class)) { - return false; - } - Annotation a = cls.getAnnotation(Inheritance.class); - if (a != null) { - return true; - } - // search up the inheritance heirarchy - return isInheritanceClass(cls.getSuperclass()); - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.lang.annotation.Annotation; +import java.sql.Types; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorType; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Inheritance; + +import com.avaje.ebeaninternal.server.core.BootupClasses; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; + +/** + * Builds the InheritInfo deployment information. + */ +public class DeployInherit { + + private final Map, DeployInheritInfo> deployMap = new LinkedHashMap, DeployInheritInfo>(); + + private final Map, InheritInfo> finalMap = new LinkedHashMap, InheritInfo>(); + + private final BootupClasses bootupClasses; + + /** + * Create the InheritInfoDeploy. + */ + public DeployInherit(BootupClasses bootupClasses) { + this.bootupClasses = bootupClasses; + initialise(); + } + + public void process(DeployBeanDescriptor desc) { + InheritInfo inheritInfo = finalMap.get(desc.getBeanType()); + desc.setInheritInfo(inheritInfo); + } + + private void initialise() { + List> entityList = bootupClasses.getEntities(); + + findInheritClasses(entityList); + buildDeployTree(); + buildFinalTree(); + } + + private void findInheritClasses(List> entityList) { + + // go through each class and initialise the info object... + for (Class cls : entityList) { + if (isInheritanceClass(cls)) { + DeployInheritInfo info = createInfo(cls); + deployMap.put(cls, info); + } + } + } + + private void buildDeployTree() { + + for (DeployInheritInfo info : deployMap.values()) { + if (!info.isRoot()) { + DeployInheritInfo parent = getInfo(info.getParent()); + parent.addChild(info); + } + } + } + + private void buildFinalTree() { + + for (DeployInheritInfo deploy : deployMap.values()) { + if (deploy.isRoot()) { + // build tree top down... + createFinalInfo(null, null, deploy); + } + } + } + + private InheritInfo createFinalInfo(InheritInfo root, InheritInfo parent, DeployInheritInfo deploy) { + + InheritInfo node = new InheritInfo(root, parent, deploy); + if (parent != null) { + parent.addChild(node); + } + finalMap.put(node.getType(), node); + + if (root == null) { + root = node; + } + + // buildFinalChildren(root, child, deploy); + for (DeployInheritInfo childDeploy : deploy.children()) { + createFinalInfo(root, node, childDeploy); + } + + return node; + } + + /** + * Build the InheritInfo for a given class. + */ + private DeployInheritInfo getInfo(Class cls) { + return deployMap.get(cls); + } + + private DeployInheritInfo createInfo(Class cls) { + + DeployInheritInfo info = new DeployInheritInfo(cls); + + Class parent = findParent(cls); + if (parent != null) { + info.setParent(parent); + } else { + // its the root of inheritance tree... + } + + Inheritance ia = (Inheritance) cls.getAnnotation(Inheritance.class); + if (ia != null) { + ia.strategy(); + } + DiscriminatorColumn da = (DiscriminatorColumn) cls.getAnnotation(DiscriminatorColumn.class); + if (da != null) { + // lowercase the discriminator column for RawSql and JSON + info.setDiscriminatorColumn(da.name().toLowerCase()); + DiscriminatorType discriminatorType = da.discriminatorType(); + if (discriminatorType.equals(DiscriminatorType.INTEGER)){ + info.setDiscriminatorType(Types.INTEGER); + } else { + info.setDiscriminatorType(Types.VARCHAR); + } + info.setDiscriminatorLength(da.length()); + } + + DiscriminatorValue dv = (DiscriminatorValue) cls.getAnnotation(DiscriminatorValue.class); + if (dv != null) { + info.setDiscriminatorValue(dv.value()); + } + + return info; + } + + private Class findParent(Class cls) { + Class superCls = cls.getSuperclass(); + if (isInheritanceClass(superCls)) { + return superCls; + } else { + return null; + } + } + + private boolean isInheritanceClass(Class cls) { + if (cls.equals(Object.class)) { + return false; + } + Annotation a = cls.getAnnotation(Inheritance.class); + if (a != null) { + return true; + } + // search up the inheritance heirarchy + return isInheritanceClass(cls.getSuperclass()); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInheritInfo.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInheritInfo.java index fca767475..95df113f8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInheritInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInheritInfo.java @@ -1,263 +1,263 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import java.sql.Types; -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.server.deploy.InheritInfo; - -/** - * Represents a node in the Inheritance tree. - * Holds information regarding Super Subclass support. - */ -public class DeployInheritInfo { - - /** - * the default discriminator column according to the JPA 1.0 spec. - */ - private static final String JPA_DEFAULT_DISCRIM_COLUMN = "dtype"; - - private int discriminatorLength; - - private int discriminatorType; - - private String discriminatorStringValue; - private Object discriminatorObjectValue; - - private String discriminatorColumn; - - private String discriminatorWhere; - - private Class type; - - private Class parent; - - private ArrayList children = new ArrayList(); - - /** - * Create for a given type. - */ - public DeployInheritInfo(Class type){ - this.type = type; - } - - /** - * return the type. - */ - public Class getType() { - return type; - } - - /** - * Return the type of the root object. - */ - public Class getParent() { - return parent; - } - - /** - * Set the type of the root object. - */ - public void setParent(Class parent) { - this.parent = parent; - } - - /** - * Return true if this is abstract node. - */ - public boolean isAbstract() { - return (discriminatorObjectValue == null); - } - - /** - * Return true if this is the root node. - */ - public boolean isRoot(){ - return parent == null; - } - - /** - * Return the child nodes. - */ - public List children() { - return children; - } - - /** - * Add a child node. - */ - public void addChild(DeployInheritInfo childInfo){ - children.add(childInfo); - } - - /** - * Return the derived where for the discriminator. - */ - public String getDiscriminatorWhere() { - return discriminatorWhere; - } - - /** - * Set the derived where for the discriminator. - */ - public void setDiscriminatorWhere(String discriminatorWhere) { - this.discriminatorWhere = discriminatorWhere; - } - - /** - * Return the column name of the discriminator. - */ - public String getDiscriminatorColumn(InheritInfo parent) { - if (discriminatorColumn == null){ - if (parent == null){ - discriminatorColumn = JPA_DEFAULT_DISCRIM_COLUMN; - } else { - discriminatorColumn = parent.getDiscriminatorColumn(); - } - } - return discriminatorColumn; - } - - /** - * Set the column name of the discriminator. - */ - public void setDiscriminatorColumn(String discriminatorColumn) { - this.discriminatorColumn = discriminatorColumn; - } - - public int getDiscriminatorLength(InheritInfo parent) { - if (discriminatorLength == 0){ - if (parent == null){ - discriminatorLength = 10; - } else { - discriminatorLength = parent.getDiscriminatorLength(); - } - } - return discriminatorLength; - } - - /** - * Return the sql type of the discriminator value. - */ - public int getDiscriminatorType(InheritInfo parent) { - if (discriminatorType == 0){ - if (parent == null){ - discriminatorType = Types.VARCHAR; - } else { - discriminatorType = parent.getDiscriminatorType(); - } - } - return discriminatorType; - } - - /** - * Set the sql type of the discriminator. - */ - public void setDiscriminatorType(int discriminatorType) { - this.discriminatorType = discriminatorType; - } - - /** - * Return the length of the discriminator column. - */ - public int getDiscriminatorLength() { - return discriminatorLength; - } - - /** - * Set the length of the discriminator column. - */ - public void setDiscriminatorLength(int discriminatorLength) { - this.discriminatorLength = discriminatorLength; - } - - /** - * Return the discriminator value for this node. - */ - public Object getDiscriminatorObjectValue() { - return discriminatorObjectValue; - } - - public String getDiscriminatorStringValue() { - return discriminatorStringValue; - } - - /** - * Set the discriminator value for this node. - */ - public void setDiscriminatorValue(String value) { - if (value != null){ - value = value.trim(); - if (value.length() == 0){ - value = null; - } else { - discriminatorStringValue = value; - // convert the value if desired - if (discriminatorType == Types.INTEGER){ - this.discriminatorObjectValue = Integer.valueOf(value.toString()); - } else { - this.discriminatorObjectValue = value; - } - } - } - } - - public String getWhere() { - - List discList = new ArrayList(); - - appendDiscriminator(discList); - - return buildWhereLiteral(discList); - } - - private void appendDiscriminator(List list) { - if (discriminatorObjectValue != null){ - list.add(discriminatorObjectValue); - } - for (DeployInheritInfo child : children) { - child.appendDiscriminator(list); - } - } - - private String buildWhereLiteral(List discList) { - int size = discList.size(); - if (size == 0){ - return ""; - } - StringBuilder sb = new StringBuilder(); - sb.append(discriminatorColumn); - if (size == 1){ - sb.append(" = "); - } else { - sb.append(" in ("); - } - for (int i = 0; i < discList.size(); i++) { - appendSqlLiteralValue(i, discList.get(i), sb); - } - if (size > 1){ - sb.append(")"); - } - return sb.toString(); - } - - private void appendSqlLiteralValue(int count, Object value, StringBuilder sb) { - if (count > 0){ - sb.append(","); - } - if (value instanceof String){ - sb.append("'").append(value).append("'"); - } else { - sb.append(value); - } - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("InheritInfo[").append(type.getName()).append("]"); - sb.append(" root[").append(parent.getName()).append("]"); - sb.append(" disValue[").append(discriminatorStringValue).append("]"); - return sb.toString(); - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebeaninternal.server.deploy.InheritInfo; + +/** + * Represents a node in the Inheritance tree. + * Holds information regarding Super Subclass support. + */ +public class DeployInheritInfo { + + /** + * the default discriminator column according to the JPA 1.0 spec. + */ + private static final String JPA_DEFAULT_DISCRIM_COLUMN = "dtype"; + + private int discriminatorLength; + + private int discriminatorType; + + private String discriminatorStringValue; + private Object discriminatorObjectValue; + + private String discriminatorColumn; + + private String discriminatorWhere; + + private Class type; + + private Class parent; + + private ArrayList children = new ArrayList(); + + /** + * Create for a given type. + */ + public DeployInheritInfo(Class type){ + this.type = type; + } + + /** + * return the type. + */ + public Class getType() { + return type; + } + + /** + * Return the type of the root object. + */ + public Class getParent() { + return parent; + } + + /** + * Set the type of the root object. + */ + public void setParent(Class parent) { + this.parent = parent; + } + + /** + * Return true if this is abstract node. + */ + public boolean isAbstract() { + return (discriminatorObjectValue == null); + } + + /** + * Return true if this is the root node. + */ + public boolean isRoot(){ + return parent == null; + } + + /** + * Return the child nodes. + */ + public List children() { + return children; + } + + /** + * Add a child node. + */ + public void addChild(DeployInheritInfo childInfo){ + children.add(childInfo); + } + + /** + * Return the derived where for the discriminator. + */ + public String getDiscriminatorWhere() { + return discriminatorWhere; + } + + /** + * Set the derived where for the discriminator. + */ + public void setDiscriminatorWhere(String discriminatorWhere) { + this.discriminatorWhere = discriminatorWhere; + } + + /** + * Return the column name of the discriminator. + */ + public String getDiscriminatorColumn(InheritInfo parent) { + if (discriminatorColumn == null){ + if (parent == null){ + discriminatorColumn = JPA_DEFAULT_DISCRIM_COLUMN; + } else { + discriminatorColumn = parent.getDiscriminatorColumn(); + } + } + return discriminatorColumn; + } + + /** + * Set the column name of the discriminator. + */ + public void setDiscriminatorColumn(String discriminatorColumn) { + this.discriminatorColumn = discriminatorColumn; + } + + public int getDiscriminatorLength(InheritInfo parent) { + if (discriminatorLength == 0){ + if (parent == null){ + discriminatorLength = 10; + } else { + discriminatorLength = parent.getDiscriminatorLength(); + } + } + return discriminatorLength; + } + + /** + * Return the sql type of the discriminator value. + */ + public int getDiscriminatorType(InheritInfo parent) { + if (discriminatorType == 0){ + if (parent == null){ + discriminatorType = Types.VARCHAR; + } else { + discriminatorType = parent.getDiscriminatorType(); + } + } + return discriminatorType; + } + + /** + * Set the sql type of the discriminator. + */ + public void setDiscriminatorType(int discriminatorType) { + this.discriminatorType = discriminatorType; + } + + /** + * Return the length of the discriminator column. + */ + public int getDiscriminatorLength() { + return discriminatorLength; + } + + /** + * Set the length of the discriminator column. + */ + public void setDiscriminatorLength(int discriminatorLength) { + this.discriminatorLength = discriminatorLength; + } + + /** + * Return the discriminator value for this node. + */ + public Object getDiscriminatorObjectValue() { + return discriminatorObjectValue; + } + + public String getDiscriminatorStringValue() { + return discriminatorStringValue; + } + + /** + * Set the discriminator value for this node. + */ + public void setDiscriminatorValue(String value) { + if (value != null){ + value = value.trim(); + if (value.length() == 0){ + value = null; + } else { + discriminatorStringValue = value; + // convert the value if desired + if (discriminatorType == Types.INTEGER){ + this.discriminatorObjectValue = Integer.valueOf(value.toString()); + } else { + this.discriminatorObjectValue = value; + } + } + } + } + + public String getWhere() { + + List discList = new ArrayList(); + + appendDiscriminator(discList); + + return buildWhereLiteral(discList); + } + + private void appendDiscriminator(List list) { + if (discriminatorObjectValue != null){ + list.add(discriminatorObjectValue); + } + for (DeployInheritInfo child : children) { + child.appendDiscriminator(list); + } + } + + private String buildWhereLiteral(List discList) { + int size = discList.size(); + if (size == 0){ + return ""; + } + StringBuilder sb = new StringBuilder(); + sb.append(discriminatorColumn); + if (size == 1){ + sb.append(" = "); + } else { + sb.append(" in ("); + } + for (int i = 0; i < discList.size(); i++) { + appendSqlLiteralValue(i, discList.get(i), sb); + } + if (size > 1){ + sb.append(")"); + } + return sb.toString(); + } + + private void appendSqlLiteralValue(int count, Object value, StringBuilder sb) { + if (count > 0){ + sb.append(","); + } + if (value instanceof String){ + sb.append("'").append(value).append("'"); + } else { + sb.append(value); + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("InheritInfo[").append(type.getName()).append("]"); + sb.append(" root[").append(parent.getName()).append("]"); + sb.append(" disValue[").append(discriminatorStringValue).append("]"); + return sb.toString(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java index 252bcd51a..4589a4e46 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java @@ -1,228 +1,228 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import java.sql.Types; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.PersistenceException; - -import com.avaje.ebean.config.EncryptDeploy; -import com.avaje.ebean.config.EncryptDeployManager; -import com.avaje.ebean.config.EncryptKeyManager; -import com.avaje.ebean.config.Encryptor; -import com.avaje.ebean.config.NamingConvention; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebean.config.TableName; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; -import com.avaje.ebeaninternal.server.type.DataEncryptSupport; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.server.type.ScalarTypeEnumStandard; -import com.avaje.ebeaninternal.server.type.SimpleAesEncryptor; -import com.avaje.ebeaninternal.server.type.TypeManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Utility object to help processing deployment information. - */ -public class DeployUtil { - - private static final Logger logger = LoggerFactory.getLogger(DeployUtil.class); - - - - /** - * Assumes CLOB rather than LONGVARCHAR. - */ - private static final int dbCLOBType = Types.CLOB; - - /** - * Assumes BLOB rather than LONGVARBINARY. This should probably be - * configurable. - */ - private static final int dbBLOBType = Types.BLOB; - - private final NamingConvention namingConvention; - - private final TypeManager typeManager; - - private final String manyToManyAlias; - - private final DatabasePlatform dbPlatform; - - private final EncryptDeployManager encryptDeployManager; - - private final EncryptKeyManager encryptKeyManager; - - private final Encryptor bytesEncryptor; - - public DeployUtil(TypeManager typeMgr, ServerConfig serverConfig) { - - this.typeManager = typeMgr; - this.namingConvention = serverConfig.getNamingConvention(); - this.dbPlatform = serverConfig.getDatabasePlatform(); - this.encryptDeployManager = serverConfig.getEncryptDeployManager(); - this.encryptKeyManager = serverConfig.getEncryptKeyManager(); - - Encryptor be = serverConfig.getEncryptor(); - this.bytesEncryptor = be != null ? be : new SimpleAesEncryptor(); - - // this alias is used for ManyToMany lazy loading queries - this.manyToManyAlias = "zzzzzz"; - } - - public TypeManager getTypeManager() { - return typeManager; - } - - public DatabasePlatform getDbPlatform() { - return dbPlatform; - } - - public NamingConvention getNamingConvention() { - return namingConvention; - } - - /** - * Check that the EncryptKeyManager has been defined. - */ - public void checkEncryptKeyManagerDefined(String fullPropName) { - if (encryptKeyManager == null){ - String msg = "Using encryption on "+fullPropName+" but no EncryptKeyManager defined!"; - throw new PersistenceException(msg); - } - } - - public EncryptDeploy getEncryptDeploy(TableName table, String column) { - if (encryptDeployManager == null){ - return EncryptDeploy.ANNOTATION; - } - return encryptDeployManager.getEncryptDeploy(table, column); - } - - public DataEncryptSupport createDataEncryptSupport(String table, String column) { - return new DataEncryptSupport(encryptKeyManager, bytesEncryptor, table, column); - } - - /** - * Return the table alias used for ManyToMany joins. - */ - public String getManyToManyAlias() { - return manyToManyAlias; - } - - public ScalarType setEnumScalarType(Enumerated enumerated, DeployBeanProperty prop) { - - Class enumType = prop.getPropertyType(); - if (!enumType.isEnum()) { - throw new IllegalArgumentException("Class ["+enumType+"] is Not a Enum?"); - } - ScalarType scalarType = typeManager.getScalarType(enumType); - if (scalarType == null) { - // see if it has a Mapping in avaje.properties - scalarType = typeManager.createEnumScalarType(enumType); - if (scalarType == null){ - // use JPA normal Enum type (without mapping) - EnumType type = enumerated != null? enumerated.value(): null; - scalarType = createEnumScalarTypePerSpec(enumType, type, prop.getDbType()); - } - - typeManager.add(scalarType); - } - prop.setScalarType(scalarType); - prop.setDbType(scalarType.getJdbcType()); - return scalarType; - } - - private ScalarType createEnumScalarTypePerSpec(Class enumType, EnumType type, int dbType) { - - if (type == null) { - // default as per spec is ORDINAL - return new ScalarTypeEnumStandard.OrdinalEnum(enumType); - - } else if (type == EnumType.ORDINAL) { - return new ScalarTypeEnumStandard.OrdinalEnum(enumType); - - } else { - return new ScalarTypeEnumStandard.StringEnum(enumType); - } - } - - /** - * Find the ScalarType for this property. - *

- * This determines if there is a conversion required from the logical (bean) - * type to a DB (jdbc) type. This is the case for java.util.Date etc. - *

- */ - public void setScalarType(DeployBeanProperty property) { - - if (property.getScalarType() != null){ - // already has a ScalarType assigned. - // this will be an Enum type... - return; - } - if (property instanceof DeployBeanPropertyCompound){ - // compound properties have a CvoInternalType instead - return; - } - - ScalarType scalarType = getScalarType(property); - if (scalarType != null){ - // set the jdbc type this maps to - - property.setDbType(scalarType.getJdbcType()); - property.setScalarType(scalarType); - } - } - - private ScalarType getScalarType(DeployBeanProperty property) { - - // Note that Temporal types already have dbType - // set via annotations - Class propType = property.getPropertyType(); - ScalarType scalarType = typeManager.getScalarType(propType, property.getDbType()); - if (scalarType != null) { - return scalarType; - } - - String msg = property.getFullBeanName()+" has no ScalarType - type[" + propType.getName() + "]"; - if (!property.isTransient()){ - throw new PersistenceException(msg); - - } else { - // this is ok... - logger.trace("... transient property "+msg); - return null; - } - } - - /** - * This property is marked as a Lob object. - */ - public void setLobType(DeployBeanProperty prop) { - - // is String or byte[] ? used to determine if its a CLOB or BLOB - Class type = prop.getPropertyType(); - - // this also sets the lob flag on DeployBeanProperty - int lobType = isClobType(type) ? dbCLOBType : dbBLOBType; - - ScalarType scalarType = typeManager.getScalarType(type, lobType); - if (scalarType == null) { - // this should never occur actually - throw new RuntimeException("No ScalarType for LOB type ["+type+"] ["+lobType+"]"); - } - prop.setDbType(lobType); - prop.setScalarType(scalarType); - } - - public boolean isClobType(Class type){ - if (type.equals(String.class)){ - return true; - } - return false; - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.sql.Types; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.PersistenceException; + +import com.avaje.ebean.config.EncryptDeploy; +import com.avaje.ebean.config.EncryptDeployManager; +import com.avaje.ebean.config.EncryptKeyManager; +import com.avaje.ebean.config.Encryptor; +import com.avaje.ebean.config.NamingConvention; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.config.TableName; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; +import com.avaje.ebeaninternal.server.type.DataEncryptSupport; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.avaje.ebeaninternal.server.type.ScalarTypeEnumStandard; +import com.avaje.ebeaninternal.server.type.SimpleAesEncryptor; +import com.avaje.ebeaninternal.server.type.TypeManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility object to help processing deployment information. + */ +public class DeployUtil { + + private static final Logger logger = LoggerFactory.getLogger(DeployUtil.class); + + + + /** + * Assumes CLOB rather than LONGVARCHAR. + */ + private static final int dbCLOBType = Types.CLOB; + + /** + * Assumes BLOB rather than LONGVARBINARY. This should probably be + * configurable. + */ + private static final int dbBLOBType = Types.BLOB; + + private final NamingConvention namingConvention; + + private final TypeManager typeManager; + + private final String manyToManyAlias; + + private final DatabasePlatform dbPlatform; + + private final EncryptDeployManager encryptDeployManager; + + private final EncryptKeyManager encryptKeyManager; + + private final Encryptor bytesEncryptor; + + public DeployUtil(TypeManager typeMgr, ServerConfig serverConfig) { + + this.typeManager = typeMgr; + this.namingConvention = serverConfig.getNamingConvention(); + this.dbPlatform = serverConfig.getDatabasePlatform(); + this.encryptDeployManager = serverConfig.getEncryptDeployManager(); + this.encryptKeyManager = serverConfig.getEncryptKeyManager(); + + Encryptor be = serverConfig.getEncryptor(); + this.bytesEncryptor = be != null ? be : new SimpleAesEncryptor(); + + // this alias is used for ManyToMany lazy loading queries + this.manyToManyAlias = "zzzzzz"; + } + + public TypeManager getTypeManager() { + return typeManager; + } + + public DatabasePlatform getDbPlatform() { + return dbPlatform; + } + + public NamingConvention getNamingConvention() { + return namingConvention; + } + + /** + * Check that the EncryptKeyManager has been defined. + */ + public void checkEncryptKeyManagerDefined(String fullPropName) { + if (encryptKeyManager == null){ + String msg = "Using encryption on "+fullPropName+" but no EncryptKeyManager defined!"; + throw new PersistenceException(msg); + } + } + + public EncryptDeploy getEncryptDeploy(TableName table, String column) { + if (encryptDeployManager == null){ + return EncryptDeploy.ANNOTATION; + } + return encryptDeployManager.getEncryptDeploy(table, column); + } + + public DataEncryptSupport createDataEncryptSupport(String table, String column) { + return new DataEncryptSupport(encryptKeyManager, bytesEncryptor, table, column); + } + + /** + * Return the table alias used for ManyToMany joins. + */ + public String getManyToManyAlias() { + return manyToManyAlias; + } + + public ScalarType setEnumScalarType(Enumerated enumerated, DeployBeanProperty prop) { + + Class enumType = prop.getPropertyType(); + if (!enumType.isEnum()) { + throw new IllegalArgumentException("Class ["+enumType+"] is Not a Enum?"); + } + ScalarType scalarType = typeManager.getScalarType(enumType); + if (scalarType == null) { + // see if it has a Mapping in avaje.properties + scalarType = typeManager.createEnumScalarType(enumType); + if (scalarType == null){ + // use JPA normal Enum type (without mapping) + EnumType type = enumerated != null? enumerated.value(): null; + scalarType = createEnumScalarTypePerSpec(enumType, type, prop.getDbType()); + } + + typeManager.add(scalarType); + } + prop.setScalarType(scalarType); + prop.setDbType(scalarType.getJdbcType()); + return scalarType; + } + + private ScalarType createEnumScalarTypePerSpec(Class enumType, EnumType type, int dbType) { + + if (type == null) { + // default as per spec is ORDINAL + return new ScalarTypeEnumStandard.OrdinalEnum(enumType); + + } else if (type == EnumType.ORDINAL) { + return new ScalarTypeEnumStandard.OrdinalEnum(enumType); + + } else { + return new ScalarTypeEnumStandard.StringEnum(enumType); + } + } + + /** + * Find the ScalarType for this property. + *

+ * This determines if there is a conversion required from the logical (bean) + * type to a DB (jdbc) type. This is the case for java.util.Date etc. + *

+ */ + public void setScalarType(DeployBeanProperty property) { + + if (property.getScalarType() != null){ + // already has a ScalarType assigned. + // this will be an Enum type... + return; + } + if (property instanceof DeployBeanPropertyCompound){ + // compound properties have a CvoInternalType instead + return; + } + + ScalarType scalarType = getScalarType(property); + if (scalarType != null){ + // set the jdbc type this maps to + + property.setDbType(scalarType.getJdbcType()); + property.setScalarType(scalarType); + } + } + + private ScalarType getScalarType(DeployBeanProperty property) { + + // Note that Temporal types already have dbType + // set via annotations + Class propType = property.getPropertyType(); + ScalarType scalarType = typeManager.getScalarType(propType, property.getDbType()); + if (scalarType != null) { + return scalarType; + } + + String msg = property.getFullBeanName()+" has no ScalarType - type[" + propType.getName() + "]"; + if (!property.isTransient()){ + throw new PersistenceException(msg); + + } else { + // this is ok... + logger.trace("... transient property "+msg); + return null; + } + } + + /** + * This property is marked as a Lob object. + */ + public void setLobType(DeployBeanProperty prop) { + + // is String or byte[] ? used to determine if its a CLOB or BLOB + Class type = prop.getPropertyType(); + + // this also sets the lob flag on DeployBeanProperty + int lobType = isClobType(type) ? dbCLOBType : dbBLOBType; + + ScalarType scalarType = typeManager.getScalarType(type, lobType); + if (scalarType == null) { + // this should never occur actually + throw new RuntimeException("No ScalarType for LOB type ["+type+"] ["+lobType+"]"); + } + prop.setDbType(lobType); + prop.setScalarType(scalarType); + } + + public boolean isClobType(Class type){ + if (type.equals(String.class)){ + return true; + } + return false; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/ReadAnnotations.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/ReadAnnotations.java index f4fb04484..eaafc12dd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/ReadAnnotations.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/ReadAnnotations.java @@ -1,58 +1,58 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; - - -/** - * Read the deployment annotations for the bean. - */ -public class ReadAnnotations { - - /** - * Read the initial non-relationship annotations included Id and EmbeddedId. - *

- * We then have enough to create BeanTables which are used in readAssociations - * to resolve the relationships etc. - *

- */ - public void readInitial(DeployBeanInfo info, boolean eagerFetchLobs){ - - try { - new AnnotationClass(info).parse(); - new AnnotationFields(info, eagerFetchLobs).parse(); - - } catch (RuntimeException e){ - String msg = "Error reading annotations for "+info; - throw new RuntimeException(msg, e); - } - } - - - /** - * Read and process the associated relationship annotations. - *

- * These can only be processed after the BeanTables have been created - *

- *

- * This uses the factory as a call back to get the BeanTable for a given - * associated bean. - *

- */ - public void readAssociations(DeployBeanInfo info, BeanDescriptorManager factory){ - - try { - - new AnnotationAssocOnes(info, factory).parse(); - new AnnotationAssocManys(info, factory).parse(); - - // read the Sql annotations last because they may be - // dependent on field level annotations - new AnnotationSql(info).parse(); - - } catch (RuntimeException e){ - String msg = "Error reading annotations for "+info; - throw new RuntimeException(msg, e); - } - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; + + +/** + * Read the deployment annotations for the bean. + */ +public class ReadAnnotations { + + /** + * Read the initial non-relationship annotations included Id and EmbeddedId. + *

+ * We then have enough to create BeanTables which are used in readAssociations + * to resolve the relationships etc. + *

+ */ + public void readInitial(DeployBeanInfo info, boolean eagerFetchLobs){ + + try { + new AnnotationClass(info).parse(); + new AnnotationFields(info, eagerFetchLobs).parse(); + + } catch (RuntimeException e){ + String msg = "Error reading annotations for "+info; + throw new RuntimeException(msg, e); + } + } + + + /** + * Read and process the associated relationship annotations. + *

+ * These can only be processed after the BeanTables have been created + *

+ *

+ * This uses the factory as a call back to get the BeanTable for a given + * associated bean. + *

+ */ + public void readAssociations(DeployBeanInfo info, BeanDescriptorManager factory){ + + try { + + new AnnotationAssocOnes(info, factory).parse(); + new AnnotationAssocManys(info, factory).parse(); + + // read the Sql annotations last because they may be + // dependent on field level annotations + new AnnotationSql(info).parse(); + + } catch (RuntimeException e){ + String msg = "Error reading annotations for "+info; + throw new RuntimeException(msg, e); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/TransientProperties.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/TransientProperties.java index 7e078373f..612060b79 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/TransientProperties.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/TransientProperties.java @@ -1,51 +1,51 @@ -package com.avaje.ebeaninternal.server.deploy.parse; - -import java.util.List; - -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; - -/** - * Mark transient properties. - */ -public class TransientProperties { - - public TransientProperties() { - } - - /** - * Mark any additional properties as transient. - */ - public void process(DeployBeanDescriptor desc) { - - List props = desc.propertiesBase(); - for (int i = 0; i < props.size(); i++) { - DeployBeanProperty prop = props.get(i); - if (!prop.isDbRead() && !prop.isDbInsertable() && !prop.isDbUpdateable()) { - // non-transient... - prop.setTransient(true); - } - } - - List> ones = desc.propertiesAssocOne(); - for (int i = 0; i < ones.size(); i++) { - DeployBeanPropertyAssocOne prop = ones.get(i); - if (prop.getBeanTable() == null) { - if (!prop.isEmbedded()) { - prop.setTransient(true); - } - } - } - - List> manys = desc.propertiesAssocMany(); - for (int i = 0; i < manys.size(); i++) { - DeployBeanPropertyAssocMany prop = manys.get(i); - if (prop.getBeanTable() == null) { - prop.setTransient(true); - } - } - - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.util.List; + +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; + +/** + * Mark transient properties. + */ +public class TransientProperties { + + public TransientProperties() { + } + + /** + * Mark any additional properties as transient. + */ + public void process(DeployBeanDescriptor desc) { + + List props = desc.propertiesBase(); + for (int i = 0; i < props.size(); i++) { + DeployBeanProperty prop = props.get(i); + if (!prop.isDbRead() && !prop.isDbInsertable() && !prop.isDbUpdateable()) { + // non-transient... + prop.setTransient(true); + } + } + + List> ones = desc.propertiesAssocOne(); + for (int i = 0; i < ones.size(); i++) { + DeployBeanPropertyAssocOne prop = ones.get(i); + if (prop.getBeanTable() == null) { + if (!prop.isEmbedded()) { + prop.setTransient(true); + } + } + } + + List> manys = desc.propertiesAssocMany(); + for (int i = 0; i < manys.size(); i++) { + DeployBeanPropertyAssocMany prop = manys.get(i); + if (prop.getBeanTable() == null) { + prop.setTransient(true); + } + } + + } +}