44 */
55package org .hibernate .orm .test .jpa .enhancement ;
66
7- import static org .hibernate .testing .transaction .TransactionUtil .doInJPA ;
8- import static org .junit .jupiter .api .Assertions .assertFalse ;
9- import static org .junit .jupiter .api .Assertions .assertTrue ;
10-
7+ import jakarta .persistence .Basic ;
8+ import jakarta .persistence .Entity ;
9+ import jakarta .persistence .EntityManager ;
10+ import jakarta .persistence .FetchType ;
11+ import jakarta .persistence .Id ;
12+ import jakarta .persistence .PreUpdate ;
13+ import jakarta .persistence .Table ;
1114import org .hibernate .Hibernate ;
15+ import org .hibernate .bytecode .enhance .spi .LazyPropertyInitializer ;
1216import org .hibernate .persister .entity .EntityPersister ;
13-
1417import org .hibernate .testing .bytecode .enhancement .extension .BytecodeEnhanced ;
1518import org .hibernate .testing .orm .junit .DomainModel ;
1619import org .hibernate .testing .orm .junit .JiraKey ;
1720import org .hibernate .testing .orm .junit .SessionFactory ;
1821import org .hibernate .testing .orm .junit .SessionFactoryScope ;
1922import org .hibernate .testing .transaction .TransactionUtil .JPATransactionVoidFunction ;
23+ import org .junit .jupiter .api .AfterEach ;
2024import org .junit .jupiter .api .BeforeEach ;
2125import org .junit .jupiter .api .Test ;
2226
23- import jakarta .persistence .Basic ;
24- import jakarta .persistence .Entity ;
25- import jakarta .persistence .EntityManager ;
26- import jakarta .persistence .FetchType ;
27- import jakarta .persistence .GeneratedValue ;
28- import jakarta .persistence .Id ;
29- import jakarta .persistence .PreUpdate ;
30- import jakarta .persistence .Table ;
31-
32- import java .util .Arrays ;
27+ import static org .hibernate .testing .transaction .TransactionUtil .doInJPA ;
28+ import static org .junit .jupiter .api .Assertions .assertArrayEquals ;
29+ import static org .junit .jupiter .api .Assertions .assertFalse ;
30+ import static org .junit .jupiter .api .Assertions .assertTrue ;
3331
3432
33+ /**
34+ * Test the behavior of bytecode lazy attributes and {@link PreUpdate @PreUpdate} callbacks
35+ */
36+ @ SuppressWarnings ("JUnitMalformedDeclaration" )
3537@ JiraKey ("HHH-7573" )
36- @ DomainModel (
37- annotatedClasses = {
38- TestLazyPropertyOnPreUpdate .EntityWithLazyProperty .class
39- }
40- )
38+ @ DomainModel ( annotatedClasses = { TestLazyPropertyOnPreUpdate .EntityWithCallback .class } )
4139@ SessionFactory
4240@ BytecodeEnhanced
4341public class TestLazyPropertyOnPreUpdate {
42+ private static final byte [] INITIAL_VALUE = new byte [] { 0x2A };
4443
45- private EntityWithLazyProperty entity ;
46-
44+ private static boolean UPDATE_IN_PRE_UPDATE ;
45+ private static final byte [] PRE_UPDATE_VALUE = new byte [] { 0x2A , 0x2A , 0x2A , 0x2A };
4746
48- @ BeforeEach
49- public void prepare (SessionFactoryScope scope ) throws Exception {
50- EntityPersister ep = scope .getSessionFactory ().getMappingMetamodel ().getEntityDescriptor (
51- EntityWithLazyProperty .class .getName () );
52- assertTrue ( ep .getBytecodeEnhancementMetadata ().isEnhancedForLazyLoading () );
53-
54- byte [] testArray = new byte [] { 0x2A };
55-
56- scope .inTransaction ( em -> {
57- //persist the test entity.d
58- entity = new EntityWithLazyProperty ();
59- entity .setSomeField ( "TEST" );
60- entity .setLazyData ( testArray );
61- em .persist ( entity );
62- } );
63-
64- checkLazyField ( scope , entity , testArray );
65- }
47+ private EntityWithCallback entityUnderTest ;
6648
6749 /**
68- * Set a non lazy field, therefore the lazyData field will be LazyPropertyInitializer.UNFETCHED_PROPERTY
69- * for both state and newState so the field should not change. This should no longer cause a ClassCastException .
50+ * Update the `name` field; the ` lazyData` field will remain
51+ * {@linkplain LazyPropertyInitializer#UNFETCHED_PROPERTY uninitialized} .
7052 */
7153 @ Test
7254 public void testNoUpdate (SessionFactoryScope scope ) {
@@ -75,43 +57,41 @@ public void testNoUpdate(SessionFactoryScope scope) {
7557 doInJPA ( scope ::getSessionFactory , new JPATransactionVoidFunction () {
7658 @ Override
7759 public void accept (EntityManager em ) {
78- entity = em .find ( EntityWithLazyProperty .class , entity . id );
79- entity . setSomeField ( "TEST1 " );
80- assertFalse ( Hibernate .isPropertyInitialized ( entity , "lazyData" ) );
60+ entityUnderTest = em .find ( EntityWithCallback .class , 1 );
61+ entityUnderTest . setName ( "updated name " );
62+ assertFalse ( Hibernate .isPropertyInitialized ( entityUnderTest , "lazyData" ) );
8163 }
8264
8365 @ Override
8466 public void afterTransactionCompletion () {
85- assertFalse ( Hibernate .isPropertyInitialized ( entity , "lazyData" ) );
67+ assertFalse ( Hibernate .isPropertyInitialized ( entityUnderTest , "lazyData" ) );
8668 }
8769 } );
8870
89- checkLazyField ( scope , entity , testArray );
71+ checkLazyField ( scope , testArray );
9072 }
9173
9274 /**
93- * Set the updateLazyFieldInPreUpdate flag so that the lazy field is updated from within the
94- * PreUpdate annotated callback method. So state == LazyPropertyInitializer.UNFETCHED_PROPERTY and
95- * newState == EntityWithLazyProperty.PRE_UPDATE_VALUE. This should no longer cause a ClassCastException.
75+ * Set UPDATE_FIELD_IN_CALLBACK so that `lazyField` is updated during the pre-update callback.
9676 */
9777 @ Test
9878 public void testPreUpdate (SessionFactoryScope scope ) {
79+ UPDATE_IN_PRE_UPDATE = true ;
9980 doInJPA ( scope ::getSessionFactory , new JPATransactionVoidFunction () {
10081 @ Override
10182 public void accept (EntityManager em ) {
102- entity = em .find ( EntityWithLazyProperty .class , entity .id );
103- entity .setUpdateLazyFieldInPreUpdate ( true );
104- entity .setSomeField ( "TEST2" );
105- assertFalse ( Hibernate .isPropertyInitialized ( entity , "lazyData" ) );
83+ entityUnderTest = em .find ( EntityWithCallback .class , 1 );
84+ entityUnderTest .setName ( "updated name" );
85+ assertFalse ( Hibernate .isPropertyInitialized ( entityUnderTest , "lazyData" ) );
10686 }
10787
10888 @ Override
10989 public void afterTransactionCompletion () {
110- assertTrue ( Hibernate .isPropertyInitialized ( entity , "lazyData" ) );
90+ assertTrue ( Hibernate .isPropertyInitialized ( entityUnderTest , "lazyData" ) );
11191 }
11292 } );
11393
114- checkLazyField ( scope , entity , EntityWithLazyProperty . PRE_UPDATE_VALUE );
94+ checkLazyField ( scope , PRE_UPDATE_VALUE );
11595 }
11696
11797 /**
@@ -121,70 +101,95 @@ public void afterTransactionCompletion() {
121101 */
122102 @ Test
123103 public void testPreUpdateOverride (SessionFactoryScope scope ) {
124- byte [] testArray = new byte [] { 0x2A } ;
104+ UPDATE_IN_PRE_UPDATE = true ;
125105
126106 scope .inTransaction ( em -> {
127- entity = em .find ( EntityWithLazyProperty .class , entity .id );
128- entity .setUpdateLazyFieldInPreUpdate ( true );
129- assertFalse ( Hibernate .isPropertyInitialized ( entity , "lazyData" ) );
130- entity .setLazyData ( testArray );
131- assertTrue ( Hibernate .isPropertyInitialized ( entity , "lazyData" ) );
132- entity .setSomeField ( "TEST3" );
107+ entityUnderTest = em .find ( EntityWithCallback .class , 1 );
108+ assertFalse ( Hibernate .isPropertyInitialized ( entityUnderTest , "lazyData" ) );
109+ entityUnderTest .setLazyData ( INITIAL_VALUE );
110+ assertTrue ( Hibernate .isPropertyInitialized ( entityUnderTest , "lazyData" ) );
111+ entityUnderTest .setName ( "updated name" );
133112 } );
134113
135- checkLazyField ( scope , entity , EntityWithLazyProperty . PRE_UPDATE_VALUE );
114+ checkLazyField ( scope , PRE_UPDATE_VALUE );
136115 }
137116
138- private void checkLazyField (SessionFactoryScope scope , EntityWithLazyProperty entity , byte [] expected ) {
117+ private void checkLazyField (SessionFactoryScope scope , byte [] expected ) {
139118 // reload the entity and check the lazy value matches what we expect.
140119 scope .inTransaction ( em -> {
141- EntityWithLazyProperty testEntity = em .find ( EntityWithLazyProperty .class , entity . id );
120+ EntityWithCallback testEntity = em .find ( EntityWithCallback .class , 1 );
142121 assertFalse ( Hibernate .isPropertyInitialized ( testEntity , "lazyData" ) );
143- assertTrue ( Arrays . equals ( expected , testEntity .lazyData ) );
122+ assertArrayEquals ( expected , testEntity .lazyData );
144123 assertTrue ( Hibernate .isPropertyInitialized ( testEntity , "lazyData" ) );
145124 } );
146125 }
147126
148- // --- //
127+ @ BeforeEach
128+ public void createTestData (SessionFactoryScope scope ) throws Exception {
129+ EntityPersister ep = scope .getSessionFactory ().getMappingMetamodel ().getEntityDescriptor ( EntityWithCallback .class );
130+ assertTrue ( ep .getBytecodeEnhancementMetadata ().isEnhancedForLazyLoading () );
149131
150- /**
151- * Test entity with a lazy property which requires build time instrumentation.
152- *
153- * @author Martin Ball
154- */
155- @ Entity
156- @ Table (name = "ENTITY_WITH_LAZY_PROPERTY" )
157- static class EntityWithLazyProperty {
158132
159- public static final byte [] PRE_UPDATE_VALUE = new byte [] { 0x2A , 0x2A , 0x2A , 0x2A };
133+ scope .inTransaction ( em -> {
134+ entityUnderTest = new EntityWithCallback ( 1 , "initial name" , INITIAL_VALUE );
135+ em .persist ( entityUnderTest );
136+ } );
160137
161- @ Id
162- @ GeneratedValue
163- private Long id ;
138+ checkLazyField ( scope , INITIAL_VALUE );
139+ }
140+
141+ @ AfterEach
142+ public void dropTestData (SessionFactoryScope scope ) throws Exception {
143+ scope .dropData ();
144+ }
164145
146+ @ Entity (name ="EntityWithCallback" )
147+ @ Table (name ="entity_with_callback" )
148+ public static class EntityWithCallback {
149+ @ Id
150+ private Integer id ;
151+ @ Basic (fetch = FetchType .EAGER )
152+ private String name ;
165153 @ Basic (fetch = FetchType .LAZY )
166154 private byte [] lazyData ;
167155
168- private String someField ;
169-
170- private boolean updateLazyFieldInPreUpdate ;
156+ public EntityWithCallback () {
157+ }
171158
172- public void setLazyData (byte [] lazyData ) {
159+ public EntityWithCallback (Integer id , String name , byte [] lazyData ) {
160+ this .id = id ;
161+ this .name = name ;
173162 this .lazyData = lazyData ;
174163 }
175164
176- public void setSomeField (String someField ) {
177- this .someField = someField ;
165+ public Integer getId () {
166+ return id ;
167+ }
168+
169+ public void setId (Integer id ) {
170+ this .id = id ;
178171 }
179172
180- public void setUpdateLazyFieldInPreUpdate (boolean updateLazyFieldInPreUpdate ) {
181- this .updateLazyFieldInPreUpdate = updateLazyFieldInPreUpdate ;
173+ public String getName () {
174+ return name ;
175+ }
176+
177+ public void setName (String name ) {
178+ this .name = name ;
179+ }
180+
181+ public byte [] getLazyData () {
182+ return lazyData ;
183+ }
184+
185+ public void setLazyData (byte [] lazyData ) {
186+ this .lazyData = lazyData ;
182187 }
183188
184189 @ PreUpdate
185190 public void onPreUpdate () {
186191 //Allow the update of the lazy field from within the pre update to check that this does not break things.
187- if ( updateLazyFieldInPreUpdate ) {
192+ if ( UPDATE_IN_PRE_UPDATE ) {
188193 this .lazyData = PRE_UPDATE_VALUE ;
189194 }
190195 }
0 commit comments