Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@
import org.hibernate.type.ComponentType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.ManyToOneType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.MutabilityPlan;
Expand Down Expand Up @@ -1183,6 +1184,15 @@ private static List<UniqueKeyEntry> initUniqueKeyEntries(final AbstractEntityPer
uniqueKeys.add( new UniqueKeyEntry( ukName, index, type ) );
}
}
else if ( associationType instanceof ManyToOneType manyToOneType
&& manyToOneType.isLogicalOneToOne() && manyToOneType.isReferenceToPrimaryKey() ) {
final AttributeMapping attributeMapping = aep.findAttributeMapping( manyToOneType.getPropertyName() );
if ( attributeMapping != null ) {
final int index = attributeMapping.getStateArrayPosition();
final Type type = aep.getPropertyTypes()[index];
uniqueKeys.add( new UniqueKeyEntry( manyToOneType.getPropertyName(), index, type ) );
}
}
}
}
return toSmallList( uniqueKeys );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.engine.internal.ForeignKeys;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityHolder;
import org.hibernate.engine.spi.EntityKey;
Expand Down Expand Up @@ -75,6 +76,7 @@
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.type.ManyToOneType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.MutabilityPlan;

Expand Down Expand Up @@ -622,6 +624,8 @@ private boolean areKeysEqual(Object key1, Object key2) {
protected void resolveInstanceSubInitializers(EntityInitializerData data) {
final int subclassId = data.concreteDescriptor.getSubclassId();
final EntityEntry entityEntry = data.entityHolder.getEntityEntry();
assert entityEntry != null : "This method should only be called if the entity is already initialized";

final Initializer<?>[] initializers;
final ImmutableBitSet maybeLazySet;
if ( data.entityHolder.getEntityInitializer() == this ) {
Expand Down Expand Up @@ -981,11 +985,13 @@ else if ( lazyInitializer.isUninitialized() ) {
registerLoadingEntity( data, data.entityInstanceForNotify );
}
else {
data.setState( State.INITIALIZED );
data.entityInstanceForNotify = lazyInitializer.getImplementation();
data.concreteDescriptor = session.getEntityPersister( null, data.entityInstanceForNotify );
resolveEntityKey( data, lazyInitializer.getIdentifier() );
data.entityHolder = persistenceContext.getEntityHolder( data.entityKey );
// Even though the lazyInitializer reports it is initialized, check if the entity holder reports initialized,
// because in a nested initialization scenario, this nested initializer must initialize the entity
data.setState( data.entityHolder.isInitialized() ? State.INITIALIZED : State.RESOLVED );
}
if ( identifierAssembler != null ) {
final Initializer<?> initializer = identifierAssembler.getInitializer();
Expand Down Expand Up @@ -1582,11 +1588,22 @@ protected void registerPossibleUniqueKeyEntries(
// one used here, which it will be

if ( resolvedEntityState[index] != null ) {
final Object key;
if ( type instanceof ManyToOneType manyToOneType ) {
key = ForeignKeys.getEntityIdentifierIfNotUnsaved(
manyToOneType.getAssociatedEntityName(),
resolvedEntityState[index],
session
);
}
else {
key = resolvedEntityState[index];
}
final EntityUniqueKey entityUniqueKey = new EntityUniqueKey(
data.concreteDescriptor.getRootEntityDescriptor().getEntityName(),
//polymorphism comment above
ukName,
resolvedEntityState[index],
key,
type,
session.getFactory()
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.bytecode.enhancement.batch;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Hibernate;
import org.hibernate.annotations.BatchSize;
import org.hibernate.cfg.AvailableSettings;

import org.hibernate.testing.bytecode.enhancement.extension.BytecodeEnhanced;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

@DomainModel(
annotatedClasses = {
RefreshAndBatchTest.User.class,
RefreshAndBatchTest.UserInfo.class,
RefreshAndBatchTest.Phone.class,
}

)
@SessionFactory
@ServiceRegistry(
settings = {
@Setting(name = AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, value = "100")

}
)
@JiraKey("HHH-18608")
@BytecodeEnhanced(runNotEnhancedAsWell = true)
public class RefreshAndBatchTest {

@BeforeEach
public void setUp(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
UserInfo info = new UserInfo( "info" );
Phone phone = new Phone( "123456" );
info.addPhone( phone );
User user = new User( 1L, "user1", info );
session.persist( user );
}
);
}

@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createMutationQuery( "delete User" ).executeUpdate();
session.createMutationQuery( "delete Phone" ).executeUpdate();
session.createMutationQuery( "delete UserInfo" ).executeUpdate();
}
);
}

@Test
public void testRefresh(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
User user = session.createQuery( "select u from User u where u.id = :id", User.class )
.setParameter( "id", 1L )
.getSingleResult();
assertThat( Hibernate.isInitialized( user.getInfo() ) ).isFalse();
session.refresh( user.getInfo() );
assertThat( Hibernate.isInitialized( user.getInfo() ) ).isTrue();
}
);
}

@Entity(name = "User")
@Table(name = "USER_TABLE")
@BatchSize(size = 5)
public static class User {

@Id
private Long id;

@Column
private String name;

@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "INFO_ID", referencedColumnName = "ID")
private UserInfo info;

public User() {
}

public User(long id, String name, UserInfo info) {
this.id = id;
this.name = name;
this.info = info;
info.user = this;
}

public long getId() {
return id;
}

public String getName() {
return name;
}

public UserInfo getInfo() {
return info;
}
}

@Entity(name = "UserInfo")
public static class UserInfo {
@Id
@GeneratedValue
private Long id;

@OneToOne(mappedBy = "info", fetch = FetchType.LAZY)
private User user;

private String info;

@OneToMany(mappedBy = "info", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Phone> phoneList;

public long getId() {
return id;
}

public UserInfo() {
}

public UserInfo(String info) {
this.info = info;
}

public User getUser() {
return user;
}

public String getInfo() {
return info;
}

public List<Phone> getPhoneList() {
return phoneList;
}

public void addPhone(Phone phone) {
if ( phoneList == null ) {
phoneList = new ArrayList<>();
}
this.phoneList.add( phone );
phone.info = this;
}
}

@Entity(name = "Phone")
public static class Phone {
@Id
@Column(name = "PHONE_NUMBER")
private String number;

@ManyToOne
@JoinColumn(name = "INFO_ID")
private UserInfo info;

public Phone() {
}

public Phone(String number) {
this.number = number;
}

public String getNumber() {
return number;
}

public UserInfo getInfo() {
return info;
}
}
}
Loading
Loading