Java tutorial
/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.mapping; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.StringTokenizer; import org.hibernate.EntityMode; import org.hibernate.MappingException; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.spi.Mapping; import org.hibernate.internal.FilterConfiguration; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.JoinedIterator; import org.hibernate.internal.util.collections.SingletonIterator; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.Alias; /** * Mapping for an entity. * * @author Gavin King */ public abstract class PersistentClass implements AttributeContainer, Serializable, Filterable, MetaAttributable { private static final Alias PK_ALIAS = new Alias(15, "PK"); public static final String NULL_DISCRIMINATOR_MAPPING = "null"; public static final String NOT_NULL_DISCRIMINATOR_MAPPING = "not null"; private final MetadataBuildingContext metadataBuildingContext; private String entityName; private String className; private transient Class mappedClass; private String proxyInterfaceName; private transient Class proxyInterface; private String jpaEntityName; private String discriminatorValue; private boolean lazy; private java.util.List<Property> properties = new ArrayList<>(); private java.util.List<Property> declaredProperties = new ArrayList<>(); private final java.util.List<Subclass> subclasses = new ArrayList<>(); private final java.util.List<Property> subclassProperties = new ArrayList<>(); private final java.util.List<Table> subclassTables = new ArrayList<>(); private boolean dynamicInsert; private boolean dynamicUpdate; private int batchSize = -1; private boolean selectBeforeUpdate; private java.util.Map metaAttributes; private java.util.List<Join> joins = new ArrayList<>(); private final java.util.List<Join> subclassJoins = new ArrayList<>(); private final java.util.List<FilterConfiguration> filters = new ArrayList<>(); protected final java.util.Set<String> synchronizedTables = new HashSet<>(); private String loaderName; private Boolean isAbstract; private boolean hasSubselectLoadableCollections; private Component identifierMapper; // Custom SQL private String customSQLInsert; private boolean customInsertCallable; private ExecuteUpdateResultCheckStyle insertCheckStyle; private String customSQLUpdate; private boolean customUpdateCallable; private ExecuteUpdateResultCheckStyle updateCheckStyle; private String customSQLDelete; private boolean customDeleteCallable; private ExecuteUpdateResultCheckStyle deleteCheckStyle; private java.util.Map tuplizerImpls; private MappedSuperclass superMappedSuperclass; private Component declaredIdentifierMapper; private OptimisticLockStyle optimisticLockStyle; private boolean isCached; public PersistentClass(MetadataBuildingContext metadataBuildingContext) { this.metadataBuildingContext = metadataBuildingContext; } public ServiceRegistry getServiceRegistry() { return metadataBuildingContext.getBuildingOptions().getServiceRegistry(); } public String getClassName() { return className; } public void setClassName(String className) { this.className = className == null ? null : className.intern(); this.mappedClass = null; } public String getProxyInterfaceName() { return proxyInterfaceName; } public void setProxyInterfaceName(String proxyInterfaceName) { this.proxyInterfaceName = proxyInterfaceName; this.proxyInterface = null; } public Class getMappedClass() throws MappingException { if (className == null) { return null; } try { if (mappedClass == null) { mappedClass = metadataBuildingContext.getBootstrapContext().getClassLoaderAccess() .classForName(className); } return mappedClass; } catch (ClassLoadingException e) { throw new MappingException("entity class not found: " + className, e); } } public Class getProxyInterface() { if (proxyInterfaceName == null) { return null; } try { if (proxyInterface == null) { proxyInterface = metadataBuildingContext.getBootstrapContext().getClassLoaderAccess() .classForName(proxyInterfaceName); } return proxyInterface; } catch (ClassLoadingException e) { throw new MappingException("proxy class not found: " + proxyInterfaceName, e); } } public boolean useDynamicInsert() { return dynamicInsert; } abstract int nextSubclassId(); public abstract int getSubclassId(); public boolean useDynamicUpdate() { return dynamicUpdate; } public void setDynamicInsert(boolean dynamicInsert) { this.dynamicInsert = dynamicInsert; } public void setDynamicUpdate(boolean dynamicUpdate) { this.dynamicUpdate = dynamicUpdate; } public String getDiscriminatorValue() { return discriminatorValue; } public void addSubclass(Subclass subclass) throws MappingException { // inheritance cycle detection (paranoid check) PersistentClass superclass = getSuperclass(); while (superclass != null) { if (subclass.getEntityName().equals(superclass.getEntityName())) { throw new MappingException("Circular inheritance mapping detected: " + subclass.getEntityName() + " will have it self as superclass when extending " + getEntityName()); } superclass = superclass.getSuperclass(); } subclasses.add(subclass); } public boolean hasSubclasses() { return subclasses.size() > 0; } public int getSubclassSpan() { int n = subclasses.size(); for (Subclass subclass : subclasses) { n += subclass.getSubclassSpan(); } return n; } /** * Iterate over subclasses in a special 'order', most derived subclasses * first. */ public Iterator getSubclassIterator() { Iterator[] iters = new Iterator[subclasses.size() + 1]; Iterator iter = subclasses.iterator(); int i = 0; while (iter.hasNext()) { iters[i++] = ((Subclass) iter.next()).getSubclassIterator(); } iters[i] = subclasses.iterator(); return new JoinedIterator(iters); } public Iterator getSubclassClosureIterator() { ArrayList iters = new ArrayList(); iters.add(new SingletonIterator(this)); Iterator iter = getSubclassIterator(); while (iter.hasNext()) { PersistentClass clazz = (PersistentClass) iter.next(); iters.add(clazz.getSubclassClosureIterator()); } return new JoinedIterator(iters); } public Table getIdentityTable() { return getRootTable(); } public Iterator getDirectSubclasses() { return subclasses.iterator(); } @Override public void addProperty(Property p) { properties.add(p); declaredProperties.add(p); p.setPersistentClass(this); } public abstract Table getTable(); public String getEntityName() { return entityName; } public abstract boolean isMutable(); public abstract boolean hasIdentifierProperty(); public abstract Property getIdentifierProperty(); public abstract Property getDeclaredIdentifierProperty(); public abstract KeyValue getIdentifier(); public abstract Property getVersion(); public abstract Property getDeclaredVersion(); public abstract Value getDiscriminator(); public abstract boolean isInherited(); public abstract boolean isPolymorphic(); public abstract boolean isVersioned(); public boolean isCached() { return isCached; } public void setCached(boolean cached) { isCached = cached; } /** * @deprecated Use {@link #isCached} instead */ @Deprecated public boolean isCachingExplicitlyRequested() { return isCached(); } /** * @deprecated Use {@link #setCached} instead */ @Deprecated public void setCachingExplicitlyRequested(boolean cached) { setCached(cached); } public abstract String getCacheConcurrencyStrategy(); public abstract String getNaturalIdCacheRegionName(); public abstract PersistentClass getSuperclass(); public abstract boolean isExplicitPolymorphism(); public abstract boolean isDiscriminatorInsertable(); public abstract Iterator getPropertyClosureIterator(); public abstract Iterator getTableClosureIterator(); public abstract Iterator getKeyClosureIterator(); protected void addSubclassProperty(Property prop) { subclassProperties.add(prop); } protected void addSubclassJoin(Join join) { subclassJoins.add(join); } protected void addSubclassTable(Table subclassTable) { subclassTables.add(subclassTable); } public Iterator getSubclassPropertyClosureIterator() { ArrayList iters = new ArrayList(); iters.add(getPropertyClosureIterator()); iters.add(subclassProperties.iterator()); for (int i = 0; i < subclassJoins.size(); i++) { Join join = subclassJoins.get(i); iters.add(join.getPropertyIterator()); } return new JoinedIterator(iters); } public Iterator getSubclassJoinClosureIterator() { return new JoinedIterator(getJoinClosureIterator(), subclassJoins.iterator()); } public Iterator getSubclassTableClosureIterator() { return new JoinedIterator(getTableClosureIterator(), subclassTables.iterator()); } public boolean isClassOrSuperclassJoin(Join join) { return joins.contains(join); } public boolean isClassOrSuperclassTable(Table closureTable) { return getTable() == closureTable; } public boolean isLazy() { return lazy; } public void setLazy(boolean lazy) { this.lazy = lazy; } public abstract boolean hasEmbeddedIdentifier(); public abstract Class getEntityPersisterClass(); public abstract void setEntityPersisterClass(Class classPersisterClass); public abstract Table getRootTable(); public abstract RootClass getRootClass(); public abstract KeyValue getKey(); public void setDiscriminatorValue(String discriminatorValue) { this.discriminatorValue = discriminatorValue; } public void setEntityName(String entityName) { this.entityName = entityName == null ? null : entityName.intern(); } public void createPrimaryKey() { //Primary key constraint final Table table = getTable(); PrimaryKey pk = new PrimaryKey(table); pk.setName(PK_ALIAS.toAliasString(table.getName())); table.setPrimaryKey(pk); pk.addColumns(getKey().getColumnIterator()); } public abstract String getWhere(); public int getBatchSize() { return batchSize; } public void setBatchSize(int batchSize) { this.batchSize = batchSize; } public boolean hasSelectBeforeUpdate() { return selectBeforeUpdate; } public void setSelectBeforeUpdate(boolean selectBeforeUpdate) { this.selectBeforeUpdate = selectBeforeUpdate; } /** * Build an iterator of properties which may be referenced in association mappings. * <p> * Includes properties defined in superclasses of the mapping inheritance. * Includes all properties defined as part of a join. * * @see #getReferencedProperty for a discussion of "referenceable" * @return The referenceable property iterator. */ public Iterator getReferenceablePropertyIterator() { return getPropertyClosureIterator(); } /** * Given a property path, locate the appropriate referenceable property reference. * <p/> * A referenceable property is a property which can be a target of a foreign-key * mapping (e.g. {@code @ManyToOne}, {@code @OneToOne}). * * @param propertyPath The property path to resolve into a property reference. * * @return The property reference (never null). * * @throws MappingException If the property could not be found. */ public Property getReferencedProperty(String propertyPath) throws MappingException { try { return getRecursiveProperty(propertyPath, getReferenceablePropertyIterator()); } catch (MappingException e) { throw new MappingException( "property-ref [" + propertyPath + "] not found on entity [" + getEntityName() + "]", e); } } public Property getRecursiveProperty(String propertyPath) throws MappingException { try { return getRecursiveProperty(propertyPath, getPropertyClosureIterator()); } catch (MappingException e) { throw new MappingException( "property [" + propertyPath + "] not found on entity [" + getEntityName() + "]", e); } } private Property getRecursiveProperty(String propertyPath, Iterator iter) throws MappingException { Property property = null; StringTokenizer st = new StringTokenizer(propertyPath, ".", false); try { while (st.hasMoreElements()) { final String element = (String) st.nextElement(); if (property == null) { Property identifierProperty = getIdentifierProperty(); if (identifierProperty != null && identifierProperty.getName().equals(element)) { // we have a mapped identifier property and the root of // the incoming property path matched that identifier // property property = identifierProperty; } else if (identifierProperty == null && getIdentifierMapper() != null) { // we have an embedded composite identifier try { identifierProperty = getProperty(element, getIdentifierMapper().getPropertyIterator()); if (identifierProperty != null) { // the root of the incoming property path matched one // of the embedded composite identifier properties property = identifierProperty; } } catch (MappingException ignore) { // ignore it... } } if (property == null) { property = getProperty(element, iter); } } else { //flat recursive algorithm property = ((Component) property.getValue()).getProperty(element); } } } catch (MappingException e) { throw new MappingException( "property [" + propertyPath + "] not found on entity [" + getEntityName() + "]"); } return property; } private Property getProperty(String propertyName, Iterator iterator) throws MappingException { if (iterator.hasNext()) { String root = StringHelper.root(propertyName); while (iterator.hasNext()) { Property prop = (Property) iterator.next(); if (prop.getName().equals(root)) { return prop; } } } throw new MappingException("property [" + propertyName + "] not found on entity [" + getEntityName() + "]"); } public Property getProperty(String propertyName) throws MappingException { Iterator iter = getPropertyClosureIterator(); Property identifierProperty = getIdentifierProperty(); if (identifierProperty != null && identifierProperty.getName().equals(StringHelper.root(propertyName))) { return identifierProperty; } else { return getProperty(propertyName, iter); } } /** * Check to see if this PersistentClass defines a property with the given name. * * @param name The property name to check * * @return {@code true} if a property with that name exists; {@code false} if not */ @SuppressWarnings("WeakerAccess") public boolean hasProperty(String name) { final Property identifierProperty = getIdentifierProperty(); if (identifierProperty != null && identifierProperty.getName().equals(name)) { return true; } final Iterator itr = getPropertyClosureIterator(); while (itr.hasNext()) { final Property property = (Property) itr.next(); if (property.getName().equals(name)) { return true; } } return false; } /** * Check to see if a property with the given name exists in the super hierarchy * of this PersistentClass. Does not check this PersistentClass, just up the * hierarchy * * @param name The property name to check * * @return {@code true} if a property with that name exists; {@code false} if not */ public boolean isPropertyDefinedInSuperHierarchy(String name) { return getSuperclass() != null && getSuperclass().isPropertyDefinedInHierarchy(name); } /** * Check to see if a property with the given name exists in this PersistentClass * or in any of its super hierarchy. Unlike {@link #isPropertyDefinedInSuperHierarchy}, * this method does check this PersistentClass * * @param name The property name to check * * @return {@code true} if a property with that name exists; {@code false} if not */ @SuppressWarnings({ "WeakerAccess", "RedundantIfStatement" }) public boolean isPropertyDefinedInHierarchy(String name) { if (hasProperty(name)) { return true; } if (getSuperMappedSuperclass() != null && getSuperMappedSuperclass().isPropertyDefinedInHierarchy(name)) { return true; } if (getSuperclass() != null && getSuperclass().isPropertyDefinedInHierarchy(name)) { return true; } return false; } /** * @deprecated prefer {@link #getOptimisticLockStyle} */ @Deprecated public int getOptimisticLockMode() { return getOptimisticLockStyle().getOldCode(); } /** * @deprecated prefer {@link #setOptimisticLockStyle} */ @Deprecated public void setOptimisticLockMode(int optimisticLockMode) { setOptimisticLockStyle(OptimisticLockStyle.interpretOldCode(optimisticLockMode)); } public OptimisticLockStyle getOptimisticLockStyle() { return optimisticLockStyle; } public void setOptimisticLockStyle(OptimisticLockStyle optimisticLockStyle) { this.optimisticLockStyle = optimisticLockStyle; } public void validate(Mapping mapping) throws MappingException { Iterator iter = getPropertyIterator(); while (iter.hasNext()) { Property prop = (Property) iter.next(); if (!prop.isValid(mapping)) { throw new MappingException("property mapping has wrong number of columns: " + StringHelper.qualify(getEntityName(), prop.getName()) + " type: " + prop.getType().getName()); } } checkPropertyDuplication(); checkColumnDuplication(); } private void checkPropertyDuplication() throws MappingException { HashSet<String> names = new HashSet<>(); Iterator iter = getPropertyIterator(); while (iter.hasNext()) { Property prop = (Property) iter.next(); if (!names.add(prop.getName())) { throw new MappingException( "Duplicate property mapping of " + prop.getName() + " found in " + getEntityName()); } } } public boolean isDiscriminatorValueNotNull() { return NOT_NULL_DISCRIMINATOR_MAPPING.equals(getDiscriminatorValue()); } public boolean isDiscriminatorValueNull() { return NULL_DISCRIMINATOR_MAPPING.equals(getDiscriminatorValue()); } public java.util.Map getMetaAttributes() { return metaAttributes; } public void setMetaAttributes(java.util.Map metas) { this.metaAttributes = metas; } public MetaAttribute getMetaAttribute(String name) { return metaAttributes == null ? null : (MetaAttribute) metaAttributes.get(name); } @Override public String toString() { return getClass().getName() + '(' + getEntityName() + ')'; } public Iterator getJoinIterator() { return joins.iterator(); } public Iterator getJoinClosureIterator() { return joins.iterator(); } public void addJoin(Join join) { joins.add(join); join.setPersistentClass(this); } public int getJoinClosureSpan() { return joins.size(); } public int getPropertyClosureSpan() { int span = properties.size(); for (Join join : joins) { span += join.getPropertySpan(); } return span; } public int getJoinNumber(Property prop) { int result = 1; Iterator iter = getSubclassJoinClosureIterator(); while (iter.hasNext()) { Join join = (Join) iter.next(); if (join.containsProperty(prop)) { return result; } result++; } return 0; } /** * Build an iterator over the properties defined on this class. The returned * iterator only accounts for "normal" properties (i.e. non-identifier * properties). * <p/> * Differs from {@link #getUnjoinedPropertyIterator} in that the returned iterator * will include properties defined as part of a join. * <p/> * Differs from {@link #getReferenceablePropertyIterator} in that the properties * defined in superclasses of the mapping inheritance are not included. * * @return An iterator over the "normal" properties. */ public Iterator getPropertyIterator() { ArrayList iterators = new ArrayList(); iterators.add(properties.iterator()); for (int i = 0; i < joins.size(); i++) { Join join = joins.get(i); iterators.add(join.getPropertyIterator()); } return new JoinedIterator(iterators); } /** * Build an iterator over the properties defined on this class <b>which * are not defined as part of a join</b>. As with {@link #getPropertyIterator}, * the returned iterator only accounts for non-identifier properties. * * @return An iterator over the non-joined "normal" properties. */ public Iterator getUnjoinedPropertyIterator() { return properties.iterator(); } public void setCustomSQLInsert(String customSQLInsert, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLInsert = customSQLInsert; this.customInsertCallable = callable; this.insertCheckStyle = checkStyle; } public String getCustomSQLInsert() { return customSQLInsert; } public boolean isCustomInsertCallable() { return customInsertCallable; } public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() { return insertCheckStyle; } public void setCustomSQLUpdate(String customSQLUpdate, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLUpdate = customSQLUpdate; this.customUpdateCallable = callable; this.updateCheckStyle = checkStyle; } public String getCustomSQLUpdate() { return customSQLUpdate; } public boolean isCustomUpdateCallable() { return customUpdateCallable; } public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() { return updateCheckStyle; } public void setCustomSQLDelete(String customSQLDelete, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLDelete = customSQLDelete; this.customDeleteCallable = callable; this.deleteCheckStyle = checkStyle; } public String getCustomSQLDelete() { return customSQLDelete; } public boolean isCustomDeleteCallable() { return customDeleteCallable; } public ExecuteUpdateResultCheckStyle getCustomSQLDeleteCheckStyle() { return deleteCheckStyle; } public void addFilter(String name, String condition, boolean autoAliasInjection, java.util.Map<String, String> aliasTableMap, java.util.Map<String, String> aliasEntityMap) { filters.add( new FilterConfiguration(name, condition, autoAliasInjection, aliasTableMap, aliasEntityMap, this)); } public java.util.List getFilters() { return filters; } public boolean isForceDiscriminator() { return false; } public abstract boolean isJoinedSubclass(); public String getLoaderName() { return loaderName; } public void setLoaderName(String loaderName) { this.loaderName = loaderName == null ? null : loaderName.intern(); } public abstract java.util.Set getSynchronizedTables(); public void addSynchronizedTable(String table) { synchronizedTables.add(table); } public Boolean isAbstract() { return isAbstract; } public void setAbstract(Boolean isAbstract) { this.isAbstract = isAbstract; } protected void checkColumnDuplication(Set distinctColumns, Iterator columns) throws MappingException { while (columns.hasNext()) { Selectable columnOrFormula = (Selectable) columns.next(); if (!columnOrFormula.isFormula()) { Column col = (Column) columnOrFormula; if (!distinctColumns.add(col.getName())) { throw new MappingException( "Repeated column in mapping for entity: " + getEntityName() + " column: " + col.getName() + " (should be mapped with insert=\"false\" update=\"false\")"); } } } } protected void checkPropertyColumnDuplication(Set distinctColumns, Iterator properties) throws MappingException { while (properties.hasNext()) { Property prop = (Property) properties.next(); if (prop.getValue() instanceof Component) { //TODO: remove use of instanceof! Component component = (Component) prop.getValue(); checkPropertyColumnDuplication(distinctColumns, component.getPropertyIterator()); } else { if (prop.isUpdateable() || prop.isInsertable()) { checkColumnDuplication(distinctColumns, prop.getColumnIterator()); } } } } protected Iterator getNonDuplicatedPropertyIterator() { return getUnjoinedPropertyIterator(); } protected Iterator getDiscriminatorColumnIterator() { return Collections.emptyIterator(); } protected void checkColumnDuplication() { HashSet cols = new HashSet(); if (getIdentifierMapper() == null) { //an identifier mapper => getKey will be included in the getNonDuplicatedPropertyIterator() //and checked later, so it needs to be excluded checkColumnDuplication(cols, getKey().getColumnIterator()); } checkColumnDuplication(cols, getDiscriminatorColumnIterator()); checkPropertyColumnDuplication(cols, getNonDuplicatedPropertyIterator()); Iterator iter = getJoinIterator(); while (iter.hasNext()) { cols.clear(); Join join = (Join) iter.next(); checkColumnDuplication(cols, join.getKey().getColumnIterator()); checkPropertyColumnDuplication(cols, join.getPropertyIterator()); } } public abstract Object accept(PersistentClassVisitor mv); public String getJpaEntityName() { return jpaEntityName; } public void setJpaEntityName(String jpaEntityName) { this.jpaEntityName = jpaEntityName; } public boolean hasPojoRepresentation() { return getClassName() != null; } public boolean hasSubselectLoadableCollections() { return hasSubselectLoadableCollections; } public void setSubselectLoadableCollections(boolean hasSubselectCollections) { this.hasSubselectLoadableCollections = hasSubselectCollections; } public Component getIdentifierMapper() { return identifierMapper; } public Component getDeclaredIdentifierMapper() { return declaredIdentifierMapper; } public void setDeclaredIdentifierMapper(Component declaredIdentifierMapper) { this.declaredIdentifierMapper = declaredIdentifierMapper; } public boolean hasIdentifierMapper() { return identifierMapper != null; } public void setIdentifierMapper(Component handle) { this.identifierMapper = handle; } public void addTuplizer(EntityMode entityMode, String implClassName) { if (tuplizerImpls == null) { tuplizerImpls = new HashMap(); } tuplizerImpls.put(entityMode, implClassName); } public String getTuplizerImplClassName(EntityMode mode) { if (tuplizerImpls == null) { return null; } return (String) tuplizerImpls.get(mode); } public java.util.Map getTuplizerMap() { if (tuplizerImpls == null) { return null; } return java.util.Collections.unmodifiableMap(tuplizerImpls); } public boolean hasNaturalId() { Iterator props = getRootClass().getPropertyIterator(); while (props.hasNext()) { if (((Property) props.next()).isNaturalIdentifier()) { return true; } } return false; } // The following methods are added to support @MappedSuperclass in the metamodel public Iterator getDeclaredPropertyIterator() { ArrayList iterators = new ArrayList(); iterators.add(declaredProperties.iterator()); for (int i = 0; i < joins.size(); i++) { Join join = joins.get(i); iterators.add(join.getDeclaredPropertyIterator()); } return new JoinedIterator(iterators); } public void addMappedsuperclassProperty(Property p) { properties.add(p); p.setPersistentClass(this); } public MappedSuperclass getSuperMappedSuperclass() { return superMappedSuperclass; } public void setSuperMappedSuperclass(MappedSuperclass superMappedSuperclass) { this.superMappedSuperclass = superMappedSuperclass; } // End of @Mappedsuperclass support }