1
+ /*
2
+ * Copyright 2025 the original author or authors.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * https://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ package org .springframework .data .jpa .repository .query ;
17
+
18
+ import static org .assertj .core .api .Assertions .*;
19
+
20
+ import jakarta .persistence .Column ;
21
+ import jakarta .persistence .Entity ;
22
+ import jakarta .persistence .EntityManager ;
23
+ import jakarta .persistence .GeneratedValue ;
24
+ import jakarta .persistence .Id ;
25
+ import jakarta .persistence .JoinColumn ;
26
+ import jakarta .persistence .PersistenceContext ;
27
+ import jakarta .persistence .Table ;
28
+
29
+ import org .hibernate .annotations .Any ;
30
+ import org .hibernate .annotations .AnyDiscriminator ;
31
+ import org .hibernate .annotations .AnyDiscriminatorValue ;
32
+ import org .hibernate .annotations .AnyKeyJavaClass ;
33
+ import org .junit .jupiter .api .Test ;
34
+ import org .junit .jupiter .api .extension .ExtendWith ;
35
+ import org .springframework .beans .factory .annotation .Autowired ;
36
+ import org .springframework .context .annotation .Configuration ;
37
+ import org .springframework .context .annotation .ImportResource ;
38
+ import org .springframework .data .jpa .repository .JpaRepository ;
39
+ import org .springframework .data .jpa .repository .config .EnableJpaRepositories ;
40
+ import org .springframework .test .context .ContextConfiguration ;
41
+ import org .springframework .test .context .junit .jupiter .SpringExtension ;
42
+ import org .springframework .transaction .annotation .Transactional ;
43
+
44
+ /**
45
+ * Integration tests for Hibernate @Any annotation support in query derivation.
46
+ *
47
+ * @author Hyunjoon Park
48
+ */
49
+ @ ExtendWith (SpringExtension .class )
50
+ @ ContextConfiguration
51
+ @ Transactional
52
+ class HibernateAnyAnnotationIntegrationTests {
53
+
54
+ @ PersistenceContext EntityManager em ;
55
+ @ Autowired TestEntityRepository repository ;
56
+
57
+ @ Test
58
+ void derivedQueryMethodShouldWorkWithAnyAnnotation () {
59
+
60
+ MonitorableEntity monitorable = new MonitorableEntity ();
61
+ monitorable .name = "Test Entity" ;
62
+ em .persist (monitorable );
63
+
64
+ TestEntity entity = new TestEntity ();
65
+ entity .monitorObject = monitorable ;
66
+ entity = repository .save (entity );
67
+
68
+ em .flush ();
69
+ em .clear ();
70
+
71
+ // This would throw IllegalArgumentException without the fix
72
+ var result = repository .findByMonitorObjectId (monitorable .id );
73
+
74
+ assertThat (result ).hasSize (1 );
75
+ assertThat (result .get (0 ).id ).isEqualTo (entity .id );
76
+ }
77
+
78
+ @ Test
79
+ void shouldHandleNullAnyAssociations () {
80
+
81
+ TestEntity entity = new TestEntity ();
82
+ entity .monitorObject = null ;
83
+ repository .save (entity );
84
+
85
+ var result = repository .findByMonitorObjectId (999L );
86
+
87
+ assertThat (result ).isEmpty ();
88
+ }
89
+
90
+ @ Test
91
+ void shouldSupportMultipleAnyAssociationTypes () {
92
+
93
+ MonitorableEntity monitorable = new MonitorableEntity ();
94
+ monitorable .name = "First Type" ;
95
+ em .persist (monitorable );
96
+
97
+ AnotherMonitorableEntity another = new AnotherMonitorableEntity ();
98
+ another .code = "ABC123" ;
99
+ em .persist (another );
100
+
101
+ TestEntity entity1 = new TestEntity ();
102
+ entity1 .monitorObject = monitorable ;
103
+ repository .save (entity1 );
104
+
105
+ TestEntity entity2 = new TestEntity ();
106
+ entity2 .monitorObject = another ;
107
+ repository .save (entity2 );
108
+
109
+ em .flush ();
110
+ em .clear ();
111
+
112
+ var result1 = repository .findByMonitorObjectId (monitorable .id );
113
+ var result2 = repository .findByMonitorObjectId (another .id );
114
+
115
+ assertThat (result1 ).hasSize (1 );
116
+ assertThat (result2 ).hasSize (1 );
117
+ }
118
+
119
+ @ Test
120
+ void countByAnyAssociationShouldWork () {
121
+
122
+ MonitorableEntity monitorable = new MonitorableEntity ();
123
+ monitorable .name = "Test" ;
124
+ em .persist (monitorable );
125
+
126
+ TestEntity entity1 = new TestEntity ();
127
+ entity1 .monitorObject = monitorable ;
128
+ repository .save (entity1 );
129
+
130
+ TestEntity entity2 = new TestEntity ();
131
+ entity2 .monitorObject = monitorable ;
132
+ repository .save (entity2 );
133
+
134
+ long count = repository .countByMonitorObjectId (monitorable .id );
135
+
136
+ assertThat (count ).isEqualTo (2 );
137
+ }
138
+
139
+ @ Test
140
+ void existsByAnyAssociationShouldWork () {
141
+
142
+ MonitorableEntity monitorable = new MonitorableEntity ();
143
+ monitorable .name = "Test" ;
144
+ em .persist (monitorable );
145
+
146
+ TestEntity entity = new TestEntity ();
147
+ entity .monitorObject = monitorable ;
148
+ repository .save (entity );
149
+
150
+ boolean exists = repository .existsByMonitorObjectId (monitorable .id );
151
+
152
+ assertThat (exists ).isTrue ();
153
+ }
154
+
155
+
156
+ @ Entity
157
+ @ Table (name = "test_entity" )
158
+ static class TestEntity {
159
+
160
+ @ Id
161
+ @ GeneratedValue
162
+ Long id ;
163
+
164
+ @ Any
165
+ @ AnyDiscriminator // Default is STRING type
166
+ @ AnyDiscriminatorValue (discriminator = "monitorable" , entity = MonitorableEntity .class )
167
+ @ AnyDiscriminatorValue (discriminator = "another" , entity = AnotherMonitorableEntity .class )
168
+ @ AnyKeyJavaClass (Long .class )
169
+ @ JoinColumn (name = "monitor_object_id" )
170
+ @ Column (name = "monitor_object_type" )
171
+ Object monitorObject ;
172
+ }
173
+
174
+ @ Entity
175
+ @ Table (name = "monitorable_entity" )
176
+ static class MonitorableEntity {
177
+
178
+ @ Id
179
+ @ GeneratedValue
180
+ Long id ;
181
+
182
+ String name ;
183
+ }
184
+
185
+ @ Entity
186
+ @ Table (name = "another_monitorable_entity" )
187
+ static class AnotherMonitorableEntity {
188
+
189
+ @ Id
190
+ @ GeneratedValue
191
+ Long id ;
192
+
193
+ String code ;
194
+ }
195
+
196
+ public interface TestEntityRepository extends JpaRepository <TestEntity , Long > {
197
+
198
+ java .util .List <TestEntity > findByMonitorObjectId (Long id );
199
+
200
+ long countByMonitorObjectId (Long id );
201
+
202
+ boolean existsByMonitorObjectId (Long id );
203
+ }
204
+
205
+ @ Configuration
206
+ @ EnableJpaRepositories (considerNestedRepositories = true )
207
+ @ ImportResource ("classpath:infrastructure.xml" )
208
+ static class Config {
209
+ }
210
+ }
0 commit comments