HHH-13725 Work on circular fetch detection
This commit is contained in:
parent
461e559184
commit
aa3ff4507d
|
@ -299,7 +299,8 @@ public class MetamodelSelectBuilderProcess {
|
|||
|
||||
final Fetch biDirectionalFetch = circularFetchDetector.findBiDirectionalFetch(
|
||||
fetchParent,
|
||||
fetchable
|
||||
fetchable,
|
||||
creationState
|
||||
);
|
||||
|
||||
if ( biDirectionalFetch != null ) {
|
||||
|
|
|
@ -13,8 +13,10 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
|
|||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
|
@ -24,6 +26,7 @@ import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
|||
import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlAstProcessingState;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupBuilder;
|
||||
|
@ -36,7 +39,6 @@ import org.hibernate.sql.results.internal.domain.entity.EntityFetch;
|
|||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.spi.Fetch;
|
||||
import org.hibernate.sql.results.spi.FetchParent;
|
||||
import org.hibernate.sql.results.spi.FetchableContainer;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
|
@ -46,7 +48,6 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
|
|||
private final String sqlAliasStem;
|
||||
private final boolean isNullable;
|
||||
private final ForeignKeyDescriptor foreignKeyDescriptor;
|
||||
private String mappedBy;
|
||||
|
||||
public SingularAssociationAttributeMapping(
|
||||
String name,
|
||||
|
@ -70,7 +71,6 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
|
|||
this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( name );
|
||||
this.isNullable = isNullable;
|
||||
this.foreignKeyDescriptor = foreignKeyDescriptor;
|
||||
this.mappedBy = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -226,17 +226,40 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isCircular(FetchParent fetchParent) {
|
||||
NavigablePath navigablePath = fetchParent.getNavigablePath();
|
||||
if ( navigablePath.getParent() == null ) {
|
||||
public boolean isCircular(FetchParent fetchParent, SqlAstProcessingState creationState) {
|
||||
final NavigablePath navigablePath = fetchParent.getNavigablePath();
|
||||
|
||||
final NavigablePath parent = navigablePath.getParent();
|
||||
if ( parent == null ) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
NavigablePath parentParentNavigablePath = navigablePath.getParent();
|
||||
if ( parentParentNavigablePath.getLocalName().equals( getMappedTypeDescriptor().getEntityName() ) ) {
|
||||
final String entityName = getMappedTypeDescriptor().getEntityName();
|
||||
if ( parent.getLocalName().equals( entityName ) ) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
NavigablePath parentOfParent = parent.getParent();
|
||||
if ( parentOfParent == null ) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if ( parentOfParent.getParent() != null ) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
final EntityPersister entityDescriptor = creationState.getSqlAstCreationState()
|
||||
.getCreationContext()
|
||||
.getDomainModel()
|
||||
.findEntityDescriptor( parentOfParent.getFullPath() );
|
||||
final String parentEntityName = entityDescriptor.findSubPart( parent.getLocalName() ).getJavaTypeDescriptor()
|
||||
.getJavaType()
|
||||
.getName();
|
||||
if ( parentEntityName.equals( entityName ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,7 +172,8 @@ public class StandardSqmSelectTranslator
|
|||
public void accept(Fetchable fetchable) {
|
||||
final Fetch biDirectionalFetch = circularFetchDetector.findBiDirectionalFetch(
|
||||
fetchParent,
|
||||
fetchable
|
||||
fetchable,
|
||||
getSqlAstCreationState().getCurrentProcessingState()
|
||||
);
|
||||
|
||||
if ( biDirectionalFetch != null ) {
|
||||
|
|
|
@ -293,6 +293,30 @@ public abstract class AbstractSqlAstWalker
|
|||
// }
|
||||
|
||||
renderTableReference( tableGroup.getPrimaryTableReference() );
|
||||
|
||||
renderTableReferenceJoins( tableGroup );
|
||||
|
||||
// if ( tableGroup.getGroupAlias() != null ) {
|
||||
// sqlAppender.appendSql( CLOSE_PARENTHESIS );
|
||||
// sqlAppender.appendSql( AS_KEYWORD );
|
||||
// sqlAppender.appendSql( tableGroup.getGroupAlias() );
|
||||
// }
|
||||
|
||||
processTableGroupJoins( tableGroup );
|
||||
}
|
||||
|
||||
protected void renderTableGroup(TableGroup tableGroup, Predicate predicate) {
|
||||
// NOTE : commented out blocks render the TableGroup as a CTE
|
||||
|
||||
// if ( tableGroup.getGroupAlias() != null ) {
|
||||
// sqlAppender.appendSql( OPEN_PARENTHESIS );
|
||||
// }
|
||||
|
||||
renderTableReference( tableGroup.getPrimaryTableReference() );
|
||||
|
||||
appendSql( " on " );
|
||||
predicate.accept( this );
|
||||
|
||||
renderTableReferenceJoins( tableGroup );
|
||||
|
||||
// if ( tableGroup.getGroupAlias() != null ) {
|
||||
|
@ -355,17 +379,11 @@ public abstract class AbstractSqlAstWalker
|
|||
appendSql( tableGroupJoin.getJoinType().getText() );
|
||||
appendSql( " join " );
|
||||
|
||||
renderTableGroup( joinedGroup );
|
||||
|
||||
clauseStack.push( Clause.WHERE );
|
||||
try {
|
||||
if ( tableGroupJoin.getPredicate() != null && !tableGroupJoin.getPredicate().isEmpty() ) {
|
||||
appendSql( " on " );
|
||||
tableGroupJoin.getPredicate().accept( this );
|
||||
}
|
||||
if ( tableGroupJoin.getPredicate() != null && !tableGroupJoin.getPredicate().isEmpty() ) {
|
||||
renderTableGroup( joinedGroup, tableGroupJoin.getPredicate() );
|
||||
}
|
||||
finally {
|
||||
clauseStack.pop();
|
||||
else {
|
||||
renderTableGroup( joinedGroup );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,15 @@ package org.hibernate.sql.results.internal.domain;
|
|||
|
||||
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.query.NavigablePath;
|
||||
import org.hibernate.sql.results.spi.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.spi.BiDirectionalFetch;
|
||||
import org.hibernate.sql.results.spi.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.spi.Fetch;
|
||||
import org.hibernate.sql.results.spi.FetchParent;
|
||||
import org.hibernate.sql.results.spi.FetchParentAccess;
|
||||
|
@ -23,30 +27,23 @@ import org.hibernate.sql.results.spi.RowProcessingState;
|
|||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
|
||||
/**
|
||||
* Implementation of BiDirectionalFetch for the `oa` case - the bi-dir fetch
|
||||
* refers to another fetch (`a`)
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class BiDirectionalFetchImpl implements BiDirectionalFetch {
|
||||
public class BiDirectionalFetchImpl implements BiDirectionalFetch, Fetchable {
|
||||
private final NavigablePath navigablePath;
|
||||
private Fetchable fetchable;
|
||||
private NavigablePath referencedNavigablePath;
|
||||
private final FetchParent fetchParent;
|
||||
private final Fetch referencedFetch;
|
||||
|
||||
/**
|
||||
* Create the bi-dir fetch
|
||||
*
|
||||
* @param navigablePath `Person(p).address.owner.address`
|
||||
* @param fetchParent The parent for the `oa` fetch is `o`
|
||||
* @param referencedFetch `RootBiDirectionalFetchImpl(a)` (because `a` is itself also a bi-dir fetch referring to the `p root)
|
||||
*/
|
||||
public BiDirectionalFetchImpl(
|
||||
NavigablePath navigablePath,
|
||||
FetchParent fetchParent,
|
||||
Fetch referencedFetch) {
|
||||
this.navigablePath = navigablePath;
|
||||
Fetchable fetchable,
|
||||
NavigablePath referencedNavigablePath) {
|
||||
this.fetchParent = fetchParent;
|
||||
this.referencedFetch = referencedFetch;
|
||||
this.navigablePath = navigablePath;
|
||||
this.fetchable = fetchable;
|
||||
this.referencedNavigablePath = referencedNavigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -56,7 +53,7 @@ public class BiDirectionalFetchImpl implements BiDirectionalFetch {
|
|||
|
||||
@Override
|
||||
public NavigablePath getReferencedPath() {
|
||||
return referencedFetch.getNavigablePath();
|
||||
return referencedNavigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -66,7 +63,7 @@ public class BiDirectionalFetchImpl implements BiDirectionalFetch {
|
|||
|
||||
@Override
|
||||
public Fetchable getFetchedMapping() {
|
||||
return referencedFetch.getFetchedMapping();
|
||||
return fetchable;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -81,10 +78,42 @@ public class BiDirectionalFetchImpl implements BiDirectionalFetch {
|
|||
AssemblerCreationState creationState) {
|
||||
return new CircularFetchAssembler(
|
||||
getReferencedPath(),
|
||||
referencedFetch.getFetchedMapping().getJavaTypeDescriptor()
|
||||
fetchable.getJavaTypeDescriptor()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFetchableName() {
|
||||
return fetchable.getFetchableName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPartName() {
|
||||
return fetchable.getFetchableName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaTypeDescriptor getJavaTypeDescriptor() {
|
||||
return fetchable.getJavaTypeDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchStrategy getMappedFetchStrategy() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetch generateFetch(
|
||||
FetchParent fetchParent,
|
||||
NavigablePath fetchablePath,
|
||||
FetchTiming fetchTiming,
|
||||
boolean selected,
|
||||
LockMode lockMode,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private static class CircularFetchAssembler implements DomainResultAssembler {
|
||||
private final NavigablePath circularPath;
|
||||
private final JavaTypeDescriptor javaTypeDescriptor;
|
||||
|
@ -98,7 +127,13 @@ public class BiDirectionalFetchImpl implements BiDirectionalFetch {
|
|||
|
||||
@Override
|
||||
public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) {
|
||||
return rowProcessingState.resolveInitializer( circularPath ).getInitializedInstance();
|
||||
Initializer initializer = rowProcessingState.resolveInitializer( circularPath );
|
||||
if ( initializer.getInitializedInstance() == null ) {
|
||||
initializer.resolveKey( rowProcessingState );
|
||||
initializer.resolveInstance( rowProcessingState );
|
||||
initializer.initializeInstance( rowProcessingState );
|
||||
}
|
||||
return initializer.getInitializedInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,146 +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;
|
||||
|
||||
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.query.NavigablePath;
|
||||
import org.hibernate.sql.results.spi.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.spi.BiDirectionalFetch;
|
||||
import org.hibernate.sql.results.spi.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
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.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.spi.RowProcessingState;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class RootBiDirectionalFetchImpl implements BiDirectionalFetch, Fetchable {
|
||||
private final NavigablePath navigablePath;
|
||||
private Fetchable fetchable;
|
||||
private NavigablePath referencedNavigablePath;
|
||||
private final FetchParent fetchParent;
|
||||
|
||||
public RootBiDirectionalFetchImpl(
|
||||
NavigablePath navigablePath,
|
||||
FetchParent fetchParent,
|
||||
Fetchable fetchable,
|
||||
NavigablePath referencedNavigablePath) {
|
||||
this.fetchParent = fetchParent;
|
||||
this.navigablePath = navigablePath;
|
||||
this.fetchable = fetchable;
|
||||
this.referencedNavigablePath = referencedNavigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getReferencedPath() {
|
||||
return referencedNavigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParent getFetchParent() {
|
||||
return fetchParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetchable getFetchedMapping() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNullable() {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler createAssembler(
|
||||
FetchParentAccess parentAccess,
|
||||
Consumer<Initializer> collector,
|
||||
AssemblerCreationState creationState) {
|
||||
return new CircularFetchAssembler(
|
||||
getReferencedPath(),
|
||||
fetchable.getJavaTypeDescriptor()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFetchableName() {
|
||||
// An entity itself is not fetchable
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPartName() {
|
||||
return navigablePath.getLocalName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaTypeDescriptor getJavaTypeDescriptor() {
|
||||
return fetchable.getJavaTypeDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchStrategy getMappedFetchStrategy() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetch generateFetch(
|
||||
FetchParent fetchParent,
|
||||
NavigablePath fetchablePath,
|
||||
FetchTiming fetchTiming,
|
||||
boolean selected,
|
||||
LockMode lockMode,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private static class CircularFetchAssembler implements DomainResultAssembler {
|
||||
private final NavigablePath circularPath;
|
||||
private final JavaTypeDescriptor javaTypeDescriptor;
|
||||
|
||||
public CircularFetchAssembler(
|
||||
NavigablePath circularPath,
|
||||
JavaTypeDescriptor javaTypeDescriptor) {
|
||||
this.circularPath = circularPath;
|
||||
this.javaTypeDescriptor = javaTypeDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) {
|
||||
Initializer initializer = rowProcessingState.resolveInitializer( circularPath );
|
||||
if ( initializer.getInitializedInstance() == null ) {
|
||||
initializer.resolveKey( rowProcessingState );
|
||||
initializer.resolveInstance( rowProcessingState );
|
||||
initializer.initializeInstance( rowProcessingState );
|
||||
}
|
||||
return initializer.getInitializedInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaTypeDescriptor getAssembledJavaTypeDescriptor() {
|
||||
return javaTypeDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -8,8 +8,8 @@ package org.hibernate.sql.results.spi;
|
|||
|
||||
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.SqlAstProcessingState;
|
||||
import org.hibernate.sql.results.internal.domain.BiDirectionalFetchImpl;
|
||||
import org.hibernate.sql.results.internal.domain.RootBiDirectionalFetchImpl;
|
||||
|
||||
/**
|
||||
* Maintains state while processing a Fetch graph to be able to detect
|
||||
|
@ -19,35 +19,26 @@ import org.hibernate.sql.results.internal.domain.RootBiDirectionalFetchImpl;
|
|||
*/
|
||||
public class CircularFetchDetector {
|
||||
|
||||
public Fetch findBiDirectionalFetch(FetchParent fetchParent, Fetchable fetchable) {
|
||||
if ( ! fetchable.isCircular( fetchParent ) ) {
|
||||
public Fetch findBiDirectionalFetch(FetchParent fetchParent, Fetchable fetchable, SqlAstProcessingState creationState) {
|
||||
if ( !fetchable.isCircular( fetchParent, creationState ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( fetchParent instanceof Fetch ) {
|
||||
final Fetch fetchParentAsFetch = (Fetch) fetchParent;
|
||||
|
||||
final NavigablePath parentParentPath = fetchParent.getNavigablePath().getParent();
|
||||
assert fetchParent.getNavigablePath().getParent() != null;
|
||||
|
||||
assert fetchParentAsFetch.getFetchParent().getNavigablePath().equals( parentParentPath );
|
||||
|
||||
final NavigablePath navigablePath = fetchParent.getNavigablePath();
|
||||
if ( navigablePath.getParent().getParent() == null ) {
|
||||
return new BiDirectionalFetchImpl(
|
||||
fetchParent.getNavigablePath().append( fetchable.getFetchableName() ),
|
||||
navigablePath,
|
||||
fetchParent,
|
||||
fetchParentAsFetch
|
||||
fetchable,
|
||||
fetchParent.getNavigablePath().getParent()
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
||||
// note : the "`fetchParentAsFetch` is `RootBiDirectionalFetchImpl`" case would
|
||||
// be handled in the `Fetch` block since `RootBiDirectionalFetchImpl` is a Fetch
|
||||
|
||||
return new RootBiDirectionalFetchImpl(
|
||||
new NavigablePath( fetchable.getJavaTypeDescriptor().getJavaType().getName() ),
|
||||
return new BiDirectionalFetchImpl(
|
||||
navigablePath.append( fetchable.getFetchableName() ),
|
||||
fetchParent,
|
||||
fetchable,
|
||||
fetchParent.getNavigablePath()
|
||||
navigablePath.getParent()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.hibernate.engine.FetchStrategy;
|
|||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.SqlAstProcessingState;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
|
@ -34,7 +35,7 @@ public interface Fetchable extends ModelPart {
|
|||
String resultVariable,
|
||||
DomainResultCreationState creationState);
|
||||
|
||||
default boolean isCircular(FetchParent fetchParent){
|
||||
default boolean isCircular(FetchParent fetchParent, SqlAstProcessingState creationState){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 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.orm.test.crud.onetoone;
|
||||
package org.hibernate.orm.test.sql.exec.onetoone;
|
||||
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Entity;
|
||||
|
|
|
@ -16,7 +16,6 @@ import javax.persistence.Table;
|
|||
import org.hibernate.Hibernate;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.FailureExpected;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
|
@ -42,7 +41,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
)
|
||||
@ServiceRegistry
|
||||
@SessionFactory(generateStatistics = true)
|
||||
@FailureExpected
|
||||
public class EntityWithBidirectionalOneToOneJoinTableTest {
|
||||
|
||||
@BeforeEach
|
||||
|
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
* 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.orm.test.sql.exec.onetoone.bidirectional;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@DomainModel(
|
||||
annotatedClasses = {
|
||||
EntityWithOneBidirectionalOneToOneJoinTableTest.Parent.class,
|
||||
EntityWithOneBidirectionalOneToOneJoinTableTest.Child.class,
|
||||
}
|
||||
)
|
||||
@ServiceRegistry
|
||||
@SessionFactory(generateStatistics = true)
|
||||
public class EntityWithOneBidirectionalOneToOneJoinTableTest {
|
||||
|
||||
@BeforeEach
|
||||
public void setUp(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
Parent parent = new Parent( 1, "Hibernate" );
|
||||
Child child = new Child( 2, parent );
|
||||
child.setName( "Acme" );
|
||||
session.save( parent );
|
||||
session.save( child );
|
||||
} );
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from Parent" ).executeUpdate();
|
||||
session.createQuery( "delete from Child" ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetParent(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final Parent parent = session.get( Parent.class, 1 );
|
||||
Child child = parent.getChild();
|
||||
assertThat( child, CoreMatchers.notNullValue() );
|
||||
assertTrue(
|
||||
Hibernate.isInitialized( child ),
|
||||
"The child eager OneToOne association is not initialized"
|
||||
);
|
||||
assertThat( child.getName(), equalTo( "Acme" ) );
|
||||
assertThat( child.getParent(), CoreMatchers.notNullValue() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetChild(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final Child child = session.get( Child.class, 2 );
|
||||
Parent parent = child.getParent();
|
||||
assertThat( parent, CoreMatchers.notNullValue() );
|
||||
assertTrue(
|
||||
Hibernate.isInitialized( parent ),
|
||||
"The parent eager OneToOne association is not initialized"
|
||||
);
|
||||
assertThat( parent.getDescription(), CoreMatchers.notNullValue() );
|
||||
|
||||
Child child1 = parent.getChild();
|
||||
assertThat( child1, CoreMatchers.notNullValue() );
|
||||
assertTrue(
|
||||
Hibernate.isInitialized( child1 ),
|
||||
"The child eager OneToOne association is not initialized"
|
||||
);
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHqlSelectChild(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final String queryString = "SELECT c FROM Child c JOIN c.parent d WHERE d.id = :id";
|
||||
final Child child = session.createQuery( queryString, Child.class )
|
||||
.setParameter( "id", 1 )
|
||||
.getSingleResult();
|
||||
|
||||
assertThat( child.getParent(), CoreMatchers.notNullValue() );
|
||||
|
||||
String description = child.getParent().getDescription();
|
||||
assertThat( description, CoreMatchers.notNullValue() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHqlSelectParent(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final Parent parent = session.createQuery(
|
||||
"SELECT p FROM Parent p JOIN p.child WHERE p.id = :id",
|
||||
Parent.class
|
||||
)
|
||||
.setParameter( "id", 1 )
|
||||
.getSingleResult();
|
||||
|
||||
Child child = parent.getChild();
|
||||
assertThat( child, CoreMatchers.notNullValue() );
|
||||
assertTrue(
|
||||
Hibernate.isInitialized( child ),
|
||||
"the child have to be initialized"
|
||||
);
|
||||
String name = child.getName();
|
||||
assertThat( name, CoreMatchers.notNullValue() );
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final Parent parent = session.createQuery(
|
||||
"SELECT p FROM Parent p JOIN p.child WHERE p.id = :id",
|
||||
Parent.class
|
||||
)
|
||||
.setParameter( "id", 1 )
|
||||
.getSingleResult();
|
||||
|
||||
Child child = parent.getChild();
|
||||
assertThat( child, CoreMatchers.notNullValue() );
|
||||
assertTrue(
|
||||
Hibernate.isInitialized( child ),
|
||||
"The child have to be initialized"
|
||||
);
|
||||
String name = child.getName();
|
||||
assertThat( name, CoreMatchers.notNullValue() );
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Parent")
|
||||
@Table(name = "PARENT")
|
||||
public static class Parent {
|
||||
private Integer id;
|
||||
|
||||
private String description;
|
||||
private Child child;
|
||||
|
||||
Parent() {
|
||||
}
|
||||
|
||||
public Parent(Integer id, String description) {
|
||||
this.id = id;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@OneToOne
|
||||
@JoinTable(name = "PARENT_CHILD", inverseJoinColumns = @JoinColumn(name = "child_id"), joinColumns = @JoinColumn(name = "parent_id"))
|
||||
public Child getChild() {
|
||||
return child;
|
||||
}
|
||||
|
||||
public void setChild(Child other) {
|
||||
this.child = other;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "Child")
|
||||
@Table(name = "CHILD")
|
||||
public static class Child {
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
private Parent parent;
|
||||
|
||||
Child() {
|
||||
}
|
||||
|
||||
Child(Integer id, Parent parent) {
|
||||
this.id = id;
|
||||
this.parent = parent;
|
||||
this.parent.setChild( this );
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@OneToOne(mappedBy = "child")
|
||||
public Parent getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(Parent parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue