HHH-16006 - Implement an "additional mapping" contributor SPI

This commit is contained in:
Steve Ebersole 2023-01-09 15:41:56 -06:00
parent ab86055565
commit a9ef9045f2
8 changed files with 301 additions and 22 deletions

View File

@ -18,11 +18,16 @@ import java.util.Set;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.internal.InFlightMetadataCollectorImpl;
import org.hibernate.boot.internal.MetadataBuildingContextRootImpl;
import org.hibernate.boot.jaxb.Origin;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmHibernateMapping;
import org.hibernate.boot.jaxb.internal.MappingBinder;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.boot.model.TypeContributor;
import org.hibernate.boot.model.process.internal.ManagedResourcesImpl;
import org.hibernate.boot.model.process.internal.ScanningCoordinator;
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.boot.model.relational.Sequence;
import org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl;
import org.hibernate.boot.model.source.internal.hbm.EntityHierarchyBuilder;
import org.hibernate.boot.model.source.internal.hbm.EntityHierarchySourceImpl;
@ -33,6 +38,8 @@ import org.hibernate.boot.model.source.spi.MetadataSourceProcessor;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.AdditionalJaxbMappingProducer;
import org.hibernate.boot.spi.AdditionalMappingContributions;
import org.hibernate.boot.spi.AdditionalMappingContributor;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.boot.spi.MetadataContributor;
@ -43,6 +50,8 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.SqlTypes;
@ -309,8 +318,138 @@ public class MetadataBuildingProcess {
processor.finishUp();
processAdditionalMappingContributions( options, metadataCollector, classLoaderService, rootMetadataBuildingContext );
processAdditionalJaxbMappingProducer( options, metadataCollector, classLoaderService, rootMetadataBuildingContext, jandexView );
applyExtraQueryImports( managedResources, metadataCollector );
return metadataCollector.buildMetadataInstance( rootMetadataBuildingContext );
}
private static void processAdditionalMappingContributions(MetadataBuildingOptions options, InFlightMetadataCollectorImpl metadataCollector, ClassLoaderService classLoaderService, MetadataBuildingContextRootImpl rootMetadataBuildingContext) {
final EntityHierarchyBuilder hierarchyBuilder = new EntityHierarchyBuilder();
final AdditionalMappingContributionsImpl contributions = new AdditionalMappingContributionsImpl(
metadataCollector,
options,
rootMetadataBuildingContext,
hierarchyBuilder
);
final MappingBinder mappingBinder;
if ( options.isXmlMappingEnabled() ) {
mappingBinder = new MappingBinder(
classLoaderService,
new MappingBinder.Options() {
@Override
public boolean validateMappings() {
return false;
}
@Override
public boolean transformHbmMappings() {
return false;
}
}
);
}
else {
mappingBinder = null;
}
final Collection<AdditionalMappingContributor> additionalMappingContributors = classLoaderService.loadJavaServices( AdditionalMappingContributor.class );
additionalMappingContributors.forEach( (contributor) -> {
contributions.setCurrentContributor( contributor.getContributorName() );
try {
contributor.contribute(
contributions,
metadataCollector,
mappingBinder,
rootMetadataBuildingContext
);
}
finally {
contributions.setCurrentContributor( null );
}
} );
final ModelBinder binder = ModelBinder.prepare( rootMetadataBuildingContext );
for ( EntityHierarchySourceImpl entityHierarchySource : hierarchyBuilder.buildHierarchies() ) {
binder.bindEntityHierarchy( entityHierarchySource );
}
}
private static class AdditionalMappingContributionsImpl implements AdditionalMappingContributions {
private final InFlightMetadataCollectorImpl metadataCollector;
private final MetadataBuildingOptions options;
private final MetadataBuildingContextRootImpl rootMetadataBuildingContext;
private final EntityHierarchyBuilder hierarchyBuilder;
private String currentContributor;
public AdditionalMappingContributionsImpl(
InFlightMetadataCollectorImpl metadataCollector,
MetadataBuildingOptions options,
MetadataBuildingContextRootImpl rootMetadataBuildingContext,
EntityHierarchyBuilder hierarchyBuilder) {
this.metadataCollector = metadataCollector;
this.options = options;
this.rootMetadataBuildingContext = rootMetadataBuildingContext;
this.hierarchyBuilder = hierarchyBuilder;
}
public String getCurrentContributor() {
return currentContributor;
}
public void setCurrentContributor(String contributor) {
this.currentContributor = contributor == null ? "orm" : contributor;
}
@Override
public void contributeBinding(JaxbHbmHibernateMapping hbmJaxbBinding, Origin origin) {
if ( ! options.isXmlMappingEnabled() ) {
return;
}
hierarchyBuilder.indexMappingDocument( new MappingDocument(
currentContributor,
hbmJaxbBinding,
origin,
rootMetadataBuildingContext
) );
}
@Override
public void contributeEntity(PersistentClass entity) {
metadataCollector.addEntityBinding( entity );
}
@Override
public void contributeTable(Table table) {
final Namespace namespace = metadataCollector.getDatabase().locateNamespace(
table.getCatalogIdentifier(),
table.getSchemaIdentifier()
);
namespace.registerTable( table.getNameIdentifier(), table );
metadataCollector.addTableNameBinding( table.getNameIdentifier(), table );
}
@Override
public void contributeSequence(Sequence sequence) {
final Namespace namespace = metadataCollector.getDatabase().locateNamespace(
sequence.getName().getCatalogName(),
sequence.getName().getSchemaName()
);
namespace.registerSequence( sequence.getName().getSequenceName(), sequence );
}
@Override
public void contributeAuxiliaryDatabaseObject(AuxiliaryDatabaseObject auxiliaryDatabaseObject) {
metadataCollector.addAuxiliaryDatabaseObject( auxiliaryDatabaseObject );
}
}
@Deprecated
private static void processAdditionalJaxbMappingProducer(MetadataBuildingOptions options, InFlightMetadataCollectorImpl metadataCollector, ClassLoaderService classLoaderService, MetadataBuildingContextRootImpl rootMetadataBuildingContext, IndexView jandexView) {
if ( options.isXmlMappingEnabled() ) {
//noinspection deprecation
final Iterable<AdditionalJaxbMappingProducer> producers = classLoaderService.loadJavaServices( AdditionalJaxbMappingProducer.class );
if ( producers != null ) {
final EntityHierarchyBuilder hierarchyBuilder = new EntityHierarchyBuilder();
@ -348,10 +487,6 @@ public class MetadataBuildingProcess {
}
}
}
applyExtraQueryImports( managedResources, metadataCollector );
return metadataCollector.buildMetadataInstance( rootMetadataBuildingContext );
}
private static void applyExtraQueryImports(

View File

@ -11,9 +11,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -96,6 +94,18 @@ public class Namespace {
return tables.get( logicalTableName );
}
public void registerTable(Identifier logicalName, Table table) {
final Table previous = tables.put( logicalName, table );
if ( previous != null ) {
log.debugf(
"Replacing Table registration(%s) : %s -> %s",
logicalName,
previous,
table
);
}
}
/**
* Creates a mapping Table instance.
*
@ -133,6 +143,13 @@ public class Namespace {
return sequences.get( name );
}
public void registerSequence(Identifier logicalName, Sequence sequence) {
if ( sequences.containsKey( logicalName ) ) {
throw new HibernateException( "Sequence was already registered with that name [" + logicalName.toString() + "]" );
}
sequences.put( logicalName, sequence );
}
public Sequence createSequence(Identifier logicalName, Function<Identifier,Sequence> creator) {
if ( sequences.containsKey( logicalName ) ) {
throw new HibernateException( "Sequence was already registered with that name [" + logicalName.toString() + "]" );

View File

@ -16,8 +16,7 @@ import org.jboss.jandex.IndexView;
/**
* @author Steve Ebersole
*
* @deprecated Intended for Envers integration until we can migrate Envers
* away from XML generation for building its model.
* @deprecated Use {@linkplain AdditionalMappingContributor} instead
*/
@Deprecated
public interface AdditionalJaxbMappingProducer {

View File

@ -0,0 +1,29 @@
/*
* 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.spi;
import org.hibernate.Incubating;
import org.hibernate.boot.jaxb.Origin;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmHibernateMapping;
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
import org.hibernate.boot.model.relational.Sequence;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
/**
* Collector for contributions from {@linkplain AdditionalMappingContributor contributors}
*
* @author Steve Ebersole
*/
@Incubating
public interface AdditionalMappingContributions {
void contributeBinding(JaxbHbmHibernateMapping hbmJaxbBinding, Origin origin);
void contributeEntity(PersistentClass entity);
void contributeTable(Table table);
void contributeSequence(Sequence sequence);
void contributeAuxiliaryDatabaseObject(AuxiliaryDatabaseObject auxiliaryDatabaseObject);
}

View File

@ -0,0 +1,40 @@
/*
* 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.spi;
import org.hibernate.Incubating;
import org.hibernate.boot.jaxb.internal.MappingBinder;
/**
* Contract allowing pluggable contributions of additional
* mapping objects.
*
* Resolvable as a {@linkplain java.util.ServiceLoader Java service}.
*
* @author Steve Ebersole
*/
@Incubating
public interface AdditionalMappingContributor {
default String getContributorName() {
return null;
}
/**
* Contribute the additional mappings
*
* @param contributions Collector of the contributions
* @param metadata Current (live) metadata
* @param jaxbBinder JAXB binding support for XML documents. May be {@code null}
* if XML processing is {@linkplain MetadataBuildingOptions#isXmlMappingEnabled() disabled}
* @param buildingContext Access to useful contextual details
*/
void contribute(
AdditionalMappingContributions contributions,
InFlightMetadataCollector metadata,
MappingBinder jaxbBinder,
MetadataBuildingContext buildingContext);
}

View File

@ -0,0 +1,65 @@
/*
* 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.envers.boot.internal;
import org.hibernate.HibernateException;
import org.hibernate.boot.jaxb.Origin;
import org.hibernate.boot.jaxb.SourceType;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmHibernateMapping;
import org.hibernate.boot.jaxb.internal.MappingBinder;
import org.hibernate.boot.spi.AdditionalMappingContributions;
import org.hibernate.boot.spi.AdditionalMappingContributor;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.envers.configuration.internal.MappingCollector;
import org.hibernate.service.ServiceRegistry;
import static org.hibernate.cfg.AvailableSettings.XML_MAPPING_ENABLED;
/**
* @author Steve Ebersole
*/
public class AdditionalMappingContributorImpl implements AdditionalMappingContributor {
@Override
public String getContributorName() {
return "envers";
}
@Override
public void contribute(
AdditionalMappingContributions contributions,
InFlightMetadataCollector metadata,
MappingBinder jaxbBinder,
MetadataBuildingContext buildingContext) {
final MetadataBuildingOptions metadataBuildingOptions = metadata.getMetadataBuildingOptions();
final ServiceRegistry serviceRegistry = metadataBuildingOptions.getServiceRegistry();
final EnversService enversService = serviceRegistry.getService( EnversService.class );
if ( !enversService.isEnabled() ) {
// short-circuit if envers integration has been disabled.
return;
}
if ( !metadataBuildingOptions.isXmlMappingEnabled() ) {
throw new HibernateException( "Hibernate Envers currently requires XML mapping to be enabled."
+ " Please don't disable setting `" + XML_MAPPING_ENABLED
+ "`; alternatively disable Hibernate Envers." );
}
final MappingCollector mappingCollector = new MappingCollector() {
private final Origin origin = new Origin( SourceType.OTHER, "envers" );
@Override
public void addDocument(JaxbHbmHibernateMapping mapping) {
contributions.contributeBinding( mapping, origin );
}
};
enversService.initialize( metadata, mappingCollector );
}
}

View File

@ -1,13 +0,0 @@
#
# 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>.
#
#
# 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>.
#
org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl

View File

@ -0,0 +1,7 @@
#
# 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.
#
org.hibernate.envers.boot.internal.AdditionalMappingContributorImpl