OPENJPA-181 : Fix class cast exception by passing along the StoreQuery context

whenever we pass around an Executor, so that the StoreQuery and Executor are
always matched.



git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@523046 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
A. Abram White 2007-03-27 19:24:54 +00:00
parent df02d831e2
commit f7aef45814
8 changed files with 211 additions and 55 deletions

View File

@ -108,11 +108,11 @@ public abstract class AbstractStoreQuery
implements Executor {
public Number executeDelete(StoreQuery q, Object[] params) {
return q.getContext().deleteInMemory(this, params);
return q.getContext().deleteInMemory(q, this, params);
}
public Number executeUpdate(StoreQuery q, Object[] params) {
return q.getContext().updateInMemory(this, params);
return q.getContext().updateInMemory(q, this, params);
}
public String[] getDataStoreActions(StoreQuery q, Object[] params,

View File

@ -403,17 +403,19 @@ public class DelegatingQuery
}
}
public Number deleteInMemory(StoreQuery.Executor ex, Object[] params) {
public Number deleteInMemory(StoreQuery q, StoreQuery.Executor ex,
Object[] params) {
try {
return _query.deleteInMemory(ex, params);
return _query.deleteInMemory(q, ex, params);
} catch (RuntimeException re) {
throw translate(re);
}
}
public Number updateInMemory(StoreQuery.Executor ex, Object[] params) {
public Number updateInMemory(StoreQuery q, StoreQuery.Executor ex,
Object[] params) {
try {
return _query.updateInMemory(ex, params);
return _query.updateInMemory(q, ex, params);
} catch (RuntimeException re) {
throw translate(re);
}

View File

@ -679,7 +679,7 @@ public class ExpressionStoreQuery
Number num = ((ExpressionStoreQuery) q).executeDelete(this, _meta,
_metas, _subs, _facts, _exps, params);
if (num == null)
return q.getContext().deleteInMemory(this, params);
return q.getContext().deleteInMemory(q, this, params);
return num;
}
@ -687,7 +687,7 @@ public class ExpressionStoreQuery
Number num = ((ExpressionStoreQuery) q).executeUpdate(this, _meta,
_metas, _subs, _facts, _exps, params);
if (num == null)
return q.getContext().updateInMemory(this, params);
return q.getContext().updateInMemory(q, this, params);
return num;
}

View File

@ -252,13 +252,15 @@ public interface QueryContext {
* Helper method to delete the objects found by executing a query on
* the given executor.
*/
public Number deleteInMemory(StoreQuery.Executor ex, Object[] params);
public Number deleteInMemory(StoreQuery q, StoreQuery.Executor ex,
Object[] params);
/**
* Helper method to update the objects found by executing a query on
* the given executor.
*/
public Number updateInMemory(StoreQuery.Executor ex, Object[] params);
public Number updateInMemory(StoreQuery q, StoreQuery.Executor ex,
Object[] params);
/**
* Helper method to instantiate the class with the given name, taking

View File

@ -558,7 +558,7 @@ public class QueryImpl
try {
assertOpen();
StoreQuery.Executor ex = compileForExecutor();
getResultPacker(ex);
getResultPacker(_storeQuery, ex);
ex.validate(_storeQuery);
} finally {
unlock();
@ -782,17 +782,17 @@ public class QueryImpl
StoreQuery.Executor ex = (isInMemory(operation))
? compileForInMemory(comp) : compileForDataStore(comp);
assertParameters(ex, params);
assertParameters(_storeQuery, ex, params);
if (_log.isTraceEnabled())
logExecution(operation, ex.getParameterTypes(_storeQuery),
params);
if (operation == OP_SELECT)
return execute(ex, params);
return execute(_storeQuery, ex, params);
if (operation == OP_DELETE)
return delete(ex, params);
return delete(_storeQuery, ex, params);
if (operation == OP_UPDATE)
return update(ex, params);
return update(_storeQuery, ex, params);
throw new UnsupportedException();
} catch (OpenJPAException ke) {
throw ke;
@ -826,16 +826,16 @@ public class QueryImpl
Object[] arr = (params.isEmpty()) ? StoreQuery.EMPTY_OBJECTS :
toParameterArray(ex.getParameterTypes(_storeQuery), params);
assertParameters(ex, arr);
assertParameters(_storeQuery, ex, arr);
if (_log.isTraceEnabled())
logExecution(operation, params);
if (operation == OP_SELECT)
return execute(ex, arr);
return execute(_storeQuery, ex, arr);
if (operation == OP_DELETE)
return delete(ex, arr);
return delete(_storeQuery, ex, arr);
if (operation == OP_UPDATE)
return update(ex, arr);
return update(_storeQuery, ex, arr);
throw new UnsupportedException();
} catch (OpenJPAException ke) {
throw ke;
@ -964,21 +964,22 @@ public class QueryImpl
* values. All other execute methods delegate to this one or to
* {@link #execute(StoreQuery.Executor,Map)} after validation and locking.
*/
private Object execute(StoreQuery.Executor ex, Object[] params)
private Object execute(StoreQuery q, StoreQuery.Executor ex,
Object[] params)
throws Exception {
// if this is an impossible result range, return null / empty list
StoreQuery.Range range = new StoreQuery.Range(_startIdx, _endIdx);
if (!_rangeSet)
ex.getRange(_storeQuery, params, range);
ex.getRange(q, params, range);
if (range.start >= range.end)
return emptyResult(ex);
return emptyResult(q, ex);
// execute; if we have a result class or we have only one result
// and so need to remove it from its array, wrap in a packing rop
range.lrs = isLRS(range.start, range.end);
ResultObjectProvider rop = ex.executeQuery(_storeQuery, params, range);
ResultObjectProvider rop = ex.executeQuery(q, params, range);
try {
return toResult(ex, rop, range);
return toResult(q, ex, rop, range);
} catch (Exception e) {
if (rop != null)
try { rop.close(); } catch (Exception e2) {}
@ -993,16 +994,16 @@ public class QueryImpl
* The return value will be a Number indicating the number of
* instances deleted.
*/
private Number delete(StoreQuery.Executor ex, Object[] params)
private Number delete(StoreQuery q, StoreQuery.Executor ex, Object[] params)
throws Exception {
assertBulkModify(ex, params);
return ex.executeDelete(_storeQuery, params);
assertBulkModify(q, ex, params);
return ex.executeDelete(q, params);
}
public Number deleteInMemory(StoreQuery.Executor executor,
public Number deleteInMemory(StoreQuery q, StoreQuery.Executor executor,
Object[] params) {
try {
Object o = execute(executor, params);
Object o = execute(q, executor, params);
if (!(o instanceof Collection))
o = Collections.singleton(o);
@ -1024,16 +1025,16 @@ public class QueryImpl
* The return value will be a Number indicating the number of
* instances updated.
*/
private Number update(StoreQuery.Executor ex, Object[] params)
private Number update(StoreQuery q, StoreQuery.Executor ex, Object[] params)
throws Exception {
assertBulkModify(ex, params);
return ex.executeUpdate(_storeQuery, params);
assertBulkModify(q, ex, params);
return ex.executeUpdate(q, params);
}
public Number updateInMemory(StoreQuery.Executor executor,
public Number updateInMemory(StoreQuery q, StoreQuery.Executor executor,
Object[] params) {
try {
Object o = execute(executor, params);
Object o = execute(q, executor, params);
if (!(o instanceof Collection))
o = Collections.singleton(o);
@ -1188,13 +1189,13 @@ public class QueryImpl
/**
* Return the query result for the given result object provider.
*/
protected Object toResult(StoreQuery.Executor ex, ResultObjectProvider rop,
StoreQuery.Range range)
protected Object toResult(StoreQuery q, StoreQuery.Executor ex,
ResultObjectProvider rop, StoreQuery.Range range)
throws Exception {
// pack projections if necessary
String[] aliases = ex.getProjectionAliases(_storeQuery);
if (!ex.isPacking(_storeQuery)) {
ResultPacker packer = getResultPacker(ex);
String[] aliases = ex.getProjectionAliases(q);
if (!ex.isPacking(q)) {
ResultPacker packer = getResultPacker(q, ex);
if (packer != null || aliases.length == 1)
rop = new PackingResultObjectProvider(rop, packer,
aliases.length);
@ -1202,15 +1203,14 @@ public class QueryImpl
// if single result, extract it
if (_unique == Boolean.TRUE || (aliases.length > 0
&& !ex.hasGrouping(_storeQuery) && ex.isAggregate(_storeQuery)))
&& !ex.hasGrouping(q) && ex.isAggregate(q)))
return singleResult(rop, range);
// now that we've executed the query, we can call isAggregate and
// hasGrouping efficiently
boolean detach = (_broker.getAutoDetach() &
AutoDetach.DETACH_NONTXREAD) > 0 && !_broker.isActive();
boolean lrs = range.lrs && !ex.isAggregate(_storeQuery)
&& !ex.hasGrouping(_storeQuery);
boolean lrs = range.lrs && !ex.isAggregate(q) && !ex.hasGrouping(q);
ResultList res = (!detach && lrs) ? _fc.newResultList(rop)
: new EagerResultList(rop);
@ -1228,23 +1228,23 @@ public class QueryImpl
/**
* Return a result packer for this projection, or null.
*/
private ResultPacker getResultPacker(StoreQuery.Executor ex) {
private ResultPacker getResultPacker(StoreQuery q, StoreQuery.Executor ex) {
if (_packer != null)
return _packer;
Class resultClass = (_resultClass != null) ? _resultClass
: ex.getResultClass(_storeQuery);
: ex.getResultClass(q);
if (resultClass == null)
return null;
String[] aliases = ex.getProjectionAliases(_storeQuery);
String[] aliases = ex.getProjectionAliases(q);
if (aliases.length == 0) {
// result class but no result; means candidate is being set
// into some result class
_packer = new ResultPacker(_class, getAlias(), resultClass);
} else if (resultClass != null) {
// projection
Class[] types = ex.getProjectionTypes(_storeQuery);
Class[] types = ex.getProjectionTypes(q);
_packer = new ResultPacker(types, aliases, resultClass);
}
return _packer;
@ -1253,9 +1253,9 @@ public class QueryImpl
/**
* Create an empty result for this query.
*/
private Object emptyResult(StoreQuery.Executor ex) {
private Object emptyResult(StoreQuery q, StoreQuery.Executor ex) {
if (_unique == Boolean.TRUE || (_unique == null
&& !ex.hasGrouping(_storeQuery) && ex.isAggregate(_storeQuery)))
&& !ex.hasGrouping(q) && ex.isAggregate(q)))
return null;
return Collections.EMPTY_LIST;
}
@ -1387,7 +1387,7 @@ public class QueryImpl
StoreQuery.Executor ex = compileForExecutor();
Object[] arr = toParameterArray(ex.getParameterTypes(_storeQuery),
params);
assertParameters(ex, arr);
assertParameters(_storeQuery, ex, arr);
StoreQuery.Range range = new StoreQuery.Range(_startIdx, _endIdx);
if (!_rangeSet)
ex.getRange(_storeQuery, arr, range);
@ -1636,14 +1636,15 @@ public class QueryImpl
* Check that we are in a state to be able to perform a bulk operation;
* also flush the current modfications if any elements are currently dirty.
*/
private void assertBulkModify(StoreQuery.Executor ex, Object[] params) {
private void assertBulkModify(StoreQuery q, StoreQuery.Executor ex,
Object[] params) {
_broker.assertActiveTransaction();
if (_startIdx != 0 || _endIdx != Long.MAX_VALUE)
throw new UserException(_loc.get("no-modify-range"));
if (_resultClass != null)
throw new UserException(_loc.get("no-modify-resultclass"));
StoreQuery.Range range = new StoreQuery.Range();
ex.getRange(_storeQuery, params, range);
ex.getRange(q, params, range);
if (range.start != 0 || range.end != Long.MAX_VALUE)
throw new UserException(_loc.get("no-modify-range"));
}
@ -1651,11 +1652,12 @@ public class QueryImpl
/**
* Checks that the passed parameters match the declarations.
*/
protected void assertParameters(StoreQuery.Executor ex, Object[] params) {
if (!_storeQuery.requiresParameterDeclarations())
protected void assertParameters(StoreQuery q, StoreQuery.Executor ex,
Object[] params) {
if (!q.requiresParameterDeclarations())
return;
LinkedMap paramTypes = ex.getParameterTypes(_storeQuery);
LinkedMap paramTypes = ex.getParameterTypes(q);
int typeCount = paramTypes.size();
if (typeCount > params.length)
throw new UserException(_loc.get("unbound-params",

View File

@ -0,0 +1,42 @@
/*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed 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.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class CascadeChild {
@Id
@GeneratedValue
private long id;
private String name;
public long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed 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.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
@Entity
public class CascadeParent {
@Id
@GeneratedValue
private long id;
private String name;
@OneToOne(cascade=CascadeType.ALL)
private CascadeChild child;
public long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public CascadeChild getChild() {
return child;
}
public void setChild(CascadeChild child) {
this.child = child;
}
}

View File

@ -2,6 +2,7 @@ package org.apache.openjpa.persistence.datacache;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAPersistence;
@ -14,7 +15,7 @@ public class TestBulkJPQLAndDataCache
private Object oid;
public TestBulkJPQLAndDataCache() {
super(AllFieldTypes.class);
super(AllFieldTypes.class, CascadeParent.class, CascadeChild.class);
}
@Override
@ -43,6 +44,24 @@ public class TestBulkJPQLAndDataCache
em.close();
}
public void tearDown()
throws Exception {
if (emf == null)
return;
try {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.createQuery("DELETE FROM AllFieldTypes").executeUpdate();
em.createQuery("DELETE FROM CascadeParent").executeUpdate();
em.createQuery("DELETE FROM CascadeChild").executeUpdate();
em.getTransaction().commit();
em.close();
} catch (Exception e) {
}
super.tearDown();
}
public void testBulkDelete() {
OpenJPAEntityManager em =
OpenJPAPersistence.cast(emf.createEntityManager());
@ -88,4 +107,38 @@ public class TestBulkJPQLAndDataCache
em.close();
}
public void testBulkDeleteOfCascadingEntity() {
CascadeParent parent = new CascadeParent();
parent.setName("parent");
CascadeChild child = new CascadeChild();
child.setName("child");
parent.setChild(child);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(parent);
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
assertEquals(1, em.createQuery("SELECT o FROM CascadeParent o").
getResultList().size());
assertEquals(1, em.createQuery("SELECT o FROM CascadeChild o").
getResultList().size());
em.close();
em = emf.createEntityManager();
em.getTransaction().begin();
em.createQuery("DELETE FROM CascadeParent o").executeUpdate();
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
assertEquals(0, em.createQuery("SELECT o FROM CascadeParent o").
getResultList().size());
assertEquals(0, em.createQuery("SELECT o FROM CascadeChild o").
getResultList().size());
em.close();
}
}