Skip to content

Commit db24cf1

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

File tree

2 files changed

+189
-19
lines changed

2 files changed

+189
-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
@@ -12,6 +12,7 @@
1212
import java.sql.NClob;
1313
import java.sql.SQLException;
1414

15+
import org.checkerframework.checker.nullness.qual.Nullable;
1516
import org.hibernate.Internal;
1617
import org.hibernate.engine.spi.SessionFactoryImplementor;
1718
import org.hibernate.metamodel.mapping.BasicValuedMapping;
@@ -130,13 +131,13 @@ public static Object[] getJdbcValues(
130131

131132
private static int injectJdbcValues(
132133
EmbeddableMappingType embeddableMappingType,
133-
Object domainValue,
134+
@Nullable Object domainValue,
134135
Object[] jdbcValues,
135136
int jdbcIndex,
136137
WrapperOptions options) throws SQLException {
137138
return injectJdbcValues(
138139
embeddableMappingType,
139-
embeddableMappingType.getValues( domainValue ),
140+
domainValue == null ? null : embeddableMappingType.getValues( domainValue ),
140141
jdbcValues,
141142
jdbcIndex,
142143
options
@@ -145,12 +146,15 @@ private static int injectJdbcValues(
145146

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

0 commit comments

Comments
 (0)