Author: faywang
Date: Thu Sep 9 06:47:46 2010
New Revision: 995333
URL: http://svn.apache.org/viewvc?rev=995333&view=rev
Log:
OPENJPA-1784: improve dirty-checking for persistent maps when the get method is
called to retrieve an embeddable value.
Added:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/LocalizedString.java
(with props)
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/MultilingualString.java
(with props)
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/TestMapUpdate.java
(with props)
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerHandlerMapTableFieldStrategy.java
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMaps.java
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerHandlerMapTableFieldStrategy.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerHandlerMapTableFieldStrategy.java?rev=995333&r1=995332&r2=995333&view=diff
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerHandlerMapTableFieldStrategy.java
(original)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerHandlerMapTableFieldStrategy.java
Thu Sep 9 06:47:46 2010
@@ -21,6 +21,7 @@ package org.apache.openjpa.jdbc.meta.str
import java.sql.*;
import java.util.*;
+import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.jdbc.identifier.DBIdentifier;
import org.apache.openjpa.jdbc.kernel.*;
import org.apache.openjpa.jdbc.meta.*;
@@ -214,8 +215,11 @@ public class HandlerHandlerMapTableField
for (Iterator itr = change.iterator(); itr.hasNext();) {
mkey = itr.next();
+ Object mval = map.get(mkey);
+ if ((mval instanceof PersistenceCapable) &&
!((PersistenceCapable)mval).pcIsDirty())
+ continue;
HandlerStrategies.where(key, mkey, store, changeRow, _kcols);
- HandlerStrategies.set(val, map.get(mkey), store, changeRow,
+ HandlerStrategies.set(val, mval, store, changeRow,
_vcols, _vio, true);
rm.flushSecondaryRow(changeRow);
}
Modified:
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMaps.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMaps.java?rev=995333&r1=995332&r2=995333&view=diff
==============================================================================
---
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMaps.java
(original)
+++
openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMaps.java
Thu Sep 9 06:47:46 2010
@@ -23,10 +23,11 @@ import java.io.ObjectStreamException;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Map;
-import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
+import org.apache.openjpa.enhance.PersistenceCapable;
+
/**
* Utility methods used by map proxies.
*
@@ -75,6 +76,32 @@ public class ProxyMaps
}
/**
+ * Call before invoking {...@link Map#get} on super.
+ */
+ public static boolean beforeGet(ProxyMap map, Object key) {
+ assertAllowedType(key, map.getKeyType());
+ return map.containsKey(key);
+ }
+
+ /**
+ * Call after invoking {...@link Map#get} on super.
+ *
+ * @param ret the return value from the super's method
+ * @param before the return value from {...@link #beforeGet}
+ * @return the value to return from {...@link Map#get}
+ */
+ public static Object afterGet(ProxyMap map, Object key,
+ Object ret, boolean before) {
+ if (before) {
+ if (map.getChangeTracker() != null && (ret instanceof
PersistenceCapable))
+ ((MapChangeTracker) map.getChangeTracker()).changed(key, ret,
+ ret);
+ }
+ return ret;
+ }
+
+
+ /**
* Call before invoking {...@link Map#put} on super.
*/
public static boolean beforePut(ProxyMap map, Object key, Object value) {
Added:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/LocalizedString.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/LocalizedString.java?rev=995333&view=auto
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/LocalizedString.java
(added)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/LocalizedString.java
Thu Sep 9 06:47:46 2010
@@ -0,0 +1,87 @@
+package org.apache.openjpa.persistence.jdbc.maps.update;
+
+import java.io.Serializable;
+
+import javax.persistence.Embeddable;
+
+/**
+ * A LocalizedString is any text string combined with a language code. The
+ * language codes are two lower-case characters according to ISO-639-1, e.g.
+ * "de" = German, "en" = English.
+ *
+ * The language may be null for strings like phone numbers.
+ *
+ * @author Harald Wellmann
+ *
+ */
+...@embeddable
+public class LocalizedString implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /** Language code. */
+ private String language;
+
+ private String string;
+
+ public LocalizedString() {
+ }
+
+ public LocalizedString(String language, String text) {
+ this.language = language;
+ this.string = text;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public String getString() {
+ return string;
+ }
+
+ public void setString(String text) {
+ this.string = text;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((language == null) ? 0 : language.hashCode());
+ result = prime * result + ((string == null) ? 0 : string.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ LocalizedString other = (LocalizedString) obj;
+ if (language == null) {
+ if (other.language != null)
+ return false;
+ } else if (!language.equals(other.language))
+ return false;
+ if (string == null) {
+ if (other.string != null)
+ return false;
+ } else if (!string.equals(other.string))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return string;
+ }
+}
Propchange:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/LocalizedString.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/MultilingualString.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/MultilingualString.java?rev=995333&view=auto
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/MultilingualString.java
(added)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/MultilingualString.java
Thu Sep 9 06:47:46 2010
@@ -0,0 +1,67 @@
+package org.apache.openjpa.persistence.jdbc.maps.update;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.Table;
+
+
+...@entity
+...@table(name = "multilingual_string")
+public class MultilingualString {
+
+ private static final long serialVersionUID = 1L;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE)
+ @Column(name = "string_id")
+ private long id;
+
+ @ElementCollection(fetch=FetchType.EAGER)
+ @MapKeyColumn(name = "language_key")
+ @CollectionTable(name = "multilingual_string_map", joinColumns =
@JoinColumn(name = "string_id"))
+ private Map<String, LocalizedString> map = new HashMap<String,
LocalizedString>();
+
+ public MultilingualString() {}
+
+ public MultilingualString(String lang, String text) {
+ setText(lang, text);
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Map<String, LocalizedString> getMap() {
+ return map;
+ }
+
+ public void setMap(Map<String, LocalizedString> map) {
+ this.map = map;
+ }
+
+ public void setText(String lang, String text) {
+ map.put(lang, new LocalizedString(lang, text));
+ }
+
+ public String getText(String lang) {
+ if (map.containsKey(lang)) {
+ return map.get(lang).getString();
+ }
+ return null;
+ }
+}
Propchange:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/MultilingualString.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/TestMapUpdate.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/TestMapUpdate.java?rev=995333&view=auto
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/TestMapUpdate.java
(added)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/TestMapUpdate.java
Thu Sep 9 06:47:46 2010
@@ -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.jdbc.maps.update;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.EntityTransaction;
+
+import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI;
+import org.apache.openjpa.persistence.test.SQLListenerTestCase;
+
+/**
+ *
+ * @author Harald Wellmann
+ *
+ */
+public class TestMapUpdate extends SQLListenerTestCase {
+
+ private MultilingualString entity1;
+ private OpenJPAEntityManagerSPI em;
+
+ public void setUp() {
+ super.setUp(CLEAR_TABLES,
+ MultilingualString.class,
+ LocalizedString.class);
+ createObj(emf);
+ em = emf.createEntityManager();
+ }
+
+ public void testUpdateMapKey() throws Exception {
+ em.getTransaction().begin();
+
+ MultilingualString ms = em.find(MultilingualString.class,
entity1.getId());
+ assertNotNull(ms);
+
+ // Overwrite an existing map entry.
+ // The key is now dirty, and OpenJPA will generate an SQL UPDATE.
+ ms.setText("en", "Good evening");
+ em.getTransaction().commit();
+ em.clear();
+
+ em.getTransaction().begin();
+ ms = em.find(MultilingualString.class, entity1.getId());
+ assertEquals("Good evening", ms.getText("en"));
+ em.getTransaction().commit();
+ }
+
+ public void testUpdateMapValue() throws Exception {
+ em.getTransaction().begin();
+
+ MultilingualString ms = em.find(MultilingualString.class,
entity1.getId());
+ assertNotNull(ms);
+
+ // Change an existing map value. This makes the map dirty,
+ // but OpenJPA does not recognize it. No SQL UPDATE is generated.
+ ms.getMap().get("en").setString("Good evening");
+ em.getTransaction().commit();
+ em.clear();
+
+ em.getTransaction().begin();
+ ms = em.find(MultilingualString.class, entity1.getId());
+
+ // This assertion fails, the entity still has the old value.
+ assertEquals("Good evening", ms.getText("en"));
+ em.getTransaction().commit();
+ }
+
+ private void createObj(EntityManagerFactory emf) {
+ EntityManager em = emf.createEntityManager();
+ EntityTransaction tran = em.getTransaction();
+ tran.begin();
+
+ entity1 = new MultilingualString("de", "Guten Tag");
+ entity1.setText("en", "Good morning");
+ em.persist(entity1);
+
+ em.flush();
+ tran.commit();
+ em.close();
+ }
+}
+
Propchange:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/maps/update/TestMapUpdate.java
------------------------------------------------------------------------------
svn:eol-style = native