Fixed
Details
Assignee
Andrea BorieroAndrea BorieroReporter
Georg EchterlingGeorg EchterlingWorked in
Components
Fix versions
Affects versions
Priority
Major
Details
Details
Assignee
Andrea Boriero
Andrea BorieroReporter
Georg Echterling
Georg EchterlingWorked in
Components
Fix versions
Affects versions
Priority
Created November 29, 2022 at 10:10 AM
Updated February 7, 2023 at 11:20 AM
Resolved December 20, 2022 at 12:54 PM
Fetching an Entity containing an Embeddable fails if the Embeddable has more fields than the Entity and is not loaded eagerly. This exception happens before the Entity is loaded from the database.
java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1 at org.hibernate.metamodel.mapping.internal.BasicAttributeMapping.generateFetch(BasicAttributeMapping.java:304) at org.hibernate.sql.results.graph.FetchParent.generateFetchableFetch(FetchParent.java:105) at org.hibernate.loader.ast.internal.LoaderSelectBuilder.lambda$createFetchableBiConsumer$5(LoaderSelectBuilder.java:833) at org.hibernate.loader.ast.internal.LoaderSelectBuilder.visitFetches(LoaderSelectBuilder.java:672) at org.hibernate.loader.ast.internal.LoaderSqlAstCreationState.visitFetches(LoaderSqlAstCreationState.java:123) at org.hibernate.sql.results.graph.AbstractFetchParent.afterInitialize(AbstractFetchParent.java:32) at org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl.<init>(EmbeddableFetchImpl.java:75) at org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping.generateFetch(EmbeddedAttributeMapping.java:238) at org.hibernate.sql.results.graph.FetchParent.generateFetchableFetch(FetchParent.java:105) at org.hibernate.loader.ast.internal.LoaderSelectBuilder.lambda$createFetchableBiConsumer$5(LoaderSelectBuilder.java:833) at org.hibernate.loader.ast.internal.LoaderSelectBuilder.visitFetches(LoaderSelectBuilder.java:672) at org.hibernate.loader.ast.internal.LoaderSqlAstCreationState.visitFetches(LoaderSqlAstCreationState.java:123) at org.hibernate.sql.results.graph.AbstractFetchParent.afterInitialize(AbstractFetchParent.java:32) at org.hibernate.sql.results.graph.entity.AbstractEntityResultGraphNode.afterInitialize(AbstractEntityResultGraphNode.java:100) at org.hibernate.persister.entity.AbstractEntityPersister.createDomainResult(AbstractEntityPersister.java:1298) at org.hibernate.loader.ast.internal.LoaderSelectBuilder.generateSelect(LoaderSelectBuilder.java:451) at org.hibernate.loader.ast.internal.LoaderSelectBuilder.createSelect(LoaderSelectBuilder.java:178) at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.createLoadPlan(SingleIdEntityLoaderStandardImpl.java:180) at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.resolveLoadPlan(SingleIdEntityLoaderStandardImpl.java:153) at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.load(SingleIdEntityLoaderStandardImpl.java:66) at org.hibernate.persister.entity.AbstractEntityPersister.doLoad(AbstractEntityPersister.java:4380) at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4370) at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:590) at org.hibernate.event.internal.DefaultLoadEventListener.loadFromCacheOrDatasource(DefaultLoadEventListener.java:576) at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:545) at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:538) at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:200) at org.hibernate.event.internal.DefaultLoadEventListener.loadWithRegularProxy(DefaultLoadEventListener.java:280) at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:235) at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:104) at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:76) at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118) at org.hibernate.internal.SessionImpl.fireLoadNoChecks(SessionImpl.java:1221) at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1209) at org.hibernate.loader.access.IdentifierLoadAccessImpl.doLoad(IdentifierLoadAccessImpl.java:192) at org.hibernate.loader.access.IdentifierLoadAccessImpl.lambda$load$1(IdentifierLoadAccessImpl.java:158) at org.hibernate.loader.access.IdentifierLoadAccessImpl.perform(IdentifierLoadAccessImpl.java:105) at org.hibernate.loader.access.IdentifierLoadAccessImpl.load(IdentifierLoadAccessImpl.java:158) at org.hibernate.internal.SessionImpl.find(SessionImpl.java:2336) at org.hibernate.internal.SessionImpl.find(SessionImpl.java:2303) at org.hibernate.orm.test.query.sql.EmbeddableLazyFetchTest.lambda$testSelect$2(EmbeddableLazyFetchTest.java:60) at org.hibernate.testing.orm.transaction.TransactionUtil.wrapInTransaction(TransactionUtil.java:49) at org.hibernate.testing.orm.transaction.TransactionUtil.inTransaction(TransactionUtil.java:24) at org.hibernate.testing.orm.junit.SessionFactoryExtension$SessionFactoryScopeImpl.inTransaction(SessionFactoryExtension.java:375) at org.hibernate.testing.orm.junit.SessionFactoryExtension$SessionFactoryScopeImpl.inTransaction(SessionFactoryExtension.java:352) at org.hibernate.orm.test.query.sql.EmbeddableLazyFetchTest.testSelect(EmbeddableLazyFetchTest.java:56)
BasicAttributeMapping.generateFetch seems to load the Entity’s property laziness with the Embeddable’s property index.
Since this bug is very similar to https://hibernate.atlassian.net/browse/HHH-15658, I have adapted its test case. In the test, the Embeddable is loaded lazily because it is omitted from the EntityGraph:
package org.hibernate.orm.test.query.sql; import java.util.Collections; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.orm.junit.DomainModel; 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 jakarta.persistence.Embeddable; import jakarta.persistence.Entity; import jakarta.persistence.Id; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; @DomainModel( annotatedClasses = EmbeddableLazyFetchTest.Person.class ) @SessionFactory @TestForIssue(jiraKey = "HHH-15778") public class EmbeddableLazyFetchTest { @BeforeEach public void setUp(SessionFactoryScope scope) { scope.inTransaction( session -> { Address address = new Address( "Milan", "Italy", "Italy", "20133" ); Person person = new Person( 1, address ); session.persist( person ); } ); } @AfterEach public void tearDown(SessionFactoryScope scope) { scope.inTransaction( session -> session.createMutationQuery( "delete from Person" ).executeUpdate() ); } @Test public void testSelect(SessionFactoryScope scope) { scope.inTransaction( session -> { RootGraphImplementor<Person> graph = session.createEntityGraph( Person.class ); Person person = session.find( Person.class, 1, Collections.singletonMap( "jakarta.persistence.fetchgraph", graph ) ); assertThat( person ).isNotNull(); } ); } @Entity(name = "Person") public static class Person { @Id private Integer id; private Address address; public Person() { } public Person(Integer id, Address address) { this.id = id; this.address = address; } } @Embeddable public static class Address { private String city; private String state; private String country; private String postcode; public Address() { } public Address(String city, String state, String country, String postcode) { this.city = city; this.state = state; this.country = country; this.postcode = postcode; } } }