-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HHH-17350 - Work on hibernate-models, XSD and JAXB HHH-16114 - Improve boot metamodel binding HHH-15996 - Develop an abstraction for Annotation in annotation processing HHH-16012 - Develop an abstraction for domain model Class refs HHH-15997 - Support for dynamic models in orm.xml HHH-15698 - Support for entity-name in mapping.xsd
- Loading branch information
Showing
15 changed files
with
548 additions
and
178 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
...-core/src/main/java/org/hibernate/boot/models/bind/internal/JoinedSubclassKeyHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/* | ||
* 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.models.bind.internal; | ||
|
||
import java.util.List; | ||
import java.util.Locale; | ||
|
||
import org.hibernate.MappingException; | ||
import org.hibernate.boot.spi.MetadataBuildingContext; | ||
import org.hibernate.internal.util.StringHelper; | ||
import org.hibernate.internal.util.collections.CollectionHelper; | ||
import org.hibernate.mapping.Column; | ||
import org.hibernate.mapping.DependantValue; | ||
import org.hibernate.mapping.JoinedSubclass; | ||
import org.hibernate.mapping.KeyValue; | ||
import org.hibernate.models.spi.AnnotationUsage; | ||
import org.hibernate.models.spi.ClassDetails; | ||
|
||
import jakarta.persistence.ForeignKey; | ||
import jakarta.persistence.PrimaryKeyJoinColumn; | ||
|
||
/** | ||
* @author Steve Ebersole | ||
*/ | ||
public record JoinedSubclassKeyHandler( | ||
ClassDetails subclassDetails, | ||
JoinedSubclass subclass, | ||
MetadataBuildingContext buildingContext) implements ResolutionCallback<KeyValue> { | ||
|
||
@Override | ||
public boolean handleResolution(KeyValue targetKeyValue) { | ||
final List<AnnotationUsage<PrimaryKeyJoinColumn>> pkJoinColumns = subclassDetails.getRepeatedAnnotationUsages( PrimaryKeyJoinColumn.class ); | ||
final DependantValue fkValue = new DependantValue( buildingContext, subclass.getTable(), targetKeyValue ); | ||
|
||
if ( CollectionHelper.isEmpty( pkJoinColumns ) ) { | ||
handleImplicitJoinColumns( targetKeyValue, fkValue ); | ||
} | ||
else { | ||
handleExplicitJoinColumns( pkJoinColumns, targetKeyValue, fkValue ); | ||
} | ||
|
||
final AnnotationUsage<ForeignKey> fkAnn = subclassDetails.getAnnotationUsage( ForeignKey.class ); | ||
|
||
final String foreignKeyName = fkAnn == null | ||
// todo : generate the name here - this *is* equiv to legacy second pass | ||
? "" | ||
: fkAnn.getString( "name" ); | ||
final String foreignKeyDefinition = fkAnn == null | ||
? "" | ||
: fkAnn.getString( "foreignKeyDefinition" ); | ||
|
||
final org.hibernate.mapping.ForeignKey foreignKey = subclass.getTable().createForeignKey( | ||
foreignKeyName, | ||
fkValue.getColumns(), | ||
subclass.getRootClass().getEntityName(), | ||
foreignKeyDefinition, | ||
targetKeyValue.getColumns() | ||
); | ||
foreignKey.setReferencedTable( subclass.getRootTable() ); | ||
|
||
return true; | ||
} | ||
|
||
private void handleImplicitJoinColumns(KeyValue targetKeyValue, DependantValue fkValue) { | ||
targetKeyValue.getColumns().forEach( (column) -> { | ||
final Column fkColumn = column.clone(); | ||
subclass.getTable().getPrimaryKey().addColumn( fkColumn ); | ||
fkValue.addColumn( fkColumn ); | ||
} ); | ||
} | ||
|
||
private void handleExplicitJoinColumns( | ||
List<AnnotationUsage<PrimaryKeyJoinColumn>> pkJoinColumns, | ||
KeyValue targetKeyValue, | ||
DependantValue fkValue) { | ||
for ( int i = 0; i < targetKeyValue.getColumnSpan(); i++ ) { | ||
final Column targetColumn = targetKeyValue.getColumns().get( i ); | ||
final var joinColumnAnn = resolveMatchingJoinColumnAnn( | ||
targetColumn, | ||
pkJoinColumns, | ||
i | ||
); | ||
|
||
final Column keyColumn = ColumnHelper.bindColumn( joinColumnAnn, targetColumn::getName, true, false ); | ||
subclass().getTable().getPrimaryKey().addColumn( keyColumn ); | ||
fkValue.addColumn( keyColumn ); | ||
} | ||
} | ||
|
||
private AnnotationUsage<PrimaryKeyJoinColumn> resolveMatchingJoinColumnAnn( | ||
Column targetPkColumn, | ||
List<AnnotationUsage<PrimaryKeyJoinColumn>> keyJoinColumns, | ||
int pkColumnPosition) { | ||
for ( int j = 0; j < keyJoinColumns.size(); j++ ) { | ||
final var keyJoinColumn = keyJoinColumns.get( j ); | ||
final String name = keyJoinColumn.getString( "name" ); | ||
final String referencedColumnName = keyJoinColumn.getString( "referencedColumnName" ); | ||
if ( StringHelper.isEmpty( name ) && StringHelper.isEmpty( referencedColumnName ) ) { | ||
// assume positional match | ||
// todo : is this correct? the only other option is to throw an exception | ||
if ( j == pkColumnPosition ) { | ||
return keyJoinColumn; | ||
} | ||
} | ||
else if ( StringHelper.isNotEmpty( referencedColumnName ) ) { | ||
if ( targetPkColumn.getName().equals( referencedColumnName ) ) { | ||
return keyJoinColumn; | ||
} | ||
} | ||
else { | ||
assert StringHelper.isNotEmpty( name ); | ||
if ( targetPkColumn.getName().equals( name ) ) { | ||
return keyJoinColumn; | ||
} | ||
} | ||
} | ||
|
||
throw new MappingException( | ||
String.format( | ||
Locale.ROOT, | ||
"Unable to match primary key column [%s] to any PrimaryKeyJoinColumn - %s", | ||
targetPkColumn.getName(), | ||
subclass().getEntityName() | ||
) | ||
); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
hibernate-core/src/main/java/org/hibernate/boot/models/bind/internal/Observable.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* | ||
* 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.models.bind.internal; | ||
|
||
/** | ||
* @author Steve Ebersole | ||
*/ | ||
public interface Observable<T> { | ||
void whenResolved(ResolutionCallback<T> callback); | ||
} |
67 changes: 67 additions & 0 deletions
67
hibernate-core/src/main/java/org/hibernate/boot/models/bind/internal/ObservableHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* 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.models.bind.internal; | ||
|
||
import java.util.Iterator; | ||
import java.util.List; | ||
|
||
import org.hibernate.internal.util.collections.CollectionHelper; | ||
import org.hibernate.models.ModelsException; | ||
|
||
import static org.hibernate.boot.models.bind.ModelBindingLogging.MODEL_BINDING_LOGGER; | ||
|
||
/** | ||
* @author Steve Ebersole | ||
*/ | ||
public class ObservableHelper { | ||
public static <T> void processCallbacks( | ||
T resolvedInstance, | ||
List<? extends ResolutionCallback<T>> callbacks) { | ||
processCallbacks( resolvedInstance, callbacks, false ); | ||
} | ||
|
||
public static <T> void processCallbacks( | ||
T resolvedInstance, | ||
List<? extends ResolutionCallback<T>> callbacks, | ||
boolean allowUnresolved) { | ||
if ( CollectionHelper.isEmpty( callbacks ) ) { | ||
return; | ||
} | ||
|
||
int processedCount = 0; | ||
final Iterator<? extends ResolutionCallback<T>> secondPassItr = callbacks.iterator(); | ||
while ( secondPassItr.hasNext() ) { | ||
final ResolutionCallback<T> callback = secondPassItr.next(); | ||
try { | ||
final boolean success = callback.handleResolution( resolvedInstance ); | ||
if ( success ) { | ||
processedCount++; | ||
secondPassItr.remove(); | ||
} | ||
} | ||
catch (Exception e) { | ||
// todo : handle cases where the caught exception is a non-transient, invariant condition | ||
// indicating to immediately stop the processing and throw an error (either the original | ||
// or a new one. | ||
MODEL_BINDING_LOGGER.debug( "Error processing second pass", e ); | ||
} | ||
} | ||
|
||
if ( !callbacks.isEmpty() ) { | ||
if ( processedCount == 0 ) { | ||
// there are callbacks in the queue, but we were not able to | ||
// successfully process any of them. this is a non-changing | ||
// error condition - throw an exception, unless `allowUnresolved` == true | ||
if ( !allowUnresolved ) { | ||
throw new ModelsException( "Unable to process second-pass list" ); | ||
} | ||
} | ||
|
||
processCallbacks( resolvedInstance, callbacks ); | ||
} | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
hibernate-core/src/main/java/org/hibernate/boot/models/bind/internal/ResolutionCallback.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* 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.models.bind.internal; | ||
|
||
/** | ||
* Provides callback notification when an object of interest is | ||
* fully resolved and all of its state available. | ||
* | ||
* @author Steve Ebersole | ||
*/ | ||
@FunctionalInterface | ||
public interface ResolutionCallback<T> { | ||
/** | ||
* Callback to use the fully resolved {@code resolvedThing} | ||
* | ||
* @param resolvedThing The resolved object of interest | ||
* | ||
* @return {@code true} if processing was successful; {@code false} otherwise | ||
*/ | ||
boolean handleResolution(T resolvedThing); | ||
} |
Oops, something went wrong.