| /* |
| * 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.jdbc.query.cache; |
| |
| import javax.persistence.EntityManager; |
| import javax.persistence.Query; |
| |
| import org.apache.openjpa.datacache.ConcurrentQueryCache; |
| import org.apache.openjpa.jdbc.conf.JDBCConfiguration; |
| import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory; |
| import org.apache.openjpa.persistence.OpenJPAPersistence; |
| import org.apache.openjpa.persistence.QueryResultCacheImpl; |
| import org.apache.openjpa.persistence.test.SingleEMFTestCase; |
| import org.apache.openjpa.util.CacheMap; |
| |
| public abstract class AbstractQueryCacheTest extends SingleEMFTestCase { |
| private Class[] entityClassTypes = { Part.class, PartBase.class, |
| PartComposite.class, Supplier.class, Usage.class}; |
| |
| protected boolean deleteData = false; |
| protected boolean recreateData = true; |
| |
| @Override |
| public void setUp(Object... props) { |
| int arrLen = entityClassTypes.length + props.length; |
| Object args[] = new Object[arrLen]; |
| |
| // Add the entity class types supported by this testing |
| int idx = 0; |
| for (Class clazz : entityClassTypes) { |
| args[idx++] = clazz; |
| } |
| |
| // Add the property parameters passed in by the subclass |
| for (Object obj : props) { |
| args[idx++] = obj; |
| } |
| |
| // Invoke superclass' implementation of setUp()... |
| super.setUp(args); |
| |
| // Not all databases support GenerationType.IDENTITY column(s) |
| if (checkSupportsIdentityGenerationType() && recreateData) { |
| // deletes any data leftover data in the database due to the failed |
| // last run of this testcase |
| deleteAllData(); |
| reCreateData(); |
| } |
| } |
| |
| @Override |
| public void tearDown() throws Exception { |
| if (deleteData) { |
| deleteAllData(); |
| } |
| super.tearDown(); |
| } |
| |
| /** |
| * Populate the database with data that is consumable by the query cache |
| * tests. |
| * |
| */ |
| protected void reCreateData() { |
| EntityManager em = emf.createEntityManager(); |
| em.getTransaction().begin(); |
| |
| Supplier s1 = new Supplier(1, "S1"); |
| em.persist(s1); |
| Supplier s2 = new Supplier(2, "S2"); |
| em.persist(s2); |
| Supplier s3 = new Supplier(3, "S3"); |
| em.persist(s3); |
| |
| PartBase p1 = new PartBase(10, "Wheel", 150, 15.00); |
| em.persist(p1); |
| PartBase p2 = new PartBase(11, "Frame", 550.00, 25.00); |
| em.persist(p2); |
| PartBase p3 = new PartBase(12, "HandleBar", 125.00, 80.00); |
| em.persist(p3); |
| |
| s1.addPart(p1).addPart(p2).addPart(p3); |
| s2.addPart(p1).addPart(p3); |
| |
| PartComposite p4 = new PartComposite(20, "Bike", 180, 1.0); |
| em.persist(p4); |
| p4.addSubPart(em, 2, p1).addSubPart(em, 1, p2).addSubPart(em, 1, p3); |
| |
| em.getTransaction().commit(); |
| em.close(); |
| } |
| |
| /** |
| * Remove all rows from the database. |
| * |
| */ |
| protected void deleteAllData() { |
| EntityManager em = emf.createEntityManager(); |
| em.getTransaction().begin(); |
| |
| em.createNativeQuery("delete from Supplier_Part").executeUpdate(); |
| em.createQuery("delete from PartBase s").executeUpdate(); |
| em.createQuery("delete from Supplier s").executeUpdate(); |
| em.createQuery("delete from Usage u").executeUpdate(); |
| em.createQuery("delete from Part p").executeUpdate(); |
| |
| em.getTransaction().commit(); |
| em.close(); |
| } |
| |
| /** |
| * Populate the query cache with 35 entries. |
| * |
| */ |
| protected void loadQueryCache() { |
| EntityManager em = emf.createEntityManager(); |
| em.getTransaction().begin(); |
| String qry = "select p from PartBase p where p.cost > ?1"; |
| for (int i=120; i<155; i++) { |
| Query q = em.createQuery(qry); |
| q.setParameter(1, (double) i); |
| q.getResultList(); |
| } |
| em.getTransaction().commit(); |
| em.close(); |
| } |
| |
| /** |
| * Update an entity that is associated with the queries in the |
| * query cache. |
| * |
| */ |
| protected void updateAnEntity() { |
| EntityManager em = emf.createEntityManager(); |
| em.getTransaction().begin(); |
| |
| // Update entity |
| PartBase p = em.find(PartBase.class,11); |
| double oldcost = p.getCost(); |
| if (p != null) { |
| p.setCost((oldcost + 10.0)); |
| } |
| |
| em.getTransaction().commit(); |
| em.close(); |
| } |
| |
| /** |
| * Fetches a reference to the EntityManagerFactory's query cache |
| * object. |
| * |
| */ |
| protected ConcurrentQueryCache getQueryCache() { |
| OpenJPAEntityManagerFactory oemf = OpenJPAPersistence.cast(emf); |
| QueryResultCacheImpl scache = |
| (QueryResultCacheImpl) oemf.getQueryResultCache(); |
| |
| return (ConcurrentQueryCache) scache.getDelegate(); |
| } |
| |
| /** |
| * Returns the current size of the EntityManagerFactory's |
| * query cache. |
| * |
| */ |
| protected int queryCacheGet() { |
| ConcurrentQueryCache dcache = getQueryCache(); |
| CacheMap map = dcache.getCacheMap(); |
| return map.size(); |
| } |
| |
| /** |
| * Returns true if the database supports GenerationType.IDENTITY column(s), |
| * false if it does not. |
| * |
| */ |
| protected boolean checkSupportsIdentityGenerationType() { |
| return (((JDBCConfiguration) emf.getConfiguration()). |
| getDBDictionaryInstance().supportsAutoAssign); |
| } |
| |
| /* |
| * Common tests -- the following tests are common to any eviction policy |
| * used by OpenJPA's query cache implementation. |
| * |
| */ |
| |
| /** |
| * Tests the query cache eviction function with the following test logic: |
| * |
| * 1) Populate the query cache with the entries supplied by loadQueryCache() |
| * 2) Sleep 20ms (to avoid race conditions with the Timestamp eviction |
| * policy.) |
| * 3) Update one of the entities associated with the cached queries, in |
| * order to dirty the query cache |
| * 4) Insert a row into the database via native queries (this approach |
| * avoids dirtying the query cache, ensuring step #3 remains the |
| * control step.) |
| * 5) With a common criteria of all part entities with a cost > 120, |
| * examine the query result list sizes from both a native query |
| * select invocation, and a JPA query invocation. Because the |
| * query cache was dirtied by step #3, the JPA query should invoke |
| * a fresh SELECT operation on the database. |
| * |
| */ |
| public void testEviction() { |
| // Not all databases support GenerationType.IDENTITY column(s) |
| if (!checkSupportsIdentityGenerationType()) { |
| return; |
| } |
| |
| loadQueryCache(); |
| try { |
| Thread.sleep(20); |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| updateAnEntity(); |
| |
| EntityManager em = emf.createEntityManager(); |
| em.getTransaction().begin(); |
| |
| String insert1 = |
| "insert into Part(partno,parttype,name,cost,mass)" + |
| " values(13,'PartBase','breakes',1000.0,100.0)"; |
| em.createNativeQuery(insert1).executeUpdate(); |
| String insert2 = |
| "insert into Supplier_Part(suppliers_sid,supplies_partno)" + |
| " values(1,13)"; |
| em.createNativeQuery(insert2).executeUpdate(); |
| |
| em.getTransaction().commit(); |
| em.close(); |
| |
| em = emf.createEntityManager(); |
| em.getTransaction().begin(); |
| |
| String sql = "select partno from Part where cost > 120 "; |
| Query nativeq = em.createNativeQuery(sql); |
| int nativelistSize = nativeq.getResultList().size(); |
| |
| em.getTransaction().commit(); |
| em.close(); |
| |
| em = emf.createEntityManager(); |
| em.getTransaction().begin(); |
| Query q = em.createQuery("select p from PartBase p where p.cost>?1"); |
| q.setParameter(1, 120.0); |
| int jpalistSize = q.getResultList().size(); |
| |
| em.getTransaction().commit(); |
| em.close(); |
| |
| // The resultlist of nativelist and jpalist should be the same |
| // in both eviction policies(dafault/timestamp) |
| assertEquals(nativelistSize,jpalistSize); |
| |
| this.deleteData = true; |
| this.recreateData = true; |
| } |
| } |