Skip to content

Commit c1b22d3

Browse files
committed
HHH-19861 Added New Testcase with RelationTargetNotFoundAction
1 parent 29d84ca commit c1b22d3

File tree

1 file changed

+263
-0
lines changed

1 file changed

+263
-0
lines changed
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.envers.test.integration.query;
6+
7+
import jakarta.persistence.ConstraintMode;
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.EntityManager;
10+
import jakarta.persistence.EntityNotFoundException;
11+
import jakarta.persistence.FetchType;
12+
import jakarta.persistence.ForeignKey;
13+
import jakarta.persistence.GeneratedValue;
14+
import jakarta.persistence.GenerationType;
15+
import jakarta.persistence.Id;
16+
import jakarta.persistence.JoinColumn;
17+
import jakarta.persistence.ManyToOne;
18+
import jakarta.persistence.Table;
19+
import org.hibernate.envers.AuditReader;
20+
import org.hibernate.envers.Audited;
21+
import org.hibernate.envers.RelationTargetAuditMode;
22+
import org.hibernate.envers.RelationTargetNotFoundAction;
23+
import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase;
24+
import org.hibernate.orm.test.envers.Priority;
25+
import org.hibernate.testing.orm.junit.JiraKey;
26+
import org.junit.Test;
27+
28+
import static org.junit.Assert.assertEquals;
29+
import static org.junit.Assert.assertNotNull;
30+
import static org.junit.Assert.assertNull;
31+
import static org.junit.Assert.fail;
32+
33+
/**
34+
* Tests that {@link org.hibernate.envers.RelationTargetNotFoundAction} works correctly when combined with
35+
* {@link RelationTargetAuditMode#NOT_AUDITED}. When the target entity is deleted, the behavior depends on
36+
* the configured action: ERROR throws {@link jakarta.persistence.EntityNotFoundException}, while IGNORE
37+
* returns null.
38+
*
39+
* To allow deletion of Child entities without foreign key constraint violations, the relationship uses
40+
* {@link jakarta.persistence.ConstraintMode#NO_CONSTRAINT} to prevent database foreign key creation.
41+
*
42+
* @author Minjae Seon
43+
*/
44+
@JiraKey(value = "HHH-19861")
45+
public class NotAuditedRelationTargetNotFoundActionTest extends BaseEnversJPAFunctionalTestCase {
46+
47+
private Long childForErrorId;
48+
private Long childForIgnoreId;
49+
private Long parentWithErrorId;
50+
private Long parentWithIgnoreId;
51+
52+
@Entity(name = "Child")
53+
@Table(name = "Child")
54+
@Audited
55+
public static class Child {
56+
@Id
57+
@GeneratedValue(strategy = GenerationType.AUTO)
58+
private Long id;
59+
60+
private String name;
61+
62+
public Child() {
63+
}
64+
65+
public Child(String name) {
66+
this.name = name;
67+
}
68+
69+
public Long getId() {
70+
return id;
71+
}
72+
73+
public void setId(Long id) {
74+
this.id = id;
75+
}
76+
77+
public String getName() {
78+
return name;
79+
}
80+
81+
public void setName(String name) {
82+
this.name = name;
83+
}
84+
}
85+
86+
@Entity(name = "ParentWithError")
87+
@Table(name = "ParentWithError")
88+
@Audited
89+
public static class ParentWithError {
90+
@Id
91+
@GeneratedValue(strategy = GenerationType.AUTO)
92+
private Long id;
93+
94+
private String content;
95+
96+
@ManyToOne(fetch = FetchType.LAZY)
97+
@JoinColumn(name = "child_id", nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
98+
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED, targetNotFoundAction = RelationTargetNotFoundAction.ERROR)
99+
private Child child;
100+
101+
public ParentWithError() {
102+
}
103+
104+
public ParentWithError(String content, Child child) {
105+
this.content = content;
106+
this.child = child;
107+
}
108+
109+
public Long getId() {
110+
return id;
111+
}
112+
113+
public void setId(Long id) {
114+
this.id = id;
115+
}
116+
117+
public String getContent() {
118+
return content;
119+
}
120+
121+
public void setContent(String content) {
122+
this.content = content;
123+
}
124+
125+
public Child getChild() {
126+
return child;
127+
}
128+
129+
public void setChild(Child child) {
130+
this.child = child;
131+
}
132+
}
133+
134+
@Entity(name = "ParentWithIgnore")
135+
@Table(name = "ParentWithIgnore")
136+
@Audited
137+
public static class ParentWithIgnore {
138+
@Id
139+
@GeneratedValue(strategy = GenerationType.AUTO)
140+
private Long id;
141+
142+
private String content;
143+
144+
@ManyToOne(fetch = FetchType.LAZY)
145+
@JoinColumn(name = "child_id", nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
146+
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED, targetNotFoundAction = RelationTargetNotFoundAction.IGNORE)
147+
private Child child;
148+
149+
public ParentWithIgnore() {
150+
}
151+
152+
public ParentWithIgnore(String content, Child child) {
153+
this.content = content;
154+
this.child = child;
155+
}
156+
157+
public Long getId() {
158+
return id;
159+
}
160+
161+
public void setId(Long id) {
162+
this.id = id;
163+
}
164+
165+
public String getContent() {
166+
return content;
167+
}
168+
169+
public void setContent(String content) {
170+
this.content = content;
171+
}
172+
173+
public Child getChild() {
174+
return child;
175+
}
176+
177+
public void setChild(Child child) {
178+
this.child = child;
179+
}
180+
}
181+
182+
@Override
183+
protected Class<?>[] getAnnotatedClasses() {
184+
return new Class<?>[]{ ParentWithError.class, ParentWithIgnore.class, Child.class };
185+
}
186+
187+
@Test
188+
@Priority(10)
189+
public void initData() {
190+
EntityManager em = getEntityManager();
191+
192+
// Revision 1: Create children and parents
193+
em.getTransaction().begin();
194+
Child childForError = new Child("Child for Error");
195+
em.persist(childForError);
196+
Child childForIgnore = new Child("Child for Ignore");
197+
em.persist(childForIgnore);
198+
199+
ParentWithError parentWithError = new ParentWithError("Initial content with error", childForError);
200+
em.persist(parentWithError);
201+
ParentWithIgnore parentWithIgnore = new ParentWithIgnore("Initial content with ignore", childForIgnore);
202+
em.persist(parentWithIgnore);
203+
em.getTransaction().commit();
204+
205+
childForErrorId = childForError.getId();
206+
childForIgnoreId = childForIgnore.getId();
207+
parentWithErrorId = parentWithError.getId();
208+
parentWithIgnoreId = parentWithIgnore.getId();
209+
}
210+
211+
@Test
212+
public void testLoadParentWithErrorAfterChildDeleted() {
213+
EntityManager em = getEntityManager();
214+
215+
// Delete the child entity that ParentWithError references
216+
em.getTransaction().begin();
217+
Child childForError = em.find(Child.class, childForErrorId);
218+
em.remove(childForError);
219+
em.getTransaction().commit();
220+
221+
// Now try to load parent's audit history with ERROR action
222+
// This should throw EntityNotFoundException
223+
AuditReader auditReader = getAuditReader();
224+
ParentWithError parentRev1 = auditReader.find(ParentWithError.class, parentWithErrorId, 1);
225+
226+
assertNotNull("Parent at revision 1 should not be null", parentRev1);
227+
assertEquals("Initial content with error", parentRev1.getContent());
228+
229+
// Try to access the child - should throw EntityNotFoundException
230+
try {
231+
Child childRef = parentRev1.getChild();
232+
// Access a property to trigger lazy loading
233+
childRef.getName();
234+
fail("Should have thrown EntityNotFoundException");
235+
}
236+
catch (EntityNotFoundException e) {
237+
// Expected behavior
238+
}
239+
}
240+
241+
@Test
242+
public void testLoadParentWithIgnoreAfterChildDeleted() {
243+
EntityManager em = getEntityManager();
244+
245+
// Delete the child entity that ParentWithIgnore references
246+
em.getTransaction().begin();
247+
Child childForIgnore = em.find(Child.class, childForIgnoreId);
248+
em.remove(childForIgnore);
249+
em.getTransaction().commit();
250+
251+
// Now try to load parent's audit history with IGNORE action
252+
// This should not throw EntityNotFoundException and return null
253+
AuditReader auditReader = getAuditReader();
254+
ParentWithIgnore parentRev1 = auditReader.find(ParentWithIgnore.class, parentWithIgnoreId, 1);
255+
256+
assertNotNull("Parent at revision 1 should not be null", parentRev1);
257+
assertEquals("Initial content with ignore", parentRev1.getContent());
258+
259+
// Try to access the child - should return null
260+
Child childRef = parentRev1.getChild();
261+
assertNull("Child reference should be null when child is deleted and IGNORE action is set", childRef);
262+
}
263+
}

0 commit comments

Comments
 (0)