4
4
*/
5
5
package org .hibernate .orm .test .jpa .enhancement ;
6
6
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 ;
11
14
import org .hibernate .Hibernate ;
15
+ import org .hibernate .bytecode .enhance .spi .LazyPropertyInitializer ;
12
16
import org .hibernate .persister .entity .EntityPersister ;
13
-
14
17
import org .hibernate .testing .bytecode .enhancement .extension .BytecodeEnhanced ;
15
18
import org .hibernate .testing .orm .junit .DomainModel ;
16
19
import org .hibernate .testing .orm .junit .JiraKey ;
17
20
import org .hibernate .testing .orm .junit .SessionFactory ;
18
21
import org .hibernate .testing .orm .junit .SessionFactoryScope ;
19
22
import org .hibernate .testing .transaction .TransactionUtil .JPATransactionVoidFunction ;
23
+ import org .junit .jupiter .api .AfterEach ;
20
24
import org .junit .jupiter .api .BeforeEach ;
21
25
import org .junit .jupiter .api .Test ;
22
26
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 ;
33
31
34
32
33
+ /**
34
+ * Test the behavior of bytecode lazy attributes and {@link PreUpdate @PreUpdate} callbacks
35
+ */
36
+ @ SuppressWarnings ("JUnitMalformedDeclaration" )
35
37
@ JiraKey ("HHH-7573" )
36
- @ DomainModel (
37
- annotatedClasses = {
38
- TestLazyPropertyOnPreUpdate .EntityWithLazyProperty .class
39
- }
40
- )
38
+ @ DomainModel ( annotatedClasses = { TestLazyPropertyOnPreUpdate .EntityWithCallback .class } )
41
39
@ SessionFactory
42
40
@ BytecodeEnhanced
43
41
public class TestLazyPropertyOnPreUpdate {
42
+ private static final byte [] INITIAL_VALUE = new byte [] { 0x2A };
44
43
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 };
47
46
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 ;
66
48
67
49
/**
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} .
70
52
*/
71
53
@ Test
72
54
public void testNoUpdate (SessionFactoryScope scope ) {
@@ -75,43 +57,41 @@ public void testNoUpdate(SessionFactoryScope scope) {
75
57
doInJPA ( scope ::getSessionFactory , new JPATransactionVoidFunction () {
76
58
@ Override
77
59
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" ) );
81
63
}
82
64
83
65
@ Override
84
66
public void afterTransactionCompletion () {
85
- assertFalse ( Hibernate .isPropertyInitialized ( entity , "lazyData" ) );
67
+ assertFalse ( Hibernate .isPropertyInitialized ( entityUnderTest , "lazyData" ) );
86
68
}
87
69
} );
88
70
89
- checkLazyField ( scope , entity , testArray );
71
+ checkLazyField ( scope , testArray );
90
72
}
91
73
92
74
/**
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.
96
76
*/
97
77
@ Test
98
78
public void testPreUpdate (SessionFactoryScope scope ) {
79
+ UPDATE_IN_PRE_UPDATE = true ;
99
80
doInJPA ( scope ::getSessionFactory , new JPATransactionVoidFunction () {
100
81
@ Override
101
82
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" ) );
106
86
}
107
87
108
88
@ Override
109
89
public void afterTransactionCompletion () {
110
- assertTrue ( Hibernate .isPropertyInitialized ( entity , "lazyData" ) );
90
+ assertTrue ( Hibernate .isPropertyInitialized ( entityUnderTest , "lazyData" ) );
111
91
}
112
92
} );
113
93
114
- checkLazyField ( scope , entity , EntityWithLazyProperty . PRE_UPDATE_VALUE );
94
+ checkLazyField ( scope , PRE_UPDATE_VALUE );
115
95
}
116
96
117
97
/**
@@ -121,70 +101,95 @@ public void afterTransactionCompletion() {
121
101
*/
122
102
@ Test
123
103
public void testPreUpdateOverride (SessionFactoryScope scope ) {
124
- byte [] testArray = new byte [] { 0x2A } ;
104
+ UPDATE_IN_PRE_UPDATE = true ;
125
105
126
106
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" );
133
112
} );
134
113
135
- checkLazyField ( scope , entity , EntityWithLazyProperty . PRE_UPDATE_VALUE );
114
+ checkLazyField ( scope , PRE_UPDATE_VALUE );
136
115
}
137
116
138
- private void checkLazyField (SessionFactoryScope scope , EntityWithLazyProperty entity , byte [] expected ) {
117
+ private void checkLazyField (SessionFactoryScope scope , byte [] expected ) {
139
118
// reload the entity and check the lazy value matches what we expect.
140
119
scope .inTransaction ( em -> {
141
- EntityWithLazyProperty testEntity = em .find ( EntityWithLazyProperty .class , entity . id );
120
+ EntityWithCallback testEntity = em .find ( EntityWithCallback .class , 1 );
142
121
assertFalse ( Hibernate .isPropertyInitialized ( testEntity , "lazyData" ) );
143
- assertTrue ( Arrays . equals ( expected , testEntity .lazyData ) );
122
+ assertArrayEquals ( expected , testEntity .lazyData );
144
123
assertTrue ( Hibernate .isPropertyInitialized ( testEntity , "lazyData" ) );
145
124
} );
146
125
}
147
126
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 () );
149
131
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 {
158
132
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
+ } );
160
137
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
+ }
164
145
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 ;
165
153
@ Basic (fetch = FetchType .LAZY )
166
154
private byte [] lazyData ;
167
155
168
- private String someField ;
169
-
170
- private boolean updateLazyFieldInPreUpdate ;
156
+ public EntityWithCallback () {
157
+ }
171
158
172
- public void setLazyData (byte [] lazyData ) {
159
+ public EntityWithCallback (Integer id , String name , byte [] lazyData ) {
160
+ this .id = id ;
161
+ this .name = name ;
173
162
this .lazyData = lazyData ;
174
163
}
175
164
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 ;
178
171
}
179
172
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 ;
182
187
}
183
188
184
189
@ PreUpdate
185
190
public void onPreUpdate () {
186
191
//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 ) {
188
193
this .lazyData = PRE_UPDATE_VALUE ;
189
194
}
190
195
}
0 commit comments