HHH-13725 - Implement ToOne Associations support
This commit is contained in:
parent
a99881a103
commit
09d1dd3daf
|
@ -29,7 +29,7 @@ import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
|||
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
||||
import org.hibernate.sql.ast.tree.from.TableReferenceCollector;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.results.internal.domain.entity.DelayedEntityFetch;
|
||||
import org.hibernate.sql.results.internal.domain.entity.DelayedEntityFetchImpl;
|
||||
import org.hibernate.sql.results.internal.domain.entity.EntityFetchImpl;
|
||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.spi.Fetch;
|
||||
|
@ -87,14 +87,13 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
|
|||
LockMode lockMode,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
||||
final TableGroup lhsTableGroup = sqlAstCreationState.getFromClauseAccess()
|
||||
.getTableGroup( fetchParent.getNavigablePath() );
|
||||
|
||||
if ( fetchTiming == FetchTiming.IMMEDIATE && selected ) {
|
||||
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
||||
|
||||
TableGroup lhsTableGroup = sqlAstCreationState.getFromClauseAccess()
|
||||
.getTableGroup( fetchParent.getNavigablePath() );
|
||||
|
||||
if ( sqlAstCreationState.getFromClauseAccess().findTableGroup( fetchablePath ) == null ) {
|
||||
// todo (6.0) : verify the JoinType si correct
|
||||
// todo (6.0) : verify the JoinType is correct
|
||||
JoinType joinType;
|
||||
if ( isNullable ) {
|
||||
joinType = JoinType.LEFT;
|
||||
|
@ -132,12 +131,13 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
|
|||
);
|
||||
}
|
||||
|
||||
return new DelayedEntityFetch(
|
||||
return new DelayedEntityFetchImpl(
|
||||
fetchParent,
|
||||
this,
|
||||
lockMode,
|
||||
!selected,
|
||||
isNullable,
|
||||
fetchablePath,
|
||||
foreignKeyDescriptor.createDomainResult( fetchablePath, lhsTableGroup, creationState ),
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
package org.hibernate.sql.results.internal.domain.entity;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class DelayedEntityAssembler {
|
||||
}
|
|
@ -1,192 +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
|
||||
*/
|
||||
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.spi.EntityKey;
|
||||
import org.hibernate.metamodel.mapping.internal.SingularAssociationAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.results.internal.domain.AbstractFetchParentAccess;
|
||||
import org.hibernate.sql.results.spi.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.spi.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.spi.EntityInitializer;
|
||||
import org.hibernate.sql.results.spi.Fetch;
|
||||
import org.hibernate.sql.results.spi.FetchParent;
|
||||
import org.hibernate.sql.results.spi.FetchParentAccess;
|
||||
import org.hibernate.sql.results.spi.Fetchable;
|
||||
import org.hibernate.sql.results.spi.Initializer;
|
||||
import org.hibernate.sql.results.spi.RowProcessingState;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class DelayedEntityFetch implements Fetch {
|
||||
|
||||
private FetchParent fetchParent;
|
||||
private SingularAssociationAttributeMapping fetchedAttribute;
|
||||
private final LockMode lockMode;
|
||||
private final NavigablePath navigablePath;
|
||||
private final boolean nullable;
|
||||
private final DomainResultCreationState creationState;
|
||||
|
||||
public DelayedEntityFetch(
|
||||
FetchParent fetchParent,
|
||||
SingularAssociationAttributeMapping fetchedAttribute,
|
||||
LockMode lockMode,
|
||||
boolean nullable,
|
||||
NavigablePath navigablePath,
|
||||
DomainResultCreationState creationState) {
|
||||
this.fetchParent = fetchParent;
|
||||
this.fetchedAttribute = fetchedAttribute;
|
||||
this.lockMode = lockMode;
|
||||
this.nullable = nullable;
|
||||
this.navigablePath = navigablePath;
|
||||
this.creationState = creationState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParent getFetchParent() {
|
||||
return fetchParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetchable getFetchedMapping() {
|
||||
return fetchedAttribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNullable() {
|
||||
return nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
Consumer<Initializer> collector,
|
||||
AssemblerCreationState creationState) {
|
||||
EntityInitializer entityInitializer = new DelayedEntityFetchInitializer(
|
||||
parentAccess,
|
||||
navigablePath,
|
||||
(EntityPersister) fetchedAttribute.getMappedTypeDescriptor()
|
||||
);
|
||||
collector.accept( entityInitializer );
|
||||
return new EntityAssembler( fetchedAttribute.getJavaTypeDescriptor(), entityInitializer );
|
||||
|
||||
}
|
||||
|
||||
private static class DelayedEntityFetchInitializer extends AbstractFetchParentAccess implements EntityInitializer {
|
||||
|
||||
private final FetchParentAccess parentAccess;
|
||||
private final NavigablePath navigablePath;
|
||||
private final EntityPersister concreteDescriptor;
|
||||
|
||||
private Object entityInstance;
|
||||
|
||||
protected DelayedEntityFetchInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor
|
||||
) {
|
||||
this.parentAccess = parentAccess;
|
||||
this.navigablePath = fetchedNavigable;
|
||||
this.concreteDescriptor = concreteDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState rowProcessingState) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
final EntityKey entityKey = new EntityKey(
|
||||
parentAccess.getParentKey(),
|
||||
concreteDescriptor
|
||||
);
|
||||
Object fkValue = entityKey.getIdentifierValue();
|
||||
|
||||
// todo (6.0) : technically the entity could be managed or cached already. who/what handles that?
|
||||
|
||||
// todo (6.0) : could also be getting loaded elsewhere (LoadingEntityEntry)
|
||||
if ( fkValue == null ) {
|
||||
// todo (6.0) : check this is the correct behaviour
|
||||
entityInstance = null;
|
||||
}
|
||||
else {
|
||||
if ( concreteDescriptor.hasProxy() ) {
|
||||
entityInstance = concreteDescriptor.createProxy(
|
||||
fkValue,
|
||||
rowProcessingState.getSession()
|
||||
);
|
||||
}
|
||||
else if ( concreteDescriptor
|
||||
.getBytecodeEnhancementMetadata()
|
||||
.isEnhancedForLazyLoading() ) {
|
||||
entityInstance = concreteDescriptor.instantiate(
|
||||
fkValue,
|
||||
rowProcessingState.getSession()
|
||||
);
|
||||
}
|
||||
|
||||
notifyParentResolutionListeners( entityInstance );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
entityInstance = null;
|
||||
|
||||
clearParentResolutionListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityPersister getEntityDescriptor() {
|
||||
return concreteDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEntityInstance() {
|
||||
return entityInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParentKey() {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResolutionListener(Consumer<Object> listener) {
|
||||
if ( entityInstance != null ) {
|
||||
listener.accept( entityInstance );
|
||||
}
|
||||
else {
|
||||
super.registerResolutionListener( listener );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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.sql.results.internal.domain.entity;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.metamodel.mapping.internal.SingularAssociationAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.results.spi.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.spi.DomainResult;
|
||||
import org.hibernate.sql.results.spi.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.spi.EntityInitializer;
|
||||
import org.hibernate.sql.results.spi.Fetch;
|
||||
import org.hibernate.sql.results.spi.FetchParent;
|
||||
import org.hibernate.sql.results.spi.FetchParentAccess;
|
||||
import org.hibernate.sql.results.spi.Fetchable;
|
||||
import org.hibernate.sql.results.spi.Initializer;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class DelayedEntityFetchImpl implements Fetch {
|
||||
|
||||
private FetchParent fetchParent;
|
||||
private SingularAssociationAttributeMapping fetchedAttribute;
|
||||
private final LockMode lockMode;
|
||||
private final NavigablePath navigablePath;
|
||||
private final boolean nullable;
|
||||
private DomainResult fkResult;
|
||||
private final DomainResultCreationState creationState;
|
||||
|
||||
public DelayedEntityFetchImpl(
|
||||
FetchParent fetchParent,
|
||||
SingularAssociationAttributeMapping fetchedAttribute,
|
||||
LockMode lockMode,
|
||||
boolean nullable,
|
||||
NavigablePath navigablePath,
|
||||
DomainResult fkResult,
|
||||
DomainResultCreationState creationState) {
|
||||
this.fetchParent = fetchParent;
|
||||
this.fetchedAttribute = fetchedAttribute;
|
||||
this.lockMode = lockMode;
|
||||
this.nullable = nullable;
|
||||
this.navigablePath = navigablePath;
|
||||
this.fkResult = fkResult;
|
||||
this.creationState = creationState;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParent getFetchParent() {
|
||||
return fetchParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetchable getFetchedMapping() {
|
||||
return fetchedAttribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNullable() {
|
||||
return nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
Consumer<Initializer> collector,
|
||||
AssemblerCreationState creationState) {
|
||||
EntityInitializer entityInitializer = new DelayedEntityFetchInitializer(
|
||||
parentAccess,
|
||||
navigablePath,
|
||||
(EntityPersister) fetchedAttribute.getMappedTypeDescriptor(),
|
||||
fkResult.createResultAssembler( collector, creationState )
|
||||
);
|
||||
collector.accept( entityInitializer );
|
||||
return new EntityAssembler( fetchedAttribute.getJavaTypeDescriptor(), entityInitializer );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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.sql.results.internal.domain.entity;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.results.internal.domain.AbstractFetchParentAccess;
|
||||
import org.hibernate.sql.results.spi.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.spi.EntityInitializer;
|
||||
import org.hibernate.sql.results.spi.FetchParentAccess;
|
||||
import org.hibernate.sql.results.spi.RowProcessingState;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class DelayedEntityFetchInitializer extends AbstractFetchParentAccess implements EntityInitializer {
|
||||
|
||||
private final FetchParentAccess parentAccess;
|
||||
private final NavigablePath navigablePath;
|
||||
private final EntityPersister concreteDescriptor;
|
||||
private final DomainResultAssembler fkValueAssembler;
|
||||
|
||||
|
||||
private Object entityInstance;
|
||||
private Object fkValue;
|
||||
|
||||
protected DelayedEntityFetchInitializer(
|
||||
FetchParentAccess parentAccess,
|
||||
NavigablePath fetchedNavigable,
|
||||
EntityPersister concreteDescriptor,
|
||||
DomainResultAssembler fkValueAssembler
|
||||
) {
|
||||
this.parentAccess = parentAccess;
|
||||
this.navigablePath = fetchedNavigable;
|
||||
this.concreteDescriptor = concreteDescriptor;
|
||||
this.fkValueAssembler = fkValueAssembler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveKey(RowProcessingState rowProcessingState) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveInstance(RowProcessingState rowProcessingState) {
|
||||
fkValue = fkValueAssembler.assemble( rowProcessingState );
|
||||
|
||||
// todo (6.0) : technically the entity could be managed or cached already. who/what handles that?
|
||||
|
||||
// todo (6.0) : could also be getting loaded elsewhere (LoadingEntityEntry)
|
||||
if ( fkValue == null ) {
|
||||
// todo (6.0) : check this is the correct behaviour
|
||||
entityInstance = null;
|
||||
}
|
||||
else {
|
||||
if ( concreteDescriptor.hasProxy() ) {
|
||||
entityInstance = concreteDescriptor.createProxy(
|
||||
fkValue,
|
||||
rowProcessingState.getSession()
|
||||
);
|
||||
}
|
||||
else if ( concreteDescriptor
|
||||
.getBytecodeEnhancementMetadata()
|
||||
.isEnhancedForLazyLoading() ) {
|
||||
entityInstance = concreteDescriptor.instantiate(
|
||||
fkValue,
|
||||
rowProcessingState.getSession()
|
||||
);
|
||||
}
|
||||
|
||||
notifyParentResolutionListeners( entityInstance );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeInstance(RowProcessingState rowProcessingState) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
entityInstance = null;
|
||||
fkValue = null;
|
||||
|
||||
clearParentResolutionListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityPersister getEntityDescriptor() {
|
||||
return concreteDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEntityInstance() {
|
||||
return entityInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParentKey() {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResolutionListener(Consumer<Object> listener) {
|
||||
if ( entityInstance != null ) {
|
||||
listener.accept( entityInstance );
|
||||
}
|
||||
else {
|
||||
super.registerResolutionListener( listener );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,17 +35,17 @@ import static org.junit.Assert.assertTrue;
|
|||
*/
|
||||
@DomainModel(
|
||||
annotatedClasses = {
|
||||
LazyManyToOneTest.SimpleEntity.class,
|
||||
LazyManyToOneTest.OtherEntity.class,
|
||||
LazyManyToOneTest.AnotherSimpleEntity.class
|
||||
ManyToOneTest.SimpleEntity.class,
|
||||
ManyToOneTest.OtherEntity.class,
|
||||
ManyToOneTest.AnotherSimpleEntity.class
|
||||
}
|
||||
)
|
||||
@ServiceRegistry
|
||||
@SessionFactory
|
||||
public class LazyManyToOneTest {
|
||||
public class ManyToOneTest {
|
||||
|
||||
@Test
|
||||
public void testSelect(SessionFactoryScope scope) {
|
||||
public void testHqlSelect(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
OtherEntity otherEntity = session.
|
||||
|
@ -53,14 +53,51 @@ public class LazyManyToOneTest {
|
|||
.uniqueResult();
|
||||
|
||||
assertThat( otherEntity.getName(), is( "Bar" ) );
|
||||
assertFalse( Hibernate.isInitialized( otherEntity.getSimpleEntity() ) );
|
||||
assertFalse( Hibernate.isInitialized( otherEntity.getAnotherSimpleEntity() ) );
|
||||
SimpleEntity simpleEntity = otherEntity.getSimpleEntity();
|
||||
assertFalse( Hibernate.isInitialized( simpleEntity ) );
|
||||
|
||||
AnotherSimpleEntity anotherSimpleEntity = otherEntity.getAnotherSimpleEntity();
|
||||
// the ManyToOne is eager but the value is null so a second query is not executed
|
||||
assertTrue( Hibernate.isInitialized( anotherSimpleEntity ) );
|
||||
|
||||
assertThat( simpleEntity.getName(), is( "Fab" ) );
|
||||
|
||||
assertTrue( Hibernate.isInitialized( simpleEntity ) );
|
||||
}
|
||||
);
|
||||
|
||||
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 );
|
||||
}
|
||||
);
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
OtherEntity otherEntity = session.
|
||||
createQuery( "from OtherEntity", OtherEntity.class )
|
||||
.uniqueResult();
|
||||
|
||||
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 ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectWithFetchJoin(SessionFactoryScope scope) {
|
||||
public void testHQLSelectWithFetchJoin(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
OtherEntity otherEntity = session.
|
||||
|
@ -71,7 +108,8 @@ public class LazyManyToOneTest {
|
|||
assertTrue( Hibernate.isInitialized( otherEntity.getSimpleEntity() ) );
|
||||
assertThat( otherEntity.getSimpleEntity(), notNullValue() );
|
||||
assertThat( otherEntity.getSimpleEntity().getName(), is( "Fab" ) );
|
||||
assertFalse( Hibernate.isInitialized( otherEntity.getAnotherSimpleEntity() ) );
|
||||
AnotherSimpleEntity anotherSimpleEntity = otherEntity.getAnotherSimpleEntity();
|
||||
assertTrue( Hibernate.isInitialized( anotherSimpleEntity ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -81,20 +119,22 @@ public class LazyManyToOneTest {
|
|||
scope.inTransaction(
|
||||
session -> {
|
||||
OtherEntity otherEntity = session.
|
||||
createQuery( "from OtherEntity o join fetch o.simpleEntity left join fetch o.anotherSimpleEntity", OtherEntity.class )
|
||||
createQuery(
|
||||
"from OtherEntity o join fetch o.simpleEntity left join fetch o.anotherSimpleEntity",
|
||||
OtherEntity.class
|
||||
)
|
||||
.uniqueResult();
|
||||
|
||||
assertThat( otherEntity.getName(), is( "Bar" ) );
|
||||
assertTrue( Hibernate.isInitialized( otherEntity.getSimpleEntity() ) );
|
||||
assertThat( otherEntity.getSimpleEntity(), notNullValue() );
|
||||
assertThat( otherEntity.getSimpleEntity().getName(), is( "Fab" ) );
|
||||
assertTrue (Hibernate.isInitialized( otherEntity.getAnotherSimpleEntity() ) );
|
||||
assertThat( otherEntity.getAnotherSimpleEntity(), nullValue( ) );
|
||||
assertTrue( Hibernate.isInitialized( otherEntity.getAnotherSimpleEntity() ) );
|
||||
assertThat( otherEntity.getAnotherSimpleEntity(), nullValue() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGet(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
|
@ -108,6 +148,23 @@ public class LazyManyToOneTest {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelete(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.remove( session.get( OtherEntity.class, 2 ) );
|
||||
}
|
||||
|
||||
);
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
assertThat( session.get( OtherEntity.class, 2 ), nullValue() );
|
||||
assertThat( session.get( SimpleEntity.class, 1 ), notNullValue() );
|
||||
assertThat( session.get( AnotherSimpleEntity.class, 3 ), notNullValue() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
|
@ -145,6 +202,7 @@ public class LazyManyToOneTest {
|
|||
public static class OtherEntity {
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
private SimpleEntity simpleEntity;
|
||||
|
||||
private AnotherSimpleEntity anotherSimpleEntity;
|
Loading…
Reference in New Issue