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