HHH-13204 Introduce a configuration flag to skip processing of XML mapping metadata

This commit is contained in:
Sanne Grinovero 2019-01-15 17:41:16 +00:00 committed by Guillaume Smet
parent a26ff3b4f9
commit ed0cec0ddb
9 changed files with 297 additions and 128 deletions

View File

@ -69,7 +69,6 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.AbstractStandardBasicType;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
@ -616,6 +615,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
private IdGeneratorInterpreterImpl idGenerationTypeInterpreter = new IdGeneratorInterpreterImpl();
private String schemaCharset;
private boolean xmlMappingEnabled;
public MetadataBuildingOptionsImpl(StandardServiceRegistry serviceRegistry) {
this.serviceRegistry = serviceRegistry;
@ -627,6 +627,12 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
this.multiTenancyStrategy = MultiTenancyStrategy.determineMultiTenancyStrategy( configService.getSettings() );
this.xmlMappingEnabled = configService.getSetting(
AvailableSettings.XML_MAPPING_ENABLED,
StandardConverters.BOOLEAN,
true
);
this.implicitDiscriminatorsForJoinedInheritanceSupported = configService.getSetting(
AvailableSettings.IMPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS,
StandardConverters.BOOLEAN,
@ -901,6 +907,11 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
return schemaCharset;
}
@Override
public boolean isXmlMappingEnabled() {
return xmlMappingEnabled;
}
/**
* Yuck. This is needed because JPA lets users define "global building options"
* in {@code orm.xml} mappings. Forget that there are generally multiple

View File

@ -28,7 +28,6 @@ import org.hibernate.boot.model.source.internal.hbm.ModelBinder;
import org.hibernate.boot.model.source.spi.MetadataSourceProcessor;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.AdditionalJaxbMappingProducer;
import org.hibernate.boot.spi.BasicTypeRegistration;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.boot.spi.MetadataContributor;
@ -148,10 +147,10 @@ public class MetadataBuildingProcess {
// to unified model
final MetadataSourceProcessor processor = new MetadataSourceProcessor() {
private final HbmMetadataSourceProcessorImpl hbmProcessor = new HbmMetadataSourceProcessorImpl(
managedResources,
rootMetadataBuildingContext
);
private final MetadataSourceProcessor hbmProcessor =
options.isXmlMappingEnabled()
? new HbmMetadataSourceProcessorImpl( managedResources, rootMetadataBuildingContext )
: new NoOpMetadataSourceProcessorImpl();
private final AnnotationMetadataSourceProcessorImpl annotationProcessor = new AnnotationMetadataSourceProcessorImpl(
managedResources,

View File

@ -0,0 +1,71 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.boot.model.process.spi;
import java.util.Set;
import org.hibernate.boot.model.source.spi.MetadataSourceProcessor;
/**
* A no-op implementation of MetadataSourceProcessor.
* This is useful to replace other processors when they are disabled.
*/
final class NoOpMetadataSourceProcessorImpl implements MetadataSourceProcessor {
@Override
public void prepare() {
}
@Override
public void processTypeDefinitions() {
}
@Override
public void processQueryRenames() {
}
@Override
public void processNamedQueries() {
}
@Override
public void processAuxiliaryDatabaseObjectDefinitions() {
}
@Override
public void processIdentifierGenerators() {
}
@Override
public void processFilterDefinitions() {
}
@Override
public void processFetchProfiles() {
}
@Override
public void prepareForEntityHierarchyProcessing() {
}
@Override
public void processEntityHierarchies(Set<String> processedEntityNames) {
}
@Override
public void postProcessEntityHierarchies() {
}
@Override
public void processResultSetMappings() {
}
@Override
public void finishUp() {
}
}

View File

@ -75,30 +75,34 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
final AttributeConverterManager attributeConverterManager = new AttributeConverterManager( rootMetadataBuildingContext );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Ewww. This is temporary until we migrate to Jandex + StAX for annotation binding
if ( rootMetadataBuildingContext.getBuildingOptions().isXmlMappingEnabled() ) {
final JPAMetadataProvider jpaMetadataProvider = (JPAMetadataProvider) ( (MetadataProviderInjector) reflectionManager ).getMetadataProvider();
for ( Binding xmlBinding : managedResources.getXmlMappingBindings() ) {
// if ( !MappingBinder.DelayedOrmXmlData.class.isInstance( xmlBinding.getRoot() ) ) {
// continue;
// }
//
// // convert the StAX representation in delayedOrmXmlData to DOM because that's what commons-annotations needs
// final MappingBinder.DelayedOrmXmlData delayedOrmXmlData = (MappingBinder.DelayedOrmXmlData) xmlBinding.getRoot();
// org.dom4j.Document dom4jDocument = toDom4jDocument( delayedOrmXmlData );
if ( !org.dom4j.Document.class.isInstance( xmlBinding.getRoot() ) ) {
continue;
}
org.dom4j.Document dom4jDocument = (Document) xmlBinding.getRoot();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Ewww. This is temporary until we migrate to Jandex + StAX for annotation binding
final List<String> classNames = jpaMetadataProvider.getXMLContext().addDocument( dom4jDocument );
for ( String className : classNames ) {
xClasses.add( toXClass( className, reflectionManager ) );
}
final JPAMetadataProvider jpaMetadataProvider = (JPAMetadataProvider) ( (MetadataProviderInjector) reflectionManager )
.getMetadataProvider();
for ( Binding xmlBinding : managedResources.getXmlMappingBindings() ) {
// if ( !MappingBinder.DelayedOrmXmlData.class.isInstance( xmlBinding.getRoot() ) ) {
// continue;
// }
//
// // convert the StAX representation in delayedOrmXmlData to DOM because that's what commons-annotations needs
// final MappingBinder.DelayedOrmXmlData delayedOrmXmlData = (MappingBinder.DelayedOrmXmlData) xmlBinding.getRoot();
// org.dom4j.Document dom4jDocument = toDom4jDocument( delayedOrmXmlData );
if ( !org.dom4j.Document.class.isInstance( xmlBinding.getRoot() ) ) {
continue;
}
org.dom4j.Document dom4jDocument = (Document) xmlBinding.getRoot();
final List<String> classNames = jpaMetadataProvider.getXMLContext().addDocument( dom4jDocument );
for ( String className : classNames ) {
xClasses.add( toXClass( className, reflectionManager ) );
}
}
jpaMetadataProvider.getXMLContext().applyDiscoveredAttributeConverters( attributeConverterManager );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
jpaMetadataProvider.getXMLContext().applyDiscoveredAttributeConverters( attributeConverterManager );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final ClassLoaderService cls = rootMetadataBuildingContext.getBuildingOptions().getServiceRegistry().getService( ClassLoaderService.class );
for ( String className : managedResources.getAnnotatedClassNames() ) {

View File

@ -197,4 +197,9 @@ public abstract class AbstractDelegatingMetadataBuildingOptions implements Metad
return delegate.getSchemaCharset();
}
@Override
public boolean isXmlMappingEnabled() {
return delegate.isXmlMappingEnabled();
}
}

View File

@ -241,6 +241,10 @@ public interface MetadataBuildingOptions {
return null;
}
default boolean isXmlMappingEnabled() {
return true;
}
/**
* Access to any SQL functions explicitly registered with the MetadataBuilder. This
* does not include Dialect defined functions, etc.

View File

@ -689,6 +689,14 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
*/
String KEYWORD_AUTO_QUOTING_ENABLED = "hibernate.auto_quote_keyword";
/**
* Allows to skip processing of XML Mapping.
* This is for people using exclusively annotations to define their model, and might
* be able to improve efficiency of booting Hibernate ORM.
* By default, the XML mapping is taken into account.
* @since 5.4.1
*/
String XML_MAPPING_ENABLED = "hibernate.xml_mapping_enabled";
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -8,6 +8,7 @@ package org.hibernate.cfg.annotations.reflection;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -39,11 +40,17 @@ import org.dom4j.Element;
*/
@SuppressWarnings("unchecked")
public class JPAMetadataProvider implements MetadataProvider {
private final MetadataProvider delegate = new JavaMetadataProvider();
private final ClassLoaderAccess classLoaderAccess;
private final XMLContext xmlContext;
/**
* We allow fully disabling XML sources so to improve the efficiency of
* the boot process for those not using it.
*/
private final boolean xmlMappingEnabled;
private Map<Object, Object> defaults;
private Map<AnnotatedElement, AnnotationReader> cache = new HashMap<AnnotatedElement, AnnotationReader>(100);
@ -66,17 +73,19 @@ public class JPAMetadataProvider implements MetadataProvider {
}
return delegate;
}
} );
},
metadataBuildingOptions.isXmlMappingEnabled() );
}
public JPAMetadataProvider(BootstrapContext bootstrapContext) {
this( bootstrapContext.getClassLoaderAccess() );
this( bootstrapContext.getClassLoaderAccess(),
bootstrapContext.getMetadataBuildingOptions().isXmlMappingEnabled() );
}
JPAMetadataProvider(ClassLoaderAccess classLoaderAccess) {
JPAMetadataProvider(ClassLoaderAccess classLoaderAccess, boolean xmlMetadataEnabled) {
this.classLoaderAccess = classLoaderAccess;
this.xmlContext = new XMLContext( classLoaderAccess );
;
this.xmlMappingEnabled = xmlMetadataEnabled;
}
//all of the above can be safely rebuilt from XMLContext: only XMLContext this object is serialized
@ -94,106 +103,112 @@ public class JPAMetadataProvider implements MetadataProvider {
}
return reader;
}
@Override
public Map<Object, Object> getDefaults() {
if ( defaults == null ) {
defaults = new HashMap<>();
XMLContext.Default xmlDefaults = xmlContext.getDefault( null );
defaults.put( "schema", xmlDefaults.getSchema() );
defaults.put( "catalog", xmlDefaults.getCatalog() );
defaults.put( "delimited-identifier", xmlDefaults.getDelimitedIdentifier() );
defaults.put( "cascade-persist", xmlDefaults.getCascadePersist() );
List<Class> entityListeners = new ArrayList<Class>();
for ( String className : xmlContext.getDefaultEntityListeners() ) {
try {
entityListeners.add( classLoaderAccess.classForName( className ) );
}
catch ( ClassLoadingException e ) {
throw new IllegalStateException( "Default entity listener class not found: " + className );
}
}
defaults.put( EntityListeners.class, entityListeners );
for ( Element element : xmlContext.getAllDocuments() ) {
@SuppressWarnings( "unchecked" )
List<Element> elements = element.elements( "sequence-generator" );
List<SequenceGenerator> sequenceGenerators = ( List<SequenceGenerator> ) defaults.get( SequenceGenerator.class );
if ( sequenceGenerators == null ) {
sequenceGenerators = new ArrayList<>();
defaults.put( SequenceGenerator.class, sequenceGenerators );
}
for ( Element subelement : elements ) {
sequenceGenerators.add( JPAOverriddenAnnotationReader.buildSequenceGeneratorAnnotation( subelement ) );
}
elements = element.elements( "table-generator" );
List<TableGenerator> tableGenerators = ( List<TableGenerator> ) defaults.get( TableGenerator.class );
if ( tableGenerators == null ) {
tableGenerators = new ArrayList<>();
defaults.put( TableGenerator.class, tableGenerators );
}
for ( Element subelement : elements ) {
tableGenerators.add(
JPAOverriddenAnnotationReader.buildTableGeneratorAnnotation(
subelement, xmlDefaults
)
);
}
List<NamedQuery> namedQueries = ( List<NamedQuery> ) defaults.get( NamedQuery.class );
if ( namedQueries == null ) {
namedQueries = new ArrayList<>();
defaults.put( NamedQuery.class, namedQueries );
}
List<NamedQuery> currentNamedQueries = JPAOverriddenAnnotationReader.buildNamedQueries(
element,
false,
xmlDefaults,
classLoaderAccess
);
namedQueries.addAll( currentNamedQueries );
List<NamedNativeQuery> namedNativeQueries = ( List<NamedNativeQuery> ) defaults.get( NamedNativeQuery.class );
if ( namedNativeQueries == null ) {
namedNativeQueries = new ArrayList<>();
defaults.put( NamedNativeQuery.class, namedNativeQueries );
}
List<NamedNativeQuery> currentNamedNativeQueries = JPAOverriddenAnnotationReader.buildNamedQueries(
element,
true,
xmlDefaults,
classLoaderAccess
);
namedNativeQueries.addAll( currentNamedNativeQueries );
List<SqlResultSetMapping> sqlResultSetMappings = ( List<SqlResultSetMapping> ) defaults.get(
SqlResultSetMapping.class
);
if ( sqlResultSetMappings == null ) {
sqlResultSetMappings = new ArrayList<>();
defaults.put( SqlResultSetMapping.class, sqlResultSetMappings );
}
List<SqlResultSetMapping> currentSqlResultSetMappings = JPAOverriddenAnnotationReader.buildSqlResultsetMappings(
element,
xmlDefaults,
classLoaderAccess
);
sqlResultSetMappings.addAll( currentSqlResultSetMappings );
List<NamedStoredProcedureQuery> namedStoredProcedureQueries = (List<NamedStoredProcedureQuery>)defaults.get( NamedStoredProcedureQuery.class );
if(namedStoredProcedureQueries==null){
namedStoredProcedureQueries = new ArrayList<>( );
defaults.put( NamedStoredProcedureQuery.class, namedStoredProcedureQueries );
}
List<NamedStoredProcedureQuery> currentNamedStoredProcedureQueries = JPAOverriddenAnnotationReader.buildNamedStoreProcedureQueries(
element,
xmlDefaults,
classLoaderAccess
);
namedStoredProcedureQueries.addAll( currentNamedStoredProcedureQueries );
}
if ( xmlMappingEnabled == false ) {
return Collections.emptyMap();
}
else {
if ( defaults == null ) {
defaults = new HashMap<>();
XMLContext.Default xmlDefaults = xmlContext.getDefault( null );
defaults.put( "schema", xmlDefaults.getSchema() );
defaults.put( "catalog", xmlDefaults.getCatalog() );
defaults.put( "delimited-identifier", xmlDefaults.getDelimitedIdentifier() );
defaults.put( "cascade-persist", xmlDefaults.getCascadePersist() );
List<Class> entityListeners = new ArrayList<Class>();
for ( String className : xmlContext.getDefaultEntityListeners() ) {
try {
entityListeners.add( classLoaderAccess.classForName( className ) );
}
catch ( ClassLoadingException e ) {
throw new IllegalStateException( "Default entity listener class not found: " + className );
}
}
defaults.put( EntityListeners.class, entityListeners );
for ( Element element : xmlContext.getAllDocuments() ) {
@SuppressWarnings( "unchecked" )
List<Element> elements = element.elements( "sequence-generator" );
List<SequenceGenerator> sequenceGenerators = ( List<SequenceGenerator> ) defaults.get( SequenceGenerator.class );
if ( sequenceGenerators == null ) {
sequenceGenerators = new ArrayList<>();
defaults.put( SequenceGenerator.class, sequenceGenerators );
}
for ( Element subelement : elements ) {
sequenceGenerators.add( JPAOverriddenAnnotationReader.buildSequenceGeneratorAnnotation( subelement ) );
}
elements = element.elements( "table-generator" );
List<TableGenerator> tableGenerators = ( List<TableGenerator> ) defaults.get( TableGenerator.class );
if ( tableGenerators == null ) {
tableGenerators = new ArrayList<>();
defaults.put( TableGenerator.class, tableGenerators );
}
for ( Element subelement : elements ) {
tableGenerators.add(
JPAOverriddenAnnotationReader.buildTableGeneratorAnnotation(
subelement, xmlDefaults
)
);
}
List<NamedQuery> namedQueries = ( List<NamedQuery> ) defaults.get( NamedQuery.class );
if ( namedQueries == null ) {
namedQueries = new ArrayList<>();
defaults.put( NamedQuery.class, namedQueries );
}
List<NamedQuery> currentNamedQueries = JPAOverriddenAnnotationReader.buildNamedQueries(
element,
false,
xmlDefaults,
classLoaderAccess
);
namedQueries.addAll( currentNamedQueries );
List<NamedNativeQuery> namedNativeQueries = ( List<NamedNativeQuery> ) defaults.get( NamedNativeQuery.class );
if ( namedNativeQueries == null ) {
namedNativeQueries = new ArrayList<>();
defaults.put( NamedNativeQuery.class, namedNativeQueries );
}
List<NamedNativeQuery> currentNamedNativeQueries = JPAOverriddenAnnotationReader.buildNamedQueries(
element,
true,
xmlDefaults,
classLoaderAccess
);
namedNativeQueries.addAll( currentNamedNativeQueries );
List<SqlResultSetMapping> sqlResultSetMappings = ( List<SqlResultSetMapping> ) defaults.get(
SqlResultSetMapping.class
);
if ( sqlResultSetMappings == null ) {
sqlResultSetMappings = new ArrayList<>();
defaults.put( SqlResultSetMapping.class, sqlResultSetMappings );
}
List<SqlResultSetMapping> currentSqlResultSetMappings = JPAOverriddenAnnotationReader.buildSqlResultsetMappings(
element,
xmlDefaults,
classLoaderAccess
);
sqlResultSetMappings.addAll( currentSqlResultSetMappings );
List<NamedStoredProcedureQuery> namedStoredProcedureQueries = (List<NamedStoredProcedureQuery>)defaults.get( NamedStoredProcedureQuery.class );
if(namedStoredProcedureQueries==null){
namedStoredProcedureQueries = new ArrayList<>( );
defaults.put( NamedStoredProcedureQuery.class, namedStoredProcedureQueries );
}
List<NamedStoredProcedureQuery> currentNamedStoredProcedureQueries = JPAOverriddenAnnotationReader.buildNamedStoreProcedureQueries(
element,
xmlDefaults,
classLoaderAccess
);
namedStoredProcedureQueries.addAll( currentNamedStoredProcedureQueries );
}
}
return defaults;
}
return defaults;
}
public XMLContext getXMLContext() {

View File

@ -0,0 +1,52 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.boot.model.source.internal.hbm;
import java.util.Map;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
/**
* Verifies that setting org.hibernate.cfg.AvailableSettings#XML_MAPPING_ENABLED to
* false actually ignores the mapping files.
*/
public class XMLMappingDisabledTest extends BaseNonConfigCoreFunctionalTestCase {
@Test
public void xmlMappedEntityIsIgnored() throws Exception {
// If this booted we're good: the XML mapping used in this test is invalid.
}
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] {
AnnotationEntity.class,
HBMEntity.class
};
}
@Override
protected String[] getMappings() {
return new String[] {
"HBMEntity.hbm.xml"
};
}
@Override
protected String getBaseForMappings() {
return "/org/hibernate/boot/model/source/internal/hbm/";
}
@Override
protected void addSettings(Map settings) {
settings.put( AvailableSettings.XML_MAPPING_ENABLED, "false" );
}
}