com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor Maven / Gradle / Ivy
/**
* 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.config.lucene.IndexDefn;
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.ConcurrencyMode;
import com.avaje.ebeaninternal.server.core.ReferenceOptions;
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.DeployNamedQuery;
import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
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 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 ReferenceOptions referenceOptions;
/**
* If set overrides the find implementation. Server side only.
*/
private BeanFinder beanFinder;
private UseIndex useIndex;
private IndexDefn> indexDefn;
/**
* 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 IndexDefn> getIndexDefn() {
return indexDefn;
}
public void setIndexDefn(IndexDefn> indexDefn) {
this.indexDefn = indexDefn;
}
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 ReferenceOptions getReferenceOptions() {
return referenceOptions;
}
/**
* Set the reference options.
*/
public void setReferenceOptions(ReferenceOptions referenceOptions) {
this.referenceOptions = referenceOptions;
}
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 - 2025 Weber Informatics LLC | Privacy Policy