HHH-13725 - Implement ToOne Associations support
This commit is contained in:
parent
09d1dd3daf
commit
dd772ab2a0
|
@ -83,6 +83,8 @@ public class DelayedEntityFetchImpl implements Fetch {
|
|||
EntityInitializer entityInitializer = new DelayedEntityFetchInitializer(
|
||||
parentAccess,
|
||||
navigablePath,
|
||||
fetchedAttribute.getMappedFetchStrategy(),
|
||||
lockMode,
|
||||
(EntityPersister) fetchedAttribute.getMappedTypeDescriptor(),
|
||||
fkResult.createResultAssembler( collector, creationState )
|
||||
);
|
||||
|
|
|
@ -8,7 +8,10 @@ package org.hibernate.sql.results.internal.domain.entity;
|
|||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.FetchStrategy;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.results.internal.domain.AbstractFetchParentAccess;
|
||||
|
@ -25,6 +28,8 @@ public class DelayedEntityFetchInitializer extends AbstractFetchParentAccess imp
|
|||
|
||||
private final FetchParentAccess parentAccess;
|
||||
private final NavigablePath navigablePath;
|
||||
private FetchStrategy mappedFetchedStrategy;
|
||||
private LockMode lockMode;
|
||||
private final EntityPersister concreteDescriptor;
|
||||
private final DomainResultAssembler fkValueAssembler;
|
||||
|
||||
|
@ -35,11 +40,15 @@ public class DelayedEntityFetchInitializer extends AbstractFetchParentAccess imp
|
|||
protected DelayedEntityFetchInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
NavigablePath fetchedNavigable,
|
||||
FetchStrategy mappedFetchedStrategy,
|
||||
LockMode lockMode,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler fkValueAssembler
|
||||
) {
|
||||
this.parentAccess = parentAccess;
|
||||
this.navigablePath = fetchedNavigable;
|
||||
this.mappedFetchedStrategy = mappedFetchedStrategy;
|
||||
this.lockMode = lockMode;
|
||||
this.concreteDescriptor = concreteDescriptor;
|
||||
this.fkValueAssembler = fkValueAssembler;
|
||||
}
|
||||
|
@ -66,19 +75,24 @@ public class DelayedEntityFetchInitializer extends AbstractFetchParentAccess imp
|
|||
entityInstance = null;
|
||||
}
|
||||
else {
|
||||
if ( concreteDescriptor.hasProxy() ) {
|
||||
entityInstance = concreteDescriptor.createProxy(
|
||||
fkValue,
|
||||
rowProcessingState.getSession()
|
||||
);
|
||||
if ( mappedFetchedStrategy.getTiming() != FetchTiming.IMMEDIATE ) {
|
||||
if ( concreteDescriptor.hasProxy() ) {
|
||||
entityInstance = concreteDescriptor.createProxy(
|
||||
fkValue,
|
||||
rowProcessingState.getSession()
|
||||
);
|
||||
}
|
||||
else if ( concreteDescriptor
|
||||
.getBytecodeEnhancementMetadata()
|
||||
.isEnhancedForLazyLoading() ) {
|
||||
entityInstance = concreteDescriptor.instantiate(
|
||||
fkValue,
|
||||
rowProcessingState.getSession()
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ( concreteDescriptor
|
||||
.getBytecodeEnhancementMetadata()
|
||||
.isEnhancedForLazyLoading() ) {
|
||||
entityInstance = concreteDescriptor.instantiate(
|
||||
fkValue,
|
||||
rowProcessingState.getSession()
|
||||
);
|
||||
else {
|
||||
entityInstance = concreteDescriptor.load( fkValue, null, lockMode, rowProcessingState.getSession() );
|
||||
}
|
||||
|
||||
notifyParentResolutionListeners( entityInstance );
|
||||
|
|
|
@ -14,6 +14,7 @@ import javax.persistence.ManyToOne;
|
|||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
|
@ -41,17 +42,22 @@ import static org.junit.Assert.assertTrue;
|
|||
}
|
||||
)
|
||||
@ServiceRegistry
|
||||
@SessionFactory
|
||||
@SessionFactory(generateStatistics = true)
|
||||
public class ManyToOneTest {
|
||||
|
||||
@Test
|
||||
public void testHqlSelect(SessionFactoryScope scope) {
|
||||
StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
|
||||
statistics.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
|
||||
OtherEntity otherEntity = session.
|
||||
createQuery( "from OtherEntity", OtherEntity.class )
|
||||
.uniqueResult();
|
||||
|
||||
assertThat( statistics.getPrepareStatementCount(), is(1L) );
|
||||
|
||||
assertThat( otherEntity.getName(), is( "Bar" ) );
|
||||
SimpleEntity simpleEntity = otherEntity.getSimpleEntity();
|
||||
assertFalse( Hibernate.isInitialized( simpleEntity ) );
|
||||
|
@ -61,6 +67,7 @@ public class ManyToOneTest {
|
|||
assertTrue( Hibernate.isInitialized( anotherSimpleEntity ) );
|
||||
|
||||
assertThat( simpleEntity.getName(), is( "Fab" ) );
|
||||
assertThat( statistics.getPrepareStatementCount(), is(2L) );
|
||||
|
||||
assertTrue( Hibernate.isInitialized( simpleEntity ) );
|
||||
}
|
||||
|
@ -79,30 +86,37 @@ public class ManyToOneTest {
|
|||
}
|
||||
);
|
||||
|
||||
statistics.clear();
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
OtherEntity otherEntity = session.
|
||||
createQuery( "from OtherEntity", OtherEntity.class )
|
||||
.uniqueResult();
|
||||
// the ManyToOne is eager but the value is not null so a second query is executed
|
||||
assertThat( statistics.getPrepareStatementCount(), is(2L) );
|
||||
|
||||
assertThat( otherEntity.getName(), is( "Bar" ) );
|
||||
SimpleEntity simpleEntity = otherEntity.getSimpleEntity();
|
||||
assertFalse( Hibernate.isInitialized( simpleEntity ) );
|
||||
|
||||
AnotherSimpleEntity anotherSimpleEntity = otherEntity.getAnotherSimpleEntity();
|
||||
// the ManyToOne is eager but the value is not null so a second query is executed
|
||||
assertTrue( Hibernate.isInitialized( anotherSimpleEntity ) );
|
||||
assertThat( statistics.getPrepareStatementCount(), is(2L) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHQLSelectWithFetchJoin(SessionFactoryScope scope) {
|
||||
StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
|
||||
statistics.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
OtherEntity otherEntity = session.
|
||||
createQuery( "from OtherEntity o join fetch o.simpleEntity", OtherEntity.class )
|
||||
.uniqueResult();
|
||||
assertThat( statistics.getPrepareStatementCount(), is(1L) );
|
||||
|
||||
assertThat( otherEntity.getName(), is( "Bar" ) );
|
||||
assertTrue( Hibernate.isInitialized( otherEntity.getSimpleEntity() ) );
|
||||
|
@ -110,12 +124,16 @@ public class ManyToOneTest {
|
|||
assertThat( otherEntity.getSimpleEntity().getName(), is( "Fab" ) );
|
||||
AnotherSimpleEntity anotherSimpleEntity = otherEntity.getAnotherSimpleEntity();
|
||||
assertTrue( Hibernate.isInitialized( anotherSimpleEntity ) );
|
||||
assertThat( statistics.getPrepareStatementCount(), is(1L) );
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectWithBothFetchJoin(SessionFactoryScope scope) {
|
||||
StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
|
||||
statistics.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
OtherEntity otherEntity = session.
|
||||
|
@ -124,6 +142,7 @@ public class ManyToOneTest {
|
|||
OtherEntity.class
|
||||
)
|
||||
.uniqueResult();
|
||||
assertThat( statistics.getPrepareStatementCount(), is(1L) );
|
||||
|
||||
assertThat( otherEntity.getName(), is( "Bar" ) );
|
||||
assertTrue( Hibernate.isInitialized( otherEntity.getSimpleEntity() ) );
|
||||
|
@ -131,12 +150,48 @@ public class ManyToOneTest {
|
|||
assertThat( otherEntity.getSimpleEntity().getName(), is( "Fab" ) );
|
||||
assertTrue( Hibernate.isInitialized( otherEntity.getAnotherSimpleEntity() ) );
|
||||
assertThat( otherEntity.getAnotherSimpleEntity(), nullValue() );
|
||||
assertThat( statistics.getPrepareStatementCount(), is(1L) );
|
||||
}
|
||||
);
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
OtherEntity otherEntity = session.
|
||||
createQuery( "from OtherEntity", OtherEntity.class )
|
||||
.uniqueResult();
|
||||
AnotherSimpleEntity anotherSimpleEntity = new AnotherSimpleEntity();
|
||||
anotherSimpleEntity.setId( 3 );
|
||||
anotherSimpleEntity.setName( "other" );
|
||||
session.save( anotherSimpleEntity );
|
||||
otherEntity.setAnotherSimpleEntity( anotherSimpleEntity );
|
||||
}
|
||||
);
|
||||
|
||||
statistics.clear();
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
OtherEntity otherEntity = session.
|
||||
createQuery( "from OtherEntity o join fetch o.simpleEntity left join fetch o.anotherSimpleEntity", OtherEntity.class )
|
||||
.uniqueResult();
|
||||
// the ManyToOne is eager but the value is not null so a second query is executed
|
||||
assertThat( statistics.getPrepareStatementCount(), is(1L) );
|
||||
|
||||
assertThat( otherEntity.getName(), is( "Bar" ) );
|
||||
SimpleEntity simpleEntity = otherEntity.getSimpleEntity();
|
||||
assertTrue( Hibernate.isInitialized( simpleEntity ) );
|
||||
|
||||
AnotherSimpleEntity anotherSimpleEntity = otherEntity.getAnotherSimpleEntity();
|
||||
assertTrue( Hibernate.isInitialized( anotherSimpleEntity ) );
|
||||
assertThat( statistics.getPrepareStatementCount(), is(1L) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGet(SessionFactoryScope scope) {
|
||||
StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
|
||||
statistics.clear();
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
OtherEntity otherEntity = session.get( OtherEntity.class, 2 );
|
||||
|
@ -144,6 +199,7 @@ public class ManyToOneTest {
|
|||
assertThat( otherEntity.getName(), is( "Bar" ) );
|
||||
assertFalse( Hibernate.isInitialized( otherEntity.getSimpleEntity() ) );
|
||||
assertTrue( Hibernate.isInitialized( otherEntity.getAnotherSimpleEntity() ) );
|
||||
assertThat( statistics.getPrepareStatementCount(), is(1L) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -160,7 +216,6 @@ public class ManyToOneTest {
|
|||
session -> {
|
||||
assertThat( session.get( OtherEntity.class, 2 ), nullValue() );
|
||||
assertThat( session.get( SimpleEntity.class, 1 ), notNullValue() );
|
||||
assertThat( session.get( AnotherSimpleEntity.class, 3 ), notNullValue() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -190,6 +245,7 @@ public class ManyToOneTest {
|
|||
work -> {
|
||||
Statement statement = work.createStatement();
|
||||
statement.execute( "delete from mapping_other_entity" );
|
||||
statement.execute( "delete from mapping_another_simple_entity" );
|
||||
statement.execute( "delete from mapping_simple_entity" );
|
||||
statement.close();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue