HHH-18883 fix for TransientObjectException
This commit is contained in:
parent
1fe23ae2ed
commit
f9d2e8e974
|
@ -334,10 +334,11 @@ public final class ForeignKeys {
|
|||
* Return the identifier of the persistent or transient object, or throw
|
||||
* an exception if the instance is "unsaved"
|
||||
* <p>
|
||||
* Used by OneToOneType and ManyToOneType to determine what id value should
|
||||
* Used by {@link org.hibernate.type.OneToOneType} and
|
||||
* {@link org.hibernate.type.ManyToOneType} to determine what id value should
|
||||
* be used for an object that may or may not be associated with the session.
|
||||
* This does a "best guess" using any/all info available to use (not just the
|
||||
* EntityEntry).
|
||||
* {@link EntityEntry}).
|
||||
*
|
||||
* @param entityName The name of the entity
|
||||
* @param object The entity instance
|
||||
|
@ -357,9 +358,9 @@ public final class ForeignKeys {
|
|||
else {
|
||||
final Object id = session.getContextEntityIdentifier( object );
|
||||
if ( id == null ) {
|
||||
// context-entity-identifier returns null explicitly if the entity
|
||||
// is not associated with the persistence context; so make some
|
||||
// deeper checks...
|
||||
// context-entity-identifier always returns null if the
|
||||
// entity is not associated with the persistence context;
|
||||
// so make some deeper checks...
|
||||
throwIfTransient( entityName, object, session );
|
||||
return session.getEntityPersister( entityName, object ).getIdentifier( object, session );
|
||||
}
|
||||
|
@ -369,6 +370,21 @@ public final class ForeignKeys {
|
|||
}
|
||||
}
|
||||
|
||||
public static Object getEntityIdentifier(
|
||||
final String entityName,
|
||||
final Object object,
|
||||
final SharedSessionContractImplementor session) {
|
||||
if ( object == null ) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
final Object id = session.getContextEntityIdentifier( object );
|
||||
return id == null
|
||||
? session.getEntityPersister( entityName, object ).getIdentifier( object, session )
|
||||
: id;
|
||||
}
|
||||
}
|
||||
|
||||
private static void throwIfTransient(String entityName, Object object, SharedSessionContractImplementor session) {
|
||||
if ( isTransient( entityName, object, Boolean.FALSE, session ) ) {
|
||||
throw new TransientObjectException( "Entity references an unsaved transient instance of '"
|
||||
|
|
|
@ -1583,14 +1583,14 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
|
|||
final Type type = entry.getPropertyType();
|
||||
|
||||
// polymorphism not really handled completely correctly,
|
||||
// perhaps...well, actually its ok, assuming that the
|
||||
// perhaps...well, actually it's ok, assuming that the
|
||||
// entity name used in the lookup is the same as the
|
||||
// one used here, which it will be
|
||||
|
||||
if ( resolvedEntityState[index] != null ) {
|
||||
final Object key;
|
||||
if ( type instanceof ManyToOneType manyToOneType ) {
|
||||
key = ForeignKeys.getEntityIdentifierIfNotUnsaved(
|
||||
key = ForeignKeys.getEntityIdentifier(
|
||||
manyToOneType.getAssociatedEntityName(),
|
||||
resolvedEntityState[index],
|
||||
session
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Copyright Red Hat Inc. and Hibernate Authors
|
||||
*/
|
||||
package org.hibernate.orm.test.bytecode.enhancement.batch;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.testing.orm.junit.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import static jakarta.persistence.FetchType.LAZY;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DomainModel(
|
||||
annotatedClasses = {
|
||||
HandleVersionNumbersInitializedToNegativeValueTests.RootEntity.class,
|
||||
HandleVersionNumbersInitializedToNegativeValueTests.ChildEntity.class
|
||||
}
|
||||
)
|
||||
@ServiceRegistry(
|
||||
settings = {
|
||||
// For your own convenience to see generated queries:
|
||||
@Setting(name = AvailableSettings.SHOW_SQL, value = "true"),
|
||||
@Setting(name = AvailableSettings.FORMAT_SQL, value = "true"),
|
||||
}
|
||||
)
|
||||
@SessionFactory
|
||||
class HandleVersionNumbersInitializedToNegativeValueTests {
|
||||
|
||||
@Test @JiraKey("HHH-18883")
|
||||
void hhh18883Test(SessionFactoryScope scope) {
|
||||
var id = UUID.randomUUID();
|
||||
scope.inTransaction(session -> {
|
||||
RootEntity rootEntity = new RootEntity(id, new ChildEntity());
|
||||
session.persist(rootEntity);
|
||||
});
|
||||
|
||||
scope.inTransaction(session -> {
|
||||
RootEntity rootEntity = session.find(RootEntity.class, id);
|
||||
assertThat(rootEntity).isNotNull();
|
||||
assertThat(rootEntity.getChildEntity()).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Entity
|
||||
@Table
|
||||
public static class RootEntity {
|
||||
|
||||
@Id
|
||||
private UUID id;
|
||||
|
||||
@OneToOne(mappedBy = "rootEntity", cascade = CascadeType.ALL)
|
||||
@PrimaryKeyJoinColumn
|
||||
private ChildEntity childEntity;
|
||||
|
||||
@Version
|
||||
private int version = -1;
|
||||
|
||||
public RootEntity() {
|
||||
}
|
||||
|
||||
public RootEntity(UUID id, ChildEntity childEntity) {
|
||||
setId(id);
|
||||
setChildEntity(childEntity);
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ChildEntity getChildEntity() {
|
||||
return childEntity;
|
||||
}
|
||||
|
||||
public void setChildEntity(ChildEntity childEntity) {
|
||||
this.childEntity = childEntity;
|
||||
if (childEntity != null) {
|
||||
childEntity.setRootEntity(this);
|
||||
}
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(int version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) {
|
||||
return false;
|
||||
}
|
||||
RootEntity event = (RootEntity) o;
|
||||
return Objects.equals(id, event.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table
|
||||
public static class ChildEntity {
|
||||
|
||||
@Id
|
||||
private UUID id;
|
||||
|
||||
@OneToOne(fetch = LAZY)
|
||||
@MapsId
|
||||
private RootEntity rootEntity;
|
||||
|
||||
@Version
|
||||
private int version = -1;
|
||||
|
||||
public ChildEntity() {
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public RootEntity getRootEntity() {
|
||||
return rootEntity;
|
||||
}
|
||||
|
||||
public void setRootEntity(RootEntity rootEntity) {
|
||||
this.rootEntity = rootEntity;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(int version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) {
|
||||
return false;
|
||||
}
|
||||
ChildEntity event = (ChildEntity) o;
|
||||
return Objects.equals(id, event.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue