Skip to content

Commit c16f20e

Browse files
committed
HHH-18730 Multi-column association in aggregate component doesn't work
1 parent 6913234 commit c16f20e

File tree

2 files changed

+187
-19
lines changed

2 files changed

+187
-19
lines changed

hibernate-core/src/main/java/org/hibernate/dialect/StructHelper.java

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.sql.NClob;
1111
import java.sql.SQLException;
1212

13+
import org.checkerframework.checker.nullness.qual.Nullable;
1314
import org.hibernate.Internal;
1415
import org.hibernate.engine.spi.SessionFactoryImplementor;
1516
import org.hibernate.metamodel.mapping.BasicValuedMapping;
@@ -127,13 +128,13 @@ public static Object[] getJdbcValues(
127128

128129
private static int injectJdbcValues(
129130
EmbeddableMappingType embeddableMappingType,
130-
Object domainValue,
131+
@Nullable Object domainValue,
131132
Object[] jdbcValues,
132133
int jdbcIndex,
133134
WrapperOptions options) throws SQLException {
134135
return injectJdbcValues(
135136
embeddableMappingType,
136-
embeddableMappingType.getValues( domainValue ),
137+
domainValue == null ? null : embeddableMappingType.getValues( domainValue ),
137138
jdbcValues,
138139
jdbcIndex,
139140
options
@@ -142,12 +143,15 @@ private static int injectJdbcValues(
142143

143144
private static int injectJdbcValues(
144145
EmbeddableMappingType embeddableMappingType,
145-
Object[] values,
146+
@Nullable Object[] values,
146147
Object[] jdbcValues,
147148
int jdbcIndex,
148149
WrapperOptions options) throws SQLException {
149150
final int jdbcValueCount = embeddableMappingType.getJdbcValueCount();
150151
final int valueCount = jdbcValueCount + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
152+
if ( values == null ) {
153+
return valueCount;
154+
}
151155
int offset = 0;
152156
for ( int i = 0; i < values.length; i++ ) {
153157
offset += injectJdbcValue(
@@ -252,22 +256,13 @@ else if ( attributeMapping instanceof EmbeddableValuedModelPart ) {
252256
);
253257
}
254258
else {
255-
jdbcValueCount = embeddableMappingType.getJdbcValueCount() + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
256-
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
257-
final int numberOfValues = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
258-
final Object[] subValues = embeddableMappingType.getValues( attributeValues[attributeIndex] );
259-
int offset = 0;
260-
for ( int i = 0; i < numberOfValues; i++ ) {
261-
offset += injectJdbcValue(
262-
getEmbeddedPart( embeddableMappingType, i ),
263-
subValues,
264-
i,
265-
jdbcValues,
266-
jdbcIndex + offset,
267-
options
268-
);
269-
}
270-
assert offset == jdbcValueCount;
259+
jdbcValueCount = injectJdbcValues(
260+
embeddableMappingType,
261+
attributeValues[attributeIndex],
262+
jdbcValues,
263+
jdbcIndex,
264+
options
265+
);
271266
}
272267
}
273268
else {
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.component;
6+
7+
import jakarta.persistence.Embeddable;
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.FetchType;
10+
import jakarta.persistence.Id;
11+
import jakarta.persistence.ManyToOne;
12+
import org.hibernate.Hibernate;
13+
import org.hibernate.annotations.Struct;
14+
import org.hibernate.testing.jdbc.SharedDriverManagerTypeCacheClearingIntegrator;
15+
import org.hibernate.testing.orm.junit.BootstrapServiceRegistry;
16+
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
17+
import org.hibernate.testing.orm.junit.DomainModel;
18+
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
19+
import org.hibernate.testing.orm.junit.SessionFactory;
20+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
21+
import org.junit.jupiter.api.AfterEach;
22+
import org.junit.jupiter.api.BeforeEach;
23+
import org.junit.jupiter.api.Test;
24+
25+
import static org.junit.jupiter.api.Assertions.assertEquals;
26+
import static org.junit.jupiter.api.Assertions.assertFalse;
27+
import static org.junit.jupiter.api.Assertions.assertTrue;
28+
29+
@BootstrapServiceRegistry(
30+
// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
31+
integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
32+
)
33+
@DomainModel(
34+
annotatedClasses = {
35+
StructComponentManyToOneCompositeTest.Book.class
36+
}
37+
)
38+
@SessionFactory
39+
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructAggregate.class)
40+
public class StructComponentManyToOneCompositeTest {
41+
42+
@BeforeEach
43+
public void setUp(SessionFactoryScope scope){
44+
scope.inTransaction(
45+
session -> {
46+
Book book1 = new Book();
47+
book1.id = 1L;
48+
book1.id2 = 1L;
49+
book1.title = "Hibernate 3";
50+
book1.author = new Author( "Gavin", null );
51+
52+
session.persist( book1 );
53+
54+
Book book2 = new Book();
55+
book2.id = 2L;
56+
book2.id2 = 2L;
57+
book2.title = "Hibernate 6";
58+
book2.author = new Author( "Steve", book1 );
59+
60+
session.persist( book2 );
61+
}
62+
);
63+
}
64+
65+
@AfterEach
66+
public void tearDown(SessionFactoryScope scope){
67+
scope.inTransaction(
68+
session ->
69+
session.createQuery( "delete from Book" ).executeUpdate()
70+
);
71+
}
72+
73+
@Test
74+
public void testGet(SessionFactoryScope scope){
75+
scope.inTransaction(
76+
session -> {
77+
Book book = session.createQuery( "from Book b where b.id = 2", Book.class ).getSingleResult();
78+
assertFalse( Hibernate.isInitialized( book.author.getFavoriteBook() ) );
79+
assertEquals( "Gavin", book.author.getFavoriteBook().getAuthor().getName() );
80+
}
81+
);
82+
}
83+
84+
@Test
85+
public void testJoin(SessionFactoryScope scope){
86+
scope.inTransaction(
87+
session -> {
88+
Book book = session.createQuery(
89+
"from Book b join fetch b.author.favoriteBook where b.id = 2",
90+
Book.class
91+
).getSingleResult();
92+
assertTrue( Hibernate.isInitialized( book.author.getFavoriteBook() ) );
93+
assertEquals( "Gavin", book.author.getFavoriteBook().getAuthor().getName() );
94+
}
95+
);
96+
}
97+
98+
@Entity(name = "Book")
99+
public static class Book {
100+
@Id
101+
private Long id;
102+
@Id
103+
private Long id2;
104+
private String title;
105+
private Author author;
106+
107+
public Long getId() {
108+
return id;
109+
}
110+
111+
public void setId(Long id) {
112+
this.id = id;
113+
}
114+
115+
public Long getId2() {
116+
return id2;
117+
}
118+
119+
public void setId2(Long id2) {
120+
this.id2 = id2;
121+
}
122+
123+
public String getTitle() {
124+
return title;
125+
}
126+
127+
public void setTitle(String title) {
128+
this.title = title;
129+
}
130+
131+
public Author getAuthor() {
132+
return author;
133+
}
134+
135+
public void setAuthor(Author author) {
136+
this.author = author;
137+
}
138+
}
139+
140+
@Embeddable
141+
@Struct( name = "author_type")
142+
public static class Author {
143+
144+
private String name;
145+
@ManyToOne(fetch = FetchType.LAZY)
146+
private Book favoriteBook;
147+
148+
public Author() {
149+
}
150+
151+
public Author(String name, Book favoriteBook) {
152+
this.name = name;
153+
this.favoriteBook = favoriteBook;
154+
}
155+
156+
public String getName() {
157+
return name;
158+
}
159+
160+
public void setName(String name) {
161+
this.name = name;
162+
}
163+
164+
public Book getFavoriteBook() {
165+
return favoriteBook;
166+
}
167+
168+
public void setFavoriteBook(Book favoriteBook) {
169+
this.favoriteBook = favoriteBook;
170+
}
171+
}
172+
173+
}

0 commit comments

Comments
 (0)