All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2006  Robin Bygrave
 * 
 * This file is part of Ebean.
 * 
 * Ebean is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *  
 * Ebean is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Ebean; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA  
 */
package com.avaje.ebeaninternal.server.deploy.meta;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.avaje.ebean.Query.UseIndex;
import com.avaje.ebean.config.TableName;
import com.avaje.ebean.config.dbplatform.IdGenerator;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.meta.MetaAutoFetchStatistic;
import com.avaje.ebeaninternal.server.core.CacheOptions;
import com.avaje.ebeaninternal.server.core.ConcurrencyMode;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistListener;
import com.avaje.ebeaninternal.server.deploy.ChainedBeanQueryAdapter;
import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint;
import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta;
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.reflect.BeanReflect;

/**
 * Describes Beans including their deployment information.
 */
public class DeployBeanDescriptor {

  static class PropOrder implements Comparator {

    public int compare(DeployBeanProperty o1, DeployBeanProperty o2) {

      int v2 = o1.getSortOrder();
      int v1 = o2.getSortOrder();
      return (v1 < v2 ? -1 : (v1 == v2 ? 0 : 1));
    }
  }

  private static final PropOrder PROP_ORDER = new PropOrder();

  private static final String I_SCALAOBJECT = "scala.ScalaObject";

  private static final Logger logger = Logger.getLogger(DeployBeanDescriptor.class.getName());

  private static final String META_BEAN_PREFIX = MetaAutoFetchStatistic.class.getName().substring(0, 20);

  /**
   * Map of BeanProperty Linked so as to preserve order.
   */
  private LinkedHashMap propMap = new LinkedHashMap();

  /**
   * The type of bean this describes.
   */
  private final Class beanType;

  private EntityType entityType;

  private final Map namedQueries = new LinkedHashMap();

  private final Map namedUpdates = new LinkedHashMap();

  private final Map rawSqlMetas = new LinkedHashMap();

  private DeployBeanPropertyAssocOne unidirectional;

  /**
   * Type of Identity generation strategy used.
   */
  private IdType idType;

  /**
   * The name of an IdGenerator (optional).
   */
  private String idGeneratorName;

  private IdGenerator idGenerator;

  /**
   * The database sequence name (optional).
   */
  private String sequenceName;

  private String ldapBaseDn;

  private String[] ldapObjectclasses;

  /**
   * Used with Identity columns but no getGeneratedKeys support.
   */
  private String selectLastInsertedId;

  private String lazyFetchIncludes;

  /**
   * The concurrency mode for beans of this type.
   */
  private ConcurrencyMode concurrencyMode = ConcurrencyMode.ALL;

  private boolean updateChangesOnly;

  /**
   * The tables this bean is dependent on.
   */
  private String[] dependantTables;

  private List compoundUniqueConstraints;

  /**
   * Extra deployment attributes.
   */
  private HashMap extraAttrMap = new HashMap();

  /**
   * The base database table.
   */
  private String baseTable;
  private TableName baseTableFull;

  /**
   * Used to provide mechanism to new EntityBean instances. Generated code
   * faster than reflection at this stage.
   */
  private BeanReflect beanReflect;

  /**
   * The EntityBean type used to create new EntityBeans.
   */
  private Class factoryType;

  private List persistControllers = new ArrayList();
  private List> persistListeners = new ArrayList>();
  private List queryAdapters = new ArrayList();

  private CacheOptions cacheOptions = new CacheOptions();

  /**
   * If set overrides the find implementation. Server side only.
   */
  private BeanFinder beanFinder;

  private UseIndex useIndex;

  /**
   * The table joins for this bean. Server side only.
   */
  private ArrayList tableJoinList = new ArrayList();

  /**
   * Inheritance information. Server side only.
   */
  private InheritInfo inheritInfo;

  private String name;

  private boolean processedRawSqlExtend;

  /**
   * Construct the BeanDescriptor.
   */
  public DeployBeanDescriptor(Class beanType) {
    this.beanType = beanType;
  }

  /**
   * Return true if this beanType is an abstract class.
   */
  public boolean isAbstract() {
    return Modifier.isAbstract(beanType.getModifiers());
  }

  /**
   * Return the default UseIndex strategy.
   */
  public UseIndex getUseIndex() {
    return useIndex;
  }

  /**
   * Set the default UseIndex strategy.
   */
  public void setUseIndex(UseIndex useIndex) {
    this.useIndex = useIndex;
  }

  public boolean isScalaObject() {
    Class[] interfaces = beanType.getInterfaces();
    for (int i = 0; i < interfaces.length; i++) {
      String iname = interfaces[i].getName();
      if (I_SCALAOBJECT.equals(iname)) {
        return true;
      }
    }
    return false;
  }

  public Collection getRawSqlMeta() {
    if (!processedRawSqlExtend) {
      rawSqlProcessExtend();
      processedRawSqlExtend = true;
    }
    return rawSqlMetas.values();
  }

  /**
   * Process the "extend" attributes of raw SQL. Aka inherit the query and
   * column mapping.
   */
  private void rawSqlProcessExtend() {

    for (DRawSqlMeta rawSqlMeta : rawSqlMetas.values()) {
      String extend = rawSqlMeta.getExtend();
      if (extend != null) {
        DRawSqlMeta parentQuery = rawSqlMetas.get(extend);
        if (parentQuery == null) {
          throw new RuntimeException("parent query [" + extend + "] not found for sql-select " + rawSqlMeta.getName());
        }
        rawSqlMeta.extend(parentQuery);
      }
    }
  }

  public DeployBeanTable createDeployBeanTable() {

    DeployBeanTable beanTable = new DeployBeanTable(getBeanType());
    beanTable.setBaseTable(baseTable);
    beanTable.setIdProperties(propertiesId());

    return beanTable;
  }

  /**
   * Check all the properties to see if they all have read and write methods
   * (required if using "subclassing" but not for "enhancement").
   */
  public boolean checkReadAndWriteMethods() {

    if (isMeta()) {
      return true;
    }
    boolean missingMethods = false;

    Iterator it = propMap.values().iterator();
    while (it.hasNext()) {
      DeployBeanProperty prop = it.next();
      if (!prop.isTransient()) {
        String m = "";
        if (prop.getReadMethod() == null) {
          m += " missing readMethod ";
        }
        if (prop.getWriteMethod() == null) {
          m += " missing writeMethod ";
        }
        if (!"".equals(m)) {
          m += ". Should it be transient?";
          String msg = "Bean property " + getFullName() + "." + prop.getName() + " has " + m;
          logger.log(Level.SEVERE, msg);
          missingMethods = true;
        }
      }
    }
    return !missingMethods;
  }

  public void setEntityType(EntityType entityType) {
    this.entityType = entityType;
  }

  public boolean isEmbedded() {
    return EntityType.EMBEDDED.equals(entityType);
  }

  public boolean isBaseTableType() {
    EntityType et = getEntityType();
    return EntityType.ORM.equals(et);
  }

  public EntityType getEntityType() {
    if (entityType == null) {
      entityType = isMeta() ? EntityType.META : EntityType.ORM;
    }
    return entityType;
  }

  /**
   * Return true if this is a Meta entity bean.
   * 

* The Meta entity beans are not based on real tables but get meta information * from memory such as all the entity bean meta data. *

*/ private boolean isMeta() { return beanType.getName().startsWith(META_BEAN_PREFIX); } public void add(DRawSqlMeta rawSqlMeta) { rawSqlMetas.put(rawSqlMeta.getName(), rawSqlMeta); if ("default".equals(rawSqlMeta.getName())) { setEntityType(EntityType.SQL); } } public void add(DeployNamedUpdate namedUpdate) { namedUpdates.put(namedUpdate.getName(), namedUpdate); } public void add(DeployNamedQuery namedQuery) { namedQueries.put(namedQuery.getName(), namedQuery); if ("default".equals(namedQuery.getName())) { setEntityType(EntityType.SQL); } } public Map getNamedQueries() { return namedQueries; } public Map getNamedUpdates() { return namedUpdates; } public BeanReflect getBeanReflect() { return beanReflect; } /** * Return the class type this BeanDescriptor describes. */ public Class getBeanType() { return beanType; } /** * Return the class type this BeanDescriptor describes. */ public Class getFactoryType() { return factoryType; } /** * Set the class used to create new EntityBean instances. *

* Normally this would be a subclass dynamically generated for this bean. *

*/ public void setFactoryType(Class factoryType) { this.factoryType = factoryType; } /** * Set the BeanReflect used to create new instances of an EntityBean. This * could use reflection or code generation to do this. */ public void setBeanReflect(BeanReflect beanReflect) { this.beanReflect = beanReflect; } /** * Returns the Inheritance mapping information. This will be null if this type * of bean is not involved in any ORM inheritance mapping. */ public InheritInfo getInheritInfo() { return inheritInfo; } /** * Set the ORM inheritance mapping information. */ public void setInheritInfo(InheritInfo inheritInfo) { this.inheritInfo = inheritInfo; } /** * Return the reference options. */ public CacheOptions getCacheOptions() { return cacheOptions; } public boolean isNaturalKeyProperty(String name) { return name.equals(cacheOptions.getNaturalKey()); } public DeployBeanPropertyAssocOne getUnidirectional() { return unidirectional; } public void setUnidirectional(DeployBeanPropertyAssocOne unidirectional) { this.unidirectional = unidirectional; } /** * Return the concurrency mode used for beans of this type. */ public ConcurrencyMode getConcurrencyMode() { return concurrencyMode; } /** * Set the concurrency mode used for beans of this type. */ public void setConcurrencyMode(ConcurrencyMode concurrencyMode) { this.concurrencyMode = concurrencyMode; } public String getLdapBaseDn() { return ldapBaseDn; } public void setLdapBaseDn(String ldapBaseDn) { this.ldapBaseDn = ldapBaseDn; } public String[] getLdapObjectclasses() { return ldapObjectclasses; } public void setLdapObjectclasses(String[] ldapObjectclasses) { this.ldapObjectclasses = ldapObjectclasses; } public boolean isUpdateChangesOnly() { return updateChangesOnly; } public void setUpdateChangesOnly(boolean updateChangesOnly) { this.updateChangesOnly = updateChangesOnly; } /** * Return the tables this bean is dependant on. This implies that if any of * these tables are modified then cached beans may be invalidated. */ public String[] getDependantTables() { return dependantTables; } /** * Add a compound unique constraint. */ public void addCompoundUniqueConstraint(CompoundUniqueContraint c) { if (compoundUniqueConstraints == null) { compoundUniqueConstraints = new ArrayList(); } compoundUniqueConstraints.add(c); } /** * Return the compound unique constraints (can be null). */ public CompoundUniqueContraint[] getCompoundUniqueConstraints() { if (compoundUniqueConstraints == null) { return null; } else { return compoundUniqueConstraints.toArray(new CompoundUniqueContraint[compoundUniqueConstraints.size()]); } } /** * Set the tables this bean is dependant on. This implies that if any of these * tables are modified then cached beans may be invalidated. */ public void setDependantTables(String[] dependantTables) { this.dependantTables = dependantTables; } /** * Return the beanFinder. Usually null unless overriding the finder. */ public BeanFinder getBeanFinder() { return beanFinder; } /** * Set the BeanFinder to use for beans of this type. This is set to override * the finding from the default. */ public void setBeanFinder(BeanFinder beanFinder) { this.beanFinder = beanFinder; } /** * Return the BeanPersistController (could be a chain of them, 1 or null). */ public BeanPersistController getPersistController() { if (persistControllers.size() == 0) { return null; } else if (persistControllers.size() == 1) { return persistControllers.get(0); } else { return new ChainedBeanPersistController(persistControllers); } } /** * Return the BeanPersistListener (could be a chain of them, 1 or null). */ public BeanPersistListener getPersistListener() { if (persistListeners.size() == 0) { return null; } else if (persistListeners.size() == 1) { return persistListeners.get(0); } else { return new ChainedBeanPersistListener(persistListeners); } } public BeanQueryAdapter getQueryAdapter() { if (queryAdapters.size() == 0) { return null; } else if (queryAdapters.size() == 1) { return queryAdapters.get(0); } else { return new ChainedBeanQueryAdapter(queryAdapters); } } /** * Set the Controller. */ public void addPersistController(BeanPersistController controller) { persistControllers.add(controller); } public void addPersistListener(BeanPersistListener listener) { persistListeners.add(listener); } public void addQueryAdapter(BeanQueryAdapter queryAdapter) { queryAdapters.add(queryAdapter); } /** * Return true if this bean type should use IdGeneration. *

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

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

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

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

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

*/ public String getSelectLastInsertedId() { return selectLastInsertedId; } /** * Set the SQL used to return the last inserted Id. */ public void setSelectLastInsertedId(String selectLastInsertedId) { this.selectLastInsertedId = selectLastInsertedId; } /** * Return the name of the IdGenerator that should be used with this type of * bean. A null value could be used to specify the 'default' IdGenerator. */ public String getIdGeneratorName() { return idGeneratorName; } /** * Set the name of the IdGenerator that should be used with this type of bean. */ public void setIdGeneratorName(String idGeneratorName) { this.idGeneratorName = idGeneratorName; } /** * Return the actual IdGenerator for this bean type (can be null). */ public IdGenerator getIdGenerator() { return idGenerator; } /** * Set the actual IdGenerator for this bean type. */ public void setIdGenerator(IdGenerator idGenerator) { this.idGenerator = idGenerator; if (idGenerator != null && idGenerator.isDbSequence()) { setSequenceName(idGenerator.getName()); } } /** * Return the includes for getReference(). */ public String getLazyFetchIncludes() { return lazyFetchIncludes; } /** * Set includes to use for lazy loading by getReference(). Note queries also * build references and includes on the actual association are used for those * references. */ public void setLazyFetchIncludes(String lazyFetchIncludes) { if (lazyFetchIncludes != null && lazyFetchIncludes.length() > 0) { this.lazyFetchIncludes = lazyFetchIncludes; } } /** * Summary description. */ public String toString() { return getFullName(); } /** * Add a TableJoin to this type of bean. For Secondary table properties. */ public void addTableJoin(DeployTableJoin join) { tableJoinList.add(join); } public List getTableJoins() { return tableJoinList; } /** * Return an Iterator of all BeanProperty. */ public Iterator propertiesAll() { return propMap.values().iterator(); } /** * Return the defaultSelectClause using FetchType.LAZY and FetchType.EAGER. */ public String getDefaultSelectClause() { StringBuilder sb = new StringBuilder(); boolean hasLazyFetch = false; Iterator it = propMap.values().iterator(); while (it.hasNext()) { DeployBeanProperty prop = it.next(); if (prop.isTransient()) { // ignore transient props etc } else if (prop instanceof DeployBeanPropertyAssocMany) { // ignore the associated many properties } else { if (prop.isFetchEager()) { sb.append(prop.getName()).append(","); } else { hasLazyFetch = true; } } } if (!hasLazyFetch) { return null; } String selectClause = sb.toString(); return selectClause.substring(0, selectClause.length() - 1); } /** * Return an Array of properties to include in default fetch (for LDAP). */ public String[] getDefaultSelectDbArray(Set defaultSelect) { ArrayList list = new ArrayList(); for (DeployBeanProperty p : propMap.values()) { if (defaultSelect != null) { if (defaultSelect.contains(p.getName())) { // properties in defaultSelect list.add(p.getDbColumn()); } } else if (!p.isTransient() && p.isDbRead()) { // non transient db properties list.add(p.getDbColumn()); } } return list.toArray(new String[list.size()]); } /** * Parse the include separating by comma or semicolon. */ public Set parseDefaultSelectClause(String rawList) { if (rawList == null) { return null; } String[] res = rawList.split(","); LinkedHashSet set = new LinkedHashSet(res.length + 3); String temp = null; for (int i = 0; i < res.length; i++) { temp = res[i].trim(); if (temp.length() > 0) { set.add(temp); } } return Collections.unmodifiableSet(set); } /** * Return the Primary Key column assuming it is a single column (not * compound). This is for the purpose of defining a sequence name. */ public String getSinglePrimaryKeyColumn() { List ids = propertiesId(); if (ids.size() == 1) { DeployBeanProperty p = ids.get(0); if (p instanceof DeployBeanPropertyAssoc) { // its a compound primary key return null; } else { return p.getDbColumn(); } } return null; } /** * Return the BeanProperty that make up the unique id. *

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

*/ public List propertiesId() { ArrayList list = new ArrayList(2); Iterator it = propMap.values().iterator(); while (it.hasNext()) { DeployBeanProperty prop = it.next(); if (prop.isId()) { list.add(prop); } } return list; } public DeployBeanPropertyAssocOne findJoinToTable(String tableName) { List> assocOne = propertiesAssocOne(); for (DeployBeanPropertyAssocOne prop : assocOne) { DeployTableJoin tableJoin = prop.getTableJoin(); if (tableJoin != null && tableJoin.getTable().equalsIgnoreCase(tableName)) { return prop; } } return null; } /** * Return an Iterator of BeanPropertyAssocOne that are not embedded. These are * effectively joined beans. For ManyToOne and OneToOne associations. */ public List> propertiesAssocOne() { ArrayList> list = new ArrayList>(); Iterator it = propMap.values().iterator(); while (it.hasNext()) { DeployBeanProperty prop = it.next(); if (prop instanceof DeployBeanPropertyAssocOne) { if (!prop.isEmbedded()) { list.add((DeployBeanPropertyAssocOne) prop); } } } return list; } /** * Return BeanPropertyAssocMany for this descriptor. */ public List> propertiesAssocMany() { ArrayList> list = new ArrayList>(); Iterator it = propMap.values().iterator(); while (it.hasNext()) { DeployBeanProperty prop = it.next(); if (prop instanceof DeployBeanPropertyAssocMany) { list.add((DeployBeanPropertyAssocMany) prop); } } return list; } /** * Returns 'Version' properties on this bean. These are 'Counter' or 'Update * Timestamp' type properties. Note version properties can also be on embedded * beans rather than on the bean itself. */ public List propertiesVersion() { ArrayList list = new ArrayList(); Iterator it = propMap.values().iterator(); while (it.hasNext()) { DeployBeanProperty prop = it.next(); if (prop instanceof DeployBeanPropertyAssoc) { } else { if (!prop.isId() && prop.isVersionColumn()) { list.add(prop); } } } return list; } /** * base properties without the unique id properties. */ public List propertiesBase() { ArrayList list = new ArrayList(); Iterator it = propMap.values().iterator(); while (it.hasNext()) { DeployBeanProperty prop = it.next(); if (prop instanceof DeployBeanPropertyAssoc) { } else { if (!prop.isId()) { list.add(prop); } } } return list; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy