Skip to content

Commit 303816b

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

File tree

4 files changed

+330
-5
lines changed

4 files changed

+330
-5
lines changed

foundation/org.eclipse.persistence.core/src/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, 2018 Oracle and/or its affiliates. All rights reserved.
3-
* Copyright (c) 1998, 2018 IBM Corporation. All rights reserved.
2+
* Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 1998, 2019 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+
// 07/02/2019-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.*;
@@ -988,6 +990,15 @@ protected Object buildObject(boolean returnCacheKey, ObjectBuildingQuery query,
988990

989991
FetchGroup fetchGroup = query.getExecutionFetchGroup(concreteDescriptor);
990992

993+
// 544202: cachedForeignKeys should be refreshed when cacheKey was invalidated
994+
if (domainWasMissing || shouldRetrieveBypassCache
995+
|| cacheKey.getInvalidationState() == CacheKey.CACHE_KEY_INVALID) {
996+
997+
if (isProtected && (cacheKey != null)) {
998+
cacheForeignKeyValues(databaseRow, cacheKey, session);
999+
}
1000+
}
1001+
9911002
if (domainWasMissing || shouldRetrieveBypassCache) {
9921003
cacheHit = false;
9931004
if (domainObject == null || shouldStoreBypassCache) {
@@ -1016,9 +1027,6 @@ protected Object buildObject(boolean returnCacheKey, ObjectBuildingQuery query,
10161027
cacheKey.setObject(domainObject);
10171028
}
10181029
concreteObjectBuilder.buildAttributesIntoObject(domainObject, cacheKey, databaseRow, query, joinManager, fetchGroup, false, session);
1019-
if (isProtected && (cacheKey != null)) {
1020-
cacheForeignKeyValues(databaseRow, cacheKey, session);
1021-
}
10221030
if (shouldMaintainCache && !shouldStoreBypassCache) {
10231031
// Set the fetch group to the domain object, after built.
10241032
if ((fetchGroup != null) && concreteDescriptor.hasFetchGroupManager()) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/********************************************************************************
2+
* Copyright (c) 2019 IBM Corporation. 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+
// 07/02/2019-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 javax.persistence.EntityManager;
22+
import javax.persistence.EntityManagerFactory;
23+
import javax.persistence.EntityTransaction;
24+
25+
import org.eclipse.persistence.jpa.JpaEntityManager;
26+
import org.eclipse.persistence.jpa.test.framework.DDLGen;
27+
import org.eclipse.persistence.jpa.test.framework.Emf;
28+
import org.eclipse.persistence.jpa.test.framework.EmfRunner;
29+
import org.eclipse.persistence.jpa.test.framework.Property;
30+
import org.eclipse.persistence.jpa.test.sharedcache.model.Student;
31+
import org.eclipse.persistence.jpa.test.sharedcache.model.Teacher;
32+
import org.eclipse.persistence.sessions.Session;
33+
import org.eclipse.persistence.sessions.SessionProfiler;
34+
import org.eclipse.persistence.tools.profiler.PerformanceMonitor;
35+
import org.junit.Before;
36+
import org.junit.Test;
37+
import org.junit.runner.RunWith;
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 Session session;
52+
53+
private PerformanceMonitor performanceMonitor;
54+
55+
@Before
56+
public void setup() {
57+
session = ((JpaEntityManager) emf.createEntityManager()).getServerSession();
58+
performanceMonitor = (PerformanceMonitor) session.getProfiler();
59+
60+
// We populate DB with 2 students and 1 teacher
61+
Student student1 = new Student(1, "Picasso");
62+
Student student2 = new Student(2, "Pablo");
63+
64+
Teacher teacher = new Teacher(1, "Foo", student1);
65+
66+
final EntityManager em = this.emf.createEntityManager();
67+
final EntityTransaction transaction = em.getTransaction();
68+
69+
transaction.begin();
70+
em.persist(student1);
71+
em.persist(student2);
72+
em.persist(teacher);
73+
transaction.commit();
74+
75+
em.close();
76+
}
77+
78+
@Test
79+
public void testSharedCacheWithNonCacheable() throws Exception {
80+
// 1 : We want to get our Teacher 1. He is supposed to be in the shared cache.
81+
// The Student linked to our Teacher should not be in the cache. (@Noncacheable)
82+
{
83+
Teacher teacher = this.findTeacher(1);
84+
85+
assertEquals(teacher.getId(), 1);
86+
assertEquals(teacher.getName(), "Foo");
87+
88+
// Our Teacher IS in shared cache
89+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 1L); // + 1
90+
91+
Student student = teacher.getStudent();
92+
93+
assertEquals(student.getId(), 1); // trigger lazy loading of our Student
94+
95+
// Our Student is NOT in shared cache
96+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 1L); // no change
97+
}
98+
99+
// 2 : We change our Teacher 1 in native SQL. The name and the linked student are changed.
100+
{
101+
EntityManager em = emf.createEntityManager();
102+
103+
EntityTransaction transaction = em.getTransaction();
104+
105+
transaction.begin();
106+
em.createNativeQuery("update TEACHER set NAME = ?, STUDENT_ID = ? where ID = ?")
107+
.setParameter(1, "Bar")
108+
.setParameter(2, 2)
109+
.setParameter(3, 1)
110+
.executeUpdate();
111+
transaction.commit();
112+
113+
em.close();
114+
}
115+
116+
// 3 : We want to get our Teacher 1 ONE MORE TIME. He is still supposed to be in the shared cache.
117+
// The Student linked to our Teacher should not be in the cache. (@Noncacheable)
118+
{
119+
Teacher teacher = this.findTeacher(1);
120+
121+
assertEquals(teacher.getId(), 1);
122+
assertEquals(teacher.getName(), "Foo");
123+
124+
// Our Teacher IS in shared cache
125+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 2L); // + 1
126+
127+
Student student = teacher.getStudent();
128+
129+
assertEquals(student.getId(), 1); // trigger lazy loading of our Student
130+
131+
// Our Student is NOT in shared cache
132+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 2L); // no change
133+
}
134+
135+
// 4 : Now we clear shared cache.
136+
{
137+
emf.getCache().evict(Teacher.class);
138+
}
139+
140+
// 5 : We want to get our Teacher 1 for the THIRD TIME. He is not in the shared cache anymore (invalidated)
141+
// Data should reflect our update from (2)
142+
{
143+
Teacher teacher = this.findTeacher(1);
144+
145+
assertEquals(teacher.getId(), 1);
146+
assertEquals(teacher.getName(), "Bar"); // Updated
147+
148+
// Our Teacher is NOT in shared cache
149+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 2L); // no change
150+
151+
Student student = teacher.getStudent();
152+
153+
assertEquals(student.getId(), 2); // trigger lazy loading of our Student
154+
155+
// Our Student is NOT in shared cache
156+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 2L); // no change
157+
}
158+
159+
// 6 : We want to get our Teacher 1 for the FOURTH TIME. He is back in the shared.
160+
// Data should reflect our update from (2)
161+
{
162+
Teacher teacher = this.findTeacher(1);
163+
164+
assertEquals(teacher.getId(), 1);
165+
assertEquals(teacher.getName(), "Bar"); // Updated
166+
167+
// Our Teacher IS in shared cache
168+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 3L); // + 1
169+
170+
Student student = teacher.getStudent();
171+
172+
// Before correction of bug 544202 this value was 1 because CacheKey.protectedForeignKeys was never updated
173+
assertEquals(student.getId(), 2); // trigger lazy loading of our Student
174+
175+
// Our Student is NOT in shared cache
176+
assertEquals(performanceMonitor.getOperationTime(SessionProfiler.CacheHits), 3L); // no change
177+
}
178+
}
179+
180+
private Teacher findTeacher(int id) {
181+
final EntityManager em = this.emf.createEntityManager();
182+
Teacher teacher = em.find(Teacher.class, 1);
183+
teacher.getStudent().getId();
184+
em.close();
185+
186+
return teacher;
187+
}
188+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/********************************************************************************
2+
* Copyright (c) 2019 IBM Corporation. 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+
// 07/02/2019-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 javax.persistence.Entity;
20+
import javax.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) 2019 IBM Corporation. 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+
// 07/02/2019-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 javax.persistence.Cacheable;
20+
import javax.persistence.Entity;
21+
import javax.persistence.Id;
22+
import javax.persistence.ManyToOne;
23+
24+
import org.eclipse.persistence.annotations.Cache;
25+
import org.eclipse.persistence.annotations.Noncacheable;
26+
import org.eclipse.persistence.config.CacheIsolationType;
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)