mirror of https://github.com/apache/openjpa.git
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:
parent
df02d831e2
commit
f7aef45814
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue