|
| 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