mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-17 00:24:57 +00:00
Merge remote-tracking branch 'upstream/main' into wip/6.0_merge
This commit is contained in:
commit
e9a933fe68
@ -60,7 +60,6 @@
|
||||
import org.hibernate.boot.spi.NaturalIdUniqueKeyBinder;
|
||||
import org.hibernate.cfg.AnnotatedClassType;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.CopyIdentifierComponentSecondPass;
|
||||
import org.hibernate.cfg.CreateKeySecondPass;
|
||||
import org.hibernate.cfg.FkSecondPass;
|
||||
import org.hibernate.cfg.IdGeneratorResolverSecondPass;
|
||||
@ -1520,7 +1519,6 @@ public Join locateJoin(Identifier tableName) {
|
||||
|
||||
private ArrayList<IdGeneratorResolverSecondPass> idGeneratorResolverSecondPassList;
|
||||
private ArrayList<SetBasicValueTypeSecondPass> setBasicValueTypeSecondPassList;
|
||||
private ArrayList<CopyIdentifierComponentSecondPass> copyIdentifierComponentSecondPasList;
|
||||
private ArrayList<FkSecondPass> fkSecondPassList;
|
||||
private ArrayList<CreateKeySecondPass> createKeySecondPasList;
|
||||
private ArrayList<SecondaryTableSecondPass> secondaryTableSecondPassList;
|
||||
@ -1542,9 +1540,6 @@ public void addSecondPass(SecondPass secondPass, boolean onTopOfTheQueue) {
|
||||
else if ( secondPass instanceof SetBasicValueTypeSecondPass ) {
|
||||
addSetBasicValueTypeSecondPass( (SetBasicValueTypeSecondPass) secondPass, onTopOfTheQueue );
|
||||
}
|
||||
else if ( secondPass instanceof CopyIdentifierComponentSecondPass ) {
|
||||
addCopyIdentifierComponentSecondPass( (CopyIdentifierComponentSecondPass) secondPass, onTopOfTheQueue );
|
||||
}
|
||||
else if ( secondPass instanceof FkSecondPass ) {
|
||||
addFkSecondPass( (FkSecondPass) secondPass, onTopOfTheQueue );
|
||||
}
|
||||
@ -1592,15 +1587,6 @@ private void addIdGeneratorResolverSecondPass(IdGeneratorResolverSecondPass seco
|
||||
addSecondPass( secondPass, idGeneratorResolverSecondPassList, onTopOfTheQueue );
|
||||
}
|
||||
|
||||
private void addCopyIdentifierComponentSecondPass(
|
||||
CopyIdentifierComponentSecondPass secondPass,
|
||||
boolean onTopOfTheQueue) {
|
||||
if ( copyIdentifierComponentSecondPasList == null ) {
|
||||
copyIdentifierComponentSecondPasList = new ArrayList<>();
|
||||
}
|
||||
addSecondPass( secondPass, copyIdentifierComponentSecondPasList, onTopOfTheQueue );
|
||||
}
|
||||
|
||||
private void addFkSecondPass(FkSecondPass secondPass, boolean onTopOfTheQueue) {
|
||||
if ( fkSecondPassList == null ) {
|
||||
fkSecondPassList = new ArrayList<>();
|
||||
@ -1652,7 +1638,6 @@ public void processSecondPasses(MetadataBuildingContext buildingContext) {
|
||||
processSecondPasses( setBasicValueTypeSecondPassList );
|
||||
|
||||
composites.forEach( Component::sortProperties );
|
||||
processCopyIdentifierSecondPassesInOrder();
|
||||
|
||||
processFkSecondPassesInOrder();
|
||||
|
||||
@ -1697,14 +1682,6 @@ private void processValueResolvers(MetadataBuildingContext buildingContext) {
|
||||
}
|
||||
}
|
||||
|
||||
private void processCopyIdentifierSecondPassesInOrder() {
|
||||
if ( copyIdentifierComponentSecondPasList == null ) {
|
||||
return;
|
||||
}
|
||||
sortCopyIdentifierComponentSecondPasses();
|
||||
processSecondPasses( copyIdentifierComponentSecondPasList );
|
||||
}
|
||||
|
||||
private void processSecondPasses(ArrayList<? extends SecondPass> secondPasses) {
|
||||
if ( secondPasses == null ) {
|
||||
return;
|
||||
@ -1717,39 +1694,6 @@ private void processSecondPasses(ArrayList<? extends SecondPass> secondPasses) {
|
||||
secondPasses.clear();
|
||||
}
|
||||
|
||||
private void sortCopyIdentifierComponentSecondPasses() {
|
||||
|
||||
ArrayList<CopyIdentifierComponentSecondPass> sorted =
|
||||
new ArrayList<>( copyIdentifierComponentSecondPasList.size() );
|
||||
Set<CopyIdentifierComponentSecondPass> toSort = new HashSet<>( copyIdentifierComponentSecondPasList );
|
||||
topologicalSort( sorted, toSort );
|
||||
copyIdentifierComponentSecondPasList = sorted;
|
||||
}
|
||||
|
||||
/* naive O(n^3) topological sort */
|
||||
private void topologicalSort( List<CopyIdentifierComponentSecondPass> sorted, Set<CopyIdentifierComponentSecondPass> toSort ) {
|
||||
while (!toSort.isEmpty()) {
|
||||
CopyIdentifierComponentSecondPass independent = null;
|
||||
|
||||
searchForIndependent:
|
||||
for ( CopyIdentifierComponentSecondPass secondPass : toSort ) {
|
||||
for ( CopyIdentifierComponentSecondPass other : toSort ) {
|
||||
if (secondPass.dependentUpon( other )) {
|
||||
continue searchForIndependent;
|
||||
}
|
||||
}
|
||||
independent = secondPass;
|
||||
break;
|
||||
}
|
||||
if (independent == null) {
|
||||
throw new MappingException( "cyclic dependency in derived identities" );
|
||||
}
|
||||
toSort.remove( independent );
|
||||
sorted.add( independent );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void processFkSecondPassesInOrder() {
|
||||
if ( fkSecondPassList == null || fkSecondPassList.isEmpty() ) {
|
||||
return;
|
||||
@ -2223,7 +2167,7 @@ private void processCachingOverrides() {
|
||||
"Cache override referenced an unknown entity : " + cacheRegionDefinition.getRole()
|
||||
);
|
||||
}
|
||||
if ( !(entityBinding instanceof RootClass) ) {
|
||||
if ( !( entityBinding instanceof RootClass ) ) {
|
||||
throw new HibernateException(
|
||||
"Cache override referenced a non-root entity : " + cacheRegionDefinition.getRole()
|
||||
);
|
||||
@ -2311,7 +2255,7 @@ private void processExportableProducers() {
|
||||
}
|
||||
|
||||
for ( Collection collection : collectionBindingMap.values() ) {
|
||||
if ( !(collection instanceof IdentifierCollection) ) {
|
||||
if ( !( collection instanceof IdentifierCollection ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,6 @@
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.boot.model.naming.Identifier;
|
||||
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
|
||||
import org.hibernate.boot.model.naming.ObjectNameNormalizer;
|
||||
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
|
||||
import org.hibernate.boot.model.relational.Database;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
@ -34,7 +32,7 @@
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public class CopyIdentifierComponentSecondPass implements SecondPass {
|
||||
public class CopyIdentifierComponentSecondPass extends FkSecondPass {
|
||||
private static final Logger log = Logger.getLogger( CopyIdentifierComponentSecondPass.class );
|
||||
|
||||
private final String referencedEntityName;
|
||||
@ -47,12 +45,25 @@ public CopyIdentifierComponentSecondPass(
|
||||
String referencedEntityName,
|
||||
Ejb3JoinColumn[] joinColumns,
|
||||
MetadataBuildingContext buildingContext) {
|
||||
super( comp, joinColumns );
|
||||
this.component = comp;
|
||||
this.referencedEntityName = referencedEntityName;
|
||||
this.buildingContext = buildingContext;
|
||||
this.joinColumns = joinColumns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReferencedEntityName() {
|
||||
return referencedEntityName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInPrimaryKey() {
|
||||
// This second pass is apparently only ever used to initialize composite identifiers
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public void doSecondPass(Map persistentClasses) throws MappingException {
|
||||
PersistentClass referencedPersistentClass = (PersistentClass) persistentClasses.get( referencedEntityName );
|
||||
@ -217,8 +228,4 @@ private void applyComponentColumnSizeValueToJoinColumn(Column column, Ejb3JoinCo
|
||||
mappingColumn.setPrecision( column.getPrecision() );
|
||||
mappingColumn.setScale( column.getScale() );
|
||||
}
|
||||
|
||||
public boolean dependentUpon( CopyIdentifierComponentSecondPass other ) {
|
||||
return this.referencedEntityName.equals( other.component.getOwner().getEntityName() );
|
||||
}
|
||||
}
|
||||
|
@ -217,6 +217,20 @@ public AbstractSharedSessionContract(SessionFactoryImpl factory, SessionCreation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the implementation provided on SharedSessionContractImplementor
|
||||
* which is not very efficient: this method is hot in Hibernate Reactive, and could
|
||||
* be hot in some ORM contexts as well.
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Integer getConfiguredJdbcBatchSize() {
|
||||
final Integer sessionJdbcBatchSize = this.jdbcBatchSize;
|
||||
return sessionJdbcBatchSize == null ?
|
||||
fastSessionServices.defaultJdbcBatchSize :
|
||||
sessionJdbcBatchSize;
|
||||
}
|
||||
|
||||
protected void addSharedSessionTransactionObserver(TransactionCoordinator transactionCoordinator) {
|
||||
}
|
||||
|
||||
|
@ -255,20 +255,6 @@ protected ActionQueue createActionQueue() {
|
||||
return new ActionQueue( this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the implementation provided on SharedSessionContractImplementor
|
||||
* which is not very efficient: this method is hot in Hibernate Reactive, and could
|
||||
* be hot in some ORM contexts as well.
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Integer getConfiguredJdbcBatchSize() {
|
||||
final Integer sessionJdbcBatchSize = getJdbcBatchSize();
|
||||
return sessionJdbcBatchSize == null ?
|
||||
fastSessionServices.defaultJdbcBatchSize :
|
||||
sessionJdbcBatchSize;
|
||||
}
|
||||
|
||||
private LockOptions getLockOptionsForRead() {
|
||||
return this.lockOptions == null ? fastSessionServices.defaultLockOptions : this.lockOptions;
|
||||
}
|
||||
|
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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.test.idclass;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hibernate.orm.test.util.SchemaUtil.getColumnNames;
|
||||
|
||||
import java.io.Serializable;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.IdClass;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test that bootstrap doesn't throw an exception
|
||||
* when an entity has a composite ID containing an association to another entity,
|
||||
* which itself has a composite ID containing an association to another entity.
|
||||
* <p>
|
||||
* This test used to fail on bootstrap with the following error:
|
||||
* <p>
|
||||
* org.hibernate.MappingException: identifier mapping has wrong number of columns: org.hibernate.test.idclass.IdClassForNestedIdWithAssociationTest$NestedIdClassEntity type: component[idClassEntity,key3]
|
||||
* at org.hibernate.mapping.RootClass.validate(RootClass.java:273)
|
||||
* at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:359)
|
||||
* at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:307)
|
||||
* at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:471)
|
||||
* at org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase.buildResources(BaseNonConfigCoreFunctionalTestCase.java:165)
|
||||
* at org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase.startUp(BaseNonConfigCoreFunctionalTestCase.java:141)
|
||||
* at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
|
||||
* at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
|
||||
* at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
|
||||
* at java.base/java.lang.reflect.Method.invoke(Method.java:566)
|
||||
* at org.hibernate.testing.junit4.TestClassMetadata.performCallbackInvocation(TestClassMetadata.java:205)
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-14918")
|
||||
public class IdClassForNestedIdWithAssociationTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { BasicEntity.class, IdClassEntity.class, NestedIdClassEntity.class };
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataTest() {
|
||||
assertThat( getColumnNames( "NestedIdClassEntity", metadata() ) )
|
||||
// Just check we're using copied IDs; otherwise the test wouldn't be able to reproduce HHH-14918.
|
||||
.containsExactlyInAnyOrder( "idClassEntity_basicEntity_key1", "idClassEntity_key2", "key3" );
|
||||
}
|
||||
|
||||
// The main goal of the test is to check that bootstrap doesn't throw an exception,
|
||||
// but it feels wrong to have a test class with just an empty test method,
|
||||
// so just check that persisting/loading works correctly.
|
||||
@Test
|
||||
public void smokeTest() {
|
||||
inTransaction( s -> {
|
||||
BasicEntity basic = new BasicEntity( 1L );
|
||||
s.persist( basic );
|
||||
IdClassEntity idClass = new IdClassEntity( basic, 2L );
|
||||
s.persist( idClass );
|
||||
NestedIdClassEntity nestedIdClass = new NestedIdClassEntity( idClass, 3L );
|
||||
s.persist( nestedIdClass );
|
||||
} );
|
||||
|
||||
inTransaction( s -> {
|
||||
NestedIdClassEntity nestedIdClass = s.get(
|
||||
NestedIdClassEntity.class,
|
||||
new NestedIdClassEntity.NestedIdClassEntityId( 1L, 2L, 3L )
|
||||
);
|
||||
assertThat( nestedIdClass )
|
||||
.extracting( NestedIdClassEntity::getKey3 )
|
||||
.isEqualTo( 3L );
|
||||
IdClassEntity idClass = nestedIdClass.getIdClassEntity();
|
||||
assertThat( idClass )
|
||||
.extracting( IdClassEntity::getKey2 )
|
||||
.isEqualTo( 2L );
|
||||
BasicEntity basic = idClass.basicEntity;
|
||||
assertThat( basic )
|
||||
.extracting( BasicEntity::getKey1 )
|
||||
.isEqualTo( 1L );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "BasicEntity")
|
||||
public static class BasicEntity {
|
||||
@Id
|
||||
Long key1;
|
||||
|
||||
protected BasicEntity() {
|
||||
}
|
||||
|
||||
public BasicEntity(long key1) {
|
||||
this.key1 = key1;
|
||||
}
|
||||
|
||||
public Long getKey1() {
|
||||
return key1;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "IdClassEntity")
|
||||
@IdClass(IdClassEntity.IdClassEntityId.class)
|
||||
public static class IdClassEntity {
|
||||
@Id
|
||||
@ManyToOne
|
||||
BasicEntity basicEntity;
|
||||
@Id
|
||||
Long key2;
|
||||
|
||||
protected IdClassEntity() {
|
||||
}
|
||||
|
||||
public IdClassEntity(BasicEntity basicEntity, long key2) {
|
||||
this.basicEntity = basicEntity;
|
||||
this.key2 = key2;
|
||||
}
|
||||
|
||||
public BasicEntity getBasicEntity() {
|
||||
return basicEntity;
|
||||
}
|
||||
|
||||
public Long getKey2() {
|
||||
return key2;
|
||||
}
|
||||
|
||||
public static class IdClassEntityId implements Serializable {
|
||||
long basicEntity;
|
||||
long key2;
|
||||
|
||||
protected IdClassEntityId() {
|
||||
}
|
||||
|
||||
public IdClassEntityId(long basicEntity, long key2) {
|
||||
this.basicEntity = basicEntity;
|
||||
this.key2 = key2;
|
||||
}
|
||||
|
||||
public long getBasicEntity() {
|
||||
return basicEntity;
|
||||
}
|
||||
|
||||
public long getKey2() {
|
||||
return key2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "NestedIdClassEntity")
|
||||
@IdClass(NestedIdClassEntity.NestedIdClassEntityId.class)
|
||||
public static class NestedIdClassEntity {
|
||||
@Id
|
||||
@ManyToOne
|
||||
IdClassEntity idClassEntity;
|
||||
@Id
|
||||
Long key3;
|
||||
|
||||
protected NestedIdClassEntity() {
|
||||
}
|
||||
|
||||
public NestedIdClassEntity(IdClassEntity idClassEntity, long key3) {
|
||||
this.idClassEntity = idClassEntity;
|
||||
this.key3 = key3;
|
||||
}
|
||||
|
||||
public IdClassEntity getIdClassEntity() {
|
||||
return idClassEntity;
|
||||
}
|
||||
|
||||
public Long getKey3() {
|
||||
return key3;
|
||||
}
|
||||
|
||||
public static class NestedIdClassEntityId implements Serializable {
|
||||
IdClassEntity.IdClassEntityId idClassEntity;
|
||||
long key3;
|
||||
|
||||
protected NestedIdClassEntityId() {
|
||||
}
|
||||
|
||||
public NestedIdClassEntityId(IdClassEntity.IdClassEntityId idClassEntity, long key3) {
|
||||
this.idClassEntity = idClassEntity;
|
||||
this.key3 = key3;
|
||||
}
|
||||
|
||||
public NestedIdClassEntityId(long basicEntity, long key2, long key3) {
|
||||
this( new IdClassEntity.IdClassEntityId( basicEntity, key2 ), key3 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user