From 7c377a90434f30c555a60bf2d9f5345d798b8bd2 Mon Sep 17 00:00:00 2001 From: "Richard G. Curtis" Date: Wed, 27 Oct 2010 14:47:44 +0000 Subject: [PATCH] OPENJPA-1856: Executing bulk updates should evict from DataCache. git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1027983 13f79535-47bb-0310-9956-ffa450edef68 --- .../openjpa/kernel/AbstractStoreQuery.java | 23 ++++- .../openjpa/kernel/ExpressionStoreQuery.java | 39 ++++++-- .../datacache/CachedEntityStatistics.java | 4 +- .../TestBulkUpdatesDataCacheEviction.java | 98 +++++++++++++++++++ 4 files changed, 150 insertions(+), 14 deletions(-) create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestBulkUpdatesDataCacheEviction.java diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java index 2abe5de0e..8b34f9e1e 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java @@ -21,6 +21,7 @@ package org.apache.openjpa.kernel; import java.util.Map; import org.apache.commons.collections.map.LinkedMap; +import org.apache.openjpa.datacache.DataCache; import org.apache.openjpa.kernel.exps.AggregateListener; import org.apache.openjpa.kernel.exps.FilterListener; import org.apache.openjpa.kernel.exps.QueryExpressions; @@ -121,11 +122,29 @@ public abstract class AbstractStoreQuery implements Executor { public Number executeDelete(StoreQuery q, Object[] params) { - return q.getContext().deleteInMemory(q, this, params); + try { + return q.getContext().deleteInMemory(q, this, params); + } finally { + for (ClassMetaData cmd : getAccessPathMetaDatas(q)) { + DataCache cache = cmd.getDataCache(); + if (cache != null) { + cache.removeAll(cmd.getDescribedType(), true); + } + } + } } public Number executeUpdate(StoreQuery q, Object[] params) { - return q.getContext().updateInMemory(q, this, params); + try { + return q.getContext().updateInMemory(q, this, params); + } finally { + for (ClassMetaData cmd : getAccessPathMetaDatas(q)) { + DataCache cache = cmd.getDataCache(); + if (cache != null) { + cache.removeAll(cmd.getDescribedType(), true); + } + } + } } public String[] getDataStoreActions(StoreQuery q, Object[] params, diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java index 3d8761463..78906042b 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ExpressionStoreQuery.java @@ -31,6 +31,7 @@ import java.util.Set; import org.apache.commons.collections.map.LinkedMap; import org.apache.openjpa.conf.OpenJPAConfiguration; +import org.apache.openjpa.datacache.DataCache; import org.apache.openjpa.kernel.exps.Subquery; import org.apache.openjpa.kernel.exps.AbstractExpressionVisitor; import org.apache.openjpa.kernel.exps.AggregateListener; @@ -778,19 +779,37 @@ public class ExpressionStoreQuery } public Number executeDelete(StoreQuery q, Object[] params) { - Number num = ((ExpressionStoreQuery) q).executeDelete(this, _meta, - _metas, _subs, _facts, _exps, params); - if (num == null) - return q.getContext().deleteInMemory(q, this, params); - return num; + try { + Number num = + ((ExpressionStoreQuery) q).executeDelete(this, _meta, _metas, _subs, _facts, _exps, params); + if (num == null) + return q.getContext().deleteInMemory(q, this, params); + return num; + } finally { + for (ClassMetaData cmd : getAccessPathMetaDatas(q)) { + DataCache cache = cmd.getDataCache(); + if (cache != null) { + cache.removeAll(cmd.getDescribedType(), true); + } + } + } } public Number executeUpdate(StoreQuery q, Object[] params) { - Number num = ((ExpressionStoreQuery) q).executeUpdate(this, _meta, - _metas, _subs, _facts, _exps, params); - if (num == null) - return q.getContext().updateInMemory(q, this, params); - return num; + try { + Number num = + ((ExpressionStoreQuery) q).executeUpdate(this, _meta, _metas, _subs, _facts, _exps, params); + if (num == null) + return q.getContext().updateInMemory(q, this, params); + return num; + } finally { + for (ClassMetaData cmd : getAccessPathMetaDatas(q)) { + DataCache cache = cmd.getDataCache(); + if (cache != null) { + cache.removeAll(cmd.getDescribedType(), true); + } + } + } } public String[] getDataStoreActions(StoreQuery q, Object[] params, diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/CachedEntityStatistics.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/CachedEntityStatistics.java index 6ed30d1b4..803edc07e 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/CachedEntityStatistics.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/CachedEntityStatistics.java @@ -46,11 +46,11 @@ public class CachedEntityStatistics { @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) Set lazyList = new HashSet(); + String firstName, lastName; + public CachedEntityStatistics() { } - private String firstName, lastName; - public int getId() { return id; } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestBulkUpdatesDataCacheEviction.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestBulkUpdatesDataCacheEviction.java new file mode 100644 index 000000000..6fd88d291 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestBulkUpdatesDataCacheEviction.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.datacache; + +import javax.persistence.Cache; +import javax.persistence.EntityManager; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +public class TestBulkUpdatesDataCacheEviction extends SingleEMFTestCase { + Object[] props = new Object[] { CLEAR_TABLES, CachedEntityStatistics.class, "openjpa.DataCache", "true" }; + + public void setUp() throws Exception { + super.setUp(props); + } + + /** + * This test ensures that after executing an update against the db, the updated type is purged from + * the DataCache. + */ + public void testUpdate() throws Exception { + EntityManager em = emf.createEntityManager(); + Cache cache = emf.getCache(); + try { + CachedEntityStatistics e = createEntity(em); + assertTrue(cache.contains(CachedEntityStatistics.class, e.getId())); + em.clear(); + + String update = "UPDATE CachedEntityStatistics s SET s.firstName = :name WHERE s.id = :id"; + String name = "name_" + System.currentTimeMillis(); + // execute update, this should result in a cache eviction + em.getTransaction().begin(); + assertEquals(1, em.createQuery(update).setParameter("name", name).setParameter("id", e.getId()) + .executeUpdate()); + em.getTransaction().commit(); + assertFalse(cache.contains(CachedEntityStatistics.class, e.getId())); + + CachedEntityStatistics postUpdate = em.find(CachedEntityStatistics.class, e.getId()); + assertEquals(name, postUpdate.getFirstName()); + + } finally { + em.close(); + } + } + + /** + * This test ensures that after executing a delete against the db, the deleted type is purged from + * the DataCache. + */ + public void testDelete() throws Exception { + EntityManager em = emf.createEntityManager(); + Cache cache = emf.getCache(); + try { + CachedEntityStatistics e = createEntity(em); + assertTrue(cache.contains(CachedEntityStatistics.class, e.getId())); + em.clear(); + + String delete = "DELETE FROM CachedEntityStatistics s WHERE s.id = :id"; + // execute update, this should result in a cache eviction + em.getTransaction().begin(); + assertEquals(1, em.createQuery(delete).setParameter("id", e.getId()).executeUpdate()); + em.getTransaction().commit(); + assertFalse(cache.contains(CachedEntityStatistics.class, e.getId())); + + CachedEntityStatistics postUpdate = em.find(CachedEntityStatistics.class, e.getId()); + assertNull(postUpdate); + + } finally { + em.close(); + } + } + + private CachedEntityStatistics createEntity(EntityManager em) { + em.getTransaction().begin(); + CachedEntityStatistics e = new CachedEntityStatistics(); + em.persist(e); + em.getTransaction().commit(); + + return e; + } + +}