mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-27 22:39:13 +00:00
HHH-16759 When ComponentType is immutable, use instantiator instead of setting property values
This commit is contained in:
parent
24467aa86e
commit
63eedee7a2
@ -48,6 +48,7 @@
|
|||||||
import org.hibernate.boot.model.internal.FkSecondPass;
|
import org.hibernate.boot.model.internal.FkSecondPass;
|
||||||
import org.hibernate.boot.model.internal.IdGeneratorResolverSecondPass;
|
import org.hibernate.boot.model.internal.IdGeneratorResolverSecondPass;
|
||||||
import org.hibernate.boot.model.internal.JPAIndexHolder;
|
import org.hibernate.boot.model.internal.JPAIndexHolder;
|
||||||
|
import org.hibernate.boot.model.internal.OptionalDeterminationSecondPass;
|
||||||
import org.hibernate.boot.model.internal.QuerySecondPass;
|
import org.hibernate.boot.model.internal.QuerySecondPass;
|
||||||
import org.hibernate.boot.model.internal.SecondaryTableFromAnnotationSecondPass;
|
import org.hibernate.boot.model.internal.SecondaryTableFromAnnotationSecondPass;
|
||||||
import org.hibernate.boot.model.internal.SecondaryTableSecondPass;
|
import org.hibernate.boot.model.internal.SecondaryTableSecondPass;
|
||||||
@ -1668,6 +1669,7 @@ public Join locateJoin(Identifier tableName) {
|
|||||||
private ArrayList<ImplicitColumnNamingSecondPass> implicitColumnNamingSecondPassList;
|
private ArrayList<ImplicitColumnNamingSecondPass> implicitColumnNamingSecondPassList;
|
||||||
|
|
||||||
private ArrayList<SecondPass> generalSecondPassList;
|
private ArrayList<SecondPass> generalSecondPassList;
|
||||||
|
private ArrayList<OptionalDeterminationSecondPass> optionalDeterminationSecondPassList;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addSecondPass(SecondPass secondPass) {
|
public void addSecondPass(SecondPass secondPass) {
|
||||||
@ -1706,6 +1708,9 @@ else if ( secondPass instanceof QuerySecondPass ) {
|
|||||||
else if ( secondPass instanceof ImplicitColumnNamingSecondPass ) {
|
else if ( secondPass instanceof ImplicitColumnNamingSecondPass ) {
|
||||||
addImplicitColumnNamingSecondPass( (ImplicitColumnNamingSecondPass) secondPass );
|
addImplicitColumnNamingSecondPass( (ImplicitColumnNamingSecondPass) secondPass );
|
||||||
}
|
}
|
||||||
|
else if ( secondPass instanceof OptionalDeterminationSecondPass ) {
|
||||||
|
addOptionalDeterminationSecondPass( (OptionalDeterminationSecondPass) secondPass );
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// add to the general SecondPass list
|
// add to the general SecondPass list
|
||||||
if ( generalSecondPassList == null ) {
|
if ( generalSecondPassList == null ) {
|
||||||
@ -1787,6 +1792,13 @@ private void addImplicitColumnNamingSecondPass(ImplicitColumnNamingSecondPass se
|
|||||||
implicitColumnNamingSecondPassList.add( secondPass );
|
implicitColumnNamingSecondPassList.add( secondPass );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addOptionalDeterminationSecondPass(OptionalDeterminationSecondPass secondPass) {
|
||||||
|
if ( optionalDeterminationSecondPassList == null ) {
|
||||||
|
optionalDeterminationSecondPassList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
optionalDeterminationSecondPassList.add( secondPass );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean inSecondPass = false;
|
private boolean inSecondPass = false;
|
||||||
|
|
||||||
@ -1813,6 +1825,7 @@ public void processSecondPasses(MetadataBuildingContext buildingContext) {
|
|||||||
|
|
||||||
processSecondPasses( querySecondPassList );
|
processSecondPasses( querySecondPassList );
|
||||||
processSecondPasses( generalSecondPassList );
|
processSecondPasses( generalSecondPassList );
|
||||||
|
processSecondPasses( optionalDeterminationSecondPassList );
|
||||||
|
|
||||||
processPropertyReferences();
|
processPropertyReferences();
|
||||||
|
|
||||||
|
@ -119,6 +119,7 @@ private static void bindAny(
|
|||||||
}
|
}
|
||||||
binder.setAccessType( inferredData.getDefaultAccess() );
|
binder.setAccessType( inferredData.getDefaultAccess() );
|
||||||
binder.setCascade( cascadeStrategy );
|
binder.setCascade( cascadeStrategy );
|
||||||
|
binder.setBuildingContext( context );
|
||||||
Property prop = binder.makeProperty();
|
Property prop = binder.makeProperty();
|
||||||
//composite FK columns are in the same table, so it's OK
|
//composite FK columns are in the same table, so it's OK
|
||||||
propertyHolder.addProperty( prop, columns, inferredData.getDeclaringClass() );
|
propertyHolder.addProperty( prop, columns, inferredData.getDeclaringClass() );
|
||||||
|
@ -1287,6 +1287,7 @@ private void bindProperty() {
|
|||||||
binder.setProperty( property );
|
binder.setProperty( property );
|
||||||
binder.setInsertable( insertable );
|
binder.setInsertable( insertable );
|
||||||
binder.setUpdatable( updatable );
|
binder.setUpdatable( updatable );
|
||||||
|
binder.setBuildingContext( buildingContext );
|
||||||
Property prop = binder.makeProperty();
|
Property prop = binder.makeProperty();
|
||||||
//we don't care about the join stuffs because the column is on the association table.
|
//we don't care about the join stuffs because the column is on the association table.
|
||||||
if ( !declaringClassSet ) {
|
if ( !declaringClassSet ) {
|
||||||
|
@ -193,7 +193,7 @@ private static Component bindEmbeddable(
|
|||||||
entityBinder,
|
entityBinder,
|
||||||
isComponentEmbedded,
|
isComponentEmbedded,
|
||||||
isIdentifierMapper,
|
isIdentifierMapper,
|
||||||
false,
|
context.getMetadataCollector().isInSecondPass(),
|
||||||
customInstantiatorImpl,
|
customInstantiatorImpl,
|
||||||
compositeUserTypeClass,
|
compositeUserTypeClass,
|
||||||
annotatedColumns,
|
annotatedColumns,
|
||||||
|
@ -112,6 +112,7 @@ public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws
|
|||||||
binder.setValue( value );
|
binder.setValue( value );
|
||||||
binder.setCascade( cascadeStrategy );
|
binder.setCascade( cascadeStrategy );
|
||||||
binder.setAccessType( inferredData.getDefaultAccess() );
|
binder.setAccessType( inferredData.getDefaultAccess() );
|
||||||
|
binder.setBuildingContext( buildingContext );
|
||||||
|
|
||||||
final LazyGroup lazyGroupAnnotation = property.getAnnotation( LazyGroup.class );
|
final LazyGroup lazyGroupAnnotation = property.getAnnotation( LazyGroup.class );
|
||||||
if ( lazyGroupAnnotation != null ) {
|
if ( lazyGroupAnnotation != null ) {
|
||||||
@ -186,6 +187,7 @@ private void bindTargetManyToOne(
|
|||||||
manyToOne.setFetchMode( oneToOne.getFetchMode() );
|
manyToOne.setFetchMode( oneToOne.getFetchMode() );
|
||||||
manyToOne.setLazy( oneToOne.isLazy() );
|
manyToOne.setLazy( oneToOne.isLazy() );
|
||||||
manyToOne.setReferencedEntityName( oneToOne.getReferencedEntityName() );
|
manyToOne.setReferencedEntityName( oneToOne.getReferencedEntityName() );
|
||||||
|
manyToOne.setReferencedPropertyName( mappedBy );
|
||||||
manyToOne.setUnwrapProxy( oneToOne.isUnwrapProxy() );
|
manyToOne.setUnwrapProxy( oneToOne.isUnwrapProxy() );
|
||||||
manyToOne.markAsLogicalOneToOne();
|
manyToOne.markAsLogicalOneToOne();
|
||||||
property.setValue( manyToOne );
|
property.setValue( manyToOne );
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* 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.model.internal;
|
||||||
|
|
||||||
|
|
||||||
|
import org.hibernate.boot.spi.SecondPass;
|
||||||
|
|
||||||
|
public interface OptionalDeterminationSecondPass extends SecondPass {
|
||||||
|
}
|
@ -48,13 +48,16 @@
|
|||||||
import org.hibernate.boot.spi.AccessType;
|
import org.hibernate.boot.spi.AccessType;
|
||||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||||
import org.hibernate.boot.spi.PropertyData;
|
import org.hibernate.boot.spi.PropertyData;
|
||||||
|
import org.hibernate.boot.spi.SecondPass;
|
||||||
import org.hibernate.engine.OptimisticLockStyle;
|
import org.hibernate.engine.OptimisticLockStyle;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.mapping.Collection;
|
import org.hibernate.mapping.Collection;
|
||||||
import org.hibernate.mapping.Component;
|
import org.hibernate.mapping.Component;
|
||||||
import org.hibernate.mapping.GeneratorCreator;
|
import org.hibernate.mapping.GeneratorCreator;
|
||||||
|
import org.hibernate.mapping.Join;
|
||||||
import org.hibernate.mapping.KeyValue;
|
import org.hibernate.mapping.KeyValue;
|
||||||
import org.hibernate.mapping.MappedSuperclass;
|
import org.hibernate.mapping.MappedSuperclass;
|
||||||
|
import org.hibernate.mapping.PersistentClass;
|
||||||
import org.hibernate.mapping.Property;
|
import org.hibernate.mapping.Property;
|
||||||
import org.hibernate.mapping.RootClass;
|
import org.hibernate.mapping.RootClass;
|
||||||
import org.hibernate.mapping.SimpleValue;
|
import org.hibernate.mapping.SimpleValue;
|
||||||
@ -448,13 +451,32 @@ private void handleMutability(Property property) {
|
|||||||
|
|
||||||
private void handleOptional(Property property) {
|
private void handleOptional(Property property) {
|
||||||
if ( this.property != null ) {
|
if ( this.property != null ) {
|
||||||
property.setOptional( !isId && isOptional( this.property ) && isNullable( property ) );
|
property.setOptional( !isId && isOptional( this.property ) );
|
||||||
}
|
if ( property.isOptional() ) {
|
||||||
}
|
final OptionalDeterminationSecondPass secondPass = persistentClasses -> {
|
||||||
|
// Defer determining whether a property and its columns are nullable,
|
||||||
|
// as handleOptional might be called when the value is not yet fully initialized
|
||||||
|
if ( property.getPersistentClass() != null ) {
|
||||||
|
for ( Join join : property.getPersistentClass().getJoins() ) {
|
||||||
|
if ( join.getProperties().contains( property ) ) {
|
||||||
|
// If this property is part of a join it is inherently optional
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isNullable(Property property) {
|
if ( !property.getValue().isNullable() ) {
|
||||||
final Value value = property.getValue();
|
property.setOptional( false );
|
||||||
return value instanceof org.hibernate.mapping.OneToMany || value.isNullable();
|
}
|
||||||
|
};
|
||||||
|
// Always register this as second pass and never execute it directly,
|
||||||
|
// even if we are in a second pass already.
|
||||||
|
// If we are in a second pass, then we are currently processing the generalSecondPassList
|
||||||
|
// to which the following call will add the second pass to,
|
||||||
|
// so it will be executed within that second pass, just a bit later
|
||||||
|
buildingContext.getMetadataCollector().addSecondPass( secondPass );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleNaturalId(Property property) {
|
private void handleNaturalId(Property property) {
|
||||||
|
@ -143,4 +143,9 @@ public void markAsLogicalOneToOne() {
|
|||||||
public boolean isLogicalOneToOne() {
|
public boolean isLogicalOneToOne() {
|
||||||
return isLogicalOneToOne;
|
return isLogicalOneToOne;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNullable() {
|
||||||
|
return getReferencedPropertyName() != null || super.isNullable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,13 @@
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.hibernate.AssertionFailure;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.annotations.NotFoundAction;
|
import org.hibernate.annotations.NotFoundAction;
|
||||||
import org.hibernate.cache.MutableCacheKeyBuilder;
|
import org.hibernate.cache.MutableCacheKeyBuilder;
|
||||||
@ -77,6 +79,7 @@
|
|||||||
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
|
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
import org.hibernate.sql.results.graph.DomainResult;
|
import org.hibernate.sql.results.graph.DomainResult;
|
||||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||||
@ -145,6 +148,7 @@ public class Entity1 {
|
|||||||
private final Set<String> targetKeyPropertyNames;
|
private final Set<String> targetKeyPropertyNames;
|
||||||
|
|
||||||
private final Cardinality cardinality;
|
private final Cardinality cardinality;
|
||||||
|
private final boolean hasJoinTable;
|
||||||
/*
|
/*
|
||||||
Capture the other side's name of a possibly bidirectional association to allow resolving circular fetches.
|
Capture the other side's name of a possibly bidirectional association to allow resolving circular fetches.
|
||||||
It may be null if the referenced property is a non-entity.
|
It may be null if the referenced property is a non-entity.
|
||||||
@ -171,6 +175,7 @@ protected ToOneAttributeMapping(ToOneAttributeMapping original) {
|
|||||||
referencedPropertyName = original.referencedPropertyName;
|
referencedPropertyName = original.referencedPropertyName;
|
||||||
targetKeyPropertyName = original.targetKeyPropertyName;
|
targetKeyPropertyName = original.targetKeyPropertyName;
|
||||||
cardinality = original.cardinality;
|
cardinality = original.cardinality;
|
||||||
|
hasJoinTable = original.hasJoinTable;
|
||||||
bidirectionalAttributePath = original.bidirectionalAttributePath;
|
bidirectionalAttributePath = original.bidirectionalAttributePath;
|
||||||
declaringTableGroupProducer = original.declaringTableGroupProducer;
|
declaringTableGroupProducer = original.declaringTableGroupProducer;
|
||||||
isKeyTableNullable = original.isKeyTableNullable;
|
isKeyTableNullable = original.isKeyTableNullable;
|
||||||
@ -242,7 +247,10 @@ public ToOneAttributeMapping(
|
|||||||
this.entityMappingType = entityMappingType;
|
this.entityMappingType = entityMappingType;
|
||||||
|
|
||||||
this.navigableRole = navigableRole;
|
this.navigableRole = navigableRole;
|
||||||
this.declaringTableGroupProducer = resolveDeclaringTableGroupProducer( declaringEntityPersister, navigableRole );
|
this.declaringTableGroupProducer = resolveDeclaringTableGroupProducer(
|
||||||
|
declaringEntityPersister,
|
||||||
|
navigableRole
|
||||||
|
);
|
||||||
if ( bootValue instanceof ManyToOne ) {
|
if ( bootValue instanceof ManyToOne ) {
|
||||||
final ManyToOne manyToOne = (ManyToOne) bootValue;
|
final ManyToOne manyToOne = (ManyToOne) bootValue;
|
||||||
this.notFoundAction = ( (ManyToOne) bootValue ).getNotFoundAction();
|
this.notFoundAction = ( (ManyToOne) bootValue ).getNotFoundAction();
|
||||||
@ -260,6 +268,7 @@ public ToOneAttributeMapping(
|
|||||||
? name
|
? name
|
||||||
: bootValue.getPropertyName();
|
: bootValue.getPropertyName();
|
||||||
if ( cardinality == Cardinality.LOGICAL_ONE_TO_ONE ) {
|
if ( cardinality == Cardinality.LOGICAL_ONE_TO_ONE ) {
|
||||||
|
boolean hasJoinTable = false;
|
||||||
// Handle join table cases
|
// Handle join table cases
|
||||||
for ( Join join : entityBinding.getJoinClosure() ) {
|
for ( Join join : entityBinding.getJoinClosure() ) {
|
||||||
if ( join.getPersistentClass().getEntityName().equals( entityBinding.getEntityName() )
|
if ( join.getPersistentClass().getEntityName().equals( entityBinding.getEntityName() )
|
||||||
@ -267,7 +276,10 @@ public ToOneAttributeMapping(
|
|||||||
&& join.getTable() == manyToOne.getTable()
|
&& join.getTable() == manyToOne.getTable()
|
||||||
&& equal( join.getKey(), manyToOne ) ) {
|
&& equal( join.getKey(), manyToOne ) ) {
|
||||||
//noinspection deprecation
|
//noinspection deprecation
|
||||||
bidirectionalAttributeName = SelectablePath.parse( join.getPropertyIterator().next().getName() );
|
bidirectionalAttributeName = SelectablePath.parse(
|
||||||
|
join.getPropertyIterator().next().getName()
|
||||||
|
);
|
||||||
|
hasJoinTable = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,8 +292,10 @@ && equal( join.getKey(), manyToOne ) ) {
|
|||||||
entityBinding.getPropertyClosure()
|
entityBinding.getPropertyClosure()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
this.hasJoinTable = hasJoinTable;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
this.hasJoinTable = false;
|
||||||
bidirectionalAttributeName = findBidirectionalOneToManyAttributeName(
|
bidirectionalAttributeName = findBidirectionalOneToManyAttributeName(
|
||||||
propertyPath,
|
propertyPath,
|
||||||
declaringType,
|
declaringType,
|
||||||
@ -294,7 +308,12 @@ && equal( join.getKey(), manyToOne ) ) {
|
|||||||
else {
|
else {
|
||||||
// Only set the bidirectional attribute name if the referenced property can actually be circular i.e. an entity type
|
// Only set the bidirectional attribute name if the referenced property can actually be circular i.e. an entity type
|
||||||
final Property property = entityBinding.getProperty( referencedPropertyName );
|
final Property property = entityBinding.getProperty( referencedPropertyName );
|
||||||
this.bidirectionalAttributePath = property != null && property.getValue() instanceof EntityType
|
this.hasJoinTable = cardinality == Cardinality.LOGICAL_ONE_TO_ONE
|
||||||
|
&& property != null
|
||||||
|
&& ( property.getValue() instanceof ManyToOne )
|
||||||
|
&& ( (ManyToOne) property.getValue() ).isLogicalOneToOne();
|
||||||
|
this.bidirectionalAttributePath = property != null && property.getValue()
|
||||||
|
.getType() instanceof EntityType
|
||||||
? SelectablePath.parse( referencedPropertyName )
|
? SelectablePath.parse( referencedPropertyName )
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
@ -302,12 +321,20 @@ && equal( join.getKey(), manyToOne ) ) {
|
|||||||
isKeyTableNullable = true;
|
isKeyTableNullable = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final String targetTableName = MappingModelCreationHelper.getTableIdentifierExpression( manyToOne.getTable(), declaringEntityPersister.getFactory() );
|
final String targetTableName = MappingModelCreationHelper.getTableIdentifierExpression(
|
||||||
|
manyToOne.getTable(),
|
||||||
|
declaringEntityPersister.getFactory()
|
||||||
|
);
|
||||||
if ( CollectionPart.Nature.fromNameExact( navigableRole.getParent().getLocalName() ) != null ) {
|
if ( CollectionPart.Nature.fromNameExact( navigableRole.getParent().getLocalName() ) != null ) {
|
||||||
// * the to-one's parent is directly a collection element or index
|
// * the to-one's parent is directly a collection element or index
|
||||||
// * therefore, its parent-parent should be the collection itself
|
// * therefore, its parent-parent should be the collection itself
|
||||||
final PluralAttributeMapping pluralAttribute = (PluralAttributeMapping) declaringEntityPersister.findByPath(
|
final PluralAttributeMapping pluralAttribute = (PluralAttributeMapping) declaringEntityPersister.findByPath(
|
||||||
navigableRole.getParent().getParent().getFullPath().substring( declaringEntityPersister.getNavigableRole().getFullPath().length() + 1 ) );
|
navigableRole.getParent()
|
||||||
|
.getParent()
|
||||||
|
.getFullPath()
|
||||||
|
.substring( declaringEntityPersister.getNavigableRole()
|
||||||
|
.getFullPath()
|
||||||
|
.length() + 1 ) );
|
||||||
assert pluralAttribute != null;
|
assert pluralAttribute != null;
|
||||||
|
|
||||||
final AbstractCollectionPersister persister = (AbstractCollectionPersister) pluralAttribute.getCollectionDescriptor();
|
final AbstractCollectionPersister persister = (AbstractCollectionPersister) pluralAttribute.getCollectionDescriptor();
|
||||||
@ -328,6 +355,7 @@ && equal( join.getKey(), manyToOne ) ) {
|
|||||||
else {
|
else {
|
||||||
assert bootValue instanceof OneToOne;
|
assert bootValue instanceof OneToOne;
|
||||||
cardinality = Cardinality.ONE_TO_ONE;
|
cardinality = Cardinality.ONE_TO_ONE;
|
||||||
|
hasJoinTable = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The otherSidePropertyName value is used to determine bidirectionality based on the navigablePath string
|
The otherSidePropertyName value is used to determine bidirectionality based on the navigablePath string
|
||||||
@ -381,7 +409,7 @@ the navigable path is NavigablePath(Card.fields.{element}.{id}.card) and it does
|
|||||||
}
|
}
|
||||||
notFoundAction = null;
|
notFoundAction = null;
|
||||||
isKeyTableNullable = isNullable();
|
isKeyTableNullable = isNullable();
|
||||||
isOptional = ! bootValue.isConstrained();
|
isOptional = !bootValue.isConstrained();
|
||||||
isInternalLoadNullable = isNullable();
|
isInternalLoadNullable = isNullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,67 +464,78 @@ the navigable path is NavigablePath(Card.fields.{element}.{id}.card) and it does
|
|||||||
}
|
}
|
||||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||||
}
|
}
|
||||||
else if ( bootValue.isReferenceToPrimaryKey() ) {
|
|
||||||
this.targetKeyPropertyName = referencedPropertyName;
|
|
||||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
|
||||||
addPrefixedPropertyNames(
|
|
||||||
targetKeyPropertyNames,
|
|
||||||
targetKeyPropertyName,
|
|
||||||
bootValue.getType(),
|
|
||||||
declaringEntityPersister.getFactory()
|
|
||||||
);
|
|
||||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
final PersistentClass entityBinding = bootValue.getBuildingContext().getMetadataCollector()
|
final PersistentClass entityBinding = bootValue.getBuildingContext().getMetadataCollector()
|
||||||
.getEntityBinding( entityMappingType.getEntityName() );
|
.getEntityBinding( entityMappingType.getEntityName() );
|
||||||
final Type propertyType = entityBinding.getRecursiveProperty( referencedPropertyName ).getType();
|
final Type propertyType = entityBinding.getRecursiveProperty( referencedPropertyName ).getType();
|
||||||
final CompositeType compositeType;
|
if ( bootValue.isReferenceToPrimaryKey() ) {
|
||||||
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
|
this.targetKeyPropertyName = referencedPropertyName;
|
||||||
&& compositeType.getPropertyNames().length == 1 ) {
|
final Set<String> targetKeyPropertyNames = new HashSet<>( 3 );
|
||||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
addPrefixedPropertyNames(
|
||||||
this.targetKeyPropertyName = compositeType.getPropertyNames()[0];
|
|
||||||
addPrefixedPropertyPaths(
|
|
||||||
targetKeyPropertyNames,
|
targetKeyPropertyNames,
|
||||||
targetKeyPropertyName,
|
targetKeyPropertyName,
|
||||||
compositeType.getSubtypes()[0],
|
propertyType,
|
||||||
declaringEntityPersister.getFactory()
|
declaringEntityPersister.getFactory()
|
||||||
);
|
);
|
||||||
addPrefixedPropertyNames(
|
addPrefixedPropertyNames(
|
||||||
targetKeyPropertyNames,
|
targetKeyPropertyNames,
|
||||||
EntityIdentifierMapping.ID_ROLE_NAME,
|
null,
|
||||||
propertyType,
|
bootValue.getType(),
|
||||||
declaringEntityPersister.getFactory()
|
declaringEntityPersister.getFactory()
|
||||||
);
|
);
|
||||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
final CompositeType compositeType;
|
||||||
this.targetKeyPropertyName = referencedPropertyName;
|
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
|
||||||
final String mapsIdAttributeName;
|
&& compositeType.getPropertyNames().length == 1 ) {
|
||||||
// If there is a "virtual property" for a non-PK join mapping, we try to see if the columns match the
|
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||||
// primary key columns and if so, we add the primary key property name as target key property
|
this.targetKeyPropertyName = compositeType.getPropertyNames()[0];
|
||||||
if ( ( mapsIdAttributeName = findMapsIdPropertyName( entityMappingType, referencedPropertyName ) ) != null ) {
|
|
||||||
addPrefixedPropertyPaths(
|
addPrefixedPropertyPaths(
|
||||||
targetKeyPropertyNames,
|
targetKeyPropertyNames,
|
||||||
mapsIdAttributeName,
|
targetKeyPropertyName,
|
||||||
entityMappingType.getEntityPersister().getIdentifierType(),
|
compositeType.getSubtypes()[0],
|
||||||
declaringEntityPersister.getFactory()
|
declaringEntityPersister.getFactory()
|
||||||
);
|
);
|
||||||
|
addPrefixedPropertyNames(
|
||||||
|
targetKeyPropertyNames,
|
||||||
|
EntityIdentifierMapping.ID_ROLE_NAME,
|
||||||
|
propertyType,
|
||||||
|
declaringEntityPersister.getFactory()
|
||||||
|
);
|
||||||
|
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||||
|
this.targetKeyPropertyName = referencedPropertyName;
|
||||||
|
final String mapsIdAttributeName;
|
||||||
|
// If there is a "virtual property" for a non-PK join mapping, we try to see if the columns match the
|
||||||
|
// primary key columns and if so, we add the primary key property name as target key property
|
||||||
|
if ( ( mapsIdAttributeName = findMapsIdPropertyName(
|
||||||
|
entityMappingType,
|
||||||
|
referencedPropertyName
|
||||||
|
) ) != null ) {
|
||||||
|
addPrefixedPropertyPaths(
|
||||||
|
targetKeyPropertyNames,
|
||||||
|
mapsIdAttributeName,
|
||||||
|
entityMappingType.getEntityPersister().getIdentifierType(),
|
||||||
|
declaringEntityPersister.getFactory()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
addPrefixedPropertyNames(
|
||||||
|
targetKeyPropertyNames,
|
||||||
|
targetKeyPropertyName,
|
||||||
|
propertyType,
|
||||||
|
declaringEntityPersister.getFactory()
|
||||||
|
);
|
||||||
|
addPrefixedPropertyNames(
|
||||||
|
targetKeyPropertyNames,
|
||||||
|
ForeignKeyDescriptor.PART_NAME,
|
||||||
|
propertyType,
|
||||||
|
declaringEntityPersister.getFactory()
|
||||||
|
);
|
||||||
|
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||||
}
|
}
|
||||||
addPrefixedPropertyNames(
|
|
||||||
targetKeyPropertyNames,
|
|
||||||
targetKeyPropertyName,
|
|
||||||
propertyType,
|
|
||||||
declaringEntityPersister.getFactory()
|
|
||||||
);
|
|
||||||
addPrefixedPropertyNames(
|
|
||||||
targetKeyPropertyNames,
|
|
||||||
ForeignKeyDescriptor.PART_NAME,
|
|
||||||
propertyType,
|
|
||||||
declaringEntityPersister.getFactory()
|
|
||||||
);
|
|
||||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -629,6 +668,7 @@ private ToOneAttributeMapping(
|
|||||||
this.targetKeyPropertyName = original.targetKeyPropertyName;
|
this.targetKeyPropertyName = original.targetKeyPropertyName;
|
||||||
this.targetKeyPropertyNames = original.targetKeyPropertyNames;
|
this.targetKeyPropertyNames = original.targetKeyPropertyNames;
|
||||||
this.cardinality = original.cardinality;
|
this.cardinality = original.cardinality;
|
||||||
|
this.hasJoinTable = original.hasJoinTable;
|
||||||
this.bidirectionalAttributePath = original.bidirectionalAttributePath;
|
this.bidirectionalAttributePath = original.bidirectionalAttributePath;
|
||||||
this.declaringTableGroupProducer = declaringTableGroupProducer;
|
this.declaringTableGroupProducer = declaringTableGroupProducer;
|
||||||
this.isInternalLoadNullable = original.isInternalLoadNullable;
|
this.isInternalLoadNullable = original.isInternalLoadNullable;
|
||||||
@ -729,7 +769,7 @@ else if ( identifierOrUniqueKeyType instanceof EmbeddedComponentType ) {
|
|||||||
final String newFkPrefix;
|
final String newFkPrefix;
|
||||||
if ( prefix == null ) {
|
if ( prefix == null ) {
|
||||||
newPrefix = propertyName;
|
newPrefix = propertyName;
|
||||||
newPkPrefix = propertyName + "." + EntityIdentifierMapping.ID_ROLE_NAME;
|
newPkPrefix = EntityIdentifierMapping.ID_ROLE_NAME;
|
||||||
newFkPrefix = ForeignKeyDescriptor.PART_NAME;
|
newFkPrefix = ForeignKeyDescriptor.PART_NAME;
|
||||||
}
|
}
|
||||||
else if ( propertyName == null ) {
|
else if ( propertyName == null ) {
|
||||||
@ -891,9 +931,8 @@ public Fetch resolveCircularFetch(
|
|||||||
FetchTiming fetchTiming,
|
FetchTiming fetchTiming,
|
||||||
DomainResultCreationState creationState) {
|
DomainResultCreationState creationState) {
|
||||||
final AssociationKey associationKey = foreignKeyDescriptor.getAssociationKey();
|
final AssociationKey associationKey = foreignKeyDescriptor.getAssociationKey();
|
||||||
|
final boolean associationKeyVisited = creationState.isAssociationKeyVisited( associationKey );
|
||||||
if ( creationState.isAssociationKeyVisited( associationKey )
|
if ( associationKeyVisited || bidirectionalAttributePath != null ) {
|
||||||
|| bidirectionalAttributePath != null && !creationState.isRegisteringVisitedAssociationKeys() ) {
|
|
||||||
NavigablePath parentNavigablePath = fetchablePath.getParent();
|
NavigablePath parentNavigablePath = fetchablePath.getParent();
|
||||||
assert parentNavigablePath.equals( fetchParent.getNavigablePath() );
|
assert parentNavigablePath.equals( fetchParent.getNavigablePath() );
|
||||||
// The parent navigable path is {fk} if we are creating the domain result for the foreign key for a circular fetch
|
// The parent navigable path is {fk} if we are creating the domain result for the foreign key for a circular fetch
|
||||||
@ -952,6 +991,12 @@ public class PrimaryKey {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( !associationKeyVisited && creationState.isRegisteringVisitedAssociationKeys() ) {
|
||||||
|
// If the current association key hasn't been visited yet and we are registering keys,
|
||||||
|
// then there can't be a circular fetch
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
class Child {
|
class Child {
|
||||||
@OneToOne
|
@OneToOne
|
||||||
@ -2069,8 +2114,34 @@ public boolean canUseParentTableGroup(
|
|||||||
|
|
||||||
private void initializeIfNeeded(TableGroup lhs, SqlAstJoinType sqlAstJoinType, TableGroup tableGroup) {
|
private void initializeIfNeeded(TableGroup lhs, SqlAstJoinType sqlAstJoinType, TableGroup tableGroup) {
|
||||||
if ( sqlAstJoinType == SqlAstJoinType.INNER && ( isNullable || !lhs.canUseInnerJoins() ) ) {
|
if ( sqlAstJoinType == SqlAstJoinType.INNER && ( isNullable || !lhs.canUseInnerJoins() ) ) {
|
||||||
// Force initialization of the underlying table group join to retain cardinality
|
if ( hasJoinTable ) {
|
||||||
tableGroup.getPrimaryTableReference();
|
// Set the join type of the table reference join to INNER to retain cardinality expectation
|
||||||
|
final TableReference lhsTableReference = lhs.resolveTableReference(
|
||||||
|
tableGroup.getNavigablePath(),
|
||||||
|
identifyingColumnsTableExpression
|
||||||
|
);
|
||||||
|
final List<TableReferenceJoin> tableReferenceJoins = lhs.getTableReferenceJoins();
|
||||||
|
for ( int i = 0; i < tableReferenceJoins.size(); i++ ) {
|
||||||
|
final TableReferenceJoin tableReferenceJoin = tableReferenceJoins.get( i );
|
||||||
|
if ( tableReferenceJoin.getJoinType() != SqlAstJoinType.INNER
|
||||||
|
&& tableReferenceJoin.getJoinedTableReference() == lhsTableReference ) {
|
||||||
|
tableReferenceJoins.set(
|
||||||
|
i,
|
||||||
|
new TableReferenceJoin(
|
||||||
|
true,
|
||||||
|
tableReferenceJoin.getJoinedTableReference(),
|
||||||
|
tableReferenceJoin.getPredicate()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new AssertionFailure( "Couldn't find table reference join for join table: " + lhsTableReference );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Force initialization of the underlying table group join to retain cardinality
|
||||||
|
tableGroup.getPrimaryTableReference();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ public void accept(SqlAstWalker sqlTreeWalker) {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getJoinType().getText() + " join " + getJoinedTableReference().toString();
|
return getJoinType().getText() + "join " + getJoinedTableReference().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -496,17 +496,12 @@ public Object replace(
|
|||||||
Object owner,
|
Object owner,
|
||||||
Map<Object, Object> copyCache) {
|
Map<Object, Object> copyCache) {
|
||||||
|
|
||||||
if ( !isMutable() ) {
|
if ( original == null && target == null ) {
|
||||||
return original;
|
|
||||||
}
|
|
||||||
if ( original == null ) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
//if ( original == target ) return target;
|
|
||||||
|
|
||||||
final Object[] originalValues = getPropertyValues( original );
|
final Object[] originalValues = getPropertyValues( original );
|
||||||
final Object[] resultValues = target == null ? new Object[originalValues.length] : getPropertyValues( target );
|
final Object[] resultValues = getPropertyValues( target );
|
||||||
|
|
||||||
final Object[] replacedValues = TypeHelper.replace(
|
final Object[] replacedValues = TypeHelper.replace(
|
||||||
originalValues,
|
originalValues,
|
||||||
resultValues,
|
resultValues,
|
||||||
@ -516,7 +511,7 @@ public Object replace(
|
|||||||
copyCache
|
copyCache
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( target == null ) {
|
if ( target == null || !isMutable() ) {
|
||||||
return instantiator().instantiate( () -> replacedValues, session.getSessionFactory() );
|
return instantiator().instantiate( () -> replacedValues, session.getSessionFactory() );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -534,24 +529,12 @@ public Object replace(
|
|||||||
Map<Object, Object> copyCache,
|
Map<Object, Object> copyCache,
|
||||||
ForeignKeyDirection foreignKeyDirection) {
|
ForeignKeyDirection foreignKeyDirection) {
|
||||||
|
|
||||||
if ( !isMutable() ) {
|
if ( original == null && target == null ) {
|
||||||
return original;
|
|
||||||
}
|
|
||||||
if ( original == null ) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
//if ( original == target ) return target;
|
|
||||||
|
|
||||||
final Object[] originalValues = getPropertyValues( original );
|
final Object[] originalValues = getPropertyValues( original );
|
||||||
final Object[] resultValues;
|
final Object[] resultValues = getPropertyValues( target );
|
||||||
|
|
||||||
if ( target == null ) {
|
|
||||||
resultValues = new Object[originalValues.length];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
resultValues = getPropertyValues( target );
|
|
||||||
}
|
|
||||||
|
|
||||||
final Object[] replacedValues = TypeHelper.replace(
|
final Object[] replacedValues = TypeHelper.replace(
|
||||||
originalValues,
|
originalValues,
|
||||||
resultValues,
|
resultValues,
|
||||||
@ -562,7 +545,7 @@ public Object replace(
|
|||||||
foreignKeyDirection
|
foreignKeyDirection
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( target == null ) {
|
if ( target == null || !isMutable() ) {
|
||||||
return instantiator().instantiate( () -> replacedValues, session.getSessionFactory() );
|
return instantiator().instantiate( () -> replacedValues, session.getSessionFactory() );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -770,7 +753,7 @@ private ValueExtractor<?> jdbcValueExtractor() {
|
|||||||
return embeddableTypeDescriptor().getAggregateMapping().getJdbcMapping().getJdbcValueExtractor();
|
return embeddableTypeDescriptor().getAggregateMapping().getJdbcMapping().getJdbcValueExtractor();
|
||||||
}
|
}
|
||||||
|
|
||||||
private EmbeddableInstantiator instantiator() {
|
protected final EmbeddableInstantiator instantiator() {
|
||||||
return embeddableTypeDescriptor().getRepresentationStrategy().getInstantiator();
|
return embeddableTypeDescriptor().getRepresentationStrategy().getInstantiator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -805,4 +788,13 @@ public EmbeddableValuedModelPart mappingModelPart() {
|
|||||||
}
|
}
|
||||||
return mappingModelPart;
|
return mappingModelPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object replacePropertyValues(Object component, Object[] values, SharedSessionContractImplementor session)
|
||||||
|
throws HibernateException {
|
||||||
|
if ( !isMutable() ) {
|
||||||
|
return instantiator().instantiate( () -> values, session.getSessionFactory() );
|
||||||
|
}
|
||||||
|
return CompositeTypeImplementor.super.replacePropertyValues( component, values, session );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ Object getPropertyValue(Object component, int index, SharedSessionContractImplem
|
|||||||
*
|
*
|
||||||
* @param component The component instance
|
* @param component The component instance
|
||||||
* @param values The values to inject
|
* @param values The values to inject
|
||||||
* @return A new instance is necessary
|
* @return A new instance as necessary
|
||||||
*
|
*
|
||||||
* @throws HibernateException Indicates an issue performing the injection
|
* @throws HibernateException Indicates an issue performing the injection
|
||||||
*
|
*
|
||||||
|
@ -193,17 +193,16 @@ public static Object[] replaceAssociations(
|
|||||||
final Type type = types[i];
|
final Type type = types[i];
|
||||||
if ( type.isComponentType() ) {
|
if ( type.isComponentType() ) {
|
||||||
final CompositeType compositeType = (CompositeType) type;
|
final CompositeType compositeType = (CompositeType) type;
|
||||||
// need to extract the component values and check for subtype replacements...
|
|
||||||
final Object[] objects =
|
|
||||||
replaceCompositeAssociations(
|
|
||||||
session,
|
|
||||||
copyCache,
|
|
||||||
foreignKeyDirection,
|
|
||||||
target[i],
|
|
||||||
currentOriginal,
|
|
||||||
compositeType
|
|
||||||
);
|
|
||||||
if ( target[i] != null ) {
|
if ( target[i] != null ) {
|
||||||
|
// need to extract the component values and check for subtype replacements...
|
||||||
|
final Object[] objects = replaceCompositeAssociations(
|
||||||
|
session,
|
||||||
|
copyCache,
|
||||||
|
foreignKeyDirection,
|
||||||
|
target[i],
|
||||||
|
currentOriginal,
|
||||||
|
compositeType
|
||||||
|
);
|
||||||
target[i] = compositeType.replacePropertyValues( target[i], objects, session );
|
target[i] = compositeType.replacePropertyValues( target[i], objects, session );
|
||||||
}
|
}
|
||||||
copied[i] = target[i];
|
copied[i] = target[i];
|
||||||
|
@ -91,9 +91,6 @@ public Object assemble(Serializable object, SharedSessionContractImplementor ses
|
|||||||
@Override
|
@Override
|
||||||
public Object replacePropertyValues(Object component, Object[] values, SharedSessionContractImplementor session)
|
public Object replacePropertyValues(Object component, Object[] values, SharedSessionContractImplementor session)
|
||||||
throws HibernateException {
|
throws HibernateException {
|
||||||
return getMappingModelPart()
|
return instantiator().instantiate( () -> values, session.getSessionFactory() );
|
||||||
.getEmbeddableTypeDescriptor()
|
|
||||||
.getRepresentationStrategy()
|
|
||||||
.getInstantiator().instantiate( () -> values, session.getSessionFactory() );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,7 @@ public void testHqlSelectParent(SessionFactoryScope scope) {
|
|||||||
.setParameter( "id", 1 )
|
.setParameter( "id", 1 )
|
||||||
.getSingleResult();
|
.getSingleResult();
|
||||||
statementInspector.assertExecutedCount( 2 );
|
statementInspector.assertExecutedCount( 2 );
|
||||||
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 2 );
|
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 );
|
||||||
statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 3 );
|
statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 3 );
|
||||||
Male son = parent.getSon();
|
Male son = parent.getSon();
|
||||||
assertThat( son, CoreMatchers.notNullValue() );
|
assertThat( son, CoreMatchers.notNullValue() );
|
||||||
|
@ -4,14 +4,17 @@
|
|||||||
import org.hibernate.testing.orm.junit.JiraKey;
|
import org.hibernate.testing.orm.junit.JiraKey;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import jakarta.persistence.Embeddable;
|
import jakarta.persistence.Embeddable;
|
||||||
import jakarta.persistence.Embedded;
|
import jakarta.persistence.Embedded;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
@DomainModel(annotatedClasses = {
|
@DomainModel(annotatedClasses = {
|
||||||
MergeRecordPropertyTestCase.MyEntity.class
|
MergeRecordPropertyTestCase.MyEntity.class
|
||||||
@ -20,6 +23,16 @@
|
|||||||
@JiraKey("HHH-16759")
|
@JiraKey("HHH-16759")
|
||||||
public class MergeRecordPropertyTestCase {
|
public class MergeRecordPropertyTestCase {
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
protected void cleanupTest(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
session.createQuery( "update MyEntity e set e.record.assoc = null" ).executeUpdate();
|
||||||
|
session.createQuery( "delete from MyEntity" ).executeUpdate();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeDetached(SessionFactoryScope scope) {
|
public void mergeDetached(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
@ -38,24 +51,53 @@ public void mergeDetached(SessionFactoryScope scope) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergeTransient(SessionFactoryScope scope) {
|
public void mergeDetachedNullComponent(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> session.merge( new MyEntity( 2L, new MyRecord( "test", "xyz" ) ) )
|
session -> session.persist( new MyEntity( 1L, new MyRecord( "test", "abc" ) ) )
|
||||||
|
);
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> session.merge( new MyEntity( 1L ) )
|
||||||
);
|
);
|
||||||
scope.inSession(
|
scope.inSession(
|
||||||
session -> {
|
session -> {
|
||||||
final MyEntity entity = session.find( MyEntity.class, 2L );
|
final MyEntity entity = session.find( MyEntity.class, 1L );
|
||||||
|
assertNull( entity.record );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mergeTransient(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> session.merge( new MyEntity( 1L, new MyRecord( "test", "xyz" ) ) )
|
||||||
|
);
|
||||||
|
scope.inSession(
|
||||||
|
session -> {
|
||||||
|
final MyEntity entity = session.find( MyEntity.class, 1L );
|
||||||
assertEquals( "test", entity.record.name );
|
assertEquals( "test", entity.record.name );
|
||||||
assertEquals( "xyz", entity.record.description );
|
assertEquals( "xyz", entity.record.description );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mergeTransientNullComponent(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> session.merge( new MyEntity( 1L ) )
|
||||||
|
);
|
||||||
|
scope.inSession(
|
||||||
|
session -> {
|
||||||
|
final MyEntity entity = session.find( MyEntity.class, 1L );
|
||||||
|
assertNull( entity.record );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mergePersistent(SessionFactoryScope scope) {
|
public void mergePersistent(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
final MyEntity entity = new MyEntity( 3L, new MyRecord( "test", "efg" ) );
|
final MyEntity entity = new MyEntity( 1L, new MyRecord( "test", "efg" ) );
|
||||||
session.persist( entity );
|
session.persist( entity );
|
||||||
entity.setRecord( new MyRecord( "test", "h" ) );
|
entity.setRecord( new MyRecord( "test", "h" ) );
|
||||||
session.merge( entity );
|
session.merge( entity );
|
||||||
@ -63,13 +105,41 @@ public void mergePersistent(SessionFactoryScope scope) {
|
|||||||
);
|
);
|
||||||
scope.inSession(
|
scope.inSession(
|
||||||
session -> {
|
session -> {
|
||||||
final MyEntity entity = session.find( MyEntity.class, 3L );
|
final MyEntity entity = session.find( MyEntity.class, 1L );
|
||||||
assertEquals( "test", entity.record.name );
|
assertEquals( "test", entity.record.name );
|
||||||
assertEquals( "h", entity.record.description );
|
assertEquals( "h", entity.record.description );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mergePersistentDetached(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
final MyEntity entity = new MyEntity( 1L, new MyRecord( "test", "abc" ) );
|
||||||
|
session.persist( entity );
|
||||||
|
session.flush();
|
||||||
|
session.clear();
|
||||||
|
final MyEntity entity2 = new MyEntity( 2L, new MyRecord( "test", "efg", new MyEntity( 1L ) ) );
|
||||||
|
final MyEntity mergedEntity = session.merge( entity2 );
|
||||||
|
assertTrue( session.contains( mergedEntity.record.assoc() ) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mergePersistentManaged(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
final MyEntity entity = new MyEntity( 1L, new MyRecord( "test", "abc" ) );
|
||||||
|
session.persist( entity );
|
||||||
|
final MyEntity entity2 = new MyEntity( 1L, new MyRecord( "test", "efg", new MyEntity( 1L ) ) );
|
||||||
|
final MyEntity mergedEntity = session.merge( entity2 );
|
||||||
|
assertTrue( session.contains( mergedEntity.record.assoc() ) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Entity(name = "MyEntity")
|
@Entity(name = "MyEntity")
|
||||||
public static class MyEntity {
|
public static class MyEntity {
|
||||||
@Id
|
@Id
|
||||||
@ -80,6 +150,10 @@ public static class MyEntity {
|
|||||||
public MyEntity() {
|
public MyEntity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MyEntity(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
public MyEntity(Long id, MyRecord record) {
|
public MyEntity(Long id, MyRecord record) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.record = record;
|
this.record = record;
|
||||||
@ -103,6 +177,9 @@ public void setRecord(MyRecord record) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Embeddable
|
@Embeddable
|
||||||
public static record MyRecord(String name, String description) {
|
public static record MyRecord(String name, String description, @ManyToOne(fetch = FetchType.LAZY) MyEntity assoc) {
|
||||||
|
public MyRecord(String name, String description) {
|
||||||
|
this( name, description, null );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user