You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by fa...@apache.org on 2009/09/26 15:42:20 UTC

svn commit: r819139 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/

Author: faywang
Date: Sat Sep 26 13:42:19 2009
New Revision: 819139

URL: http://svn.apache.org/viewvc?rev=819139&view=rev
Log:
OPENJPA-1253: support foreign key strategy for uni-directional one to many mapping using Map with entity as the Map key.

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/EntityC_U1M_Map_RelKey_FK.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/Uni_1ToM_Map_RelKey_FK.java
Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMappingInfo.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ValueMappingInfo.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerRelationMapTableFieldStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MapTableFieldStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationRelationMapTableFieldStrategy.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/EntityC.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMappingInfo.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMappingInfo.java?rev=819139&r1=819138&r2=819139&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMappingInfo.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMappingInfo.java Sat Sep 26 13:42:19 2009
@@ -23,6 +23,7 @@
 import java.util.List;
 
 import org.apache.openjpa.conf.Compatibility;
+import org.apache.openjpa.jdbc.meta.strats.MapTableFieldStrategy;
 import org.apache.openjpa.jdbc.schema.Column;
 import org.apache.openjpa.jdbc.schema.ColumnIO;
 import org.apache.openjpa.jdbc.schema.ForeignKey;
@@ -147,6 +148,17 @@
         }, schemaName, tableName, adapt);
     }
 
+    public ForeignKey getJoinForeignKey (final FieldMapping field, Table table,
+        boolean adapt) {
+        Strategy strat = field.getStrategy();
+        if (strat instanceof MapTableFieldStrategy && 
+            ((MapTableFieldStrategy)strat).isUni1ToMFK()) {
+            List cols = field.getElementMapping().getValueInfo().getColumns();
+            return getJoin(field, table, adapt, cols);
+        }
+        return null;
+    }
+    
     /**
      * Return the join from the field table to the owning class table.
      */
@@ -154,7 +166,11 @@
         boolean adapt) {
         // if we have no join columns defined, check class-level join
     	// if the given field is embedded then consider primary table of owner
-        List cols = getColumns();
+        return getJoin(field, table, adapt, getColumns());
+    }
+    
+    public ForeignKey getJoin(final FieldMapping field, Table table,
+            boolean adapt, List cols) {
         if (cols.isEmpty()) {
         	ClassMapping mapping;
         	if (field.isEmbedded() && 

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ValueMappingInfo.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ValueMappingInfo.java?rev=819139&r1=819138&r2=819139&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ValueMappingInfo.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ValueMappingInfo.java Sat Sep 26 13:42:19 2009
@@ -107,7 +107,7 @@
             getDefiningMapping(), rel, inversable, adapt);
     }
 
-    private Table getTable(ValueMapping val) {
+    public Table getTable(ValueMapping val) {
         FieldMapping field = val.getFieldMapping();
         Table table = field.getTable();
         if (table == null) {

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerRelationMapTableFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerRelationMapTableFieldStrategy.java?rev=819139&r1=819138&r2=819139&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerRelationMapTableFieldStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerRelationMapTableFieldStrategy.java Sat Sep 26 13:42:19 2009
@@ -150,7 +150,7 @@
             // map to the owner table
             handleMappedByForeignKey(adapt);
         } else if ((!isUni1ToMFK() && isBi1ToMJT()) || 
-            (!isUni1ToMFK() && !isBi1ToMJT() && mapped == null)){ 
+            (!isUni1ToMFK() && !isBi1ToMJT() && mapped == null)) { 
             // map to a separate table
             field.mapJoin(adapt, true);
             if (val.getTypeMapping().isMapped()) {
@@ -306,29 +306,21 @@
             for (Iterator itr = rem.iterator(); itr.hasNext();) {
                 mkey = itr.next();
                 if (isUni1ToMFK()){
-                    delRow = rm.getRow(field.getElementMapping().getDeclaredTypeMapping().getTable(),
-                        Row.ACTION_UPDATE, sm, true);
-                    val.setForeignKey(delRow, null);
-                } 
-                
-                HandlerStrategies.where(key, mkey, store, delRow,
-                    _kcols);
-                if (!isUni1ToMFK())
+                    updateSetNull(sm, mkey, store, rm);
+                } else {
+                    HandlerStrategies.where(key, mkey, store, delRow, _kcols);
                     rm.flushSecondaryRow(delRow);
+                }
             }
             if (!canChange && !change.isEmpty()) {
                 for (Iterator itr = change.iterator(); itr.hasNext();) {
                     mkey = itr.next();
                     if (isUni1ToMFK()){
-                        delRow = rm.getRow(field.getElementMapping().getDeclaredTypeMapping().getTable(),
-                            Row.ACTION_UPDATE, sm, true);
-                        val.setForeignKey(delRow, null);
-                    } 
-
-                    HandlerStrategies.where(key, itr.next(), store, delRow,
-                        _kcols);
-                    if (!isUni1ToMFK())
+                        updateSetNull(sm, mkey, store, rm);
+                    } else { 
+                        HandlerStrategies.where(key, itr.next(), store, delRow,  _kcols);
                         rm.flushSecondaryRow(delRow);
+                    }
                 }
             }
         }
@@ -422,8 +414,38 @@
     
     public void delete(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
         throws SQLException {
-        if ((field.getMappedBy() != null && !isBi1ToMJT()) || isUni1ToMFK())
+        if ((field.getMappedBy() != null && !isBi1ToMJT()))
             return;
+        if (isUni1ToMFK()) {
+            Map mapObj = (Map)sm.fetchObject(field.getIndex());
+            updateSetNull(sm, store, rm, mapObj.keySet());
+            return;
+        }    
+
         super.delete(sm, store, rm);
     }
+    
+    private void updateSetNull(OpenJPAStateManager sm, JDBCStore store, RowManager rm,
+        Set rem) throws SQLException {
+        for (Iterator itr = rem.iterator(); itr.hasNext();) {
+            Object mkey = itr.next();
+            updateSetNull(sm, mkey, store, rm);
+        }
+    }
+
+    private void updateSetNull(OpenJPAStateManager sm, Object mkey, 
+        JDBCStore store, RowManager rm) throws SQLException {
+        ValueMapping key = field.getKeyMapping();
+        ValueMapping val = field.getElementMapping();
+        StoreContext ctx = store.getContext();
+        ValueMappingInfo vinfo = field.getElementMapping().getValueInfo();
+        Table table = vinfo.getTable(val);
+        ForeignKey joinFK = field.getMappingInfo().getJoinForeignKey(field, table, true);
+        Row delRow = rm.getRow(field.getElementMapping().getDeclaredTypeMapping().getTable(),
+            Row.ACTION_UPDATE, sm, true);
+        delRow.whereForeignKey(joinFK, sm);
+        val.setForeignKey(delRow, null);
+        HandlerStrategies.set(key, null, store, delRow, _kcols, _kio, true);
+        HandlerStrategies.where(key, mkey, store, delRow, _kcols);
+    }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MapTableFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MapTableFieldStrategy.java?rev=819139&r1=819138&r2=819139&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MapTableFieldStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/MapTableFieldStrategy.java Sat Sep 26 13:42:19 2009
@@ -99,13 +99,13 @@
         
     }
     
-    protected boolean isBi1ToMJT() {
+    public boolean isBi1ToMJT() {
         if (_isBi1ToMJT == null)
             isNonDefaultMapping();
         return _isBi1ToMJT;
     }
     
-    protected boolean isUni1ToMFK() {
+    public boolean isUni1ToMFK() {
         if (_isUni1ToMFK == null)
             isNonDefaultMapping();
         return _isUni1ToMFK;

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationRelationMapTableFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationRelationMapTableFieldStrategy.java?rev=819139&r1=819138&r2=819139&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationRelationMapTableFieldStrategy.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationRelationMapTableFieldStrategy.java Sat Sep 26 13:42:19 2009
@@ -80,7 +80,17 @@
         kunion.setLRS(lrs);
         kunion.select(new Union.Selector() {
             public void select(Select sel, int idx) {
-                sel.whereForeignKey(field.getJoinForeignKey(),
+                ForeignKey joinFK = null;
+                if (isUni1ToMFK()) {
+                    ValueMapping val = field.getElementMapping();
+                    ValueMappingInfo vinfo = val.getValueInfo();
+                    Table table = vinfo.getTable(val);
+                    joinFK = field.getMappingInfo().getJoinForeignKey(field, table, true);
+                } else {
+                    joinFK = field.getJoinForeignKey();
+                }
+                
+                sel.whereForeignKey(joinFK,
                     sm.getObjectId(), field.getDefiningMapping(), store);
 
                 // order before select in case we're faking union with
@@ -106,21 +116,29 @@
         vunion.setLRS(lrs);
         vunion.select(new Union.Selector() {
             public void select(Select sel, int idx) {
-                sel.whereForeignKey(field.getJoinForeignKey(),
-                    sm.getObjectId(), field.getDefiningMapping(), store);
-
-                // order before select in case we're faking union with
-                // multiple selects; order vals used to merge results
-                FieldMapping mapped = field.getMappedByMapping();
-                Joins joins = joinValueRelation(sel.newJoins(), vals[idx]);
-                sel.orderBy(field.getKeyMapping().getColumns(), true, true);
-                sel.select(vals[idx], field.getElementMapping().
-                    getSelectSubclasses(), store, fetch, eagerMode, joins);
-
-                //### cheat: result joins only care about the relation path;
-                //### thus we can use first mapping of union only
-                if (idx == 0)
-                    resJoins[1] = joins;
+                if (isUni1ToMFK()) {
+                    sel.orderBy(field.getKeyMapping().getColumns(), true, true);
+                    sel.select(vals[idx], field.getElementMapping().
+                        getSelectSubclasses(), store, fetch, eagerMode, null);
+                    sel.whereForeignKey(field.getElementMapping().getForeignKey(),
+                        sm.getObjectId(), field.getElementMapping().getDeclaredTypeMapping(), store);
+                    
+                } else {
+                    sel.whereForeignKey(field.getJoinForeignKey(),
+                        sm.getObjectId(), field.getDefiningMapping(), store);
+
+                    // order before select in case we're faking union with
+                    // multiple selects; order vals used to merge results
+                    Joins joins = joinValueRelation(sel.newJoins(), vals[idx]);
+                    sel.orderBy(field.getKeyMapping().getColumns(), true, true);
+                    sel.select(vals[idx], field.getElementMapping().
+                        getSelectSubclasses(), store, fetch, eagerMode, joins);
+
+                    //### cheat: result joins only care about the relation path;
+                    //### thus we can use first mapping of union only
+                    if (idx == 0)
+                        resJoins[1] = joins;
+                }
             }
         });
 
@@ -184,10 +202,12 @@
         FieldMapping mapped = field.getMappedByMapping();
         DBDictionary dict = field.getMappingRepository().getDBDictionary();
         String keyName = null;
-        if (mapped != null) {         
+        if ((isUni1ToMFK() && !isBi1ToMJT()) || 
+            (!isUni1ToMFK() && !isBi1ToMJT() && mapped != null)) { 
             handleMappedByForeignKey(adapt);
             keyName = dict.getValidColumnName("vkey", field.getTable());
-         } else {
+         } else if ((!isUni1ToMFK() && isBi1ToMJT()) || 
+            (!isUni1ToMFK() && !isBi1ToMJT() && mapped == null)) { 
             field.mapJoin(adapt, true);
             mapTypeJoin(val, "value", adapt);
             keyName = dict.getValidColumnName("key", field.getTable());
@@ -226,13 +246,15 @@
         if (map == null || map.isEmpty())
             return;
         
-        if (field.getMappedBy() != null)
+        if (!isBi1ToMJT() && field.getMappedBy() != null)
             return;
 
-        Row row = rm.getSecondaryRow(field.getTable(), Row.ACTION_INSERT);
-        row.setForeignKey(field.getJoinForeignKey(), field.getJoinColumnIO(),
-            sm);
-
+        Row row = null;
+        if (!isUni1ToMFK()) {
+            row = rm.getSecondaryRow(field.getTable(), Row.ACTION_INSERT);
+            row.setForeignKey(field.getJoinForeignKey(), field.getJoinColumnIO(),
+                sm);
+        }
         ValueMapping key = field.getKeyMapping();
         ValueMapping val = field.getElementMapping();
         StoreContext ctx = sm.getContext();
@@ -242,10 +264,17 @@
             entry = (Map.Entry) itr.next();
             keysm = RelationStrategies.getStateManager(entry.getKey(), ctx);
             valsm = RelationStrategies.getStateManager(entry.getValue(), ctx);
+            if (isUni1ToMFK()){
+                row = rm.getRow(field.getElementMapping().getDeclaredTypeMapping().getTable(),
+                    Row.ACTION_UPDATE, valsm, true);
+                row.wherePrimaryKey(valsm);
+                val.setForeignKey(row, sm);
+            } else {
+                val.setForeignKey(row, valsm);
+            }
             key.setForeignKey(row, keysm);
-            val.setForeignKey(row, valsm);
             
-            // so far, we poplulated the key/value of each
+            // so far, we populated the key/value of each
             // map element owned by the entity.
             // In the case of ToMany, and both sides
             // use Map to represent the relation,
@@ -253,13 +282,14 @@
             // from the view point of the owned side
             PersistenceCapable obj = sm.getPersistenceCapable();
             if (!populateKey(row, valsm, obj, ctx, rm, store))
-                rm.flushSecondaryRow(row);
+                if (!isUni1ToMFK())
+                    rm.flushSecondaryRow(row);
         }
     }
 
     public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
         throws SQLException {
-        if (field.getMappedBy() != null)
+        if (field.getMappedBy() != null && !isBi1ToMJT())
             return;
         
         Map map = (Map) sm.fetchObject(field.getIndex());
@@ -289,38 +319,69 @@
         boolean canChange = val.getForeignKey().isLogical();
         Object mkey;
         if (canChange && !change.isEmpty()) {
-            Row changeRow = rm.getSecondaryRow(field.getTable(),
-                Row.ACTION_UPDATE);
-            changeRow.whereForeignKey(field.getJoinForeignKey(), sm);
-
+            Row changeRow = null;
+            if (!isUni1ToMFK()) {
+                rm.getSecondaryRow(field.getTable(),
+                    Row.ACTION_UPDATE);
+                changeRow.whereForeignKey(field.getJoinForeignKey(), sm);
+            }
             for (Iterator itr = change.iterator(); itr.hasNext();) {
                 mkey = itr.next();
+                Object mval = map.get(mkey);
+                if (mval == null) {
+                    Set<Map.Entry> entries = map.entrySet();
+                    for (Map.Entry entry : entries) {
+                        if (entry.getKey().equals(mkey))
+                            mval = entry.getValue();
+                    }
+                }
+                if (mval == null)
+                    continue;
                 keysm = RelationStrategies.getStateManager(mkey, ctx);
-                valsm = RelationStrategies.getStateManager(map.get(mkey), ctx);
+                valsm = RelationStrategies.getStateManager(mval, ctx);
                 key.whereForeignKey(changeRow, keysm);
-                val.setForeignKey(changeRow, valsm);
-                rm.flushSecondaryRow(changeRow);
+                if (isUni1ToMFK()){
+                    changeRow = rm.getRow(field.getElementMapping().getDeclaredTypeMapping().getTable(),
+                        Row.ACTION_UPDATE, valsm, true);
+                    changeRow.wherePrimaryKey(valsm);
+                    val.setForeignKey(changeRow, sm);
+                } else {
+                    val.setForeignKey(changeRow, valsm);
+                    rm.flushSecondaryRow(changeRow);
+                }
             }
         }
 
         // delete the removes
         Collection rem = ct.getRemoved();
         if (!rem.isEmpty() || (!canChange && !change.isEmpty())) {
-            Row delRow = rm.getSecondaryRow(field.getTable(),
-                Row.ACTION_DELETE);
-            delRow.whereForeignKey(field.getJoinForeignKey(), sm);
+            Row delRow = null;
+            if (!isUni1ToMFK()) {
+                delRow = rm.getSecondaryRow(field.getTable(),
+                    Row.ACTION_DELETE);
+                delRow.whereForeignKey(field.getJoinForeignKey(), sm);
+            }
 
             for (Iterator itr = rem.iterator(); itr.hasNext();) {
-                keysm = RelationStrategies.getStateManager(itr.next(), ctx);
-                key.whereForeignKey(delRow, keysm);
-                rm.flushSecondaryRow(delRow);
+                Object pc = itr.next();
+                if (isUni1ToMFK()){
+                    updateSetNull(sm, rm, pc);
+                } else {
+                    keysm = RelationStrategies.getStateManager(pc, ctx);
+                    key.whereForeignKey(delRow, keysm);
+                    rm.flushSecondaryRow(delRow);
+                }
             }
             if (!canChange && !change.isEmpty()) {
                 for (Iterator itr = change.iterator(); itr.hasNext();) {
-                    keysm = RelationStrategies.getStateManager(itr.next(),
-                        ctx);
-                    key.whereForeignKey(delRow, keysm);
-                    rm.flushSecondaryRow(delRow);
+                    Object pc = itr.next();
+                    if (isUni1ToMFK()){
+                        updateSetNull(sm, rm, pc);
+                    } else { 
+                        keysm = RelationStrategies.getStateManager(pc, ctx);
+                        key.whereForeignKey(delRow, keysm);
+                        rm.flushSecondaryRow(delRow);
+                    }
                 }
             }
         }
@@ -328,28 +389,65 @@
         // insert the adds
         Collection add = ct.getAdded();
         if (!add.isEmpty() || (!canChange && !change.isEmpty())) {
-            Row addRow = rm.getSecondaryRow(field.getTable(),
-                Row.ACTION_INSERT);
-            addRow.setForeignKey(field.getJoinForeignKey(),
-                field.getJoinColumnIO(), sm);
-
+            Row addRow = null;
+            if (!isUni1ToMFK()) {
+                addRow = rm.getSecondaryRow(field.getTable(),
+                    Row.ACTION_INSERT);
+                addRow.setForeignKey(field.getJoinForeignKey(),
+                    field.getJoinColumnIO(), sm);
+            }
             for (Iterator itr = add.iterator(); itr.hasNext();) {
                 mkey = itr.next();
+                Object mval = map.get(mkey);
+                if (mval == null) {
+                    Set<Map.Entry> entries = map.entrySet();
+                    for (Map.Entry entry : entries) {
+                        if (entry.getKey().equals(mkey))
+                            mval = entry.getValue();
+                    }
+                }
+                if (mval == null)
+                    continue;
                 keysm = RelationStrategies.getStateManager(mkey, ctx);
-                valsm = RelationStrategies.getStateManager(map.get(mkey), ctx);
-                key.setForeignKey(addRow, keysm);
-                val.setForeignKey(addRow, valsm);
-                rm.flushSecondaryRow(addRow);
+                valsm = RelationStrategies.getStateManager(mval, ctx);
+                if (isUni1ToMFK()){
+                    addRow = rm.getRow(field.getElementMapping().getDeclaredTypeMapping().getTable(),
+                        Row.ACTION_UPDATE, valsm, true);
+                    addRow.wherePrimaryKey(valsm);
+                    key.setForeignKey(addRow, keysm);
+                    val.setForeignKey(addRow, sm);
+                } else {
+                    key.setForeignKey(addRow, keysm);
+                    val.setForeignKey(addRow, valsm);
+                    rm.flushSecondaryRow(addRow);
+                }
             }
             if (!canChange && !change.isEmpty()) {
                 for (Iterator itr = change.iterator(); itr.hasNext();) {
                     mkey = itr.next();
+                    Object mval = map.get(mkey);
+                    if (mval == null) {
+                        Set<Map.Entry> entries = map.entrySet();
+                        for (Map.Entry entry : entries) {
+                            if (entry.getKey().equals(mkey))
+                                mval = entry.getValue();
+                        }
+                    }
+                    if (mval == null)
+                        continue;
                     keysm = RelationStrategies.getStateManager(mkey, ctx);
-                    valsm = RelationStrategies.getStateManager(map.get(mkey),
-                        ctx);
-                    key.setForeignKey(addRow, keysm);
-                    val.setForeignKey(addRow, valsm);
-                    rm.flushSecondaryRow(addRow);
+                    valsm = RelationStrategies.getStateManager(mval, ctx);
+                    if (isUni1ToMFK()){
+                        addRow = rm.getRow(field.getElementMapping().getDeclaredTypeMapping().getTable(),
+                            Row.ACTION_UPDATE, valsm, true);
+                        addRow.wherePrimaryKey(valsm);
+                        key.setForeignKey(addRow, keysm);
+                        val.setForeignKey(addRow, sm);
+                    } else {
+                        key.setForeignKey(addRow, keysm);
+                        val.setForeignKey(addRow, valsm);
+                        rm.flushSecondaryRow(addRow);
+                    }
                 }
             }
         }
@@ -403,4 +501,39 @@
         return RelationStrategies.toDataStoreValue(field.getKeyMapping(),
             val, store);
     }
+    
+    public void delete(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+        throws SQLException {
+        if (isUni1ToMFK()) {
+            Map mapObj = (Map)sm.fetchObject(field.getIndex());
+            updateSetNull(sm, store, rm, mapObj.keySet());
+            return;
+        }    
+        super.delete(sm, store, rm);
+    }
+    
+    private void updateSetNull(OpenJPAStateManager sm, JDBCStore store, RowManager rm,
+        Set rem) throws SQLException {
+        for (Iterator itr = rem.iterator(); itr.hasNext();) {
+            Object mkey = itr.next();
+            updateSetNull(sm, rm, mkey);
+        }
+    }
+    
+    private void updateSetNull(OpenJPAStateManager sm, RowManager rm, Object mkey) 
+        throws SQLException {
+        StoreContext ctx = sm.getContext();
+        ValueMapping key = field.getKeyMapping();
+        ValueMapping val = field.getElementMapping();
+        OpenJPAStateManager keysm = RelationStrategies.getStateManager(mkey, ctx);
+        Row delRow = rm.getRow(field.getElementMapping().getDeclaredTypeMapping().getTable(),
+                Row.ACTION_UPDATE, sm, true);
+        ValueMappingInfo vinfo = field.getElementMapping().getValueInfo();
+        Table table = vinfo.getTable(val);
+        ForeignKey joinFK = field.getMappingInfo().getJoinForeignKey(field, table, true);
+        delRow.whereForeignKey(joinFK, sm);
+        delRow.whereForeignKey(key.getForeignKey(), keysm);
+        val.setForeignKey(delRow, null);
+        key.setForeignKey(delRow, null);
+    }
 }

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/EntityC.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/EntityC.java?rev=819139&r1=819138&r2=819139&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/EntityC.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/EntityC.java Sat Sep 26 13:42:19 2009
@@ -44,4 +44,17 @@
     public void setName(String name) { 
         this.name = name; 
     }
+    
+    public int hashCode() {
+        return name.hashCode() + (int)id;
+    }
+    
+    public boolean equals(Object o) {
+        if (!(o instanceof EntityC)) return false;
+        EntityC c = (EntityC)o;
+        if (!c.name.equals(name)) return false;
+        if (c.id != id) return false;
+        return true;
+    }
+    
 }

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/EntityC_U1M_Map_RelKey_FK.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/EntityC_U1M_Map_RelKey_FK.java?rev=819139&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/EntityC_U1M_Map_RelKey_FK.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/EntityC_U1M_Map_RelKey_FK.java Sat Sep 26 13:42:19 2009
@@ -0,0 +1,61 @@
+/*
+ * 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.compat;
+
+import java.util.List;
+
+import javax.persistence.*;
+
+@Entity
+public class EntityC_U1M_Map_RelKey_FK {
+
+    @Id
+    @GeneratedValue
+    private long id;
+
+    private String name;
+
+    public long getId() { 
+        return id; 
+    }
+
+    public String getName() { 
+        return name; 
+    }
+
+    public void setName(String name) { 
+        this.name = name; 
+    }
+
+    public int hashCode() {
+        return name.hashCode() + (int)id;
+    }
+    
+    public boolean equals(Object o) {
+        if (!(o instanceof EntityC_U1M_Map_RelKey_FK)) return false;
+        EntityC_U1M_Map_RelKey_FK c = (EntityC_U1M_Map_RelKey_FK)o;
+        if (!c.name.equals(name)) return false;
+        if (c.id != id) return false;
+        return true;
+    }
+    
+    public String toString() {
+        return id + name;
+    }
+}

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java?rev=819139&r1=819138&r2=819139&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/TestSpecCompatibilityOptions.java Sat Sep 26 13:42:19 2009
@@ -341,7 +341,6 @@
         em.clear();
     }
 
-    // non default
     public void crudBi1MJT(EntityManager em) {
         Bi_1ToM_JT b = new Bi_1ToM_JT();
         b.setName("bi1mfk");
@@ -398,6 +397,9 @@
         types.add(Uni_1ToM_Map_FK.class);
         types.add(EntityC_B1M_Map_JT.class);
         types.add(Bi_1ToM_Map_JT.class);
+        types.add(EntityC_U1M_Map_RelKey_FK.class);
+        types.add(Uni_1ToM_Map_RelKey_FK.class);
+        types.add(EntityC.class);
         OpenJPAEntityManagerFactorySPI emf = createEMF2_0(types);
         EntityManager em = emf.createEntityManager();
         
@@ -408,6 +410,7 @@
             assertSQLFragnments(sql, "CREATE TABLE EntityC_U1M_Map_FK", "Uni1MFK_ID", "KEY0");
             crudUni1MMapFK(em);
             crudBi1MMapJT(em);
+            crudUni1MMapRelKeyFK(em);
         } catch (Exception e) {
             e.printStackTrace();
             fail("OneToMany mapping failed with exception message: " + e.getMessage());
@@ -526,6 +529,74 @@
         em.getTransaction().commit();
     }
     
+    public void crudUni1MMapRelKeyFK(EntityManager em) {
+        //create
+        Uni_1ToM_Map_RelKey_FK u = new Uni_1ToM_Map_RelKey_FK();
+        u.setName("uni1mfk");
+        Map<EntityC, EntityC_U1M_Map_RelKey_FK> cs = new HashMap<EntityC, EntityC_U1M_Map_RelKey_FK>();
+        EntityC_U1M_Map_RelKey_FK c1 = new EntityC_U1M_Map_RelKey_FK();
+        c1.setName("c1");
+        EntityC cKey1 = new EntityC();
+        cKey1.setName("cKey1");
+        cs.put(cKey1, c1);
+        EntityC_U1M_Map_RelKey_FK c2 = new EntityC_U1M_Map_RelKey_FK();
+        c2.setName("c2");
+        EntityC cKey2 = new EntityC();
+        cKey2.setName("cKey2");
+        cs.put(cKey2, c1);
+        cs.put(cKey2, c2);
+        u.setEntityCs(cs);
+        em.persist(u);
+        em.persist(c1);
+        em.persist(c2);
+        em.persist(cKey1);
+        em.persist(cKey2);
+        em.getTransaction().begin();
+        em.getTransaction().commit();
+
+        //update by adding a new C
+        em.getTransaction().begin();
+        cs = u.getEntityCs();
+        u.setName("uni1mfk_new");
+        EntityC_U1M_Map_RelKey_FK c3 = new EntityC_U1M_Map_RelKey_FK();
+        c3.setName("c3");
+        EntityC cKey3 = new EntityC();
+        cKey3.setName("cKey3");
+        cs.put(cKey3, c3);
+        em.persist(c3);
+        em.persist(cKey3);
+        em.getTransaction().commit();
+        
+        // update by removing a c and then add this c to a new u
+        em.getTransaction().begin();
+        EntityC_U1M_Map_RelKey_FK c4 = cs.remove(cKey1);
+        
+        Uni_1ToM_Map_RelKey_FK u2 = new Uni_1ToM_Map_RelKey_FK();
+        u2.setName("uni1mfk2");
+        Map<EntityC, EntityC_U1M_Map_RelKey_FK> cs2 = new HashMap<EntityC, EntityC_U1M_Map_RelKey_FK>();
+        cs2.put(cKey1, c4);
+        u2.setEntityCs(cs2);
+        em.persist(u2);
+        em.getTransaction().commit();
+        em.clear();
+        
+        //query
+        Query q = em.createQuery("SELECT u FROM Uni_1ToM_Map_RelKey_FK u where u.name='uni1mfk_new'");
+        Uni_1ToM_Map_RelKey_FK u1 = (Uni_1ToM_Map_RelKey_FK)q.getSingleResult();
+        assertEquals(u, u1);
+        em.clear();
+
+        //find
+        long id = u1.getId();
+        Uni_1ToM_Map_RelKey_FK findU = em.find(Uni_1ToM_Map_RelKey_FK.class, id);
+        assertEquals(u, findU);
+        
+        //remove
+        em.getTransaction().begin();
+        em.remove(findU);
+        em.getTransaction().commit();
+    }
+
     private OpenJPAEntityManagerFactorySPI createEMF2_0(List<Class<?>> types) {
         Map<Object,Object> map = new HashMap<Object,Object>();
         map.put("openjpa.jdbc.JDBCListeners", 

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/Uni_1ToM_Map_RelKey_FK.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/Uni_1ToM_Map_RelKey_FK.java?rev=819139&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/Uni_1ToM_Map_RelKey_FK.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/Uni_1ToM_Map_RelKey_FK.java Sat Sep 26 13:42:19 2009
@@ -0,0 +1,97 @@
+/*
+ * 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.compat;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.persistence.*;
+
+//non-default mapping
+//Sec 11.1.36, Example 3: 
+//    Unidirectional One-to-Many association using a foreign key mapping
+//    In Customer class:
+//    @OneToMany(orphanRemoval=true)
+//    @JoinColumn(name="CUST_ID") // join column is in table for Order
+//    public Set<Order> getOrders() {return orders;}
+
+@Entity
+public class Uni_1ToM_Map_RelKey_FK {
+
+    @Id
+    @GeneratedValue
+    private long id;
+
+    private String name;
+
+    @OneToMany(/*cascade = CascadeType.ALL,*/ fetch=FetchType.EAGER)
+    @JoinColumn(name="Uni1MFK_ID")
+    private Map<EntityC, EntityC_U1M_Map_RelKey_FK> entityCs = null;
+    
+    public long getId() { 
+        return id; 
+    }
+
+    public String getName() { 
+        return name; 
+    }
+
+    public void setName(String name) { 
+        this.name = name; 
+    }
+
+    public Map<EntityC, EntityC_U1M_Map_RelKey_FK> getEntityCs() { 
+        return entityCs; 
+    }
+
+    public void setEntityCs(Map<EntityC, EntityC_U1M_Map_RelKey_FK> entityCs) { 
+        this.entityCs = entityCs; 
+    }
+
+    public int hashCode() {
+        return name.hashCode();
+    }
+    
+    public boolean equals(Object o) {
+        if (!(o instanceof Uni_1ToM_Map_RelKey_FK)) return false;
+        Uni_1ToM_Map_RelKey_FK b = (Uni_1ToM_Map_RelKey_FK)o;
+        if (!b.name.equals(name)) return false;
+        if (b.entityCs.size() != entityCs.size()) return false;
+        
+        Set<EntityC> coll = b.entityCs.keySet();
+        for (EntityC cKey : coll) {
+            EntityC_U1M_Map_RelKey_FK val = (EntityC_U1M_Map_RelKey_FK)getValue(b.entityCs, cKey);
+            EntityC_U1M_Map_RelKey_FK val1 = (EntityC_U1M_Map_RelKey_FK)getValue(entityCs, cKey); 
+            if (!val.equals(val1))
+                return false;
+        }
+        return true;
+    }
+    
+    private Object getValue(Map map, Object mkey) {
+        Set<Map.Entry> entries = map.entrySet();
+        for (Map.Entry entry : entries) {
+            if (entry.getKey().equals(mkey))
+                return entry.getValue();
+        }
+        return null;
+    }
+}