From 2269ea2fab88e3491dac2ba957c8cf8b7f62bb7c Mon Sep 17 00:00:00 2001 From: Heath Thomann Date: Fri, 11 Feb 2011 19:23:29 +0000 Subject: [PATCH] OPENJPA-1911: fix merge problem for entities with derived id - merged this code from Fay's changes for trunk git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/2.0.x@1069924 13f79535-47bb-0310-9956-ffa450edef68 --- .../openjpa/kernel/StateManagerImpl.java | 18 ++- .../persistence/xs/AccountingHierarchy.java | 133 ++++++++++++++++++ .../xs/AccountingHierarchyRate.java | 128 +++++++++++++++++ .../xs/AccountingHierarchyRateOpenJPAKey.java | 127 +++++++++++++++++ .../persistence/xs/TestMergeComplexKey.java | 95 +++++++++++++ pom.xml | 6 +- 6 files changed, 503 insertions(+), 4 deletions(-) create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/AccountingHierarchy.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/AccountingHierarchyRate.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/AccountingHierarchyRateOpenJPAKey.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/TestMergeComplexKey.java diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java index bb2869075..b260d3f41 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java @@ -1999,9 +1999,25 @@ public class StateManagerImpl case JavaTypes.ARRAY: case JavaTypes.COLLECTION: case JavaTypes.MAP: - case JavaTypes.PC: case JavaTypes.PC_UNTYPED: break; + case JavaTypes.PC: + if (_meta.getField(field).isPrimaryKey()) { + // this field is a derived identity + //if (newVal != null && newVal.equals(curVal)) + // return; + //else { + if (curVal != null && newVal != null && + curVal instanceof PersistenceCapable && newVal instanceof PersistenceCapable) { + PersistenceCapable curPc = (PersistenceCapable) curVal; + PersistenceCapable newPc = (PersistenceCapable) newVal; + if (curPc.pcFetchObjectId().equals(newPc.pcFetchObjectId())) + return; + + } + //} + } else + break; default: if (newVal != null && newVal.equals(curVal)) return; diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/AccountingHierarchy.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/AccountingHierarchy.java new file mode 100644 index 000000000..92536144d --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/AccountingHierarchy.java @@ -0,0 +1,133 @@ +/* + * 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.xs; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Transient; + +@Entity +@Table(name = "PM_ACCOUNTING_HIERARCHY_TEST") +public class AccountingHierarchy implements Serializable { + + private static final long serialVersionUID = -1759978020595211326L; + + private String code; + private String shortDesc; + + private List accRateList = new ArrayList(0); + + private Long version; + + public AccountingHierarchy() { + } + + public AccountingHierarchy(String code) { + this.code = code; + } + + public AccountingHierarchy(String code, String shortDesc) { + this.code = code; + this.shortDesc = shortDesc; + } + + public AccountingHierarchy(String code, String shortDesc, String hierarchyType) { + this.code = code; + this.shortDesc = shortDesc; + } + + @Id + @Column(name = "code", length = 20) + public String getCode() { + return code; + } + + @Column(name = "short_desc", nullable = false, length = 50) + public String getShortDesc() { + return shortDesc; + } + + @OneToMany(mappedBy = "accountingHierarchy", fetch = FetchType.EAGER, + targetEntity = AccountingHierarchyRate.class, cascade = CascadeType.ALL, orphanRemoval = true) + public List getAccRateList() { + return accRateList; + } + + public void setCode(String code) { + this.code = code; + } + + public void setShortDesc(String shortDesc) { + this.shortDesc = shortDesc; + } + + public void setAccRateList(List accRateList) { + this.accRateList = accRateList; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((getCode() == null) ? 0 : getCode().hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof AccountingHierarchy)) + return false; + AccountingHierarchy other = (AccountingHierarchy) obj; + if (getCode() == null) { + if (other.getCode() != null) + return false; + } else if (!getCode().equals(other.getCode())) + return false; + return true; + } + + @Transient + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + @Override + public String toString() { + return "AccountingHierarchy [getCode()=" + getCode() + ", getShortDesc()=" + getShortDesc() + "]"; + } + +} + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/AccountingHierarchyRate.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/AccountingHierarchyRate.java new file mode 100644 index 000000000..dcea3473a --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/AccountingHierarchyRate.java @@ -0,0 +1,128 @@ +/* + * 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.xs; + +import java.io.Serializable; +import java.math.BigDecimal; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "PM_ACCOUNTING_HIERARCHY_RATE_TEST") +@IdClass(AccountingHierarchyRateOpenJPAKey.class) +public class AccountingHierarchyRate implements Serializable { + + private static final long serialVersionUID = 538926265319989492L; + + private String id; + private AccountingHierarchy accountingHierarchy; + + private BigDecimal currentRate; + private BigDecimal budgetRate; + + public AccountingHierarchyRate() { + } + + public AccountingHierarchyRate(String id, AccountingHierarchy accountingHierarchy, + BigDecimal currentRate, BigDecimal budgetRate) { + this.id = id; + this.accountingHierarchy = accountingHierarchy; + this.currentRate = currentRate; + this.budgetRate = budgetRate; + } + + @Id + @Column(name = "id", length = 20, nullable = false) + public String getId() { + return id; + } + + @Id + @ManyToOne(targetEntity = AccountingHierarchy.class, fetch = FetchType.LAZY) + @JoinColumn(name = "acc_hier", nullable = false) + public AccountingHierarchy getAccountingHierarchy() { + return accountingHierarchy; + } + + @Column(name = "current_rate", nullable = true, precision = 12, scale = 4) + public BigDecimal getCurrentRate() { + return currentRate; + } + + @Column(name = "budget_rate", nullable = true, precision = 12, scale = 4) + public BigDecimal getBudgetRate() { + return budgetRate; + } + + public void setId(String id) { + this.id = id; + } + + public void setAccountingHierarchy(AccountingHierarchy accountingHierarchy) { + this.accountingHierarchy = accountingHierarchy; + } + + public void setCurrentRate(BigDecimal currentRate) { + this.currentRate = currentRate; + } + + public void setBudgetRate(BigDecimal budgetRate) { + this.budgetRate = budgetRate; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); + result = prime * result + ((getAccountingHierarchy() == null) ? 0 : + getAccountingHierarchy().hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof AccountingHierarchyRate)) + return false; + AccountingHierarchyRate other = (AccountingHierarchyRate) obj; + if (getId() == null) { + if (other.getId() != null) + return false; + } else if (!getId().equals(other.getId())) + return false; + if (getAccountingHierarchy() == null) { + if (other.getAccountingHierarchy() != null) + return false; + } else if (!getAccountingHierarchy().equals(other.getAccountingHierarchy())) + return false; + return true; + } +} + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/AccountingHierarchyRateOpenJPAKey.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/AccountingHierarchyRateOpenJPAKey.java new file mode 100644 index 000000000..f38e8e458 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/AccountingHierarchyRateOpenJPAKey.java @@ -0,0 +1,127 @@ +/* + * 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.xs; + +import java.io.Serializable; + +/** + * + * Auto-generated by: + * org.apache.openjpa.enhance.ApplicationIdTool + */ +public class AccountingHierarchyRateOpenJPAKey implements Serializable { + /** + * + */ + private static final long serialVersionUID = -2345673847908844341L; + + static { + // register persistent class in JVM + try { Class.forName("org.apache.openjpa.persistence.xs.AccountingHierarchyRate"); } + catch(Exception e) {} + } + + public String accountingHierarchy; + public String id; + + public AccountingHierarchyRateOpenJPAKey() { + } + + public AccountingHierarchyRateOpenJPAKey(String str) { + fromString(str); + } + + public String getAccountingHierarchy() { + return accountingHierarchy; + } + + public void setAccountingHierarchy(String accountingHierarchy) { + this.accountingHierarchy = accountingHierarchy; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String toString() { + return accountingHierarchy + + "::" + id; + } + + public int hashCode() { + int rs = 17; + rs = rs * 37 + ((accountingHierarchy == null) ? 0 : accountingHierarchy.hashCode()); + rs = rs * 37 + ((id == null) ? 0 : id.hashCode()); + return rs; + } + + public boolean equals(Object obj) { + if(this == obj) + return true; + if(obj == null || obj.getClass() != getClass()) + return false; + + AccountingHierarchyRateOpenJPAKey other = (AccountingHierarchyRateOpenJPAKey) obj; + return ((accountingHierarchy == null && other.accountingHierarchy == null) + || (accountingHierarchy != null && accountingHierarchy.equals(other.accountingHierarchy))) + && ((id == null && other.id == null) + || (id != null && id.equals(other.id))); + } + + private void fromString(String str) { + Tokenizer toke = new Tokenizer(str); + str = toke.nextToken(); + if("null".equals(str)) + accountingHierarchy = null; + else + accountingHierarchy = str; + str = toke.nextToken(); + if("null".equals(str)) + id = null; + else + id = str; + } + + protected static class Tokenizer { + private final String str; + private int last; + + public Tokenizer (String str) { + this.str = str; + } + + public String nextToken () { + int next = str.indexOf("::", last); + String part; + if(next == -1) { + part = str.substring(last); + last = str.length(); + } else { + part = str.substring(last, next); + last = next + 2; + } + return part; + } + } +} + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/TestMergeComplexKey.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/TestMergeComplexKey.java new file mode 100644 index 000000000..52992b95e --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xs/TestMergeComplexKey.java @@ -0,0 +1,95 @@ +/* + * 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.xs; + +import java.math.BigDecimal; + +import javax.persistence.EntityManager; +import javax.persistence.EntityTransaction; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +public class TestMergeComplexKey extends SingleEMFTestCase { + Object[] props = new Object[] { AccountingHierarchy.class, AccountingHierarchyRate.class, CLEAR_TABLES }; + + @Override + public void setUp() throws Exception { + setUp(props); + } + + public void test() throws Exception { + createDate(); + EntityManager em = emf.createEntityManager(); + AccountingHierarchy accountingHierarchy = (AccountingHierarchy) em.find(AccountingHierarchy.class, "TESTING"); + accountingHierarchy.setShortDesc("NAME:" + System.currentTimeMillis()); + accountingHierarchy = roundtrip(accountingHierarchy); + em.close(); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + try { + em.merge(accountingHierarchy); + } catch (RuntimeException e) { + em.getTransaction().setRollbackOnly(); + throw e; + } finally { + if (em.getTransaction().getRollbackOnly()) { + em.getTransaction().rollback(); + } else { + em.getTransaction().commit(); + } + } + + } + + void createDate() { + EntityManager em = emf.createEntityManager(); + System.out.println(em.createQuery("select o from AccountingHierarchy o").getResultList().size()); + + String code = "TESTING"; + AccountingHierarchy accountingHierarchy = em.find(AccountingHierarchy.class, code); + if (accountingHierarchy == null) { + accountingHierarchy = new AccountingHierarchy(); + accountingHierarchy.setCode(code); + accountingHierarchy.setShortDesc("TESTING"); + + AccountingHierarchyRate accountingHierarchyRate = + new AccountingHierarchyRate("1", accountingHierarchy, BigDecimal.ONE, BigDecimal.TEN); + + accountingHierarchy.getAccRateList().add(accountingHierarchyRate); + + EntityTransaction tx = em.getTransaction(); + tx.begin(); + try { + em.persist(accountingHierarchy); + } catch (RuntimeException e) { + tx.setRollbackOnly(); + throw e; + } finally { + if (tx.getRollbackOnly()) { + tx.rollback(); + } else { + tx.commit(); + } + } + } + + } +} + diff --git a/pom.xml b/pom.xml index 31537c37d..9a851d346 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ scp://people.apache.org/home/${user.name}/public_html/openjpa/${project.version}/staging-site 512m - 1024m + 768m -Xmx${test.jvm.maxheapsize} -XX:MaxPermSize=${test.jvm.maxpermsize} ${test.jvm.arguments} 10 @@ -187,7 +187,7 @@ false - 0 + 100 **/javax.persistence.spi.PersistenceProvider @@ -938,7 +938,7 @@ false - 0 + 100 **/.*/**