From ff83975992e222de590ab6db2db95b85ab0d325a Mon Sep 17 00:00:00 2001 From: Heath Thomann Date: Wed, 23 May 2012 16:00:45 +0000 Subject: [PATCH] OPENJPA-1738: Back ported Fay's changes from trunk (2.1.x). git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/2.0.x@1341923 13f79535-47bb-0310-9956-ffa450edef68 --- .../jdbc/kernel/PreparedQueryImpl.java | 10 +- .../persistence/jdbc/sqlcache/Child.java | 73 +++++++++++ .../persistence/jdbc/sqlcache/Parent.java | 119 ++++++++++++++++++ .../persistence/jdbc/sqlcache/ParentId.java | 83 ++++++++++++ .../jdbc/sqlcache/TestPreparedQueryCache.java | 92 +++++++++++++- .../test/resources/META-INF/persistence.xml | 2 + 6 files changed, 375 insertions(+), 4 deletions(-) create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Child.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Parent.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/ParentId.java diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedQueryImpl.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedQueryImpl.java index 95ddb7b91..06a8367d8 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedQueryImpl.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PreparedQueryImpl.java @@ -331,7 +331,7 @@ public class PreparedQueryImpl implements PreparedQuery { setPersistenceCapableParameter(result, val, indices, broker); } else if (val instanceof Collection) { setCollectionValuedParameter(result, (Collection)val, indices, - key); + key, broker); } else { for (int j : indices) { if (val instanceof Enum) { @@ -386,7 +386,7 @@ public class PreparedQueryImpl implements PreparedQuery { } private void setCollectionValuedParameter(Map result, - Collection values, int[] indices, Object param) { + Collection values, int[] indices, Object param, Broker broker) { int n = values.size(); Object[] array = values.toArray(); if (n > indices.length || indices.length%n != 0) { @@ -395,7 +395,11 @@ public class PreparedQueryImpl implements PreparedQuery { } int k = 0; for (int j : indices) { - result.put(j, array[k%n]); + Object val = array[k%n]; + if (ImplHelper.isManageable(val)) + setPersistenceCapableParameter(result, val, indices, broker); + else + result.put(j, val); k++; } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Child.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Child.java new file mode 100644 index 000000000..475361ff8 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Child.java @@ -0,0 +1,73 @@ +/* + * 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.sqlcache; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +/** + * Child in a bidirectional parent-child relationship. + * + * Notes: + * a) there is no mutator for id because it is generated by JPA provider. + * + */ +@Entity +@Table(name="zchild") +public class Child { + @Id + @GeneratedValue + private String id; + + private String name; + + @ManyToOne(fetch=FetchType.LAZY) + private Parent parent; + + /** + * Restrict access to constructor for Parent to create the Child. + */ + public Child() { + + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String city) { + this.name = city; + } + + public Parent getParent() { + return parent; + } + + void setParent(Parent owner) { + this.parent = owner; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Parent.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Parent.java new file mode 100644 index 000000000..f0f9a5a05 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/Parent.java @@ -0,0 +1,119 @@ +/* + * 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.sqlcache; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +/** + * Parent in a bidirectional parent-child relationship. + * + * Note: + * a) there is no mutator for id because it is generated by JPA provider. + * + */ +@Entity +@IdClass(ParentId.class) +@Table(name="zparent") +public class Parent { + @Id + private long id; + @Id + private String name; + @Id + @OneToOne(cascade = CascadeType.ALL) + @JoinColumn(name="addrid") + private Address addrId; + + /** + * This field is mapped by the child. The child's table will hold a foreign + * key linking to the primary key of this Parent's table. In JPA + * terminology, that makes the Child the owner of this bi-directional + * relationship. + */ + @OneToMany(mappedBy="parent", cascade = CascadeType.ALL) + private Collection children; + + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void setAddrId(Address addr) { + this.addrId = addr; + } + + public Address getAddrId() { + return addrId; + } + + public Collection getChildren() { + return children; + } + + /** + * Creates and adds a child to this receiver. Creating child via the parent + * is the preferred pattern to ensure referential integrity of domain model. + */ + public Child newChild(String name) { + Child child = new Child(); + child.setName(name); + child.setParent(this); + if (children == null) + children = new ArrayList(); + children.add(child); + return child; + } + + public boolean removeChild(Child child) { + return children != null && children.remove(child); + } + + /** + * Unsafe way of adding a child. Does not warranty referential integrity. + * The caller has to ensure bi-directionality of parent-child relation is + * consistent. + */ + public void add(Child child) { + if (children == null) + children = new ArrayList(); + children.add(child); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/ParentId.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/ParentId.java new file mode 100644 index 000000000..77553f17a --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/ParentId.java @@ -0,0 +1,83 @@ +/* + * 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.sqlcache; + +public class ParentId implements java.io.Serializable { + private static final long serialVersionUID = 4262907482129342511L; + + private long id; + private String name; + private long addrId; + + private Integer hashcode = null; + + public ParentId() { + } + + public long getId() { + return id; + } + + public void setiId(long id) { + this.id = id; + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getAddrId() { + return addrId; + } + + public void setAddrId(long addrId) { + this.addrId = addrId; + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (o instanceof ParentId) { + ParentId oId = (ParentId) o; + if ( oId.id == this.id && + oId.name.equals(this.name) && + oId.addrId == this.addrId) { + return true; + } + } + + return false; + } + + public int hashCode() { + if (hashcode == null) { + String hashStr = this.id + ":" + this.name + ":" + this.addrId; + hashcode = hashStr.hashCode(); + } + return hashcode.intValue(); + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java index 4239f51fe..a1f22e515 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestPreparedQueryCache.java @@ -203,7 +203,23 @@ public class TestPreparedQueryCache extends TestCase { em.persist(o1); em.persist(o2); - + for (int i = 1; i < 10; i++) { + Parent parent = new Parent(); + parent.setId(i); + parent.setName(new String("Parent "+i)); + Address addr = new Address(); + addr.setCity("Address "+i+i); + parent.setAddrId(addr); + em.persist(addr); + for (int j = 1; j < 5; j++) { + Child child = new Child(); + child.setName("Child "+i+j); + child.setParent(parent); + parent.add(child); + } + em.persist(parent); + } + em.getTransaction().commit(); } @@ -212,6 +228,80 @@ public class TestPreparedQueryCache extends TestCase { em.close(); super.tearDown(); } + + public void testCollectionValuedParameterOfEntities() { + OpenJPAEntityManager em = emf.createEntityManager(); + String jpql1 = "select d from Department d where d.name in ('Marketing', 'Sales') order by d.name"; + String jpql2 = "select d from Department d where d.name in ('Engineering', 'Marketing') order by d.name"; + + List param1 = (List) em.createQuery(jpql1).getResultList(); + List param2 = (List) em.createQuery(jpql2).getResultList(); + em.clear(); + + String jpql = "select e from Employee e where e.department in :param"; + + List rs1 = em.createQuery(jpql).setParameter("param", param1).getResultList(); + + for (int i = 0; i < rs1.size(); i++) { + Employee e = (Employee) rs1.get(i); + assertFalse(e.getDepartment().getName().equals("Engineering")); + } + + List rs2 = (List) em.createQuery(jpql).setParameter("param", param2).getResultList(); + for (int i = 0; i < rs2.size(); i++) { + Employee e = (Employee) rs2.get(i); + assertFalse(e.getDepartment().getName().equals("Sales")); + } + + em.clear(); + String jpql3 = "select e from Employee e where e.department in (:p1, :p2, :p3)"; + Query query = em.createQuery(jpql3); + query.setParameter("p1", param1.get(0)); + query.setParameter("p2", param1.get(1)); + query.setParameter("p3", param1.get(2)); + List rs3 = query.getResultList(); + for (int i = 0; i < rs3.size(); i++) { + Employee e = (Employee) rs3.get(i); + assertTrue(e.getDepartment().getName().equals("Marketing")); + } + + em.clear(); + query = em.createQuery(jpql3); + query.setParameter("p1", param2.get(0)); + query.setParameter("p2", param2.get(1)); + query.setParameter("p3", param2.get(2)); + List rs4 = query.getResultList(); + for (int i = 0; i < rs4.size(); i++) { + Employee e = (Employee) rs4.get(i); + assertTrue(e.getDepartment().getName().equals("Engineering")); + } + + em.clear(); + String jpql4 = "select p from Parent p where p.id < 3"; + String jpql5 = "select p from Parent p where p.id > 6"; + List parm1 = em.createQuery(jpql4).getResultList(); + List parm2 = em.createQuery(jpql5).getResultList(); + + em.clear(); + String jpql6 = "select c from Child c where c.parent in ?1"; + Query qry = em.createQuery(jpql6); + qry.setParameter(1, parm1); + List c1 = qry.getResultList(); + for (int i = 0; i < c1.size(); i++) { + Child child = (Child) c1.get(i); + assertTrue(child.getParent().getId() < 3); + } + + em.clear(); + qry = em.createQuery(jpql6); + qry.setParameter(1, parm2); + List c2 = qry.getResultList(); + for (int i = 0; i < c2.size(); i++) { + Child child = (Child) c2.get(i); + assertTrue(child.getParent().getId() > 6); + } + + } public void testRepeatedParameterInSubqueryInDifferentOrderSubQLast() { OpenJPAEntityManager em = emf.createEntityManager(); diff --git a/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml b/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml index 440b2e8ea..52dd83712 100644 --- a/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml +++ b/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml @@ -208,6 +208,8 @@ org.apache.openjpa.persistence.jdbc.sqlcache.Person org.apache.openjpa.persistence.jdbc.sqlcache.Singer org.apache.openjpa.persistence.jdbc.sqlcache.OrderJPA + org.apache.openjpa.persistence.jdbc.sqlcache.Parent + org.apache.openjpa.persistence.jdbc.sqlcache.Child