OPENJPA-292 Extra JOIN on eager bi-directional relationship

commit openjpa_292_1.patch and testcase_241_292.patch on behalf of Fay Wang

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@679262 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Catalina Wei 2008-07-24 01:39:55 +00:00
parent 98802147f7
commit 62a8c5c32f
11 changed files with 858 additions and 2 deletions

View File

@ -24,6 +24,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfigurationImpl;
@ -55,6 +56,7 @@ import org.apache.openjpa.jdbc.sql.Union;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.util.ApplicationIds;
import org.apache.openjpa.util.ImplHelper;
@ -516,6 +518,23 @@ public class RelationFieldStrategy
JDBCFetchConfiguration fetch, Result res)
throws SQLException {
ClassMapping cls = field.getIndependentTypeMappings()[0];
// for inverseEager field
FieldMapping mappedByFieldMapping = field.getMappedByMapping();
PersistenceCapable mappedByValue = null;
if (mappedByFieldMapping != null) {
ValueMapping val = mappedByFieldMapping.getValueMapping();
ClassMetaData decMeta = val.getTypeMetaData();
// this inverse field does not have corresponding classMapping
// its value may be a collection/map etc.
if (decMeta != null) {
mappedByValue = sm.getPersistenceCapable();
res.setMappedByFieldMapping(mappedByFieldMapping);
res.setMappedByValue(mappedByValue);
}
}
sm.storeObject(field.getIndex(), res.load(cls, store, fetch,
eagerJoin(res.newJoins(), cls, false)));
}

View File

@ -243,7 +243,7 @@ public abstract class AbstractResult
}
public FieldMapping getMappedByFieldMapping() {
return (_gotEager) ? null : _mappedByFieldMapping;
return _mappedByFieldMapping;
}
public void setMappedByFieldMapping(FieldMapping fieldMapping) {
@ -251,7 +251,7 @@ public abstract class AbstractResult
}
public Object getMappedByValue() {
return (_gotEager) ? null : _mappedByValue;
return _mappedByValue;
}
public void setMappedByValue(Object mappedByValue) {

View File

@ -0,0 +1,122 @@
/*
* 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.relations;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Collection;
import java.util.ArrayList;
@Entity
public class Customer {
@Embeddable
public static class CustomerKey implements Serializable {
public String countryCode;
public int id;
public CustomerKey(){}
public CustomerKey(String cc, int id){
countryCode=cc;
this.id=id;
}
public String toString() {
return countryCode+"/"+id;
}
@Override
public boolean equals(Object obj){
if (obj==this) return true;
if ( ! (obj instanceof CustomerKey) ) return false;
CustomerKey key = (CustomerKey)obj;
if (key.countryCode.equals(this.countryCode) &&
key.id==this.id) return true;
return false;
}
@Override
public int hashCode() {
return this.countryCode.hashCode()
^ this.id;
}
}
public enum CreditRating { POOR, GOOD, EXCELLENT };
@EmbeddedId
CustomerKey cid;
@Column(length=30)
@Basic(fetch=FetchType.LAZY)
String name;
@Enumerated
@Basic(fetch=FetchType.LAZY)
CreditRating creditRating;
@Version
long version;
@OneToMany(fetch=FetchType.LAZY, mappedBy="customer")
private Collection<Order> orders = new ArrayList<Order>();
public Customer() {}
public Customer(CustomerKey cid, String name, CreditRating rating){
this.cid=cid;
this.name=name;
this.creditRating=rating;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public CreditRating getRating() {
return creditRating;
}
public void setRating(CreditRating rating) {
this.creditRating = rating;
}
public Collection<Order> getOrders() {
return orders;
}
public void setOrders(Collection<Order> orders) {
this.orders = orders;
}
public String toString() {
return "Customer:"+cid+" name:"+name;
}
public CustomerKey getCid() {
return cid;
}
public void setCid(CustomerKey cid) {
this.cid = cid;
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.relations;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
@Entity
@DiscriminatorValue("A1")
public class EntityA1InverseEager extends EntityAInverseEager {
private String name1;
@OneToMany(fetch=FetchType.EAGER, mappedBy="entityA")
private List<EntityBInverseEager> listB = new ArrayList<EntityBInverseEager>();
public EntityA1InverseEager() {}
public EntityA1InverseEager(String name) {
super(name);
this.name1 = name;
}
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.relations;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
@Entity
@DiscriminatorValue("A2")
public class EntityA2InverseEager extends EntityAInverseEager {
private String name2;
@OneToMany(fetch=FetchType.EAGER, mappedBy="entityA")
private List<EntityBInverseEager> listB = new ArrayList<EntityBInverseEager>();
public EntityA2InverseEager() {}
public EntityA2InverseEager(String name) {
super(name);
this.name2 = name;
}
public String getName2() {
return name2;
}
public void setName2(String name2) {
this.name2 = name2;
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.relations;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.OneToMany;
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="TYPE")
public class EntityAInverseEager {
@GeneratedValue
@Id private int id;
private String name;
@OneToMany(fetch=FetchType.EAGER, mappedBy="entityA")
private List<EntityBInverseEager> listB = new ArrayList<EntityBInverseEager>();
public EntityAInverseEager() {}
public EntityAInverseEager(String name) {
this.name = name;
}
public List<EntityBInverseEager> getListB() {
return listB;
}
public List addB(EntityBInverseEager entityB) {
listB.add(entityB);
return listB;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.relations;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class EntityBInverseEager {
@GeneratedValue
@Id private int id;
private String name;
@ManyToOne(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
private EntityAInverseEager entityA;
public EntityBInverseEager() {}
public EntityBInverseEager(String name) {
this.name = name;
}
public EntityAInverseEager getA() {
return entityA;
}
public void setA(EntityAInverseEager entityA) {
this.entityA = entityA;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,88 @@
/*
* 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.relations;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
@Entity
public class EntityCInverseEager {
@GeneratedValue
@Id private int id;
private String name;
private int age;
private int balance;
@OneToOne(fetch=FetchType.EAGER, mappedBy="entityC")
private EntityDInverseEager entityD = null;
public EntityCInverseEager() {}
public EntityCInverseEager(String name, int age, int balance) {
this.name = name;
this.age = age;
this.balance = balance;
}
public EntityDInverseEager getD() {
return entityD;
}
public void setD(EntityDInverseEager entityD) {
this.entityD = entityD;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
}

View File

@ -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.relations;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
@Entity
public class EntityDInverseEager {
@GeneratedValue
@Id private int id;
private String name;
private int loginCount;
private int logoutCount;
private String email;
@OneToOne(fetch=FetchType.EAGER)
private EntityCInverseEager entityC = null;
public EntityDInverseEager() {}
public EntityDInverseEager(String name, String email, int loginCount,
int logoutCount) {
this.name = name;
this.email = email;
this.loginCount = loginCount;
this.logoutCount = logoutCount;
}
public EntityCInverseEager getC() {
return entityC;
}
public void setC(EntityCInverseEager entityC) {
this.entityC = entityC;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getLoginCount() {
return loginCount;
}
public void setLoginCount(int loginCount) {
this.loginCount = loginCount;
}
public int getLogoutCount() {
return logoutCount;
}
public void setLogoutCount(int logoutCount) {
this.logoutCount = logoutCount;
}}

View File

@ -0,0 +1,87 @@
/*
* 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.relations;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Version;
@Entity
@Table(name="OrderTbl")
public class Order {
@Id
@GeneratedValue
int oid;
double amount;
boolean delivered;
@ManyToOne (fetch=FetchType.EAGER)
Customer customer;
@Version
long version;
public Order(){}
public Order( double amt, boolean delivered, Customer c){
amount=amt;
this.delivered=delivered;
customer=c;
if (c!=null) c.getOrders().add(this);
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public boolean isDelivered() {
return delivered;
}
public void setDelivered(boolean delivered) {
this.delivered = delivered;
}
public int getOid() {
return oid;
}
public String toString(){
return "Order:"+oid+" amount:"+amount+" delivered:"+delivered+" customer:"+
( customer!=null ? customer.getCid() : -1 );
}
}

View File

@ -0,0 +1,204 @@
/*
* 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.relations;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import junit.textui.TestRunner;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAQuery;
import org.apache.openjpa.persistence.test.SQLListenerTestCase;
public class TestInverseEagerSQL
extends SQLListenerTestCase {
public void setUp() {
setUp(Customer.class, Customer.CustomerKey.class, Order.class,
EntityAInverseEager.class, EntityA1InverseEager.class, EntityA2InverseEager.class,
EntityBInverseEager.class, EntityCInverseEager.class, EntityDInverseEager.class);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Customer.CustomerKey ck = new Customer.CustomerKey("USA", 1);
Customer c = new Customer();
c.setCid(ck);
c.setName("customer1");
em.persist(c);
for (int i = 0; i < 4; i++) {
Order order = new Order();
order.setCustomer(c);
em.persist(order);
}
EntityAInverseEager a = new EntityAInverseEager("a");
em.persist(a);
EntityA1InverseEager a1 = new EntityA1InverseEager("a1");
em.persist(a1);
EntityA2InverseEager a2 = new EntityA2InverseEager("a2");
em.persist(a2);
for (int i = 0; i < 4; i++) {
EntityBInverseEager b = new EntityBInverseEager("b" + i);
a.addB(b);
b.setA(a);
em.persist(b);
}
for (int i = 4; i < 8; i++) {
EntityBInverseEager b = new EntityBInverseEager("b" + i);
a1.addB(b);
b.setA(a1);
em.persist(b);
}
for (int i = 8; i < 12; i++) {
EntityBInverseEager b = new EntityBInverseEager("b" + i);
a2.addB(b);
b.setA(a2);
em.persist(b);
}
for (int i = 0; i < 4; i++) {
EntityCInverseEager c1 = new EntityCInverseEager("c"+i, i, i);
em.persist(c1);
EntityDInverseEager d1 = new EntityDInverseEager("d"+i, "d"+i, i, i);
em.persist(d1);
c1.setD(d1);
d1.setC(c1);
}
em.flush();
em.getTransaction().commit();
em.close();
}
public void testOneToManyInverseEagerQuery() {
sql.clear();
OpenJPAEntityManager em = emf.createEntityManager();
OpenJPAQuery q = em.createQuery("SELECT c FROM Customer c ");
List<Customer> res = q.getResultList();
assertEquals(1, res.size());
for (int i = 0; i < res.size(); i++) {
Customer c = (Customer)res.get(i);
Collection<Order> orders = c.getOrders();
for (Iterator<Order> iter=orders.iterator(); iter.hasNext();) {
Order order = (Order)iter.next();
assertEquals(order.getCustomer(), c);
}
}
assertEquals(2, sql.size());
em.close();
}
public void testOneToOneInverseEagerQuery() {
sql.clear();
OpenJPAEntityManager em = emf.createEntityManager();
String query = "select c FROM EntityCInverseEager c";
Query q = em.createQuery(query);
List<EntityCInverseEager> res = q.getResultList();
assertEquals(4, res.size());
for (int i = 0; i < res.size(); i++) {
EntityCInverseEager c = (EntityCInverseEager)res.get(i);
EntityDInverseEager d = c.getD();
assertEquals(c, d.getC());
}
assertEquals(1, sql.size());
em.close();
}
public void testOneToManyInheritanceQuery() {
sql.clear();
OpenJPAEntityManager em = emf.createEntityManager();
String query = "select a FROM EntityA1InverseEager a";
Query q = em.createQuery(query);
List list = q.getResultList();
assertEquals(1, list.size());
for (int i = 0; i < list.size(); i++) {
EntityA1InverseEager a1 = (EntityA1InverseEager)list.get(i);
Collection<EntityBInverseEager> listB = a1.getListB();
assertEquals(4, listB.size());
for (Iterator iter=listB.iterator(); iter.hasNext();) {
EntityBInverseEager b = (EntityBInverseEager)iter.next();
EntityAInverseEager a = b.getA();
assertEquals(a1, a);
}
}
assertEquals(3, sql.size());
sql.clear();
query = "select a FROM EntityA2InverseEager a";
q = em.createQuery(query);
list = q.getResultList();
assertEquals(1, list.size());
for (int i = 0; i < list.size(); i++) {
EntityA2InverseEager a2 = (EntityA2InverseEager)list.get(i);
Collection listB = a2.getListB();
assertEquals(4, listB.size());
for (Iterator iter=listB.iterator(); iter.hasNext();) {
EntityBInverseEager b = (EntityBInverseEager)iter.next();
EntityAInverseEager a = b.getA();
assertEquals(a2, a);
}
}
assertEquals(3, sql.size());
sql.clear();
query = "select a FROM EntityAInverseEager a";
q = em.createQuery(query);
list = q.getResultList();
assertEquals(3, list.size());
for (int i = 0; i < list.size(); i++) {
EntityAInverseEager a0 = (EntityAInverseEager)list.get(i);
Collection listB = a0.getListB();
assertEquals(4, listB.size());
for (Iterator iter=listB.iterator(); iter.hasNext();) {
EntityBInverseEager b = (EntityBInverseEager)iter.next();
EntityAInverseEager a = b.getA();
assertEquals(a0, a);
}
}
assertEquals(2, sql.size());
em.close();
}
public static void main(String[] args) {
TestRunner.run(TestInverseEagerSQL.class);
}
}