Skip to content

Commit 0f65723

Browse files
committed
Bug 544202 - Fix CacheKey.protectedForeignKeys update
Signed-off-by: Alexandre Jacob <[email protected]>
1 parent bc2a312 commit 0f65723

File tree

4 files changed

+328
-5
lines changed

4 files changed

+328
-5
lines changed

foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/descriptors/ObjectBuilder.java

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
2-
* Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved.
3-
* Copyright (c) 1998, 2018 IBM Corporation. All rights reserved.
2+
* Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 1998, 2020 IBM Corporation. All rights reserved.
44
*
55
* This program and the accompanying materials are made available under the
66
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -27,6 +27,8 @@
2727
// - 499335: Multiple embeddable fields can't reference same object
2828
// 02/20/2018-2.7 Will Dazey
2929
// - 529602: Added support for CLOBs in DELETE statements for Oracle
30+
// 04/19/2020-3.0 Alexandre Jacob
31+
// - 544202: Fixed refresh of foreign key values when cacheKey was invalidated
3032
package org.eclipse.persistence.internal.descriptors;
3133

3234
import java.io.Serializable;
@@ -1041,6 +1043,15 @@ protected Object buildObject(boolean returnCacheKey, ObjectBuildingQuery query,
10411043

10421044
FetchGroup fetchGroup = query.getExecutionFetchGroup(concreteDescriptor);
10431045

1046+
// 544202: cachedForeignKeys should be refreshed when cacheKey was invalidated
1047+
if (domainWasMissing || shouldRetrieveBypassCache
1048+
|| cacheKey.getInvalidationState() == CacheKey.CACHE_KEY_INVALID) {
1049+
1050+
if (isProtected && (cacheKey != null)) {
1051+
cacheForeignKeyValues(databaseRow, cacheKey, session);
1052+
}
1053+
}
1054+
10441055
if (domainWasMissing || shouldRetrieveBypassCache) {
10451056
cacheHit = false;
10461057
if (domainObject == null || shouldStoreBypassCache) {
@@ -1069,9 +1080,6 @@ protected Object buildObject(boolean returnCacheKey, ObjectBuildingQuery query,
10691080
cacheKey.setObject(domainObject);
10701081
}
10711082
concreteObjectBuilder.buildAttributesIntoObject(domainObject, cacheKey, databaseRow, query, joinManager, fetchGroup, false, session);
1072-
if (isProtected && (cacheKey != null)) {
1073-
cacheForeignKeyValues(databaseRow, cacheKey, session);
1074-
}
10751083
if (shouldMaintainCache && !shouldStoreBypassCache) {
10761084
// Set the fetch group to the domain object, after built.
10771085
if ((fetchGroup != null) && concreteDescriptor.hasFetchGroupManager()) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/*
2+
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0,
7+
* or the Eclipse Distribution License v. 1.0 which is available at
8+
* http://www.eclipse.org/org/documents/edl-v10.php.
9+
*
10+
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11+
*/
12+
13+
// Contributors:
14+
// 04/19/2020-3.0 Alexandre Jacob
15+
// - 544202: Fixed refresh of foreign key values when cacheKey was invalidated
16+
17+
package org.eclipse.persistence.jpa.test.sharedcache;
18+
19+
import static org.junit.Assert.assertEquals;
20+
21+
import org.eclipse.persistence.jpa.JpaEntityManager;
22+
import org.eclipse.persistence.jpa.test.framework.DDLGen;
23+
import org.eclipse.persistence.jpa.test.framework.Emf;
24+
import org.eclipse.persistence.jpa.test.framework.EmfRunner;
25+
import org.eclipse.persistence.jpa.test.framework.Property;
26+
import org.eclipse.persistence.jpa.test.sharedcache.model.Student;
27+
import org.eclipse.persistence.jpa.test.sharedcache.model.Teacher;
28+
import org.eclipse.persistence.sessions.Session;
29+
import org.eclipse.persistence.sessions.SessionProfiler;
30+
import org.eclipse.persistence.tools.profiler.PerformanceMonitor;
31+
import org.junit.Before;
32+
import org.junit.Test;
33+
import org.junit.runner.RunWith;
34+
35+
import jakarta.persistence.EntityManager;
36+
import jakarta.persistence.EntityManagerFactory;
37+
import jakarta.persistence.EntityTransaction;
38+
39+
@RunWith(EmfRunner.class)
40+
public class TestSharedCacheWithNonCacheable {
41+
42+
@Emf(createTables = DDLGen.DROP_CREATE, classes = { Teacher.class, Student.class },
43+
properties = {
44+
@Property(name = "eclipselink.logging.level", value = "FINE"),
45+
@Property(name = "eclipselink.logging.parameters", value = "true"),
46+
@Property(name = "eclipselink.cache.shared.default", value = "false"),
47+
@Property(name = "eclipselink.profiler", value = "PerformanceMonitor")
48+
})
49+
private EntityManagerFactory emf;
50+
51+
private PerformanceMonitor performanceMonitor;
52+
53+
@Before
54+
public void setUp() {
55+
Session session = ((JpaEntityManager) emf.createEntityManager()).getServerSession();
56+
performanceMonitor = (PerformanceMonitor) session.getProfiler();
57+
58+
// We populate DB with 2 students and 1 teacher
59+
Student student1 = new Student(1, "Picasso");
60+
Student student2 = new Student(2, "Pablo");
61+
62+
Teacher teacher = new Teacher(1, "Foo", student1);
63+
64+
final EntityManager em = this.emf.createEntityManager();
65+
final EntityTransaction transaction = em.getTransaction();
66+
67+
transaction.begin();
68+
em.persist(student1);
69+
em.persist(student2);
70+
em.persist(teacher);
71+
transaction.commit();
72+
73+
em.close();
74+
}
75+
76+
@Test
77+
public void testSharedCacheWithNonCacheable() throws Exception {
78+
// 1 : We want to get our Teacher 1. He is supposed to be in the shared cache.
79+
// The Student linked to our Teacher should not be in the cache. (@Noncacheable)
80+
{
81+
Teacher teacher = this.findTeacher(1);
82+
83+
assertEquals(teacher.getId(), 1);
84+
assertEquals(teacher.getName(), "Foo");
85+
86+
// Our Teacher IS in shared cache
87+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 1L); // + 1
88+
89+
Student student = teacher.getStudent();
90+
91+
assertEquals(student.getId(), 1); // trigger lazy loading of our Student
92+
93+
// Our Student is NOT in shared cache
94+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 1L); // no change
95+
}
96+
97+
// 2 : We change our Teacher 1 in native SQL. The name and the linked student are changed.
98+
{
99+
EntityManager em = emf.createEntityManager();
100+
101+
EntityTransaction transaction = em.getTransaction();
102+
103+
transaction.begin();
104+
em.createNativeQuery("update TEACHER set NAME = ?, STUDENT_ID = ? where ID = ?")
105+
.setParameter(1, "Bar")
106+
.setParameter(2, 2)
107+
.setParameter(3, 1)
108+
.executeUpdate();
109+
transaction.commit();
110+
111+
em.close();
112+
}
113+
114+
// 3 : We want to get our Teacher 1 ONE MORE TIME. He is still supposed to be in the shared cache.
115+
// The Student linked to our Teacher should not be in the cache. (@Noncacheable)
116+
{
117+
Teacher teacher = this.findTeacher(1);
118+
119+
assertEquals(teacher.getId(), 1);
120+
assertEquals(teacher.getName(), "Foo");
121+
122+
// Our Teacher IS in shared cache
123+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 2L); // + 1
124+
125+
Student student = teacher.getStudent();
126+
127+
assertEquals(student.getId(), 1); // trigger lazy loading of our Student
128+
129+
// Our Student is NOT in shared cache
130+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 2L); // no change
131+
}
132+
133+
// 4 : Now we clear shared cache.
134+
{
135+
emf.getCache().evict(Teacher.class);
136+
}
137+
138+
// 5 : We want to get our Teacher 1 for the THIRD TIME. He is not in the shared cache anymore (invalidated)
139+
// Data should reflect our update from (2)
140+
{
141+
Teacher teacher = this.findTeacher(1);
142+
143+
assertEquals(teacher.getId(), 1);
144+
assertEquals(teacher.getName(), "Bar"); // Updated
145+
146+
// Our Teacher is NOT in shared cache
147+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 2L); // no change
148+
149+
Student student = teacher.getStudent();
150+
151+
assertEquals(student.getId(), 2); // trigger lazy loading of our Student
152+
153+
// Our Student is NOT in shared cache
154+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 2L); // no change
155+
}
156+
157+
// 6 : We want to get our Teacher 1 for the FOURTH TIME. He is back in the shared.
158+
// Data should reflect our update from (2)
159+
{
160+
Teacher teacher = this.findTeacher(1);
161+
162+
assertEquals(teacher.getId(), 1);
163+
assertEquals(teacher.getName(), "Bar"); // Updated
164+
165+
// Our Teacher IS in shared cache
166+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 3L); // + 1
167+
168+
Student student = teacher.getStudent();
169+
170+
// Before correction of bug 544202 this value was 1 because CacheKey.protectedForeignKeys was never updated
171+
assertEquals(student.getId(), 2); // trigger lazy loading of our Student
172+
173+
// Our Student is NOT in shared cache
174+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 3L); // no change
175+
}
176+
}
177+
178+
private Teacher findTeacher(int id) {
179+
final EntityManager em = this.emf.createEntityManager();
180+
Teacher teacher = em.find(Teacher.class, id);
181+
teacher.getStudent().getId();
182+
em.close();
183+
184+
return teacher;
185+
}
186+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0,
7+
* or the Eclipse Distribution License v. 1.0 which is available at
8+
* http://www.eclipse.org/org/documents/edl-v10.php.
9+
*
10+
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11+
*/
12+
13+
// Contributors:
14+
// 04/19/2020-3.0 Alexandre Jacob
15+
// - 544202: Fixed refresh of foreign key values when cacheKey was invalidated
16+
17+
package org.eclipse.persistence.jpa.test.sharedcache.model;
18+
19+
import jakarta.persistence.Entity;
20+
import jakarta.persistence.Id;
21+
22+
@Entity
23+
public class Student {
24+
25+
@Id
26+
private int id;
27+
28+
private String name;
29+
30+
public Student() {
31+
32+
}
33+
34+
public Student(int id, String name) {
35+
this.id = id;
36+
this.name = name;
37+
}
38+
39+
public int getId() {
40+
return id;
41+
}
42+
43+
public void setId(int id) {
44+
this.id = id;
45+
}
46+
47+
public String getName() {
48+
return name;
49+
}
50+
51+
public void setName(String name) {
52+
this.name = name;
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0,
7+
* or the Eclipse Distribution License v. 1.0 which is available at
8+
* http://www.eclipse.org/org/documents/edl-v10.php.
9+
*
10+
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11+
*/
12+
13+
// Contributors:
14+
// 04/19/2020-3.0 Alexandre Jacob
15+
// - 544202: Fixed refresh of foreign key values when cacheKey was invalidated
16+
17+
package org.eclipse.persistence.jpa.test.sharedcache.model;
18+
19+
import org.eclipse.persistence.annotations.Cache;
20+
import org.eclipse.persistence.annotations.Noncacheable;
21+
import org.eclipse.persistence.config.CacheIsolationType;
22+
23+
import jakarta.persistence.Cacheable;
24+
import jakarta.persistence.Entity;
25+
import jakarta.persistence.Id;
26+
import jakarta.persistence.ManyToOne;
27+
28+
@Entity
29+
@Cacheable(value = true)
30+
@Cache(isolation = CacheIsolationType.SHARED)
31+
public class Teacher {
32+
33+
@Id
34+
private int id;
35+
36+
private String name;
37+
38+
@Noncacheable
39+
@ManyToOne
40+
private Student student;
41+
42+
public Teacher() {
43+
44+
}
45+
46+
public Teacher(int id, String name, Student student) {
47+
this.id = id;
48+
this.name = name;
49+
this.student = student;
50+
}
51+
52+
public int getId() {
53+
return id;
54+
}
55+
56+
public void setId(int id) {
57+
this.id = id;
58+
}
59+
60+
public String getName() {
61+
return name;
62+
}
63+
64+
public void setName(String name) {
65+
this.name = name;
66+
}
67+
68+
public Student getStudent() {
69+
return student;
70+
}
71+
72+
public void setStudent(Student student) {
73+
this.student = student;
74+
}
75+
}

0 commit comments

Comments
 (0)