mirror of https://github.com/apache/openjpa.git
Optimize queries of the form "select e from ... where e.rel.id = :x" to not join
across "rel" for std fk->pk joins. git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@462646 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f27898f280
commit
6937856e2b
|
@ -27,6 +27,7 @@ import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||||
import org.apache.openjpa.jdbc.meta.FieldMapping;
|
import org.apache.openjpa.jdbc.meta.FieldMapping;
|
||||||
import org.apache.openjpa.jdbc.meta.ValueMapping;
|
import org.apache.openjpa.jdbc.meta.ValueMapping;
|
||||||
import org.apache.openjpa.jdbc.schema.Column;
|
import org.apache.openjpa.jdbc.schema.Column;
|
||||||
|
import org.apache.openjpa.jdbc.schema.ForeignKey;
|
||||||
import org.apache.openjpa.jdbc.schema.Schemas;
|
import org.apache.openjpa.jdbc.schema.Schemas;
|
||||||
import org.apache.openjpa.jdbc.sql.Joins;
|
import org.apache.openjpa.jdbc.sql.Joins;
|
||||||
import org.apache.openjpa.jdbc.sql.Result;
|
import org.apache.openjpa.jdbc.sql.Result;
|
||||||
|
@ -352,6 +353,7 @@ class PCPath
|
||||||
Action action;
|
Action action;
|
||||||
Variable var;
|
Variable var;
|
||||||
Iterator itr = (_actions == null) ? null : _actions.iterator();
|
Iterator itr = (_actions == null) ? null : _actions.iterator();
|
||||||
|
FieldMapping field;
|
||||||
while (itr != null && itr.hasNext()) {
|
while (itr != null && itr.hasNext()) {
|
||||||
action = (Action) itr.next();
|
action = (Action) itr.next();
|
||||||
|
|
||||||
|
@ -369,8 +371,18 @@ class PCPath
|
||||||
rel.getTable());
|
rel.getTable());
|
||||||
} else {
|
} else {
|
||||||
// move past the previous field, if any
|
// move past the previous field, if any
|
||||||
if (pstate.field != null)
|
field = (FieldMapping) action.data;
|
||||||
|
if (pstate.field != null) {
|
||||||
|
// if this is the second-to-last field and the last is
|
||||||
|
// the related field this field joins to, no need to
|
||||||
|
// traverse: just use this field's fk columns
|
||||||
|
if (!itr.hasNext() && (flags & JOIN_REL) == 0
|
||||||
|
&& isJoinedField(pstate.field, key, field)) {
|
||||||
|
pstate.cmpfield = field;
|
||||||
|
break;
|
||||||
|
}
|
||||||
rel = traverseField(pstate, key, forceOuter, false);
|
rel = traverseField(pstate, key, forceOuter, false);
|
||||||
|
}
|
||||||
|
|
||||||
// mark if the next traversal should go through
|
// mark if the next traversal should go through
|
||||||
// the key rather than value
|
// the key rather than value
|
||||||
|
@ -378,7 +390,7 @@ class PCPath
|
||||||
forceOuter |= action.op == Action.GET_OUTER;
|
forceOuter |= action.op == Action.GET_OUTER;
|
||||||
|
|
||||||
// get mapping for the current field
|
// get mapping for the current field
|
||||||
pstate.field = (FieldMapping) action.data;
|
pstate.field = field;
|
||||||
owner = pstate.field.getDefiningMapping();
|
owner = pstate.field.getDefiningMapping();
|
||||||
if (pstate.field.getManagement()
|
if (pstate.field.getManagement()
|
||||||
!= FieldMapping.MANAGE_PERSISTENT)
|
!= FieldMapping.MANAGE_PERSISTENT)
|
||||||
|
@ -420,15 +432,50 @@ class PCPath
|
||||||
return pstate;
|
return pstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether the given source field joins to the given target field.
|
||||||
|
*/
|
||||||
|
private static boolean isJoinedField(FieldMapping src, boolean key,
|
||||||
|
FieldMapping target) {
|
||||||
|
ValueMapping vm;
|
||||||
|
switch (src.getTypeCode()) {
|
||||||
|
case JavaTypes.ARRAY:
|
||||||
|
case JavaTypes.COLLECTION:
|
||||||
|
vm = src.getElementMapping();
|
||||||
|
break;
|
||||||
|
case JavaTypes.MAP:
|
||||||
|
vm = (key) ? src.getKeyMapping() : src.getElementMapping();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
vm = src;
|
||||||
|
}
|
||||||
|
if (vm.getJoinDirection() != ValueMapping.JOIN_FORWARD)
|
||||||
|
return false;
|
||||||
|
ForeignKey fk = vm.getForeignKey();
|
||||||
|
if (fk == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// foreign key must join to target columns
|
||||||
|
Column[] rels = fk.getColumns();
|
||||||
|
Column[] pks = target.getColumns();
|
||||||
|
if (rels.length != pks.length)
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < rels.length; i++)
|
||||||
|
if (fk.getPrimaryKeyColumn(rels[i]) != pks[i])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expression state.
|
* Expression state.
|
||||||
*/
|
*/
|
||||||
private static class PathExpState
|
private static class PathExpState
|
||||||
extends ExpState {
|
extends ExpState {
|
||||||
|
|
||||||
private FieldMapping field = null;
|
public FieldMapping field = null;
|
||||||
private Column[] cols = null;
|
public FieldMapping cmpfield = null;
|
||||||
private boolean joinedRel = false;
|
public Column[] cols = null;
|
||||||
|
public boolean joinedRel = false;
|
||||||
|
|
||||||
public PathExpState(Joins joins) {
|
public PathExpState(Joins joins) {
|
||||||
super(joins);
|
super(joins);
|
||||||
|
@ -483,15 +530,16 @@ class PCPath
|
||||||
public Object toDataStoreValue(Select sel, ExpContext ctx, ExpState state,
|
public Object toDataStoreValue(Select sel, ExpContext ctx, ExpState state,
|
||||||
Object val) {
|
Object val) {
|
||||||
PathExpState pstate = (PathExpState) state;
|
PathExpState pstate = (PathExpState) state;
|
||||||
if (pstate.field != null) {
|
FieldMapping field = (pstate.cmpfield != null) ? pstate.cmpfield
|
||||||
|
: pstate.field;
|
||||||
|
if (field != null) {
|
||||||
if (_key)
|
if (_key)
|
||||||
return pstate.field.toKeyDataStoreValue(val, ctx.store);
|
return field.toKeyDataStoreValue(val, ctx.store);
|
||||||
if (pstate.field.getElement().getDeclaredTypeCode()
|
if (field.getElement().getDeclaredTypeCode() != JavaTypes.OBJECT)
|
||||||
!= JavaTypes.OBJECT)
|
return field.toDataStoreValue(val, ctx.store);
|
||||||
return pstate.field.toDataStoreValue(val, ctx.store);
|
|
||||||
|
|
||||||
val = pstate.field.getExternalValue(val, ctx.store.getContext());
|
val = field.getExternalValue(val, ctx.store.getContext());
|
||||||
return pstate.field.toDataStoreValue(val, ctx.store);
|
return field.toDataStoreValue(val, ctx.store);
|
||||||
}
|
}
|
||||||
return _class.toDataStoreValue(val, _class.getPrimaryKeyColumns(),
|
return _class.toDataStoreValue(val, _class.getPrimaryKeyColumns(),
|
||||||
ctx.store);
|
ctx.store);
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class RelationStrategies {
|
||||||
public static Object toDataStoreValue(ValueMapping vm, Object val,
|
public static Object toDataStoreValue(ValueMapping vm, Object val,
|
||||||
JDBCStore store) {
|
JDBCStore store) {
|
||||||
ClassMapping rel;
|
ClassMapping rel;
|
||||||
if (val == null || val.getClass() == vm.getType())
|
if (val == null || val.getClass() == vm.getType())
|
||||||
rel = vm.getTypeMapping(); // common case
|
rel = vm.getTypeMapping(); // common case
|
||||||
else
|
else
|
||||||
rel = vm.getMappingRepository().getMapping(val.getClass(),
|
rel = vm.getMappingRepository().getMapping(val.getClass(),
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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.query;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Version;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class ManyOneEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToOne(cascade=CascadeType.ALL)
|
||||||
|
private ManyOneEntity rel;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private Integer optLock;
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManyOneEntity getRel() {
|
||||||
|
return rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRel(ManyOneEntity rel) {
|
||||||
|
this.rel = rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* 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.query;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.EntityManagerFactory;
|
||||||
|
import javax.persistence.Persistence;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import junit.textui.TestRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that querying the id of a related many-one (or one-one) does not create
|
||||||
|
* a join across the tables.
|
||||||
|
*
|
||||||
|
* @author Abe White
|
||||||
|
*/
|
||||||
|
public class TestQueryIdOfRelationDoesNotJoin
|
||||||
|
extends TestCase {
|
||||||
|
|
||||||
|
private EntityManagerFactory emf;
|
||||||
|
private long e3Id;
|
||||||
|
|
||||||
|
public void setUp() {
|
||||||
|
Map props = new HashMap();
|
||||||
|
props.put("openjpa.MetaDataFactory", "jpa(Types="
|
||||||
|
+ ManyOneEntity.class.getName() + ")");
|
||||||
|
emf = Persistence.createEntityManagerFactory("test", props);
|
||||||
|
|
||||||
|
ManyOneEntity e1 = new ManyOneEntity();
|
||||||
|
e1.setName("e1");
|
||||||
|
ManyOneEntity e2 = new ManyOneEntity();
|
||||||
|
e2.setName("e2");
|
||||||
|
ManyOneEntity e3 = new ManyOneEntity();
|
||||||
|
e3.setName("e3");
|
||||||
|
e1.setRel(e3);
|
||||||
|
e2.setRel(e1);
|
||||||
|
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
em.persist(e1);
|
||||||
|
em.getTransaction().commit();
|
||||||
|
e3Id = e3.getId();
|
||||||
|
|
||||||
|
// we intentionally create an orphaned reference on e1.rel
|
||||||
|
em.getTransaction().begin();
|
||||||
|
em.remove(e3);
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tearDown() {
|
||||||
|
if (emf == null)
|
||||||
|
return;
|
||||||
|
try {
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
em.createQuery("delete from ManyOneEntity").executeUpdate();
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
emf.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testQuery() {
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
Query q = em.createQuery("select e from ManyOneEntity e "
|
||||||
|
+ "where e.rel.id = :id").setParameter("id", e3Id);
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(1, res.size());
|
||||||
|
|
||||||
|
ManyOneEntity e = (ManyOneEntity) res.get(0);
|
||||||
|
assertEquals("e1", e.getName());
|
||||||
|
assertNull(e.getRel());
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TestRunner.run(TestQueryIdOfRelationDoesNotJoin.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue