/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program 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 this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cfg;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MapsId;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.hibernate.AnnotationException;
import org.hibernate.DuplicateMappingException;
import org.hibernate.EmptyInterceptor;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.InvalidMappingException;
import org.hibernate.MappingException;
import org.hibernate.MappingNotFoundException;
import org.hibernate.SessionFactory;
import org.hibernate.SessionFactoryObserver;
import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.common.reflection.MetadataProvider;
import org.hibernate.annotations.common.reflection.MetadataProviderInjector;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.java.JavaReflectionManager;
import org.hibernate.cfg.annotations.reflection.JPAMetadataProvider;
import org.hibernate.cfg.beanvalidation.BeanValidationActivator;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.engine.FilterDefinition;
import org.hibernate.engine.Mapping;
import org.hibernate.engine.NamedQueryDefinition;
import org.hibernate.engine.NamedSQLQueryDefinition;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.event.AutoFlushEventListener;
import org.hibernate.event.DeleteEventListener;
import org.hibernate.event.DirtyCheckEventListener;
import org.hibernate.event.EventListeners;
import org.hibernate.event.EvictEventListener;
import org.hibernate.event.FlushEntityEventListener;
import org.hibernate.event.FlushEventListener;
import org.hibernate.event.InitializeCollectionEventListener;
import org.hibernate.event.LoadEventListener;
import org.hibernate.event.LockEventListener;
import org.hibernate.event.MergeEventListener;
import org.hibernate.event.PersistEventListener;
import org.hibernate.event.PostCollectionRecreateEventListener;
import org.hibernate.event.PostCollectionRemoveEventListener;
import org.hibernate.event.PostCollectionUpdateEventListener;
import org.hibernate.event.PostDeleteEventListener;
import org.hibernate.event.PostInsertEventListener;
import org.hibernate.event.PostLoadEventListener;
import org.hibernate.event.PostUpdateEventListener;
import org.hibernate.event.PreCollectionRecreateEventListener;
import org.hibernate.event.PreCollectionRemoveEventListener;
import org.hibernate.event.PreCollectionUpdateEventListener;
import org.hibernate.event.PreDeleteEventListener;
import org.hibernate.event.PreInsertEventListener;
import org.hibernate.event.PreLoadEventListener;
import org.hibernate.event.PreUpdateEventListener;
import org.hibernate.event.RefreshEventListener;
import org.hibernate.event.ReplicateEventListener;
import org.hibernate.event.SaveOrUpdateEventListener;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.IdentifierGeneratorAggregator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.factory.DefaultIdentifierGeneratorFactory;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.impl.SessionFactoryImpl;
import org.hibernate.mapping.AuxiliaryDatabaseObject;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.DenormalizedTable;
import org.hibernate.mapping.FetchProfile;
import org.hibernate.mapping.ForeignKey;
import org.hibernate.mapping.IdGenerator;
import org.hibernate.mapping.IdentifierCollection;
import org.hibernate.mapping.Index;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.MetadataSource;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.TypeDef;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.secure.JACCConfiguration;
import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
import org.hibernate.tool.hbm2ddl.IndexMetadata;
import org.hibernate.tool.hbm2ddl.TableMetadata;
import org.hibernate.tuple.entity.EntityTuplizerFactory;
import org.hibernate.type.BasicType;
import org.hibernate.type.SerializationException;
import org.hibernate.type.Type;
import org.hibernate.type.TypeResolver;
import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.UserType;
import org.hibernate.util.ArrayHelper;
import org.hibernate.util.CollectionHelper;
import org.hibernate.util.ConfigHelper;
import org.hibernate.util.JoinedIterator;
import org.hibernate.util.PropertiesHelper;
import org.hibernate.util.ReflectHelper;
import org.hibernate.util.SerializationHelper;
import org.hibernate.util.StringHelper;
import org.hibernate.util.XMLHelper;
/**
* An instance of <tt>Configuration</tt> allows the application
* to specify properties and mapping documents to be used when
* creating a <tt>SessionFactory</tt>. Usually an application will create
* a single <tt>Configuration</tt>, build a single instance of
* <tt>SessionFactory</tt> and then instantiate <tt>Session</tt>s in
* threads servicing client requests. The <tt>Configuration</tt> is meant
* only as an initialization-time object. <tt>SessionFactory</tt>s are
* immutable and do not retain any association back to the
* <tt>Configuration</tt>.<br>
* <br>
* A new <tt>Configuration</tt> will use the properties specified in
* <tt>hibernate.properties</tt> by default.
*
* @author Gavin King
* @see org.hibernate.SessionFactory
*/
public class Configuration implements Serializable {
private static Logger log = LoggerFactory.getLogger( Configuration.class );
/**
* Setting used to give the name of the default {@link org.hibernate.annotations.CacheConcurrencyStrategy}
* to use when either {@link javax.persistence.Cacheable @Cacheable} or
* {@link org.hibernate.annotations.Cache @Cache} is used. {@link org.hibernate.annotations.Cache @Cache(strategy="..")} is used to override.
*/
public static final String DEFAULT_CACHE_CONCURRENCY_STRATEGY = "hibernate.cache.default_cache_concurrency_strategy";
/**
* Setting which indicates whether or not the new {@link org.hibernate.id.IdentifierGenerator} are used
* for AUTO, TABLE and SEQUENCE.
* Default to false to keep backward compatibility.
*/
public static final String USE_NEW_ID_GENERATOR_MAPPINGS = "hibernate.id.new_generator_mappings";
public static final String ARTEFACT_PROCESSING_ORDER = "hibernate.mapping.precedence";
/**
* Class name of the class needed to enable Search.
*/
private static final String SEARCH_STARTUP_CLASS = "org.hibernate.search.event.EventListenerRegister";
/**
* Method to call to enable Search.
*/
private static final String SEARCH_STARTUP_METHOD = "enableHibernateSearch";
protected MetadataSourceQueue metadataSourceQueue;
private transient ReflectionManager reflectionManager;
protected Map<String, PersistentClass> classes;
protected Map<String, String> imports;
protected Map<String, Collection> collections;
protected Map<String, Table> tables;
protected List<AuxiliaryDatabaseObject> auxiliaryDatabaseObjects;
protected Map<String, NamedQueryDefinition> namedQueries;
protected Map<String, NamedSQLQueryDefinition> namedSqlQueries;
protected Map<String, ResultSetMappingDefinition> sqlResultSetMappings;
protected Map<String, TypeDef> typeDefs;
protected Map<String, FilterDefinition> filterDefinitions;
protected Map<String, FetchProfile> fetchProfiles;
protected Map tableNameBinding;
protected Map columnNameBindingPerTable;
protected List<SecondPass> secondPasses;
protected List<Mappings.PropertyReference> propertyReferences;
protected Map<ExtendsQueueEntry, ?> extendsQueue;
protected Map<String, SQLFunction> sqlFunctions;
private TypeResolver typeResolver = new TypeResolver();
private EntityTuplizerFactory entityTuplizerFactory;
// private ComponentTuplizerFactory componentTuplizerFactory; todo : HHH-3517 and HHH-1907
private Interceptor interceptor;
private Properties properties;
private EntityResolver entityResolver;
private EntityNotFoundDelegate entityNotFoundDelegate;
protected transient XMLHelper xmlHelper;
protected NamingStrategy namingStrategy;
private SessionFactoryObserver sessionFactoryObserver;
private EventListeners eventListeners;
protected final SettingsFactory settingsFactory;
private transient Mapping mapping = buildMapping();
private DefaultIdentifierGeneratorFactory identifierGeneratorFactory;
private Map<Class<?>, org.hibernate.mapping.MappedSuperclass> mappedSuperClasses;
private Map<String, IdGenerator> namedGenerators;
private Map<String, Map<String, Join>> joins;
private Map<String, AnnotatedClassType> classTypes;
private Set<String> defaultNamedQueryNames;
private Set<String> defaultNamedNativeQueryNames;
private Set<String> defaultSqlResultSetMappingNames;
private Set<String> defaultNamedGenerators;
private Map<String, Properties> generatorTables;
private Map<Table, List<UniqueConstraintHolder>> uniqueConstraintHoldersByTable;
private Map<String, String> mappedByResolver;
private Map<String, String> propertyRefResolver;
private Map<String, AnyMetaDef> anyMetaDefs;
private List<CacheHolder> caches;
private boolean inSecondPass = false;
private boolean isDefaultProcessed = false;
private boolean isValidatorNotPresentLogged;
private Map<XClass, Map<String, PropertyData>> propertiesAnnotatedWithMapsId;
private Map<XClass, Map<String, PropertyData>> propertiesAnnotatedWithIdAndToOne;
protected Configuration(SettingsFactory settingsFactory) {
this.settingsFactory = settingsFactory;
reset();
}
public Configuration() {
this( new SettingsFactory() );
}
protected void reset() {
metadataSourceQueue = new MetadataSourceQueue();
createReflectionManager();
classes = new HashMap<String,PersistentClass>();
imports = new HashMap<String,String>();
collections = new HashMap<String,Collection>();
tables = new TreeMap<String,Table>();
namedQueries = new HashMap<String,NamedQueryDefinition>();
namedSqlQueries = new HashMap<String,NamedSQLQueryDefinition>();
sqlResultSetMappings = new HashMap<String, ResultSetMappingDefinition>();
typeDefs = new HashMap<String,TypeDef>();
filterDefinitions = new HashMap<String, FilterDefinition>();
fetchProfiles = new HashMap<String, FetchProfile>();
auxiliaryDatabaseObjects = new ArrayList<AuxiliaryDatabaseObject>();
tableNameBinding = new HashMap();
columnNameBindingPerTable = new HashMap();
secondPasses = new ArrayList<SecondPass>();
propertyReferences = new ArrayList<Mappings.PropertyReference>();
extendsQueue = new HashMap<ExtendsQueueEntry, String>();
xmlHelper = new XMLHelper();
interceptor = EmptyInterceptor.INSTANCE;
properties = Environment.getProperties();
entityResolver = XMLHelper.DEFAULT_DTD_RESOLVER;
eventListeners = new EventListeners();
sqlFunctions = new HashMap<String, SQLFunction>();
entityTuplizerFactory = new EntityTuplizerFactory();
// componentTuplizerFactory = new ComponentTuplizerFactory();
identifierGeneratorFactory = new DefaultIdentifierGeneratorFactory();
mappedSuperClasses = new HashMap<Class<?>, MappedSuperclass>();
metadataSourcePrecedence = Collections.emptyList();
namedGenerators = new HashMap<String, IdGenerator>();
joins = new HashMap<String, Map<String, Join>>();
classTypes = new HashMap<String, AnnotatedClassType>();
generatorTables = new HashMap<String, Properties>();
defaultNamedQueryNames = new HashSet<String>();
defaultNamedNativeQueryNames = new HashSet<String>();
defaultSqlResultSetMappingNames = new HashSet<String>();
defaultNamedGenerators = new HashSet<String>();
uniqueConstraintHoldersByTable = new HashMap<Table, List<UniqueConstraintHolder>>();
mappedByResolver = new HashMap<String, String>();
propertyRefResolver = new HashMap<String, String>();
caches = new ArrayList<CacheHolder>();
namingStrategy = EJB3NamingStrategy.INSTANCE;
setEntityResolver( new EJB3DTDEntityResolver() );
anyMetaDefs = new HashMap<String, AnyMetaDef>();
propertiesAnnotatedWithMapsId = new HashMap<XClass, Map<String, PropertyData>>();
propertiesAnnotatedWithIdAndToOne = new HashMap<XClass, Map<String, PropertyData>>();
}
public EntityTuplizerFactory getEntityTuplizerFactory() {
return entityTuplizerFactory;
}
public ReflectionManager getReflectionManager() {
return reflectionManager;
}
// public ComponentTuplizerFactory getComponentTuplizerFactory() {
// return componentTuplizerFactory;
// }
/**
* Iterate the entity mappings
*
* @return Iterator of the entity mappings currently contained in the configuration.
*/
public Iterator<PersistentClass> getClassMappings() {
return classes.values().iterator();
}
/**
* Iterate the collection mappings
*
* @return Iterator of the collection mappings currently contained in the configuration.
*/
public Iterator getCollectionMappings() {
return collections.values().iterator();
}
/**
* Iterate the table mappings
*
* @return Iterator of the table mappings currently contained in the configuration.
*/
public Iterator<Table> getTableMappings() {
return tables.values().iterator();
}
/**
* Iterate the mapped super class mappings
* EXPERIMENTAL Consider this API as PRIVATE
*
* @return iterator over the MappedSuperclass mapping currently contained in the configuration.
*/
public Iterator<MappedSuperclass> getMappedSuperclassMappings() {
return mappedSuperClasses.values().iterator();
}
/**
* Get the mapping for a particular entity
*
* @param entityName An entity name.
* @return the entity mapping information
*/
public PersistentClass getClassMapping(String entityName) {
return classes.get( entityName );
}
/**
* Get the mapping for a particular collection role
*
* @param role a collection role
* @return The collection mapping information
*/
public Collection getCollectionMapping(String role) {
return collections.get( role );
}
/**
* Set a custom entity resolver. This entity resolver must be
* set before addXXX(misc) call.
* Default value is {@link org.hibernate.util.DTDEntityResolver}
*
* @param entityResolver entity resolver to use
*/
public void setEntityResolver(EntityResolver entityResolver) {
this.entityResolver = entityResolver;
}
public EntityResolver getEntityResolver() {
return entityResolver;
}
/**
* Retrieve the user-supplied delegate to handle non-existent entity
* scenarios. May be null.
*
* @return The user-supplied delegate
*/
public EntityNotFoundDelegate getEntityNotFoundDelegate() {
return entityNotFoundDelegate;
}
/**
* Specify a user-supplied delegate to be used to handle scenarios where an entity could not be
* located by specified id. This is mainly intended for EJB3 implementations to be able to
* control how proxy initialization errors should be handled...
*
* @param entityNotFoundDelegate The delegate to use
*/
public void setEntityNotFoundDelegate(EntityNotFoundDelegate entityNotFoundDelegate) {
this.entityNotFoundDelegate = entityNotFoundDelegate;
}
/**
* Read mappings from a particular XML file
*
* @param xmlFile a path to a file
* @return this (for method chaining purposes)
* @throws org.hibernate.MappingException Indicates inability to locate or parse
* the specified mapping file.
* @see #addFile(java.io.File)
*/
public Configuration addFile(String xmlFile) throws MappingException {
return addFile( new File( xmlFile ) );
}
/**
* Read mappings from a particular XML file
*
* @param xmlFile a path to a file
* @return this (for method chaining purposes)
* @throws MappingException Indicates inability to locate the specified mapping file. Historically this could
* have indicated a problem parsing the XML document, but that is now delayed until after {@link #buildMappings}
*/
public Configuration addFile(final File xmlFile) throws MappingException {
log.info( "Reading mappings from file: " + xmlFile.getPath() );
final String name = xmlFile.getAbsolutePath();
final InputSource inputSource;
try {
inputSource = new InputSource( new FileInputStream( xmlFile ) );
}
catch ( FileNotFoundException e ) {
throw new MappingNotFoundException( "file", xmlFile.toString() );
}
add(
new XMLHelper.MetadataXmlSource() {
private final Origin origin = new Origin() {
public String getType() {
return "file";
}
public String getName() {
return name;
}
};
public Origin getOrigin() {
return origin;
}
public InputSource getInputSource() {
return inputSource;
}
}
);
return this;
}
private XMLHelper.MetadataXml add(XMLHelper.MetadataXmlSource metadataXmlSource) {
XMLHelper.MetadataXml metadataXml = xmlHelper.readMappingDocument( entityResolver, metadataXmlSource );
add( metadataXml );
return metadataXml;
}
private void add(XMLHelper.MetadataXml metadataXml) {
if ( inSecondPass || !metadataXml.isOrmXml() ) {
metadataSourceQueue.add( metadataXml );
}
else {
final MetadataProvider metadataProvider = ( (MetadataProviderInjector) reflectionManager ).getMetadataProvider();
JPAMetadataProvider jpaMetadataProvider = ( JPAMetadataProvider ) metadataProvider;
List<String> classNames = jpaMetadataProvider.getXMLContext().addDocument( metadataXml.getXmlDocument() );
for ( String className : classNames ) {
try {
metadataSourceQueue.add( reflectionManager.classForName( className, this.getClass() ) );
}
catch ( ClassNotFoundException e ) {
throw new AnnotationException( "Unable to load class defined in XML: " + className, e );
}
}
}
}
/**
* Add a cached mapping file. A cached file is a serialized representation
* of the DOM structure of a particular mapping. It is saved from a previous
* call as a file with the name <tt>xmlFile + ".bin"</tt> where xmlFile is
* the name of the original mapping file.
* </p>
* If a cached <tt>xmlFile + ".bin"</tt> exists and is newer than
* <tt>xmlFile</tt> the <tt>".bin"</tt> file will be read directly. Otherwise
* xmlFile is read and then serialized to <tt>xmlFile + ".bin"</tt> for use
* the next time.
*
* @param xmlFile The cacheable mapping file to be added.
* @return this (for method chaining purposes)
* @throws MappingException Indicates problems reading the cached file or processing
* the non-cached file.
*/
public Configuration addCacheableFile(File xmlFile) throws MappingException {
File cachedFile = determineCachedDomFile( xmlFile );
try {
return addCacheableFileStrictly( xmlFile );
}
catch ( SerializationException e ) {
log.warn( "Could not deserialize cache file: " + cachedFile.getPath() + " : " + e );
}
catch ( FileNotFoundException e ) {
log.warn( "I/O reported cached file could not be found : " + cachedFile.getPath() + " : " + e );
}
final String name = xmlFile.getAbsolutePath();
final InputSource inputSource;
try {
inputSource = new InputSource( new FileInputStream( xmlFile ) );
}
catch ( FileNotFoundException e ) {
throw new MappingNotFoundException( "file", xmlFile.toString() );
}
log.info( "Reading mappings from file: " + xmlFile );
XMLHelper.MetadataXml metadataXml = add(
new XMLHelper.MetadataXmlSource() {
private final Origin origin = new Origin() {
public String getType() {
return "file";
}
public String getName() {
return name;
}
};
public Origin getOrigin() {
return origin;
}
public InputSource getInputSource() {
return inputSource;
}
}
);
try {
log.debug( "Writing cache file for: " + xmlFile + " to: " + cachedFile );
SerializationHelper.serialize( ( Serializable ) metadataXml.getXmlDocument(), new FileOutputStream( cachedFile ) );
}
catch ( SerializationException e ) {
log.warn( "Could not write cached file: " + cachedFile, e );
}
catch ( FileNotFoundException e ) {
log.warn( "I/O reported error writing cached file : " + cachedFile.getPath(), e );
}
return this;
}
private File determineCachedDomFile(File xmlFile) {
return new File( xmlFile.getAbsolutePath() + ".bin" );
}
/**
* <b>INTENDED FOR TESTSUITE USE ONLY!</b>
* <p/>
* Much like {@link #addCacheableFile(File)} except that here we will fail immediately if
* the cache version cannot be found or used for whatever reason
*
* @param xmlFile The xml file, not the bin!
*
* @return The dom "deserialized" from the cached file.
*
* @throws SerializationException Indicates a problem deserializing the cached dom tree
* @throws FileNotFoundException Indicates that the cached file was not found or was not usable.
*/
public Configuration addCacheableFileStrictly(File xmlFile) throws SerializationException, FileNotFoundException {
final File cachedFile = determineCachedDomFile( xmlFile );
final boolean useCachedFile = xmlFile.exists()
&& cachedFile.exists()
&& xmlFile.lastModified() < cachedFile.lastModified();
if ( ! useCachedFile ) {
throw new FileNotFoundException( "Cached file could not be found or could not be used" );
}
log.info( "Reading mappings from cache file: " + cachedFile );
Document document = ( Document ) SerializationHelper.deserialize( new FileInputStream( cachedFile ) );
add( xmlHelper.buildMetadataXml( document, "file", xmlFile.getAbsolutePath() ) );
return this;
}
/**
* Add a cacheable mapping file.
*
* @param xmlFile The name of the file to be added. This must be in a form
* useable to simply construct a {@link java.io.File} instance.
* @return this (for method chaining purposes)
* @throws MappingException Indicates problems reading the cached file or processing
* the non-cached file.
* @see #addCacheableFile(java.io.File)
*/
public Configuration addCacheableFile(String xmlFile) throws MappingException {
return addCacheableFile( new File( xmlFile ) );
}
/**
* Read mappings from a <tt>String</tt>
*
* @param xml an XML string
* @return this (for method chaining purposes)
* @throws org.hibernate.MappingException Indicates problems parsing the
* given XML string
*/
public Configuration addXML(String xml) throws MappingException {
if ( log.isDebugEnabled() ) {
log.debug( "Mapping XML:\n" + xml );
}
final InputSource inputSource = new InputSource( new StringReader( xml ) );
add(
new XMLHelper.MetadataXmlSource() {
final Origin origin = new Origin() {
public String getType() {
return "string";
}
public String getName() {
return "XML String";
}
};
public Origin getOrigin() {
return origin;
}
public InputSource getInputSource() {
return inputSource;
}
}
);
return this;
}
/**
* Read mappings from a <tt>URL</tt>
*
* @param url The url for the mapping document to be read.
* @return this (for method chaining purposes)
* @throws MappingException Indicates problems reading the URL or processing
* the mapping document.
*/
public Configuration addURL(URL url) throws MappingException {
final String urlExternalForm = url.toExternalForm();
if ( log.isDebugEnabled() ) {
log.debug( "Reading mapping document from URL : {}", urlExternalForm );
}
try {
add( url.openStream(), "URL", urlExternalForm );
}
catch ( IOException e ) {
throw new InvalidMappingException( "Unable to open url stream [" + urlExternalForm + "]", "URL", urlExternalForm, e );
}
return this;
}
private XMLHelper.MetadataXml add(InputStream inputStream, final String type, final String name) {
final XMLHelper.MetadataXmlSource.Origin origin = new XMLHelper.MetadataXmlSource.Origin() {
public String getType() {
return type;
}
public String getName() {
return name;
}
};
final InputSource inputSource = new InputSource( inputStream );
try {
return add(
new XMLHelper.MetadataXmlSource() {
public Origin getOrigin() {
return origin;
}
public InputSource getInputSource() {
return inputSource;
}
}
);
}
finally {
try {
inputStream.close();
}
catch ( IOException ignore ) {
log.trace( "Was unable to close input stream" );
}
}
}
/**
* Read mappings from a DOM <tt>Document</tt>
*
* @param doc The DOM document
* @return this (for method chaining purposes)
* @throws MappingException Indicates problems reading the DOM or processing
* the mapping document.
*/
public Configuration addDocument(org.w3c.dom.Document doc) throws MappingException {
if ( log.isDebugEnabled() ) {
log.debug( "Mapping document:\n" + doc );
}
final Document document = xmlHelper.createDOMReader().read( doc );
add( xmlHelper.buildMetadataXml( document, "unknown", null ) );
return this;
}
/**
* Read mappings from an {@link java.io.InputStream}.
*
* @param xmlInputStream The input stream containing a DOM.
* @return this (for method chaining purposes)
* @throws MappingException Indicates problems reading the stream, or
* processing the contained mapping document.
*/
public Configuration addInputStream(InputStream xmlInputStream) throws MappingException {
add( xmlInputStream, "input stream", null );
return this;
}
/**
* Read mappings as a application resource (i.e. classpath lookup).
*
* @param resourceName The resource name
* @param classLoader The class loader to use.
* @return this (for method chaining purposes)
* @throws MappingException Indicates problems locating the resource or
* processing the contained mapping document.
*/
public Configuration addResource(String resourceName, ClassLoader classLoader) throws MappingException {
log.info( "Reading mappings from resource: " + resourceName );
InputStream resourceInputStream = classLoader.getResourceAsStream( resourceName );
if ( resourceInputStream == null ) {
throw new MappingNotFoundException( "resource", resourceName );
}
add( resourceInputStream, "resource", resourceName );
return this;
}
/**
* Read mappings as a application resourceName (i.e. classpath lookup)
* trying different class loaders.
*
* @param resourceName The resource name
* @return this (for method chaining purposes)
* @throws MappingException Indicates problems locating the resource or
* processing the contained mapping document.
*/
public Configuration addResource(String resourceName) throws MappingException {
log.info( "Reading mappings from resource : " + resourceName );
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
InputStream resourceInputStream = null;
if ( contextClassLoader != null ) {
resourceInputStream = contextClassLoader.getResourceAsStream( resourceName );
}
if ( resourceInputStream == null ) {
resourceInputStream = Environment.class.getClassLoader().getResourceAsStream( resourceName );
}
if ( resourceInputStream == null ) {
throw new MappingNotFoundException( "resource", resourceName );
}
add( resourceInputStream, "resource", resourceName );
return this;
}
/**
* Read a mapping as an application resource using the convention that a class
* named <tt>foo.bar.Foo</tt> is mapped by a file <tt>foo/bar/Foo.hbm.xml</tt>
* which can be resolved as a classpath resource.
*
* @param persistentClass The mapped class
* @return this (for method chaining purposes)
* @throws MappingException Indicates problems locating the resource or
* processing the contained mapping document.
*/
public Configuration addClass(Class persistentClass) throws MappingException {
String mappingResourceName = persistentClass.getName().replace( '.', '/' ) + ".hbm.xml";
log.info( "Reading mappings from resource: " + mappingResourceName );
return addResource( mappingResourceName, persistentClass.getClassLoader() );
}
/**
* Read metadata from the annotations associated with this class.
*
* @param annotatedClass The class containing annotations
*
* @return this (for method chaining)
*/
@SuppressWarnings({ "unchecked" })
public Configuration addAnnotatedClass(Class annotatedClass) {
XClass xClass = reflectionManager.toXClass( annotatedClass );
metadataSourceQueue.add( xClass );
return this;
}
/**
* Read package-level metadata.
*
* @param packageName java package name
*
* @return this (for method chaining)
*
* @throws MappingException in case there is an error in the mapping data
*/
public Configuration addPackage(String packageName) throws MappingException {
log.info( "Mapping package {}", packageName );
try {
AnnotationBinder.bindPackage( packageName, createMappings() );
return this;
}
catch ( MappingException me ) {
log.error( "Could not parse the package-level metadata [" + packageName + "]" );
throw me;
}
}
/**
* Read all mappings from a jar file
* <p/>
* Assumes that any file named <tt>*.hbm.xml</tt> is a mapping document.
*
* @param jar a jar file
* @return this (for method chaining purposes)
* @throws MappingException Indicates problems reading the jar file or
* processing the contained mapping documents.
*/
public Configuration addJar(File jar) throws MappingException {
log.info( "Searching for mapping documents in jar: " + jar.getName() );
JarFile jarFile = null;
try {
try {
jarFile = new JarFile( jar );
}
catch (IOException ioe) {
throw new InvalidMappingException(
"Could not read mapping documents from jar: " + jar.getName(), "jar", jar.getName(),
ioe
);
}
Enumeration jarEntries = jarFile.entries();
while ( jarEntries.hasMoreElements() ) {
ZipEntry ze = (ZipEntry) jarEntries.nextElement();
if ( ze.getName().endsWith( ".hbm.xml" ) ) {
log.info( "Found mapping document in jar: " + ze.getName() );
try {
addInputStream( jarFile.getInputStream( ze ) );
}
catch (Exception e) {
throw new InvalidMappingException(
"Could not read mapping documents from jar: " + jar.getName(),
"jar",
jar.getName(),
e
);
}
}
}
}
finally {
try {
if ( jarFile != null ) {
jarFile.close();
}
}
catch (IOException ioe) {
log.error("could not close jar", ioe);
}
}
return this;
}
/**
* Read all mapping documents from a directory tree.
* <p/>
* Assumes that any file named <tt>*.hbm.xml</tt> is a mapping document.
*
* @param dir The directory
* @return this (for method chaining purposes)
* @throws MappingException Indicates problems reading the jar file or
* processing the contained mapping documents.
*/
public Configuration addDirectory(File dir) throws MappingException {
File[] files = dir.listFiles();
for ( File file : files ) {
if ( file.isDirectory() ) {
addDirectory( file );
}
else if ( file.getName().endsWith( ".hbm.xml" ) ) {
addFile( file );
}
}
return this;
}
/**
* Create a new <tt>Mappings</tt> to add class and collection mappings to.
*
* @return The created mappings
*/
public Mappings createMappings() {
return new MappingsImpl();
}
@SuppressWarnings({ "unchecked" })
private Iterator<IdentifierGenerator> iterateGenerators(Dialect dialect) throws MappingException {
TreeMap generators = new TreeMap();
String defaultCatalog = properties.getProperty( Environment.DEFAULT_CATALOG );
String defaultSchema = properties.getProperty( Environment.DEFAULT_SCHEMA );
for ( PersistentClass pc : classes.values() ) {
if ( !pc.isInherited() ) {
IdentifierGenerator ig = pc.getIdentifier().createIdentifierGenerator(
getIdentifierGeneratorFactory(),
dialect,
defaultCatalog,
defaultSchema,
(RootClass) pc
);
if ( ig instanceof PersistentIdentifierGenerator ) {
generators.put( ( (PersistentIdentifierGenerator) ig ).generatorKey(), ig );
}
else if ( ig instanceof IdentifierGeneratorAggregator ) {
( (IdentifierGeneratorAggregator) ig ).registerPersistentGenerators( generators );
}
}
}
for ( Collection collection : collections.values() ) {
if ( collection.isIdentified() ) {
IdentifierGenerator ig = ( ( IdentifierCollection ) collection ).getIdentifier().createIdentifierGenerator(
getIdentifierGeneratorFactory(),
dialect,
defaultCatalog,
defaultSchema,
null
);
if ( ig instanceof PersistentIdentifierGenerator ) {
generators.put( ( (PersistentIdentifierGenerator) ig ).generatorKey(), ig );
}
}
}
return generators.values().iterator();
}
/**
* Generate DDL for dropping tables
*
* @param dialect The dialect for which to generate the drop script
* @return The sequence of DDL commands to drop the schema objects
* @throws HibernateException Generally indicates a problem calling {@link #buildMappings()}
* @see org.hibernate.tool.hbm2ddl.SchemaExport
*/
public String[] generateDropSchemaScript(Dialect dialect) throws HibernateException {
secondPassCompile();
String defaultCatalog = properties.getProperty( Environment.DEFAULT_CATALOG );
String defaultSchema = properties.getProperty( Environment.DEFAULT_SCHEMA );
ArrayList<String> script = new ArrayList<String>( 50 );
// drop them in reverse order in case db needs it done that way...
{
ListIterator itr = auxiliaryDatabaseObjects.listIterator( auxiliaryDatabaseObjects.size() );
while ( itr.hasPrevious() ) {
AuxiliaryDatabaseObject object = (AuxiliaryDatabaseObject) itr.previous();
if ( object.appliesToDialect( dialect ) ) {
script.add( object.sqlDropString( dialect, defaultCatalog, defaultSchema ) );
}
}
}
if ( dialect.dropConstraints() ) {
Iterator itr = getTableMappings();
while ( itr.hasNext() ) {
Table table = (Table) itr.next();
if ( table.isPhysicalTable() ) {
Iterator subItr = table.getForeignKeyIterator();
while ( subItr.hasNext() ) {
ForeignKey fk = (ForeignKey) subItr.next();
if ( fk.isPhysicalConstraint() ) {
script.add(
fk.sqlDropString(
dialect,
defaultCatalog,
defaultSchema
)
);
}
}
}
}
}
Iterator itr = getTableMappings();
while ( itr.hasNext() ) {
Table table = (Table) itr.next();
if ( table.isPhysicalTable() ) {
/*Iterator subIter = table.getIndexIterator();
while ( subIter.hasNext() ) {
Index index = (Index) subIter.next();
if ( !index.isForeignKey() || !dialect.hasImplicitIndexForForeignKey() ) {
script.add( index.sqlDropString(dialect) );
}
}*/
script.add(
table.sqlDropString(
dialect,
defaultCatalog,
defaultSchema
)
);
}
}
itr = iterateGenerators( dialect );
while ( itr.hasNext() ) {
String[] lines = ( (PersistentIdentifierGenerator) itr.next() ).sqlDropStrings( dialect );
script.addAll( Arrays.asList( lines ) );
}
return ArrayHelper.toStringArray( script );
}
/**
* @param dialect The dialect for which to generate the creation script
*
* @return The sequence of DDL commands to create the schema objects
*
* @throws HibernateException Generally indicates a problem calling {@link #buildMappings()}
*
* @see org.hibernate.tool.hbm2ddl.SchemaExport
*/
@SuppressWarnings({ "unchecked" })
public String[] generateSchemaCreationScript(Dialect dialect) throws HibernateException {
secondPassCompile();
ArrayList<String> script = new ArrayList<String>( 50 );
String defaultCatalog = properties.getProperty( Environment.DEFAULT_CATALOG );
String defaultSchema = properties.getProperty( Environment.DEFAULT_SCHEMA );
Iterator iter = getTableMappings();
while ( iter.hasNext() ) {
Table table = (Table) iter.next();
if ( table.isPhysicalTable() ) {
script.add(
table.sqlCreateString(
dialect,
mapping,
defaultCatalog,
defaultSchema
)
);
Iterator<String> comments = table.sqlCommentStrings( dialect, defaultCatalog, defaultSchema );
while ( comments.hasNext() ) {
script.add( comments.next() );
}
}
}
iter = getTableMappings();
while ( iter.hasNext() ) {
Table table = (Table) iter.next();
if ( table.isPhysicalTable() ) {
if ( !dialect.supportsUniqueConstraintInCreateAlterTable() ) {
Iterator subIter = table.getUniqueKeyIterator();
while ( subIter.hasNext() ) {
UniqueKey uk = (UniqueKey) subIter.next();
String constraintString = uk.sqlCreateString( dialect, mapping, defaultCatalog, defaultSchema );
if (constraintString != null) script.add( constraintString );
}
}
Iterator subIter = table.getIndexIterator();
while ( subIter.hasNext() ) {
Index index = (Index) subIter.next();
script.add(
index.sqlCreateString(
dialect,
mapping,
defaultCatalog,
defaultSchema
)
);
}
if ( dialect.hasAlterTable() ) {
subIter = table.getForeignKeyIterator();
while ( subIter.hasNext() ) {
ForeignKey fk = (ForeignKey) subIter.next();
if ( fk.isPhysicalConstraint() ) {
script.add(
fk.sqlCreateString(
dialect, mapping,
defaultCatalog,
defaultSchema
)
);
}
}
}
}
}
iter = iterateGenerators( dialect );
while ( iter.hasNext() ) {
String[] lines = ( (PersistentIdentifierGenerator) iter.next() ).sqlCreateStrings( dialect );
script.addAll( Arrays.asList( lines ) );
}
for ( AuxiliaryDatabaseObject auxiliaryDatabaseObject : auxiliaryDatabaseObjects ) {
if ( auxiliaryDatabaseObject.appliesToDialect( dialect ) ) {
script.add( auxiliaryDatabaseObject.sqlCreateString( dialect, mapping, defaultCatalog, defaultSchema ) );
}
}
return ArrayHelper.toStringArray( script );
}
/**
* @param dialect The dialect for which to generate the creation script
* @param databaseMetadata The database catalog information for the database to be updated; needed to work out what
* should be created/altered
*
* @return The sequence of DDL commands to apply the schema objects
*
* @throws HibernateException Generally indicates a problem calling {@link #buildMappings()}
*
* @see org.hibernate.tool.hbm2ddl.SchemaExport
*/
@SuppressWarnings({ "unchecked" })
public String[] generateSchemaUpdateScript(Dialect dialect, DatabaseMetadata databaseMetadata)
throws HibernateException {
secondPassCompile();
String defaultCatalog = properties.getProperty( Environment.DEFAULT_CATALOG );
String defaultSchema = properties.getProperty( Environment.DEFAULT_SCHEMA );
ArrayList<String> script = new ArrayList<String>( 50 );
Iterator iter = getTableMappings();
while ( iter.hasNext() ) {
Table table = (Table) iter.next();
if ( table.isPhysicalTable() ) {
TableMetadata tableInfo = databaseMetadata.getTableMetadata(
table.getName(),
( table.getSchema() == null ) ? defaultSchema : table.getSchema(),
( table.getCatalog() == null ) ? defaultCatalog : table.getCatalog(),
table.isQuoted()
);
if ( tableInfo == null ) {
script.add(
table.sqlCreateString(
dialect,
mapping,
defaultCatalog,
defaultSchema
)
);
}
else {
Iterator<String> subiter = table.sqlAlterStrings(
dialect,
mapping,
tableInfo,
defaultCatalog,
defaultSchema
);
while ( subiter.hasNext() ) {
script.add( subiter.next() );
}
}
Iterator<String> comments = table.sqlCommentStrings( dialect, defaultCatalog, defaultSchema );
while ( comments.hasNext() ) {
script.add( comments.next() );
}
}
}
iter = getTableMappings();
while ( iter.hasNext() ) {
Table table = (Table) iter.next();
if ( table.isPhysicalTable() ) {
TableMetadata tableInfo = databaseMetadata.getTableMetadata(
table.getName(),
table.getSchema(),
table.getCatalog(),
table.isQuoted()
);
if ( dialect.hasAlterTable() ) {
Iterator subIter = table.getForeignKeyIterator();
while ( subIter.hasNext() ) {
ForeignKey fk = (ForeignKey) subIter.next();
if ( fk.isPhysicalConstraint() ) {
boolean create = tableInfo == null || (
tableInfo.getForeignKeyMetadata( fk ) == null && (
//Icky workaround for MySQL bug:
!( dialect instanceof MySQLDialect ) ||
tableInfo.getIndexMetadata( fk.getName() ) == null
)
);
if ( create ) {
script.add(
fk.sqlCreateString(
dialect,
mapping,
defaultCatalog,
defaultSchema
)
);
}
}
}
}
Iterator subIter = table.getIndexIterator();
while ( subIter.hasNext() ) {
final Index index = (Index) subIter.next();
// Skip if index already exists
if ( tableInfo != null && StringHelper.isNotEmpty( index.getName() ) ) {
final IndexMetadata meta = tableInfo.getIndexMetadata( index.getName() );
if ( meta != null ) {
continue;
}
}
script.add(
index.sqlCreateString(
dialect,
mapping,
defaultCatalog,
defaultSchema
)
);
}
//broken, 'cos we don't generate these with names in SchemaExport
// subIter = table.getUniqueKeyIterator();
// while ( subIter.hasNext() ) {
// UniqueKey uk = (UniqueKey) subIter.next();
// if ( tableInfo==null || tableInfo.getIndexMetadata( uk.getFilterName() ) == null ) {
// script.add( uk.sqlCreateString(dialect, mapping) );
// }
// }
}
}
iter = iterateGenerators( dialect );
while ( iter.hasNext() ) {
PersistentIdentifierGenerator generator = (PersistentIdentifierGenerator) iter.next();
Object key = generator.generatorKey();
if ( !databaseMetadata.isSequence( key ) && !databaseMetadata.isTable( key ) ) {
String[] lines = generator.sqlCreateStrings( dialect );
script.addAll( Arrays.asList( lines ) );
}
}
return ArrayHelper.toStringArray( script );
}
public void validateSchema(Dialect dialect, DatabaseMetadata databaseMetadata)throws HibernateException {
secondPassCompile();
String defaultCatalog = properties.getProperty( Environment.DEFAULT_CATALOG );
String defaultSchema = properties.getProperty( Environment.DEFAULT_SCHEMA );
Iterator iter = getTableMappings();
while ( iter.hasNext() ) {
Table table = (Table) iter.next();
if ( table.isPhysicalTable() ) {
TableMetadata tableInfo = databaseMetadata.getTableMetadata(
table.getName(),
( table.getSchema() == null ) ? defaultSchema : table.getSchema(),
( table.getCatalog() == null ) ? defaultCatalog : table.getCatalog(),
table.isQuoted());
if ( tableInfo == null ) {
throw new HibernateException( "Missing table: " + table.getName() );
}
else {
table.validateColumns( dialect, mapping, tableInfo );
}
}
}
iter = iterateGenerators( dialect );
while ( iter.hasNext() ) {
PersistentIdentifierGenerator generator = (PersistentIdentifierGenerator) iter.next();
Object key = generator.generatorKey();
if ( !databaseMetadata.isSequence( key ) && !databaseMetadata.isTable( key ) ) {
throw new HibernateException( "Missing sequence or table: " + key );
}
}
}
private void validate() throws MappingException {
Iterator iter = classes.values().iterator();
while ( iter.hasNext() ) {
( (PersistentClass) iter.next() ).validate( mapping );
}
iter = collections.values().iterator();
while ( iter.hasNext() ) {
( (Collection) iter.next() ).validate( mapping );
}
}
/**
* Call this to ensure the mappings are fully compiled/built. Usefull to ensure getting
* access to all information in the metamodel when calling e.g. getClassMappings().
*/
public void buildMappings() {
secondPassCompile();
}
protected void secondPassCompile() throws MappingException {
log.trace( "Starting secondPassCompile() processing" );
//process default values first
{
if ( !isDefaultProcessed ) {
//use global delimiters if orm.xml declare it
final Object isDelimited = reflectionManager.getDefaults().get( "delimited-identifier" );
if ( isDelimited != null && isDelimited == Boolean.TRUE ) {
getProperties().put( Environment.GLOBALLY_QUOTED_IDENTIFIERS, "true" );
}
AnnotationBinder.bindDefaults( createMappings() );
isDefaultProcessed = true;
}
}
// process metadata queue
{
metadataSourceQueue.syncAnnotatedClasses();
metadataSourceQueue.processMetadata( determineMetadataSourcePrecedence() );
}
// process cache queue
{
for ( CacheHolder holder : caches ) {
if ( holder.isClass ) {
applyCacheConcurrencyStrategy( holder );
}
else {
applyCollectionCacheConcurrencyStrategy( holder );
}
}
caches.clear();
}
try {
inSecondPass = true;
processSecondPassesOfType( PkDrivenByDefaultMapsIdSecondPass.class );
processSecondPassesOfType( SetSimpleValueTypeSecondPass.class );
processSecondPassesOfType( CopyIdentifierComponentSecondPass.class );
processFkSecondPassInOrder();
processSecondPassesOfType( CreateKeySecondPass.class );
processSecondPassesOfType( SecondaryTableSecondPass.class );
originalSecondPassCompile();
inSecondPass = false;
}
catch ( RecoverableException e ) {
//the exception was not recoverable after all
throw ( RuntimeException ) e.getCause();
}
for ( Map.Entry<Table, List<UniqueConstraintHolder>> tableListEntry : uniqueConstraintHoldersByTable.entrySet() ) {
final Table table = tableListEntry.getKey();
final List<UniqueConstraintHolder> uniqueConstraints = tableListEntry.getValue();
int uniqueIndexPerTable = 0;
for ( UniqueConstraintHolder holder : uniqueConstraints ) {
uniqueIndexPerTable++;
final String keyName = StringHelper.isEmpty( holder.getName() )
? "key" + uniqueIndexPerTable
: holder.getName();
buildUniqueKeyFromColumnNames( table, keyName, holder.getColumns() );
}
}
applyConstraintsToDDL();
}
private void processSecondPassesOfType(Class<? extends SecondPass> type) {
Iterator iter = secondPasses.iterator();
while ( iter.hasNext() ) {
SecondPass sp = ( SecondPass ) iter.next();
//do the second pass of simple value types first and remove them
if ( type.isInstance( sp ) ) {
sp.doSecondPass( classes );
iter.remove();
}
}
}
/**
* Processes FKSecondPass instances trying to resolve any
* graph circularity (ie PK made of a many to one linking to
* an entity having a PK made of a ManyToOne ...).
*/
private void processFkSecondPassInOrder() {
log.debug( "processing fk mappings (*ToOne and JoinedSubclass)" );
List<FkSecondPass> fkSecondPasses = getFKSecondPassesOnly();
if ( fkSecondPasses.size() == 0 ) {
return; // nothing to do here
}
// split FkSecondPass instances into primary key and non primary key FKs.
// While doing so build a map of class names to FkSecondPass instances depending on this class.
Map<String, Set<FkSecondPass>> isADependencyOf = new HashMap<String, Set<FkSecondPass>>();
List<FkSecondPass> endOfQueueFkSecondPasses = new ArrayList<FkSecondPass>( fkSecondPasses.size() );
for ( FkSecondPass sp : fkSecondPasses ) {
if ( sp.isInPrimaryKey() ) {
String referenceEntityName = sp.getReferencedEntityName();
PersistentClass classMapping = getClassMapping( referenceEntityName );
String dependentTable = classMapping.getTable().getQuotedName();
if ( !isADependencyOf.containsKey( dependentTable ) ) {
isADependencyOf.put( dependentTable, new HashSet<FkSecondPass>() );
}
isADependencyOf.get( dependentTable ).add( sp );
}
else {
endOfQueueFkSecondPasses.add( sp );
}
}
// using the isADependencyOf map we order the FkSecondPass recursively instances into the right order for processing
List<FkSecondPass> orderedFkSecondPasses = new ArrayList<FkSecondPass>( fkSecondPasses.size() );
for ( String tableName : isADependencyOf.keySet() ) {
buildRecursiveOrderedFkSecondPasses( orderedFkSecondPasses, isADependencyOf, tableName, tableName );
}
// process the ordered FkSecondPasses
for ( FkSecondPass sp : orderedFkSecondPasses ) {
sp.doSecondPass( classes );
}
processEndOfQueue( endOfQueueFkSecondPasses );
}
/**
* @return Returns a list of all <code>secondPasses</code> instances which are a instance of
* <code>FkSecondPass</code>.
*/
private List<FkSecondPass> getFKSecondPassesOnly() {
Iterator iter = secondPasses.iterator();
List<FkSecondPass> fkSecondPasses = new ArrayList<FkSecondPass>( secondPasses.size() );
while ( iter.hasNext() ) {
SecondPass sp = ( SecondPass ) iter.next();
//do the second pass of fk before the others and remove them
if ( sp instanceof FkSecondPass ) {
fkSecondPasses.add( ( FkSecondPass ) sp );
iter.remove();
}
}
return fkSecondPasses;
}
/**
* Recursively builds a list of FkSecondPass instances ready to be processed in this order.
* Checking all dependencies recursively seems quite expensive, but the original code just relied
* on some sort of table name sorting which failed in certain circumstances.
* <p/>
* See <tt>ANN-722</tt> and <tt>ANN-730</tt>
*
* @param orderedFkSecondPasses The list containing the <code>FkSecondPass<code> instances ready
* for processing.
* @param isADependencyOf Our lookup data structure to determine dependencies between tables
* @param startTable Table name to start recursive algorithm.
* @param currentTable The current table name used to check for 'new' dependencies.
*/
private void buildRecursiveOrderedFkSecondPasses(
List<FkSecondPass> orderedFkSecondPasses,
Map<String, Set<FkSecondPass>> isADependencyOf,
String startTable,
String currentTable) {
Set<FkSecondPass> dependencies = isADependencyOf.get( currentTable );
// bottom out
if ( dependencies == null || dependencies.size() == 0 ) {
return;
}
for ( FkSecondPass sp : dependencies ) {
String dependentTable = sp.getValue().getTable().getQuotedName();
if ( dependentTable.compareTo( startTable ) == 0 ) {
StringBuilder sb = new StringBuilder(
"Foreign key circularity dependency involving the following tables: "
);
throw new AnnotationException( sb.toString() );
}
buildRecursiveOrderedFkSecondPasses( orderedFkSecondPasses, isADependencyOf, startTable, dependentTable );
if ( !orderedFkSecondPasses.contains( sp ) ) {
orderedFkSecondPasses.add( 0, sp );
}
}
}
private void processEndOfQueue(List<FkSecondPass> endOfQueueFkSecondPasses) {
/*
* If a second pass raises a recoverableException, queue it for next round
* stop of no pass has to be processed or if the number of pass to processes
* does not diminish between two rounds.
* If some failing pass remain, raise the original exception
*/
boolean stopProcess = false;
RuntimeException originalException = null;
while ( !stopProcess ) {
List<FkSecondPass> failingSecondPasses = new ArrayList<FkSecondPass>();
Iterator<FkSecondPass> it = endOfQueueFkSecondPasses.listIterator();
while ( it.hasNext() ) {
final FkSecondPass pass = it.next();
try {
pass.doSecondPass( classes );
}
catch ( RecoverableException e ) {
failingSecondPasses.add( pass );
if ( originalException == null ) {
originalException = ( RuntimeException ) e.getCause();
}
}
}
stopProcess = failingSecondPasses.size() == 0 || failingSecondPasses.size() == endOfQueueFkSecondPasses.size();
endOfQueueFkSecondPasses = failingSecondPasses;
}
if ( endOfQueueFkSecondPasses.size() > 0 ) {
throw originalException;
}
}
private void buildUniqueKeyFromColumnNames(Table table, String keyName, String[] columnNames) {
keyName = normalizer.normalizeIdentifierQuoting( keyName );
UniqueKey uc;
int size = columnNames.length;
Column[] columns = new Column[size];
Set<Column> unbound = new HashSet<Column>();
Set<Column> unboundNoLogical = new HashSet<Column>();
for ( int index = 0; index < size; index++ ) {
final String logicalColumnName = normalizer.normalizeIdentifierQuoting( columnNames[index] );
try {
final String columnName = createMappings().getPhysicalColumnName( logicalColumnName, table );
columns[index] = new Column( columnName );
unbound.add( columns[index] );
//column equals and hashcode is based on column name
}
catch ( MappingException e ) {
unboundNoLogical.add( new Column( logicalColumnName ) );
}
}
for ( Column column : columns ) {
if ( table.containsColumn( column ) ) {
uc = table.getOrCreateUniqueKey( keyName );
uc.addColumn( table.getColumn( column ) );
unbound.remove( column );
}
}
if ( unbound.size() > 0 || unboundNoLogical.size() > 0 ) {
StringBuilder sb = new StringBuilder( "Unable to create unique key constraint (" );
for ( String columnName : columnNames ) {
sb.append( columnName ).append( ", " );
}
sb.setLength( sb.length() - 2 );
sb.append( ") on table " ).append( table.getName() ).append( ": " );
for ( Column column : unbound ) {
sb.append( column.getName() ).append( ", " );
}
for ( Column column : unboundNoLogical ) {
sb.append( column.getName() ).append( ", " );
}
sb.setLength( sb.length() - 2 );
sb.append( " not found" );
throw new AnnotationException( sb.toString() );
}
}
private void applyConstraintsToDDL() {
boolean applyOnDdl = getProperties().getProperty(
"hibernate.validator.apply_to_ddl",
"true"
)
.equalsIgnoreCase( "true" );
if ( !applyOnDdl ) {
return; // nothing to do in this case
}
applyHibernateValidatorLegacyConstraintsOnDDL();
applyBeanValidationConstraintsOnDDL();
}
@SuppressWarnings({ "unchecked" })
private void applyHibernateValidatorLegacyConstraintsOnDDL() {
//TODO search for the method only once and cache it?
Constructor validatorCtr = null;
Method applyMethod = null;
try {
Class classValidator = ReflectHelper.classForName(
"org.hibernate.validator.ClassValidator", this.getClass()
);
Class messageInterpolator = ReflectHelper.classForName(
"org.hibernate.validator.MessageInterpolator", this.getClass()
);
validatorCtr = classValidator.getDeclaredConstructor(
Class.class, ResourceBundle.class, messageInterpolator, Map.class, ReflectionManager.class
);
applyMethod = classValidator.getMethod( "apply", PersistentClass.class );
}
catch ( ClassNotFoundException e ) {
if ( !isValidatorNotPresentLogged ) {
log.info( "Hibernate Validator not found: ignoring" );
}
isValidatorNotPresentLogged = true;
}
catch ( NoSuchMethodException e ) {
throw new AnnotationException( e );
}
if ( applyMethod != null ) {
for ( PersistentClass persistentClazz : classes.values() ) {
//integrate the validate framework
String className = persistentClazz.getClassName();
if ( StringHelper.isNotEmpty( className ) ) {
try {
Object validator = validatorCtr.newInstance(
ReflectHelper.classForName( className ), null, null, null, reflectionManager
);
applyMethod.invoke( validator, persistentClazz );
}
catch ( Exception e ) {
log.warn( "Unable to apply constraints on DDL for " + className, e );
}
}
}
}
}
@SuppressWarnings({ "unchecked" })
private void applyBeanValidationConstraintsOnDDL() {
BeanValidationActivator.applyDDL( classes.values(), getProperties() );
}
private void originalSecondPassCompile() throws MappingException {
log.debug( "processing extends queue" );
processExtendsQueue();
log.debug( "processing collection mappings" );
Iterator itr = secondPasses.iterator();
while ( itr.hasNext() ) {
SecondPass sp = (SecondPass) itr.next();
if ( ! (sp instanceof QuerySecondPass) ) {
sp.doSecondPass( classes );
itr.remove();
}
}
log.debug( "processing native query and ResultSetMapping mappings" );
itr = secondPasses.iterator();
while ( itr.hasNext() ) {
SecondPass sp = (SecondPass) itr.next();
sp.doSecondPass( classes );
itr.remove();
}
log.debug( "processing association property references" );
itr = propertyReferences.iterator();
while ( itr.hasNext() ) {
Mappings.PropertyReference upr = (Mappings.PropertyReference) itr.next();
PersistentClass clazz = getClassMapping( upr.referencedClass );
if ( clazz == null ) {
throw new MappingException(
"property-ref to unmapped class: " +
upr.referencedClass
);
}
Property prop = clazz.getReferencedProperty( upr.propertyName );
if ( upr.unique ) {
( (SimpleValue) prop.getValue() ).setAlternateUniqueKey( true );
}
}
//TODO: Somehow add the newly created foreign keys to the internal collection
log.debug( "processing foreign key constraints" );
itr = getTableMappings();
Set done = new HashSet();
while ( itr.hasNext() ) {
secondPassCompileForeignKeys( (Table) itr.next(), done );
}
}
private int processExtendsQueue() {
log.debug( "processing extends queue" );
int added = 0;
ExtendsQueueEntry extendsQueueEntry = findPossibleExtends();
while ( extendsQueueEntry != null ) {
metadataSourceQueue.processHbmXml( extendsQueueEntry.getMetadataXml(), extendsQueueEntry.getEntityNames() );
extendsQueueEntry = findPossibleExtends();
}
if ( extendsQueue.size() > 0 ) {
Iterator iterator = extendsQueue.keySet().iterator();
StringBuffer buf = new StringBuffer( "Following super classes referenced in extends not found: " );
while ( iterator.hasNext() ) {
final ExtendsQueueEntry entry = ( ExtendsQueueEntry ) iterator.next();
buf.append( entry.getExplicitName() );
if ( entry.getMappingPackage() != null ) {
buf.append( "[" ).append( entry.getMappingPackage() ).append( "]" );
}
if ( iterator.hasNext() ) {
buf.append( "," );
}
}
throw new MappingException( buf.toString() );
}
return added;
}
protected ExtendsQueueEntry findPossibleExtends() {
Iterator<ExtendsQueueEntry> itr = extendsQueue.keySet().iterator();
while ( itr.hasNext() ) {
final ExtendsQueueEntry entry = itr.next();
boolean found = getClassMapping( entry.getExplicitName() ) != null
|| getClassMapping( HbmBinder.getClassName( entry.getExplicitName(), entry.getMappingPackage() ) ) != null;
if ( found ) {
itr.remove();
return entry;
}
}
return null;
}
protected void secondPassCompileForeignKeys(Table table, Set done) throws MappingException {
table.createForeignKeys();
Iterator iter = table.getForeignKeyIterator();
while ( iter.hasNext() ) {
ForeignKey fk = (ForeignKey) iter.next();
if ( !done.contains( fk ) ) {
done.add( fk );
final String referencedEntityName = fk.getReferencedEntityName();
if ( referencedEntityName == null ) {
throw new MappingException(
"An association from the table " +
fk.getTable().getName() +
" does not specify the referenced entity"
);
}
if ( log.isDebugEnabled() ) {
log.debug( "resolving reference to class: " + referencedEntityName );
}
PersistentClass referencedClass = classes.get( referencedEntityName );
if ( referencedClass == null ) {
throw new MappingException(
"An association from the table " +
fk.getTable().getName() +
" refers to an unmapped class: " +
referencedEntityName
);
}
if ( referencedClass.isJoinedSubclass() ) {
secondPassCompileForeignKeys( referencedClass.getSuperclass().getTable(), done );
}
fk.setReferencedTable( referencedClass.getTable() );
fk.alignColumns();
}
}
}
public Map<String, NamedQueryDefinition> getNamedQueries() {
return namedQueries;
}
/**
* Create a {@link SessionFactory} using the properties and mappings in this configuration. The
* {@link SessionFactory} will be immutable, so changes made to {@code this} {@link Configuration} after
* building the {@link SessionFactory} will not affect it.
*
* @return The build {@link SessionFactory}
*
* @throws HibernateException usually indicates an invalid configuration or invalid mapping information
*/
public SessionFactory buildSessionFactory() throws HibernateException {
log.debug( "Preparing to build session factory with filters : " + filterDefinitions );
secondPassCompile();
if ( ! metadataSourceQueue.isEmpty() ) {
log.warn( "mapping metadata cache was not completely processed" );
}
enableLegacyHibernateValidator();
enableBeanValidation();
enableHibernateSearch();
validate();
Environment.verifyProperties( properties );
Properties copy = new Properties();
copy.putAll( properties );
PropertiesHelper.resolvePlaceHolders( copy );
Settings settings = buildSettings( copy );
return new SessionFactoryImpl(
this,
mapping,
settings,
getInitializedEventListeners(),
sessionFactoryObserver
);
}
private static final String LEGACY_VALIDATOR_EVENT_LISTENER = "org.hibernate.validator.event.ValidateEventListener";
private void enableLegacyHibernateValidator() {
//add validator events if the jar is available
boolean enableValidatorListeners = !"false".equalsIgnoreCase(
getProperty(
"hibernate.validator.autoregister_listeners"
)
);
Class validateEventListenerClass = null;
try {
validateEventListenerClass = ReflectHelper.classForName( LEGACY_VALIDATOR_EVENT_LISTENER, Configuration.class );
}
catch ( ClassNotFoundException e ) {
//validator is not present
log.debug( "Legacy Validator not present in classpath, ignoring event listener registration" );
}
if ( enableValidatorListeners && validateEventListenerClass != null ) {
//TODO so much duplication
Object validateEventListener;
try {
validateEventListener = validateEventListenerClass.newInstance();
}
catch ( Exception e ) {
throw new AnnotationException( "Unable to load Validator event listener", e );
}
{
boolean present = false;
PreInsertEventListener[] listeners = getEventListeners().getPreInsertEventListeners();
if ( listeners != null ) {
for ( Object eventListener : listeners ) {
//not isAssignableFrom since the user could subclass
present = present || validateEventListenerClass == eventListener.getClass();
}
if ( !present ) {
int length = listeners.length + 1;
PreInsertEventListener[] newListeners = new PreInsertEventListener[length];
System.arraycopy( listeners, 0, newListeners, 0, length - 1 );
newListeners[length - 1] = ( PreInsertEventListener ) validateEventListener;
getEventListeners().setPreInsertEventListeners( newListeners );
}
}
else {
getEventListeners().setPreInsertEventListeners(
new PreInsertEventListener[] { ( PreInsertEventListener ) validateEventListener }
);
}
}
//update event listener
{
boolean present = false;
PreUpdateEventListener[] listeners = getEventListeners().getPreUpdateEventListeners();
if ( listeners != null ) {
for ( Object eventListener : listeners ) {
//not isAssignableFrom since the user could subclass
present = present || validateEventListenerClass == eventListener.getClass();
}
if ( !present ) {
int length = listeners.length + 1;
PreUpdateEventListener[] newListeners = new PreUpdateEventListener[length];
System.arraycopy( listeners, 0, newListeners, 0, length - 1 );
newListeners[length - 1] = ( PreUpdateEventListener ) validateEventListener;
getEventListeners().setPreUpdateEventListeners( newListeners );
}
}
else {
getEventListeners().setPreUpdateEventListeners(
new PreUpdateEventListener[] { ( PreUpdateEventListener ) validateEventListener }
);
}
}
}
}
private void enableBeanValidation() {
BeanValidationActivator.activateBeanValidation( getEventListeners(), getProperties() );
}
private static final String SEARCH_EVENT_LISTENER_REGISTERER_CLASS = "org.hibernate.cfg.search.HibernateSearchEventListenerRegister";
/**
* Tries to automatically register Hibernate Search event listeners by locating the
* appropriate bootstrap class and calling the <code>enableHibernateSearch</code> method.
*/
private void enableHibernateSearch() {
// load the bootstrap class
Class searchStartupClass;
try {
searchStartupClass = ReflectHelper.classForName( SEARCH_STARTUP_CLASS, getClass() );
}
catch ( ClassNotFoundException e ) {
// TODO remove this together with SearchConfiguration after 3.1.0 release of Search
// try loading deprecated HibernateSearchEventListenerRegister
try {
searchStartupClass = ReflectHelper.classForName( SEARCH_EVENT_LISTENER_REGISTERER_CLASS, getClass() );
}
catch ( ClassNotFoundException cnfe ) {
log.debug( "Search not present in classpath, ignoring event listener registration." );
return;
}
}
// call the method for registering the listeners
try {
Object searchStartupInstance = searchStartupClass.newInstance();
Method enableSearchMethod = searchStartupClass.getDeclaredMethod(
SEARCH_STARTUP_METHOD,
EventListeners.class,
Properties.class
);
enableSearchMethod.invoke( searchStartupInstance, getEventListeners(), getProperties() );
}
catch ( InstantiationException e ) {
log.debug( "Unable to instantiate {}, ignoring event listener registration.", SEARCH_STARTUP_CLASS );
}
catch ( IllegalAccessException e ) {
log.debug( "Unable to instantiate {}, ignoring event listener registration.", SEARCH_STARTUP_CLASS );
}
catch ( NoSuchMethodException e ) {
log.debug( "Method enableHibernateSearch() not found in {}.", SEARCH_STARTUP_CLASS );
}
catch ( InvocationTargetException e ) {
log.debug( "Unable to execute {}, ignoring event listener registration.", SEARCH_STARTUP_METHOD );
}
}
private EventListeners getInitializedEventListeners() {
EventListeners result = (EventListeners) eventListeners.shallowCopy();
result.initializeListeners( this );
return result;
}
/**
* Rterieve the configured {@link Interceptor}.
*
* @return The current {@link Interceptor}
*/
public Interceptor getInterceptor() {
return interceptor;
}
/**
* Set the current {@link Interceptor}
*
* @param interceptor The {@link Interceptor} to use for the {@link #buildSessionFactory() built}
* {@link SessionFactory}.
*
* @return this for method chaining
*/
public Configuration setInterceptor(Interceptor interceptor) {
this.interceptor = interceptor;
return this;
}
/**
* Get all properties
*
* @return all properties
*/
public Properties getProperties() {
return properties;
}
/**
* Get a property value by name
*
* @param propertyName The name of the property
*
* @return The value curently associated with that property name; may be null.
*/
public String getProperty(String propertyName) {
return properties.getProperty( propertyName );
}
/**
* Specify a completely new set of properties
*
* @param properties The new set of properties
*
* @return this for method chaining
*/
public Configuration setProperties(Properties properties) {
this.properties = properties;
return this;
}
/**
* Add the given properties to ours.
*
* @param extraProperties The properties to add.
*
* @return this for method chaining
*
*/
public Configuration addProperties(Properties extraProperties) {
this.properties.putAll( extraProperties );
return this;
}
/**
* Adds the incoming properties to the internal properties structure, as long as the internal structure does not
* already contain an entry for the given key.
*
* @param properties The properties to merge
*
* @return this for ethod chaining
*/
public Configuration mergeProperties(Properties properties) {
for ( Map.Entry entry : properties.entrySet() ) {
if ( this.properties.containsKey( entry.getKey() ) ) {
continue;
}
this.properties.setProperty( (String) entry.getKey(), (String) entry.getValue() );
}
return this;
}
/**
* Set a property value by name
*
* @param propertyName The name of the property to set
* @param value The new property value
*
* @return this for method chaining
*/
public Configuration setProperty(String propertyName, String value) {
properties.setProperty( propertyName, value );
return this;
}
private void addProperties(Element parent) {
Iterator itr = parent.elementIterator( "property" );
while ( itr.hasNext() ) {
Element node = (Element) itr.next();
String name = node.attributeValue( "name" );
String value = node.getText().trim();
log.debug( name + "=" + value );
properties.setProperty( name, value );
if ( !name.startsWith( "hibernate" ) ) {
properties.setProperty( "hibernate." + name, value );
}
}
Environment.verifyProperties( properties );
}
/**
* Use the mappings and properties specified in an application resource named <tt>hibernate.cfg.xml</tt>.
*
* @return this for method chaining
*
* @throws HibernateException Generally indicates we cannot find <tt>hibernate.cfg.xml</tt>
*
* @see #configure(String)
*/
public Configuration configure() throws HibernateException {
configure( "/hibernate.cfg.xml" );
return this;
}
/**
* Use the mappings and properties specified in the given application resource. The format of the resource is
* defined in <tt>hibernate-configuration-3.0.dtd</tt>.
* <p/>
* The resource is found via {@link #getConfigurationInputStream}
*
* @param resource The resource to use
*
* @return this for method chaining
*
* @throws HibernateException Generally indicates we cannot find the named resource
*
* @see #doConfigure(java.io.InputStream, String)
*/
public Configuration configure(String resource) throws HibernateException {
log.info( "configuring from resource: " + resource );
InputStream stream = getConfigurationInputStream( resource );
return doConfigure( stream, resource );
}
/**
* Get the configuration file as an <tt>InputStream</tt>. Might be overridden
* by subclasses to allow the configuration to be located by some arbitrary
* mechanism.
* <p/>
* By default here we use classpath resource resolution
*
* @param resource The resource to locate
*
* @return The stream
*
* @throws HibernateException Generally indicates we cannot find the named resource
*/
protected InputStream getConfigurationInputStream(String resource) throws HibernateException {
log.info( "Configuration resource: " + resource );
return ConfigHelper.getResourceAsStream( resource );
}
/**
* Use the mappings and properties specified in the given document. The format of the document is defined in
* <tt>hibernate-configuration-3.0.dtd</tt>.
*
* @param url URL from which you wish to load the configuration
*
* @return this for method chaining
*
* @throws HibernateException Generally indicates a problem access the url
*
* @see #doConfigure(java.io.InputStream, String)
*/
public Configuration configure(URL url) throws HibernateException {
log.info( "configuring from url: " + url.toString() );
try {
return doConfigure( url.openStream(), url.toString() );
}
catch (IOException ioe) {
throw new HibernateException( "could not configure from URL: " + url, ioe );
}
}
/**
* Use the mappings and properties specified in the given application file. The format of the file is defined in
* <tt>hibernate-configuration-3.0.dtd</tt>.
*
* @param configFile File from which you wish to load the configuration
*
* @return this for method chaining
*
* @throws HibernateException Generally indicates a problem access the file
*
* @see #doConfigure(java.io.InputStream, String)
*/
public Configuration configure(File configFile) throws HibernateException {
log.info( "configuring from file: " + configFile.getName() );
try {
return doConfigure( new FileInputStream( configFile ), configFile.toString() );
}
catch (FileNotFoundException fnfe) {
throw new HibernateException( "could not find file: " + configFile, fnfe );
}
}
/**
* Configure this configuration's state from the contents of the given input stream. The expectation is that
* the stream contents represent an XML document conforming to the Hibernate Configuration DTD. See
* {@link #doConfigure(Document)} for further details.
*
* @param stream The input stream from which to read
* @param resourceName The name to use in warning/error messages
*
* @return this for method chaining
*
* @throws HibernateException Indicates a problem reading the stream contents.
*/
protected Configuration doConfigure(InputStream stream, String resourceName) throws HibernateException {
try {
List errors = new ArrayList();
Document document = xmlHelper.createSAXReader( resourceName, errors, entityResolver )
.read( new InputSource( stream ) );
if ( errors.size() != 0 ) {
throw new MappingException( "invalid configuration", (Throwable) errors.get( 0 ) );
}
doConfigure( document );
}
catch (DocumentException e) {
throw new HibernateException( "Could not parse configuration: " + resourceName, e );
}
finally {
try {
stream.close();
}
catch (IOException ioe) {
log.warn( "could not close input stream for: " + resourceName, ioe );
}
}
return this;
}
/**
* Use the mappings and properties specified in the given XML document.
* The format of the file is defined in
* <tt>hibernate-configuration-3.0.dtd</tt>.
*
* @param document an XML document from which you wish to load the configuration
* @return A configuration configured via the <tt>Document</tt>
* @throws HibernateException if there is problem in accessing the file.
*/
public Configuration configure(org.w3c.dom.Document document) throws HibernateException {
log.info( "configuring from XML document" );
return doConfigure( xmlHelper.createDOMReader().read( document ) );
}
/**
* Parse a dom4j document conforming to the Hibernate Configuration DTD (<tt>hibernate-configuration-3.0.dtd</tt>)
* and use its information to configure this {@link Configuration}'s state
*
* @param doc The dom4j document
*
* @return this for method chaining
*
* @throws HibernateException Indicates a problem performing the configuration task
*/
protected Configuration doConfigure(Document doc) throws HibernateException {
Element sfNode = doc.getRootElement().element( "session-factory" );
String name = sfNode.attributeValue( "name" );
if ( name != null ) {
properties.setProperty( Environment.SESSION_FACTORY_NAME, name );
}
addProperties( sfNode );
parseSessionFactory( sfNode, name );
Element secNode = doc.getRootElement().element( "security" );
if ( secNode != null ) {
parseSecurity( secNode );
}
log.info( "Configured SessionFactory: " + name );
log.debug( "properties: " + properties );
return this;
}
private void parseSessionFactory(Element sfNode, String name) {
Iterator elements = sfNode.elementIterator();
while ( elements.hasNext() ) {
Element subelement = (Element) elements.next();
String subelementName = subelement.getName();
if ( "mapping".equals( subelementName ) ) {
parseMappingElement( subelement, name );
}
else if ( "class-cache".equals( subelementName ) ) {
String className = subelement.attributeValue( "class" );
Attribute regionNode = subelement.attribute( "region" );
final String region = ( regionNode == null ) ? className : regionNode.getValue();
boolean includeLazy = !"non-lazy".equals( subelement.attributeValue( "include" ) );
setCacheConcurrencyStrategy( className, subelement.attributeValue( "usage" ), region, includeLazy );
}
else if ( "collection-cache".equals( subelementName ) ) {
String role = subelement.attributeValue( "collection" );
Attribute regionNode = subelement.attribute( "region" );
final String region = ( regionNode == null ) ? role : regionNode.getValue();
setCollectionCacheConcurrencyStrategy( role, subelement.attributeValue( "usage" ), region );
}
else if ( "listener".equals( subelementName ) ) {
parseListener( subelement );
}
else if ( "event".equals( subelementName ) ) {
parseEvent( subelement );
}
}
}
private void parseMappingElement(Element mappingElement, String name) {
final Attribute resourceAttribute = mappingElement.attribute( "resource" );
final Attribute fileAttribute = mappingElement.attribute( "file" );
final Attribute jarAttribute = mappingElement.attribute( "jar" );
final Attribute packageAttribute = mappingElement.attribute( "package" );
final Attribute classAttribute = mappingElement.attribute( "class" );
if ( resourceAttribute != null ) {
final String resourceName = resourceAttribute.getValue();
log.debug( "session-factory config [{}] named resource [{}] for mapping", name, resourceName );
addResource( resourceName );
}
else if ( fileAttribute != null ) {
final String fileName = fileAttribute.getValue();
log.debug( "session-factory config [{}] named file [{}] for mapping", name, fileName );
addFile( fileName );
}
else if ( jarAttribute != null ) {
final String jarFileName = jarAttribute.getValue();
log.debug( "session-factory config [{}] named jar file [{}] for mapping", name, jarFileName );
addJar( new File( jarFileName ) );
}
else if ( packageAttribute != null ) {
final String packageName = packageAttribute.getValue();
log.debug( "session-factory config [{}] named package [{}] for mapping", name, packageName );
addPackage( packageName );
}
else if ( classAttribute != null ) {
final String className = classAttribute.getValue();
log.debug( "session-factory config [{}] named class [{}] for mapping", name, className );
try {
addAnnotatedClass( ReflectHelper.classForName( className ) );
}
catch ( Exception e ) {
throw new MappingException(
"Unable to load class [ " + className + "] declared in Hibernate configuration <mapping/> entry",
e
);
}
}
else {
throw new MappingException( "<mapping> element in configuration specifies no known attributes" );
}
}
private void parseSecurity(Element secNode) {
String contextId = secNode.attributeValue( "context" );
setProperty(Environment.JACC_CONTEXTID, contextId);
log.info( "JACC contextID: " + contextId );
JACCConfiguration jcfg = new JACCConfiguration( contextId );
Iterator grantElements = secNode.elementIterator();
while ( grantElements.hasNext() ) {
Element grantElement = (Element) grantElements.next();
String elementName = grantElement.getName();
if ( "grant".equals( elementName ) ) {
jcfg.addPermission(
grantElement.attributeValue( "role" ),
grantElement.attributeValue( "entity-name" ),
grantElement.attributeValue( "actions" )
);
}
}
}
private void parseEvent(Element element) {
String type = element.attributeValue( "type" );
List listeners = element.elements();
String[] listenerClasses = new String[ listeners.size() ];
for ( int i = 0; i < listeners.size() ; i++ ) {
listenerClasses[i] = ( (Element) listeners.get( i ) ).attributeValue( "class" );
}
log.debug( "Event listeners: " + type + "=" + StringHelper.toString( listenerClasses ) );
setListeners( type, listenerClasses );
}
private void parseListener(Element element) {
String type = element.attributeValue( "type" );
if ( type == null ) {
throw new MappingException( "No type specified for listener" );
}
String impl = element.attributeValue( "class" );
log.debug( "Event listener: " + type + "=" + impl );
setListeners( type, new String[]{impl} );
}
public void setListener(String type, String listener) {
String[] listeners = null;
if ( listener != null ) {
listeners = (String[]) Array.newInstance( String.class, 1 );
listeners[0] = listener;
}
setListeners( type, listeners );
}
public void setListeners(String type, String[] listenerClasses) {
Object[] listeners = null;
if ( listenerClasses != null ) {
listeners = (Object[]) Array.newInstance( eventListeners.getListenerClassFor(type), listenerClasses.length );
for ( int i = 0; i < listeners.length ; i++ ) {
try {
listeners[i] = ReflectHelper.classForName( listenerClasses[i] ).newInstance();
}
catch (Exception e) {
throw new MappingException(
"Unable to instantiate specified event (" + type + ") listener class: " + listenerClasses[i],
e
);
}
}
}
setListeners( type, listeners );
}
public void setListener(String type, Object listener) {
Object[] listeners = null;
if ( listener != null ) {
listeners = (Object[]) Array.newInstance( eventListeners.getListenerClassFor(type), 1 );
listeners[0] = listener;
}
setListeners( type, listeners );
}
public void setListeners(String type, Object[] listeners) {
if ( "auto-flush".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setAutoFlushEventListeners( new AutoFlushEventListener[]{} );
}
else {
eventListeners.setAutoFlushEventListeners( (AutoFlushEventListener[]) listeners );
}
}
else if ( "merge".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setMergeEventListeners( new MergeEventListener[]{} );
}
else {
eventListeners.setMergeEventListeners( (MergeEventListener[]) listeners );
}
}
else if ( "create".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPersistEventListeners( new PersistEventListener[]{} );
}
else {
eventListeners.setPersistEventListeners( (PersistEventListener[]) listeners );
}
}
else if ( "create-onflush".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPersistOnFlushEventListeners( new PersistEventListener[]{} );
}
else {
eventListeners.setPersistOnFlushEventListeners( (PersistEventListener[]) listeners );
}
}
else if ( "delete".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setDeleteEventListeners( new DeleteEventListener[]{} );
}
else {
eventListeners.setDeleteEventListeners( (DeleteEventListener[]) listeners );
}
}
else if ( "dirty-check".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setDirtyCheckEventListeners( new DirtyCheckEventListener[]{} );
}
else {
eventListeners.setDirtyCheckEventListeners( (DirtyCheckEventListener[]) listeners );
}
}
else if ( "evict".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setEvictEventListeners( new EvictEventListener[]{} );
}
else {
eventListeners.setEvictEventListeners( (EvictEventListener[]) listeners );
}
}
else if ( "flush".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setFlushEventListeners( new FlushEventListener[]{} );
}
else {
eventListeners.setFlushEventListeners( (FlushEventListener[]) listeners );
}
}
else if ( "flush-entity".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setFlushEntityEventListeners( new FlushEntityEventListener[]{} );
}
else {
eventListeners.setFlushEntityEventListeners( (FlushEntityEventListener[]) listeners );
}
}
else if ( "load".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setLoadEventListeners( new LoadEventListener[]{} );
}
else {
eventListeners.setLoadEventListeners( (LoadEventListener[]) listeners );
}
}
else if ( "load-collection".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setInitializeCollectionEventListeners(
new InitializeCollectionEventListener[]{}
);
}
else {
eventListeners.setInitializeCollectionEventListeners(
(InitializeCollectionEventListener[]) listeners
);
}
}
else if ( "lock".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setLockEventListeners( new LockEventListener[]{} );
}
else {
eventListeners.setLockEventListeners( (LockEventListener[]) listeners );
}
}
else if ( "refresh".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setRefreshEventListeners( new RefreshEventListener[]{} );
}
else {
eventListeners.setRefreshEventListeners( (RefreshEventListener[]) listeners );
}
}
else if ( "replicate".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setReplicateEventListeners( new ReplicateEventListener[]{} );
}
else {
eventListeners.setReplicateEventListeners( (ReplicateEventListener[]) listeners );
}
}
else if ( "save-update".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setSaveOrUpdateEventListeners( new SaveOrUpdateEventListener[]{} );
}
else {
eventListeners.setSaveOrUpdateEventListeners( (SaveOrUpdateEventListener[]) listeners );
}
}
else if ( "save".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setSaveEventListeners( new SaveOrUpdateEventListener[]{} );
}
else {
eventListeners.setSaveEventListeners( (SaveOrUpdateEventListener[]) listeners );
}
}
else if ( "update".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setUpdateEventListeners( new SaveOrUpdateEventListener[]{} );
}
else {
eventListeners.setUpdateEventListeners( (SaveOrUpdateEventListener[]) listeners );
}
}
else if ( "pre-load".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPreLoadEventListeners( new PreLoadEventListener[]{} );
}
else {
eventListeners.setPreLoadEventListeners( (PreLoadEventListener[]) listeners );
}
}
else if ( "pre-update".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPreUpdateEventListeners( new PreUpdateEventListener[]{} );
}
else {
eventListeners.setPreUpdateEventListeners( (PreUpdateEventListener[]) listeners );
}
}
else if ( "pre-delete".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPreDeleteEventListeners( new PreDeleteEventListener[]{} );
}
else {
eventListeners.setPreDeleteEventListeners( (PreDeleteEventListener[]) listeners );
}
}
else if ( "pre-insert".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPreInsertEventListeners( new PreInsertEventListener[]{} );
}
else {
eventListeners.setPreInsertEventListeners( (PreInsertEventListener[]) listeners );
}
}
else if ( "pre-collection-recreate".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPreCollectionRecreateEventListeners( new PreCollectionRecreateEventListener[]{} );
}
else {
eventListeners.setPreCollectionRecreateEventListeners( (PreCollectionRecreateEventListener[]) listeners );
}
}
else if ( "pre-collection-remove".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPreCollectionRemoveEventListeners( new PreCollectionRemoveEventListener[]{} );
}
else {
eventListeners.setPreCollectionRemoveEventListeners( ( PreCollectionRemoveEventListener[]) listeners );
}
}
else if ( "pre-collection-update".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPreCollectionUpdateEventListeners( new PreCollectionUpdateEventListener[]{} );
}
else {
eventListeners.setPreCollectionUpdateEventListeners( ( PreCollectionUpdateEventListener[]) listeners );
}
}
else if ( "post-load".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPostLoadEventListeners( new PostLoadEventListener[]{} );
}
else {
eventListeners.setPostLoadEventListeners( (PostLoadEventListener[]) listeners );
}
}
else if ( "post-update".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPostUpdateEventListeners( new PostUpdateEventListener[]{} );
}
else {
eventListeners.setPostUpdateEventListeners( (PostUpdateEventListener[]) listeners );
}
}
else if ( "post-delete".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPostDeleteEventListeners( new PostDeleteEventListener[]{} );
}
else {
eventListeners.setPostDeleteEventListeners( (PostDeleteEventListener[]) listeners );
}
}
else if ( "post-insert".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPostInsertEventListeners( new PostInsertEventListener[]{} );
}
else {
eventListeners.setPostInsertEventListeners( (PostInsertEventListener[]) listeners );
}
}
else if ( "post-commit-update".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPostCommitUpdateEventListeners(
new PostUpdateEventListener[]{}
);
}
else {
eventListeners.setPostCommitUpdateEventListeners( (PostUpdateEventListener[]) listeners );
}
}
else if ( "post-commit-delete".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPostCommitDeleteEventListeners(
new PostDeleteEventListener[]{}
);
}
else {
eventListeners.setPostCommitDeleteEventListeners( (PostDeleteEventListener[]) listeners );
}
}
else if ( "post-commit-insert".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPostCommitInsertEventListeners(
new PostInsertEventListener[]{}
);
}
else {
eventListeners.setPostCommitInsertEventListeners( (PostInsertEventListener[]) listeners );
}
}
else if ( "post-collection-recreate".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPostCollectionRecreateEventListeners( new PostCollectionRecreateEventListener[]{} );
}
else {
eventListeners.setPostCollectionRecreateEventListeners( (PostCollectionRecreateEventListener[]) listeners );
}
}
else if ( "post-collection-remove".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPostCollectionRemoveEventListeners( new PostCollectionRemoveEventListener[]{} );
}
else {
eventListeners.setPostCollectionRemoveEventListeners( ( PostCollectionRemoveEventListener[]) listeners );
}
}
else if ( "post-collection-update".equals( type ) ) {
if ( listeners == null ) {
eventListeners.setPostCollectionUpdateEventListeners( new PostCollectionUpdateEventListener[]{} );
}
else {
eventListeners.setPostCollectionUpdateEventListeners( ( PostCollectionUpdateEventListener[]) listeners );
}
}
else {
throw new MappingException("Unrecognized listener type [" + type + "]");
}
}
public EventListeners getEventListeners() {
return eventListeners;
}
RootClass getRootClassMapping(String clazz) throws MappingException {
try {
return (RootClass) getClassMapping( clazz );
}
catch (ClassCastException cce) {
throw new MappingException( "You may only specify a cache for root <class> mappings" );
}
}
/**
* Set up a cache for an entity class
*
* @param entityName The name of the entity to which we shoudl associate these cache settings
* @param concurrencyStrategy The cache strategy to use
*
* @return this for method chaining
*/
public Configuration setCacheConcurrencyStrategy(String entityName, String concurrencyStrategy) {
setCacheConcurrencyStrategy( entityName, concurrencyStrategy, entityName );
return this;
}
/**
* Set up a cache for an entity class, giving an explicit region name
*
* @param entityName The name of the entity to which we should associate these cache settings
* @param concurrencyStrategy The cache strategy to use
* @param region The name of the cache region to use
*
* @return this for method chaining
*/
public Configuration setCacheConcurrencyStrategy(String entityName, String concurrencyStrategy, String region) {
setCacheConcurrencyStrategy( entityName, concurrencyStrategy, region, true );
return this;
}
public void setCacheConcurrencyStrategy(
String entityName,
String concurrencyStrategy,
String region,
boolean cacheLazyProperty) throws MappingException {
caches.add( new CacheHolder( entityName, concurrencyStrategy, region, true, cacheLazyProperty ) );
}
private void applyCacheConcurrencyStrategy(CacheHolder holder) {
RootClass rootClass = getRootClassMapping( holder.role );
if ( rootClass == null ) {
throw new MappingException( "Cannot cache an unknown entity: " + holder.role );
}
rootClass.setCacheConcurrencyStrategy( holder.usage );
rootClass.setCacheRegionName( holder.region );
rootClass.setLazyPropertiesCacheable( holder.cacheLazy );
}
/**
* Set up a cache for a collection role
*
* @param collectionRole The name of the collection to which we should associate these cache settings
* @param concurrencyStrategy The cache strategy to use
*
* @return this for method chaining
*/
public Configuration setCollectionCacheConcurrencyStrategy(String collectionRole, String concurrencyStrategy) {
setCollectionCacheConcurrencyStrategy( collectionRole, concurrencyStrategy, collectionRole );
return this;
}
/**
* Set up a cache for a collection role, giving an explicit region name
*
* @param collectionRole The name of the collection to which we should associate these cache settings
* @param concurrencyStrategy The cache strategy to use
* @param region The name of the cache region to use
*
* @return this for method chaining
*/
public void setCollectionCacheConcurrencyStrategy(String collectionRole, String concurrencyStrategy, String region) {
caches.add( new CacheHolder( collectionRole, concurrencyStrategy, region, false, false ) );
}
private void applyCollectionCacheConcurrencyStrategy(CacheHolder holder) {
Collection collection = getCollectionMapping( holder.role );
if ( collection == null ) {
throw new MappingException( "Cannot cache an unknown collection: " + holder.role );
}
collection.setCacheConcurrencyStrategy( holder.usage );
collection.setCacheRegionName( holder.region );
}
/**
* Get the query language imports
*
* @return a mapping from "import" names to fully qualified class names
*/
public Map<String,String> getImports() {
return imports;
}
/**
* Create an object-oriented view of the configuration properties
*
* @return The build settings
*/
public Settings buildSettings() {
Properties clone = ( Properties ) properties.clone();
PropertiesHelper.resolvePlaceHolders( clone );
return buildSettingsInternal( clone );
}
public Settings buildSettings(Properties props) throws HibernateException {
return buildSettingsInternal( props );
}
private Settings buildSettingsInternal(Properties props) {
final Settings settings = settingsFactory.buildSettings( props );
settings.setEntityTuplizerFactory( this.getEntityTuplizerFactory() );
// settings.setComponentTuplizerFactory( this.getComponentTuplizerFactory() );
return settings;
}
public Map getNamedSQLQueries() {
return namedSqlQueries;
}
public Map getSqlResultSetMappings() {
return sqlResultSetMappings;
}
public NamingStrategy getNamingStrategy() {
return namingStrategy;
}
/**
* Set a custom naming strategy
*
* @param namingStrategy the NamingStrategy to set
*
* @return this for method chaining
*/
public Configuration setNamingStrategy(NamingStrategy namingStrategy) {
this.namingStrategy = namingStrategy;
return this;
}
/**
* Retrieve the IdentifierGeneratorFactory in effect for this configuration.
*
* @return This configuration's IdentifierGeneratorFactory.
*/
public DefaultIdentifierGeneratorFactory getIdentifierGeneratorFactory() {
return identifierGeneratorFactory;
}
public Mapping buildMapping() {
return new Mapping() {
public IdentifierGeneratorFactory getIdentifierGeneratorFactory() {
return identifierGeneratorFactory;
}
/**
* Returns the identifier type of a mapped class
*/
public Type getIdentifierType(String entityName) throws MappingException {
PersistentClass pc = classes.get( entityName );
if ( pc == null ) {
throw new MappingException( "persistent class not known: " + entityName );
}
return pc.getIdentifier().getType();
}
public String getIdentifierPropertyName(String entityName) throws MappingException {
final PersistentClass pc = classes.get( entityName );
if ( pc == null ) {
throw new MappingException( "persistent class not known: " + entityName );
}
if ( !pc.hasIdentifierProperty() ) {
return null;
}
return pc.getIdentifierProperty().getName();
}
public Type getReferencedPropertyType(String entityName, String propertyName) throws MappingException {
final PersistentClass pc = classes.get( entityName );
if ( pc == null ) {
throw new MappingException( "persistent class not known: " + entityName );
}
Property prop = pc.getReferencedProperty( propertyName );
if ( prop == null ) {
throw new MappingException(
"property not known: " +
entityName + '.' + propertyName
);
}
return prop.getType();
}
};
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
//we need reflectionManager before reading the other components (MetadataSourceQueue in particular)
final MetadataProvider metadataProvider = (MetadataProvider) ois.readObject();
this.mapping = buildMapping();
xmlHelper = new XMLHelper();
createReflectionManager(metadataProvider);
ois.defaultReadObject();
}
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
//We write MetadataProvider first as we need reflectionManager before reading the other components
final MetadataProvider metadataProvider = ( ( MetadataProviderInjector ) reflectionManager ).getMetadataProvider();
out.writeObject( metadataProvider );
out.defaultWriteObject();
}
private void createReflectionManager() {
createReflectionManager( new JPAMetadataProvider() );
}
private void createReflectionManager(MetadataProvider metadataProvider) {
reflectionManager = new JavaReflectionManager();
( ( MetadataProviderInjector ) reflectionManager ).setMetadataProvider( metadataProvider );
}
public Map getFilterDefinitions() {
return filterDefinitions;
}
public void addFilterDefinition(FilterDefinition definition) {
filterDefinitions.put( definition.getFilterName(), definition );
}
public Iterator iterateFetchProfiles() {
return fetchProfiles.values().iterator();
}
public void addFetchProfile(FetchProfile fetchProfile) {
fetchProfiles.put( fetchProfile.getName(), fetchProfile );
}
public void addAuxiliaryDatabaseObject(AuxiliaryDatabaseObject object) {
auxiliaryDatabaseObjects.add( object );
}
public Map getSqlFunctions() {
return sqlFunctions;
}
public void addSqlFunction(String functionName, SQLFunction function) {
sqlFunctions.put( functionName, function );
}
public TypeResolver getTypeResolver() {
return typeResolver;
}
/**
* Allows registration of a type into the type registry. The phrase 'override' in the method name simply
* reminds that registration *potentially* replaces a previously registered type .
*
* @param type The type to register.
*/
public void registerTypeOverride(BasicType type) {
getTypeResolver().registerTypeOverride( type );
}
public void registerTypeOverride(UserType type, String[] keys) {
getTypeResolver().registerTypeOverride( type, keys );
}
public void registerTypeOverride(CompositeUserType type, String[] keys) {
getTypeResolver().registerTypeOverride( type, keys );
}
public SessionFactoryObserver getSessionFactoryObserver() {
return sessionFactoryObserver;
}
public void setSessionFactoryObserver(SessionFactoryObserver sessionFactoryObserver) {
this.sessionFactoryObserver = sessionFactoryObserver;
}
// Mappings impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Internal implementation of the Mappings interface giving access to the Configuration's internal
* <tt>metadata repository</tt> state ({@link Configuration#classes}, {@link Configuration#tables}, etc).
*/
protected class MappingsImpl implements ExtendedMappings, Serializable {
private String schemaName;
public String getSchemaName() {
return schemaName;
}
public void setSchemaName(String schemaName) {
this.schemaName = schemaName;
}
private String catalogName;
public String getCatalogName() {
return catalogName;
}
public void setCatalogName(String catalogName) {
this.catalogName = catalogName;
}
private String defaultPackage;
public String getDefaultPackage() {
return defaultPackage;
}
public void setDefaultPackage(String defaultPackage) {
this.defaultPackage = defaultPackage;
}
private boolean autoImport;
public boolean isAutoImport() {
return autoImport;
}
public void setAutoImport(boolean autoImport) {
this.autoImport = autoImport;
}
private boolean defaultLazy;
public boolean isDefaultLazy() {
return defaultLazy;
}
public void setDefaultLazy(boolean defaultLazy) {
this.defaultLazy = defaultLazy;
}
private String defaultCascade;
public String getDefaultCascade() {
return defaultCascade;
}
public void setDefaultCascade(String defaultCascade) {
this.defaultCascade = defaultCascade;
}
private String defaultAccess;
public String getDefaultAccess() {
return defaultAccess;
}
public void setDefaultAccess(String defaultAccess) {
this.defaultAccess = defaultAccess;
}
public NamingStrategy getNamingStrategy() {
return namingStrategy;
}
public void setNamingStrategy(NamingStrategy namingStrategy) {
Configuration.this.namingStrategy = namingStrategy;
}
public TypeResolver getTypeResolver() {
return typeResolver;
}
public Iterator<PersistentClass> iterateClasses() {
return classes.values().iterator();
}
public PersistentClass getClass(String entityName) {
return classes.get( entityName );
}
public PersistentClass locatePersistentClassByEntityName(String entityName) {
PersistentClass persistentClass = classes.get( entityName );
if ( persistentClass == null ) {
String actualEntityName = imports.get( entityName );
if ( StringHelper.isNotEmpty( actualEntityName ) ) {
persistentClass = classes.get( actualEntityName );
}
}
return persistentClass;
}
public void addClass(PersistentClass persistentClass) throws DuplicateMappingException {
Object old = classes.put( persistentClass.getEntityName(), persistentClass );
if ( old != null ) {
throw new DuplicateMappingException( "class/entity", persistentClass.getEntityName() );
}
}
public void addImport(String entityName, String rename) throws DuplicateMappingException {
String existing = imports.put( rename, entityName );
if ( existing != null ) {
if ( existing.equals( entityName ) ) {
log.info( "duplicate import: {} -> {}", entityName, rename );
}
else {
throw new DuplicateMappingException(
"duplicate import: " + rename + " refers to both " + entityName +
" and " + existing + " (try using auto-import=\"false\")",
"import",
rename
);
}
}
}
public Collection getCollection(String role) {
return collections.get( role );
}
public Iterator<Collection> iterateCollections() {
return collections.values().iterator();
}
public void addCollection(Collection collection) throws DuplicateMappingException {
Object old = collections.put( collection.getRole(), collection );
if ( old != null ) {
throw new DuplicateMappingException( "collection role", collection.getRole() );
}
}
public Table getTable(String schema, String catalog, String name) {
String key = Table.qualify(catalog, schema, name);
return tables.get(key);
}
public Iterator<Table> iterateTables() {
return tables.values().iterator();
}
public Table addTable(
String schema,
String catalog,
String name,
String subselect,
boolean isAbstract) {
name = getObjectNameNormalizer().normalizeIdentifierQuoting( name );
schema = getObjectNameNormalizer().normalizeIdentifierQuoting( schema );
catalog = getObjectNameNormalizer().normalizeIdentifierQuoting( catalog );
String key = subselect == null ? Table.qualify( catalog, schema, name ) : subselect;
Table table = tables.get( key );
if ( table == null ) {
table = new Table();
table.setAbstract( isAbstract );
table.setName( name );
table.setSchema( schema );
table.setCatalog( catalog );
table.setSubselect( subselect );
tables.put( key, table );
}
else {
if ( !isAbstract ) {
table.setAbstract( false );
}
}
return table;
}
public Table addDenormalizedTable(
String schema,
String catalog,
String name,
boolean isAbstract,
String subselect,
Table includedTable) throws DuplicateMappingException {
name = getObjectNameNormalizer().normalizeIdentifierQuoting( name );
schema = getObjectNameNormalizer().normalizeIdentifierQuoting( schema );
catalog = getObjectNameNormalizer().normalizeIdentifierQuoting( catalog );
String key = subselect == null ? Table.qualify(catalog, schema, name) : subselect;
if ( tables.containsKey( key ) ) {
throw new DuplicateMappingException( "table", name );
}
Table table = new DenormalizedTable( includedTable );
table.setAbstract( isAbstract );
table.setName( name );
table.setSchema( schema );
table.setCatalog( catalog );
table.setSubselect( subselect );
tables.put( key, table );
return table;
}
public NamedQueryDefinition getQuery(String name) {
return namedQueries.get( name );
}
public void addQuery(String name, NamedQueryDefinition query) throws DuplicateMappingException {
if ( !defaultNamedQueryNames.contains( name ) ) {
applyQuery( name, query );
}
}
private void applyQuery(String name, NamedQueryDefinition query) {
checkQueryName( name );
namedQueries.put( name.intern(), query );
}
private void checkQueryName(String name) throws DuplicateMappingException {
if ( namedQueries.containsKey( name ) || namedSqlQueries.containsKey( name ) ) {
throw new DuplicateMappingException( "query", name );
}
}
public void addDefaultQuery(String name, NamedQueryDefinition query) {
applyQuery( name, query );
defaultNamedQueryNames.add( name );
}
public NamedSQLQueryDefinition getSQLQuery(String name) {
return namedSqlQueries.get( name );
}
public void addSQLQuery(String name, NamedSQLQueryDefinition query) throws DuplicateMappingException {
if ( !defaultNamedNativeQueryNames.contains( name ) ) {
applySQLQuery( name, query );
}
}
private void applySQLQuery(String name, NamedSQLQueryDefinition query) throws DuplicateMappingException {
checkQueryName( name );
namedSqlQueries.put( name.intern(), query );
}
public void addDefaultSQLQuery(String name, NamedSQLQueryDefinition query) {
applySQLQuery( name, query );
defaultNamedNativeQueryNames.add( name );
}
public ResultSetMappingDefinition getResultSetMapping(String name) {
return sqlResultSetMappings.get(name);
}
public void addResultSetMapping(ResultSetMappingDefinition sqlResultSetMapping) throws DuplicateMappingException {
if ( !defaultSqlResultSetMappingNames.contains( sqlResultSetMapping.getName() ) ) {
applyResultSetMapping( sqlResultSetMapping );
}
}
public void applyResultSetMapping(ResultSetMappingDefinition sqlResultSetMapping) throws DuplicateMappingException {
Object old = sqlResultSetMappings.put( sqlResultSetMapping.getName(), sqlResultSetMapping );
if ( old != null ) {
throw new DuplicateMappingException( "resultSet", sqlResultSetMapping.getName() );
}
}
public void addDefaultResultSetMapping(ResultSetMappingDefinition definition) {
final String name = definition.getName();
if ( !defaultSqlResultSetMappingNames.contains( name ) && getResultSetMapping( name ) != null ) {
removeResultSetMapping( name );
}
applyResultSetMapping( definition );
defaultSqlResultSetMappingNames.add( name );
}
protected void removeResultSetMapping(String name) {
sqlResultSetMappings.remove( name );
}
public TypeDef getTypeDef(String typeName) {
return typeDefs.get( typeName );
}
public void addTypeDef(String typeName, String typeClass, Properties paramMap) {
TypeDef def = new TypeDef( typeClass, paramMap );
typeDefs.put( typeName, def );
log.debug( "Added " + typeName + " with class " + typeClass );
}
public Map getFilterDefinitions() {
return filterDefinitions;
}
public FilterDefinition getFilterDefinition(String name) {
return filterDefinitions.get( name );
}
public void addFilterDefinition(FilterDefinition definition) {
filterDefinitions.put( definition.getFilterName(), definition );
}
public FetchProfile findOrCreateFetchProfile(String name, MetadataSource source) {
FetchProfile profile = fetchProfiles.get( name );
if ( profile == null ) {
profile = new FetchProfile( name, source );
fetchProfiles.put( name, profile );
}
return profile;
}
public Iterator<AuxiliaryDatabaseObject> iterateAuxliaryDatabaseObjects() {
return iterateAuxiliaryDatabaseObjects();
}
public Iterator<AuxiliaryDatabaseObject> iterateAuxiliaryDatabaseObjects() {
return auxiliaryDatabaseObjects.iterator();
}
public ListIterator<AuxiliaryDatabaseObject> iterateAuxliaryDatabaseObjectsInReverse() {
return iterateAuxiliaryDatabaseObjectsInReverse();
}
public ListIterator<AuxiliaryDatabaseObject> iterateAuxiliaryDatabaseObjectsInReverse() {
return auxiliaryDatabaseObjects.listIterator( auxiliaryDatabaseObjects.size() );
}
public void addAuxiliaryDatabaseObject(AuxiliaryDatabaseObject auxiliaryDatabaseObject) {
auxiliaryDatabaseObjects.add( auxiliaryDatabaseObject );
}
/**
* Internal struct used to help track physical table names to logical table names.
*/
private class TableDescription implements Serializable {
final String logicalName;
final Table denormalizedSupertable;
TableDescription(String logicalName, Table denormalizedSupertable) {
this.logicalName = logicalName;
this.denormalizedSupertable = denormalizedSupertable;
}
}
public String getLogicalTableName(Table table) throws MappingException {
return getLogicalTableName( table.getQuotedSchema(), table.getCatalog(), table.getQuotedName() );
}
private String getLogicalTableName(String schema, String catalog, String physicalName) throws MappingException {
String key = buildTableNameKey( schema, catalog, physicalName );
TableDescription descriptor = (TableDescription) tableNameBinding.get( key );
if (descriptor == null) {
throw new MappingException( "Unable to find physical table: " + physicalName);
}
return descriptor.logicalName;
}
public void addTableBinding(
String schema,
String catalog,
String logicalName,
String physicalName,
Table denormalizedSuperTable) throws DuplicateMappingException {
String key = buildTableNameKey( schema, catalog, physicalName );
TableDescription tableDescription = new TableDescription( logicalName, denormalizedSuperTable );
TableDescription oldDescriptor = ( TableDescription ) tableNameBinding.put( key, tableDescription );
if ( oldDescriptor != null && ! oldDescriptor.logicalName.equals( logicalName ) ) {
//TODO possibly relax that
throw new DuplicateMappingException(
"Same physical table name [" + physicalName + "] references several logical table names: [" +
oldDescriptor.logicalName + "], [" + logicalName + ']',
"table",
physicalName
);
}
}
private String buildTableNameKey(String schema, String catalog, String finalName) {
StringBuffer keyBuilder = new StringBuffer();
if (schema != null) keyBuilder.append( schema );
keyBuilder.append( ".");
if (catalog != null) keyBuilder.append( catalog );
keyBuilder.append( ".");
keyBuilder.append( finalName );
return keyBuilder.toString();
}
/**
* Internal struct used to maintain xref between physical and logical column
* names for a table. Mainly this is used to ensure that the defined
* {@link NamingStrategy} is not creating duplicate column names.
*/
private class TableColumnNameBinding implements Serializable {
private final String tableName;
private Map/*<String, String>*/ logicalToPhysical = new HashMap();
private Map/*<String, String>*/ physicalToLogical = new HashMap();
private TableColumnNameBinding(String tableName) {
this.tableName = tableName;
}
public void addBinding(String logicalName, Column physicalColumn) {
bindLogicalToPhysical( logicalName, physicalColumn );
bindPhysicalToLogical( logicalName, physicalColumn );
}
private void bindLogicalToPhysical(String logicalName, Column physicalColumn) throws DuplicateMappingException {
final String logicalKey = logicalName.toLowerCase();
final String physicalName = physicalColumn.getQuotedName();
final String existingPhysicalName = ( String ) logicalToPhysical.put( logicalKey, physicalName );
if ( existingPhysicalName != null ) {
boolean areSamePhysicalColumn = physicalColumn.isQuoted()
? existingPhysicalName.equals( physicalName )
: existingPhysicalName.equalsIgnoreCase( physicalName );
if ( ! areSamePhysicalColumn ) {
throw new DuplicateMappingException(
" Table [" + tableName + "] contains logical column name [" + logicalName
+ "] referenced by multiple physical column names: [" + existingPhysicalName
+ "], [" + physicalName + "]",
"column-binding",
tableName + "." + logicalName
);
}
}
}
private void bindPhysicalToLogical(String logicalName, Column physicalColumn) throws DuplicateMappingException {
final String physicalName = physicalColumn.getQuotedName();
final String existingLogicalName = ( String ) physicalToLogical.put( physicalName, logicalName );
if ( existingLogicalName != null && ! existingLogicalName.equals( logicalName ) ) {
throw new DuplicateMappingException(
" Table [" + tableName + "] contains phyical column name [" + physicalName
+ "] represented by different logical column names: [" + existingLogicalName
+ "], [" + logicalName + "]",
"column-binding",
tableName + "." + physicalName
);
}
}
}
public void addColumnBinding(String logicalName, Column physicalColumn, Table table) throws DuplicateMappingException {
TableColumnNameBinding binding = ( TableColumnNameBinding ) columnNameBindingPerTable.get( table );
if ( binding == null ) {
binding = new TableColumnNameBinding( table.getName() );
columnNameBindingPerTable.put( table, binding );
}
binding.addBinding( logicalName, physicalColumn );
}
public String getPhysicalColumnName(String logicalName, Table table) throws MappingException {
logicalName = logicalName.toLowerCase();
String finalName = null;
Table currentTable = table;
do {
TableColumnNameBinding binding = ( TableColumnNameBinding ) columnNameBindingPerTable.get( currentTable );
if ( binding != null ) {
finalName = ( String ) binding.logicalToPhysical.get( logicalName );
}
String key = buildTableNameKey(
currentTable.getQuotedSchema(), currentTable.getCatalog(), currentTable.getQuotedName()
);
TableDescription description = ( TableDescription ) tableNameBinding.get( key );
if ( description != null ) {
currentTable = description.denormalizedSupertable;
}
else {
currentTable = null;
}
} while ( finalName == null && currentTable != null );
if ( finalName == null ) {
throw new MappingException(
"Unable to find column with logical name " + logicalName + " in table " + table.getName()
);
}
return finalName;
}
public String getLogicalColumnName(String physicalName, Table table) throws MappingException {
String logical = null;
Table currentTable = table;
TableDescription description = null;
do {
TableColumnNameBinding binding = ( TableColumnNameBinding ) columnNameBindingPerTable.get( currentTable );
if ( binding != null ) {
logical = ( String ) binding.physicalToLogical.get( physicalName );
}
String key = buildTableNameKey(
currentTable.getQuotedSchema(), currentTable.getCatalog(), currentTable.getQuotedName()
);
description = ( TableDescription ) tableNameBinding.get( key );
if ( description != null ) {
currentTable = description.denormalizedSupertable;
}
else {
currentTable = null;
}
}
while ( logical == null && currentTable != null && description != null );
if ( logical == null ) {
throw new MappingException(
"Unable to find logical column name from physical name "
+ physicalName + " in table " + table.getName()
);
}
return logical;
}
public void addSecondPass(SecondPass sp) {
addSecondPass( sp, false );
}
public void addSecondPass(SecondPass sp, boolean onTopOfTheQueue) {
if ( onTopOfTheQueue ) {
secondPasses.add( 0, sp );
}
else {
secondPasses.add( sp );
}
}
public void addPropertyReference(String referencedClass, String propertyName) {
propertyReferences.add( new PropertyReference( referencedClass, propertyName, false ) );
}
public void addUniquePropertyReference(String referencedClass, String propertyName) {
propertyReferences.add( new PropertyReference( referencedClass, propertyName, true ) );
}
public void addToExtendsQueue(ExtendsQueueEntry entry) {
extendsQueue.put( entry, null );
}
public DefaultIdentifierGeneratorFactory getIdentifierGeneratorFactory() {
return identifierGeneratorFactory;
}
public void addMappedSuperclass(Class type, MappedSuperclass mappedSuperclass) {
mappedSuperClasses.put( type, mappedSuperclass );
}
public MappedSuperclass getMappedSuperclass(Class type) {
return mappedSuperClasses.get( type );
}
public ObjectNameNormalizer getObjectNameNormalizer() {
return normalizer;
}
public Properties getConfigurationProperties() {
return properties;
}
private Boolean useNewGeneratorMappings;
public void addDefaultGenerator(IdGenerator generator) {
this.addGenerator( generator );
defaultNamedGenerators.add( generator.getName() );
}
public boolean isInSecondPass() {
return inSecondPass;
}
public PropertyData getPropertyAnnotatedWithMapsId(XClass entityType, String propertyName) {
final Map<String, PropertyData> map = propertiesAnnotatedWithMapsId.get( entityType );
return map == null ? null : map.get( propertyName );
}
public void addPropertyAnnotatedWithMapsId(XClass entityType, PropertyData property) {
Map<String, PropertyData> map = propertiesAnnotatedWithMapsId.get( entityType );
if ( map == null ) {
map = new HashMap<String, PropertyData>();
propertiesAnnotatedWithMapsId.put( entityType, map );
}
map.put( property.getProperty().getAnnotation( MapsId.class ).value(), property );
}
public PropertyData getPropertyAnnotatedWithIdAndToOne(XClass entityType, String propertyName) {
final Map<String, PropertyData> map = propertiesAnnotatedWithIdAndToOne.get( entityType );
return map == null ? null : map.get( propertyName );
}
public void addToOneAndIdProperty(XClass entityType, PropertyData property) {
Map<String, PropertyData> map = propertiesAnnotatedWithIdAndToOne.get( entityType );
if ( map == null ) {
map = new HashMap<String, PropertyData>();
propertiesAnnotatedWithIdAndToOne.put( entityType, map );
}
map.put( property.getPropertyName(), property );
}
@SuppressWarnings({ "UnnecessaryUnboxing" })
public boolean useNewGeneratorMappings() {
if ( useNewGeneratorMappings == null ) {
final String booleanName = getConfigurationProperties().getProperty( USE_NEW_ID_GENERATOR_MAPPINGS );
useNewGeneratorMappings = Boolean.valueOf( booleanName );
}
return useNewGeneratorMappings.booleanValue();
}
public IdGenerator getGenerator(String name) {
return getGenerator( name, null );
}
public IdGenerator getGenerator(String name, Map<String, IdGenerator> localGenerators) {
if ( localGenerators != null ) {
IdGenerator result = localGenerators.get( name );
if ( result != null ) {
return result;
}
}
return namedGenerators.get( name );
}
public void addGenerator(IdGenerator generator) {
if ( !defaultNamedGenerators.contains( generator.getName() ) ) {
IdGenerator old = namedGenerators.put( generator.getName(), generator );
if ( old != null ) {
log.warn( "duplicate generator name {}", old.getName() );
}
}
}
public void addGeneratorTable(String name, Properties params) {
Object old = generatorTables.put( name, params );
if ( old != null ) {
log.warn( "duplicate generator table: {}", name );
}
}
public Properties getGeneratorTableProperties(String name, Map<String, Properties> localGeneratorTables) {
if ( localGeneratorTables != null ) {
Properties result = localGeneratorTables.get( name );
if ( result != null ) {
return result;
}
}
return generatorTables.get( name );
}
public Map<String, Join> getJoins(String entityName) {
return joins.get( entityName );
}
public void addJoins(PersistentClass persistentClass, Map<String, Join> joins) {
Object old = Configuration.this.joins.put( persistentClass.getEntityName(), joins );
if ( old != null ) {
log.warn( "duplicate joins for class: {}", persistentClass.getEntityName() );
}
}
public AnnotatedClassType getClassType(XClass clazz) {
AnnotatedClassType type = classTypes.get( clazz.getName() );
if ( type == null ) {
return addClassType( clazz );
}
else {
return type;
}
}
//FIXME should be private but is part of the ExtendedMapping contract
public AnnotatedClassType addClassType(XClass clazz) {
AnnotatedClassType type;
if ( clazz.isAnnotationPresent( Entity.class ) ) {
type = AnnotatedClassType.ENTITY;
}
else if ( clazz.isAnnotationPresent( Embeddable.class ) ) {
type = AnnotatedClassType.EMBEDDABLE;
}
else if ( clazz.isAnnotationPresent( javax.persistence.MappedSuperclass.class ) ) {
type = AnnotatedClassType.EMBEDDABLE_SUPERCLASS;
}
else {
type = AnnotatedClassType.NONE;
}
classTypes.put( clazz.getName(), type );
return type;
}
/**
* {@inheritDoc}
*/
public Map<Table, List<String[]>> getTableUniqueConstraints() {
final Map<Table, List<String[]>> deprecatedStructure = new HashMap<Table, List<String[]>>(
CollectionHelper.determineProperSizing( getUniqueConstraintHoldersByTable() ),
CollectionHelper.LOAD_FACTOR
);
for ( Map.Entry<Table, List<UniqueConstraintHolder>> entry : getUniqueConstraintHoldersByTable().entrySet() ) {
List<String[]> columnsPerConstraint = new ArrayList<String[]>(
CollectionHelper.determineProperSizing( entry.getValue().size() )
);
deprecatedStructure.put( entry.getKey(), columnsPerConstraint );
for ( UniqueConstraintHolder holder : entry.getValue() ) {
columnsPerConstraint.add( holder.getColumns() );
}
}
return deprecatedStructure;
}
public Map<Table, List<UniqueConstraintHolder>> getUniqueConstraintHoldersByTable() {
return uniqueConstraintHoldersByTable;
}
@SuppressWarnings({ "unchecked" })
public void addUniqueConstraints(Table table, List uniqueConstraints) {
List<UniqueConstraintHolder> constraintHolders = new ArrayList<UniqueConstraintHolder>(
CollectionHelper.determineProperSizing( uniqueConstraints.size() )
);
int keyNameBase = determineCurrentNumberOfUniqueConstraintHolders( table );
for ( String[] columns : ( List<String[]> ) uniqueConstraints ) {
final String keyName = "key" + keyNameBase++;
constraintHolders.add(
new UniqueConstraintHolder().setName( keyName ).setColumns( columns )
);
}
addUniqueConstraintHolders( table, constraintHolders );
}
private int determineCurrentNumberOfUniqueConstraintHolders(Table table) {
List currentHolders = getUniqueConstraintHoldersByTable().get( table );
return currentHolders == null
? 0
: currentHolders.size();
}
public void addUniqueConstraintHolders(Table table, List<UniqueConstraintHolder> uniqueConstraintHolders) {
List<UniqueConstraintHolder> holderList = getUniqueConstraintHoldersByTable().get( table );
if ( holderList == null ) {
holderList = new ArrayList<UniqueConstraintHolder>();
getUniqueConstraintHoldersByTable().put( table, holderList );
}
holderList.addAll( uniqueConstraintHolders );
}
public void addMappedBy(String entityName, String propertyName, String inversePropertyName) {
mappedByResolver.put( entityName + "." + propertyName, inversePropertyName );
}
public String getFromMappedBy(String entityName, String propertyName) {
return mappedByResolver.get( entityName + "." + propertyName );
}
public void addPropertyReferencedAssociation(String entityName, String propertyName, String propertyRef) {
propertyRefResolver.put( entityName + "." + propertyName, propertyRef );
}
public String getPropertyReferencedAssociation(String entityName, String propertyName) {
return propertyRefResolver.get( entityName + "." + propertyName );
}
public ReflectionManager getReflectionManager() {
return reflectionManager;
}
public Map getClasses() {
return classes;
}
public void addAnyMetaDef(AnyMetaDef defAnn) throws AnnotationException {
if ( anyMetaDefs.containsKey( defAnn.name() ) ) {
throw new AnnotationException( "Two @AnyMetaDef with the same name defined: " + defAnn.name() );
}
anyMetaDefs.put( defAnn.name(), defAnn );
}
public AnyMetaDef getAnyMetaDef(String name) {
return anyMetaDefs.get( name );
}
}
final ObjectNameNormalizer normalizer = new ObjectNameNormalizerImpl();
final class ObjectNameNormalizerImpl extends ObjectNameNormalizer implements Serializable {
public boolean isUseQuotedIdentifiersGlobally() {
//Do not cache this value as we lazily set it in Hibernate Annotation (AnnotationConfiguration)
//TODO use a dedicated protected useQuotedIdentifier flag in Configuration (overriden by AnnotationConfiguration)
String setting = (String) properties.get( Environment.GLOBALLY_QUOTED_IDENTIFIERS );
return setting != null && Boolean.valueOf( setting ).booleanValue();
}
public NamingStrategy getNamingStrategy() {
return namingStrategy;
}
}
protected class MetadataSourceQueue implements Serializable {
private LinkedHashMap<XMLHelper.MetadataXml, Set<String>> hbmMetadataToEntityNamesMap
= new LinkedHashMap<XMLHelper.MetadataXml, Set<String>>();
private Map<String, XMLHelper.MetadataXml> hbmMetadataByEntityNameXRef = new HashMap<String, XMLHelper.MetadataXml>();
//XClass are not serializable by default
private transient List<XClass> annotatedClasses = new ArrayList<XClass>();
//only used during the secondPhaseCompile pass, hence does not need to be serialized
private transient Map<String, XClass> annotatedClassesByEntityNameMap = new HashMap<String, XClass>();
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
annotatedClassesByEntityNameMap = new HashMap<String, XClass>();
//build back annotatedClasses
@SuppressWarnings( "unchecked" )
List<Class> serializableAnnotatedClasses = (List<Class>) ois.readObject();
annotatedClasses = new ArrayList<XClass>( serializableAnnotatedClasses.size() );
for ( Class clazz : serializableAnnotatedClasses ) {
annotatedClasses.add( reflectionManager.toXClass( clazz ) );
}
}
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
List<Class> serializableAnnotatedClasses = new ArrayList<Class>( annotatedClasses.size() );
for ( XClass xClass : annotatedClasses ) {
serializableAnnotatedClasses.add( reflectionManager.toClass( xClass ) );
}
out.writeObject( serializableAnnotatedClasses );
}
public void add(XMLHelper.MetadataXml metadataXml) {
final Document document = metadataXml.getXmlDocument();
final Element hmNode = document.getRootElement();
Attribute packNode = hmNode.attribute( "package" );
String defaultPackage = packNode != null ? packNode.getValue() : "";
Set<String> entityNames = new HashSet<String>();
findClassNames( defaultPackage, hmNode, entityNames );
for ( String entity : entityNames ) {
hbmMetadataByEntityNameXRef.put( entity, metadataXml );
}
this.hbmMetadataToEntityNamesMap.put( metadataXml, entityNames );
}
private void findClassNames(String defaultPackage, Element startNode, Set<String> names) {
// if we have some extends we need to check if those classes possibly could be inside the
// same hbm.xml file...
Iterator[] classes = new Iterator[4];
classes[0] = startNode.elementIterator( "class" );
classes[1] = startNode.elementIterator( "subclass" );
classes[2] = startNode.elementIterator( "joined-subclass" );
classes[3] = startNode.elementIterator( "union-subclass" );
Iterator classIterator = new JoinedIterator( classes );
while ( classIterator.hasNext() ) {
Element element = ( Element ) classIterator.next();
String entityName = element.attributeValue( "entity-name" );
if ( entityName == null ) {
entityName = getClassName( element.attribute( "name" ), defaultPackage );
}
names.add( entityName );
findClassNames( defaultPackage, element, names );
}
}
private String getClassName(Attribute name, String defaultPackage) {
if ( name == null ) {
return null;
}
String unqualifiedName = name.getValue();
if ( unqualifiedName == null ) {
return null;
}
if ( unqualifiedName.indexOf( '.' ) < 0 && defaultPackage != null ) {
return defaultPackage + '.' + unqualifiedName;
}
return unqualifiedName;
}
public void add(XClass annotatedClass) {
annotatedClasses.add( annotatedClass );
}
protected void syncAnnotatedClasses() {
final Iterator<XClass> itr = annotatedClasses.iterator();
while ( itr.hasNext() ) {
final XClass annotatedClass = itr.next();
if ( annotatedClass.isAnnotationPresent( Entity.class ) ) {
annotatedClassesByEntityNameMap.put( annotatedClass.getName(), annotatedClass );
continue;
}
if ( !annotatedClass.isAnnotationPresent( javax.persistence.MappedSuperclass.class ) ) {
itr.remove();
}
}
}
protected void processMetadata(List<MetadataSourceType> order) {
syncAnnotatedClasses();
for ( MetadataSourceType type : order ) {
if ( MetadataSourceType.HBM.equals( type ) ) {
processHbmXmlQueue();
}
else if ( MetadataSourceType.CLASS.equals( type ) ) {
processAnnotatedClassesQueue();
}
}
}
private void processHbmXmlQueue() {
log.debug( "Processing hbm.xml files" );
for ( Map.Entry<XMLHelper.MetadataXml, Set<String>> entry : hbmMetadataToEntityNamesMap.entrySet() ) {
// Unfortunately we have to create a Mappings instance for each iteration here
processHbmXml( entry.getKey(), entry.getValue() );
}
hbmMetadataToEntityNamesMap.clear();
hbmMetadataByEntityNameXRef.clear();
}
private void processHbmXml(XMLHelper.MetadataXml metadataXml, Set<String> entityNames) {
try {
HbmBinder.bindRoot( metadataXml, createMappings(), CollectionHelper.EMPTY_MAP, entityNames );
}
catch ( MappingException me ) {
throw new InvalidMappingException( metadataXml.getOriginType(), metadataXml.getOriginName(), me );
}
for ( String entityName : entityNames ) {
if ( annotatedClassesByEntityNameMap.containsKey( entityName ) ) {
annotatedClasses.remove( annotatedClassesByEntityNameMap.get( entityName ) );
annotatedClassesByEntityNameMap.remove( entityName );
}
}
}
private void processAnnotatedClassesQueue() {
log.debug( "Process annotated classes" );
//bind classes in the correct order calculating some inheritance state
List<XClass> orderedClasses = orderAndFillHierarchy( annotatedClasses );
Mappings mappings = createMappings();
Map<XClass, InheritanceState> inheritanceStatePerClass = AnnotationBinder.buildInheritanceStates(
orderedClasses, mappings
);
for ( XClass clazz : orderedClasses ) {
AnnotationBinder.bindClass( clazz, inheritanceStatePerClass, mappings );
final String entityName = clazz.getName();
if ( hbmMetadataByEntityNameXRef.containsKey( entityName ) ) {
hbmMetadataToEntityNamesMap.remove( hbmMetadataByEntityNameXRef.get( entityName ) );
hbmMetadataByEntityNameXRef.remove( entityName );
}
}
annotatedClasses.clear();
annotatedClassesByEntityNameMap.clear();
}
private List<XClass> orderAndFillHierarchy(List<XClass> original) {
List<XClass> copy = new ArrayList<XClass>( original );
insertMappedSuperclasses( original, copy );
// order the hierarchy
List<XClass> workingCopy = new ArrayList<XClass>( copy );
List<XClass> newList = new ArrayList<XClass>( copy.size() );
while ( workingCopy.size() > 0 ) {
XClass clazz = workingCopy.get( 0 );
orderHierarchy( workingCopy, newList, copy, clazz );
}
return newList;
}
private void insertMappedSuperclasses(List<XClass> original, List<XClass> copy) {
for ( XClass clazz : original ) {
XClass superClass = clazz.getSuperclass();
while ( superClass != null
&& !reflectionManager.equals( superClass, Object.class )
&& !copy.contains( superClass ) ) {
if ( superClass.isAnnotationPresent( Entity.class )
|| superClass.isAnnotationPresent( javax.persistence.MappedSuperclass.class ) ) {
copy.add( superClass );
}
superClass = superClass.getSuperclass();
}
}
}
private void orderHierarchy(List<XClass> copy, List<XClass> newList, List<XClass> original, XClass clazz) {
if ( clazz == null || reflectionManager.equals( clazz, Object.class ) ) {
return;
}
//process superclass first
orderHierarchy( copy, newList, original, clazz.getSuperclass() );
if ( original.contains( clazz ) ) {
if ( !newList.contains( clazz ) ) {
newList.add( clazz );
}
copy.remove( clazz );
}
}
public boolean isEmpty() {
return hbmMetadataToEntityNamesMap.isEmpty() && annotatedClasses.isEmpty();
}
}
public static final MetadataSourceType[] DEFAULT_ARTEFACT_PROCESSING_ORDER = new MetadataSourceType[] {
MetadataSourceType.HBM,
MetadataSourceType.CLASS
};
private List<MetadataSourceType> metadataSourcePrecedence;
private List<MetadataSourceType> determineMetadataSourcePrecedence() {
if ( metadataSourcePrecedence.isEmpty()
&& StringHelper.isNotEmpty( getProperties().getProperty( ARTEFACT_PROCESSING_ORDER ) ) ) {
metadataSourcePrecedence = parsePrecedence( getProperties().getProperty( ARTEFACT_PROCESSING_ORDER ) );
}
if ( metadataSourcePrecedence.isEmpty() ) {
metadataSourcePrecedence = Arrays.asList( DEFAULT_ARTEFACT_PROCESSING_ORDER );
}
metadataSourcePrecedence = Collections.unmodifiableList( metadataSourcePrecedence );
return metadataSourcePrecedence;
}
public void setPrecedence(String precedence) {
this.metadataSourcePrecedence = parsePrecedence( precedence );
}
private List<MetadataSourceType> parsePrecedence(String s) {
if ( StringHelper.isEmpty( s ) ) {
return Collections.emptyList();
}
StringTokenizer precedences = new StringTokenizer( s, ",; ", false );
List<MetadataSourceType> tmpPrecedences = new ArrayList<MetadataSourceType>();
while ( precedences.hasMoreElements() ) {
tmpPrecedences.add( MetadataSourceType.parsePrecedence( ( String ) precedences.nextElement() ) );
}
return tmpPrecedences;
}
private static class CacheHolder {
public CacheHolder(String role, String usage, String region, boolean isClass, boolean cacheLazy) {
this.role = role;
this.usage = usage;
this.region = region;
this.isClass = isClass;
this.cacheLazy = cacheLazy;
}
public String role;
public String usage;
public String region;
public boolean isClass;
public boolean cacheLazy;
}
}