Skip to content

Commit b1e4031

Browse files
committed
HHH-15327 Support mapping aggregate embeddables as struct/json
1 parent f1b9909 commit b1e4031

File tree

227 files changed

+14506
-1182
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

227 files changed

+14506
-1182
lines changed

documentation/src/test/java/org/hibernate/userguide/mapping/embeddable/EmbeddableAggregate.java

Lines changed: 469 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
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.userguide.mapping.embeddable;
8+
9+
import java.sql.Clob;
10+
import java.sql.Time;
11+
import java.sql.Timestamp;
12+
import java.time.Duration;
13+
import java.time.Instant;
14+
import java.time.LocalDate;
15+
import java.time.LocalDateTime;
16+
import java.time.LocalTime;
17+
import java.time.OffsetDateTime;
18+
import java.time.ZonedDateTime;
19+
import java.util.Date;
20+
import java.util.List;
21+
import java.util.UUID;
22+
23+
import org.hibernate.annotations.JdbcTypeCode;
24+
import org.hibernate.type.SqlTypes;
25+
26+
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
27+
import org.hibernate.testing.orm.domain.gambit.MutableValue;
28+
import org.hibernate.testing.orm.junit.BaseSessionFactoryFunctionalTest;
29+
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
30+
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
31+
import org.junit.jupiter.api.AfterEach;
32+
import org.junit.jupiter.api.BeforeEach;
33+
import org.junit.jupiter.api.Test;
34+
35+
import jakarta.persistence.Entity;
36+
import jakarta.persistence.Id;
37+
import jakarta.persistence.Tuple;
38+
39+
import static org.junit.jupiter.api.Assertions.assertEquals;
40+
import static org.junit.jupiter.api.Assertions.assertNull;
41+
42+
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonAggregate.class)
43+
public class JsonEmbeddableTest extends BaseSessionFactoryFunctionalTest {
44+
45+
@Override
46+
protected Class<?>[] getAnnotatedClasses() {
47+
return new Class<?>[] {
48+
JsonHolder.class
49+
};
50+
}
51+
52+
@BeforeEach
53+
public void setUp() {
54+
inTransaction(
55+
session -> {
56+
session.persist( new JsonHolder( 1L, EmbeddableAggregate.createAggregate1() ) );
57+
session.persist( new JsonHolder( 2L, EmbeddableAggregate.createAggregate2() ) );
58+
}
59+
);
60+
}
61+
62+
@AfterEach
63+
protected void cleanupTest() {
64+
inTransaction(
65+
session -> {
66+
session.createQuery( "delete from JsonHolder h" ).executeUpdate();
67+
}
68+
);
69+
}
70+
71+
@Test
72+
public void testUpdate() {
73+
sessionFactoryScope().inTransaction(
74+
entityManager -> {
75+
JsonHolder jsonHolder = entityManager.find( JsonHolder.class, 1L );
76+
jsonHolder.setAggregate( EmbeddableAggregate.createAggregate2() );
77+
entityManager.flush();
78+
entityManager.clear();
79+
EmbeddableAggregate.assertEquals( EmbeddableAggregate.createAggregate2(), entityManager.find( JsonHolder.class, 1L ).getAggregate() );
80+
}
81+
);
82+
}
83+
84+
@Test
85+
public void testFetch() {
86+
sessionFactoryScope().inSession(
87+
entityManager -> {
88+
List<JsonHolder> jsonHolders = entityManager.createQuery( "from JsonHolder b where b.id = 1", JsonHolder.class ).getResultList();
89+
assertEquals( 1, jsonHolders.size() );
90+
assertEquals( 1L, jsonHolders.get( 0 ).getId() );
91+
EmbeddableAggregate.assertEquals( EmbeddableAggregate.createAggregate1(), jsonHolders.get( 0 ).getAggregate() );
92+
}
93+
);
94+
}
95+
96+
@Test
97+
public void testFetchNull() {
98+
sessionFactoryScope().inSession(
99+
entityManager -> {
100+
List<JsonHolder> jsonHolders = entityManager.createQuery( "from JsonHolder b where b.id = 2", JsonHolder.class ).getResultList();
101+
assertEquals( 1, jsonHolders.size() );
102+
assertEquals( 2L, jsonHolders.get( 0 ).getId() );
103+
EmbeddableAggregate.assertEquals( EmbeddableAggregate.createAggregate2(), jsonHolders.get( 0 ).getAggregate() );
104+
}
105+
);
106+
}
107+
108+
@Test
109+
public void testDomainResult() {
110+
sessionFactoryScope().inSession(
111+
entityManager -> {
112+
List<EmbeddableAggregate> structs = entityManager.createQuery( "select b.aggregate from JsonHolder b where b.id = 1", EmbeddableAggregate.class ).getResultList();
113+
assertEquals( 1, structs.size() );
114+
EmbeddableAggregate.assertEquals( EmbeddableAggregate.createAggregate1(), structs.get( 0 ) );
115+
}
116+
);
117+
}
118+
119+
@Test
120+
public void testSelectionItems() {
121+
sessionFactoryScope().inSession(
122+
entityManager -> {
123+
List<Tuple> tuples = entityManager.createQuery(
124+
"select " +
125+
"b.aggregate.theInt," +
126+
"b.aggregate.theDouble," +
127+
"b.aggregate.theBoolean," +
128+
"b.aggregate.theNumericBoolean," +
129+
"b.aggregate.theStringBoolean," +
130+
"b.aggregate.theString," +
131+
"b.aggregate.theInteger," +
132+
"b.aggregate.theClob," +
133+
"b.aggregate.theBinary," +
134+
"b.aggregate.theDate," +
135+
"b.aggregate.theTime," +
136+
"b.aggregate.theTimestamp," +
137+
"b.aggregate.theInstant," +
138+
"b.aggregate.theUuid," +
139+
"b.aggregate.gender," +
140+
"b.aggregate.convertedGender," +
141+
"b.aggregate.ordinalGender," +
142+
"b.aggregate.theDuration," +
143+
"b.aggregate.theLocalDateTime," +
144+
"b.aggregate.theLocalDate," +
145+
"b.aggregate.theLocalTime," +
146+
"b.aggregate.theZonedDateTime," +
147+
"b.aggregate.theOffsetDateTime," +
148+
"b.aggregate.mutableValue " +
149+
"from JsonHolder b where b.id = 1",
150+
Tuple.class
151+
).getResultList();
152+
assertEquals( 1, tuples.size() );
153+
final Tuple tuple = tuples.get( 0 );
154+
final EmbeddableAggregate struct = new EmbeddableAggregate();
155+
struct.setTheInt( tuple.get( 0, int.class ) );
156+
struct.setTheDouble( tuple.get( 1, Double.class ) );
157+
struct.setTheBoolean( tuple.get( 2, Boolean.class ) );
158+
struct.setTheNumericBoolean( tuple.get( 3, Boolean.class ) );
159+
struct.setTheStringBoolean( tuple.get( 4, Boolean.class ) );
160+
struct.setTheString( tuple.get( 5, String.class ) );
161+
struct.setTheInteger( tuple.get( 6, Integer.class ) );
162+
struct.setTheClob( tuple.get( 7, Clob.class ) );
163+
struct.setTheBinary( tuple.get( 8, byte[].class ) );
164+
struct.setTheDate( tuple.get( 9, Date.class ) );
165+
struct.setTheTime( tuple.get( 10, Time.class ) );
166+
struct.setTheTimestamp( tuple.get( 11, Timestamp.class ) );
167+
struct.setTheInstant( tuple.get( 12, Instant.class ) );
168+
struct.setTheUuid( tuple.get( 13, UUID.class ) );
169+
struct.setGender( tuple.get( 14, EntityOfBasics.Gender.class ) );
170+
struct.setConvertedGender( tuple.get( 15, EntityOfBasics.Gender.class ) );
171+
struct.setOrdinalGender( tuple.get( 16, EntityOfBasics.Gender.class ) );
172+
struct.setTheDuration( tuple.get( 17, Duration.class ) );
173+
struct.setTheLocalDateTime( tuple.get( 18, LocalDateTime.class ) );
174+
struct.setTheLocalDate( tuple.get( 19, LocalDate.class ) );
175+
struct.setTheLocalTime( tuple.get( 20, LocalTime.class ) );
176+
struct.setTheZonedDateTime( tuple.get( 21, ZonedDateTime.class ) );
177+
struct.setTheOffsetDateTime( tuple.get( 22, OffsetDateTime.class ) );
178+
struct.setMutableValue( tuple.get( 23, MutableValue.class ) );
179+
EmbeddableAggregate.assertEquals( EmbeddableAggregate.createAggregate1(), struct );
180+
}
181+
);
182+
}
183+
184+
@Test
185+
public void testDeleteWhere() {
186+
sessionFactoryScope().inTransaction(
187+
entityManager -> {
188+
entityManager.createQuery( "delete JsonHolder b where b.aggregate is not null" ).executeUpdate();
189+
assertNull( entityManager.find( JsonHolder.class, 1L ) );
190+
191+
}
192+
);
193+
}
194+
195+
@Test
196+
public void testUpdateAggregate() {
197+
sessionFactoryScope().inTransaction(
198+
entityManager -> {
199+
entityManager.createQuery( "update JsonHolder b set b.aggregate = null" ).executeUpdate();
200+
assertNull( entityManager.find( JsonHolder.class, 1L ).getAggregate() );
201+
}
202+
);
203+
}
204+
205+
@Test
206+
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonComponentUpdate.class)
207+
public void testUpdateAggregateMember() {
208+
sessionFactoryScope().inTransaction(
209+
entityManager -> {
210+
entityManager.createQuery( "update JsonHolder b set b.aggregate.theString = null" ).executeUpdate();
211+
EmbeddableAggregate struct = EmbeddableAggregate.createAggregate1();
212+
struct.setTheString( null );
213+
EmbeddableAggregate.assertEquals( struct, entityManager.find( JsonHolder.class, 1L ).getAggregate() );
214+
}
215+
);
216+
}
217+
218+
@Test
219+
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonComponentUpdate.class)
220+
public void testUpdateMultipleAggregateMembers() {
221+
sessionFactoryScope().inTransaction(
222+
entityManager -> {
223+
entityManager.createQuery( "update JsonHolder b set b.aggregate.theString = null, b.aggregate.theUuid = null" ).executeUpdate();
224+
EmbeddableAggregate struct = EmbeddableAggregate.createAggregate1();
225+
struct.setTheString( null );
226+
struct.setTheUuid( null );
227+
EmbeddableAggregate.assertEquals( struct, entityManager.find( JsonHolder.class, 1L ).getAggregate() );
228+
}
229+
);
230+
}
231+
232+
@Test
233+
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonComponentUpdate.class)
234+
public void testUpdateAllAggregateMembers() {
235+
sessionFactoryScope().inTransaction(
236+
entityManager -> {
237+
EmbeddableAggregate struct = EmbeddableAggregate.createAggregate1();
238+
entityManager.createQuery(
239+
"update JsonHolder b set " +
240+
"b.aggregate.theInt = :theInt," +
241+
"b.aggregate.theDouble = :theDouble," +
242+
"b.aggregate.theBoolean = :theBoolean," +
243+
"b.aggregate.theNumericBoolean = :theNumericBoolean," +
244+
"b.aggregate.theStringBoolean = :theStringBoolean," +
245+
"b.aggregate.theString = :theString," +
246+
"b.aggregate.theInteger = :theInteger," +
247+
"b.aggregate.theClob = :theClob," +
248+
"b.aggregate.theBinary = :theBinary," +
249+
"b.aggregate.theDate = :theDate," +
250+
"b.aggregate.theTime = :theTime," +
251+
"b.aggregate.theTimestamp = :theTimestamp," +
252+
"b.aggregate.theInstant = :theInstant," +
253+
"b.aggregate.theUuid = :theUuid," +
254+
"b.aggregate.gender = :gender," +
255+
"b.aggregate.convertedGender = :convertedGender," +
256+
"b.aggregate.ordinalGender = :ordinalGender," +
257+
"b.aggregate.theDuration = :theDuration," +
258+
"b.aggregate.theLocalDateTime = :theLocalDateTime," +
259+
"b.aggregate.theLocalDate = :theLocalDate," +
260+
"b.aggregate.theLocalTime = :theLocalTime," +
261+
"b.aggregate.theZonedDateTime = :theZonedDateTime," +
262+
"b.aggregate.theOffsetDateTime = :theOffsetDateTime," +
263+
"b.aggregate.mutableValue = :mutableValue " +
264+
"where b.id = 2"
265+
)
266+
.setParameter( "theInt", struct.getTheInt() )
267+
.setParameter( "theDouble", struct.getTheDouble() )
268+
.setParameter( "theBoolean", struct.isTheBoolean() )
269+
.setParameter( "theNumericBoolean", struct.isTheNumericBoolean() )
270+
.setParameter( "theStringBoolean", struct.isTheStringBoolean() )
271+
.setParameter( "theString", struct.getTheString() )
272+
.setParameter( "theInteger", struct.getTheInteger() )
273+
.setParameter( "theClob", struct.getTheClob() )
274+
.setParameter( "theBinary", struct.getTheBinary() )
275+
.setParameter( "theDate", struct.getTheDate() )
276+
.setParameter( "theTime", struct.getTheTime() )
277+
.setParameter( "theTimestamp", struct.getTheTimestamp() )
278+
.setParameter( "theInstant", struct.getTheInstant() )
279+
.setParameter( "theUuid", struct.getTheUuid() )
280+
.setParameter( "gender", struct.getGender() )
281+
.setParameter( "convertedGender", struct.getConvertedGender() )
282+
.setParameter( "ordinalGender", struct.getOrdinalGender() )
283+
.setParameter( "theDuration", struct.getTheDuration() )
284+
.setParameter( "theLocalDateTime", struct.getTheLocalDateTime() )
285+
.setParameter( "theLocalDate", struct.getTheLocalDate() )
286+
.setParameter( "theLocalTime", struct.getTheLocalTime() )
287+
.setParameter( "theZonedDateTime", struct.getTheZonedDateTime() )
288+
.setParameter( "theOffsetDateTime", struct.getTheOffsetDateTime() )
289+
.setParameter( "mutableValue", struct.getMutableValue() )
290+
.executeUpdate();
291+
EmbeddableAggregate.assertEquals( EmbeddableAggregate.createAggregate1(), entityManager.find( JsonHolder.class, 2L ).getAggregate() );
292+
}
293+
);
294+
}
295+
296+
//tag::embeddable-json-type-mapping-example[]
297+
@Entity(name = "JsonHolder")
298+
public static class JsonHolder {
299+
300+
@Id
301+
private Long id;
302+
@JdbcTypeCode(SqlTypes.JSON)
303+
private EmbeddableAggregate aggregate;
304+
305+
//Getters and setters are omitted for brevity
306+
//end::embeddable-json-type-mapping-example[]
307+
308+
public JsonHolder() {
309+
}
310+
311+
public JsonHolder(Long id, EmbeddableAggregate aggregate) {
312+
this.id = id;
313+
this.aggregate = aggregate;
314+
}
315+
316+
public Long getId() {
317+
return id;
318+
}
319+
320+
public void setId(Long id) {
321+
this.id = id;
322+
}
323+
324+
public EmbeddableAggregate getAggregate() {
325+
return aggregate;
326+
}
327+
328+
public void setAggregate(EmbeddableAggregate aggregate) {
329+
this.aggregate = aggregate;
330+
}
331+
332+
//tag::embeddable-json-type-mapping-example[]
333+
}
334+
335+
//end::embeddable-json-type-mapping-example[]
336+
}

0 commit comments

Comments
 (0)