openjpa-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From p..@apache.org
Subject svn commit: r592917 [1/3] - 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/main/java/org/apache/openjpa/persistence/jdbc/ openjpa-...
Date Wed, 07 Nov 2007 21:46:11 GMT
Author: pcl
Date: Wed Nov  7 13:46:09 2007
New Revision: 592917

URL: http://svn.apache.org/viewvc?rev=592917&view=rev
Log:
OPENJPA-417. Added support for containers of primitives, embeddeds, externalizables, etc.

This does not include test cases for the following annotations:

    KeyClassCriteria
    KeyColumns (KeyColumn is tested, and follows similar pathways)
    KeyEmbeddedMapping
    KeyJoinColumns (KeyJoinColumn is tested, and follows similar pathways)
    KeyStrategy
    ElementColumns (ElementColumn is tested, and follows similar pathways)
    ElementEmbeddedMapping
    ElementStrategy
    XEmbeddedMapping
    XMappingOverrides (XMappingOverride is tested, and follows similar pathways)

This change presents a backward-compat issue for OpenJPA: in the past, a Collection<String> or Map<String,String> would be mapped as a BLOB by specifying @PersistentMap or @PersistentCollection on the field.

Added:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ElementEmbedValueHandler.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerHandlerMapTableFieldStrategy.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/RelationHandlerMapTableFieldStrategy.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationRelationMapTableFieldStrategy.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/ElementColumn.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/ElementColumns.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/ElementEmbeddedMapping.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/ElementStrategy.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/KeyClassCriteria.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/KeyColumn.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/KeyColumns.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/KeyEmbeddedMapping.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/KeyForeignKey.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/KeyIndex.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/KeyJoinColumn.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/KeyJoinColumns.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/KeyNonpolymorphic.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/KeyStrategy.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XEmbeddedMapping.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XMappingOverride.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XMappingOverrides.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/NonstandardMappingEntity.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/NonstandardMappingMappedSuper.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/PointHandler.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestNonstandardMappingAnnotations.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/HandlerCollectionInstance.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/HandlerToHandlerMapInstance.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/HandlerToRelationMapInstance.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/RelationToHandlerMapInstance.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/RelationToRelationMapInstance.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/SimpleEmbeddable.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestHandlerCollections.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestHandlerToHandlerMaps.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestHandlerToRelationMaps.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestRelationToHandlerMaps.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/TestRelationToRelationMaps.java
Removed:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/fields/TestPersistentMapTableConfiguration.java
Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/MappingTag.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestDiscriminator.java

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java?rev=592917&r1=592916&r2=592917&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java Wed Nov  7 13:46:09 2007
@@ -34,11 +34,15 @@
 import org.apache.openjpa.jdbc.meta.strats.CharArrayValueHandler;
 import org.apache.openjpa.jdbc.meta.strats.ClassNameDiscriminatorStrategy;
 import org.apache.openjpa.jdbc.meta.strats.ClobValueHandler;
+import org.apache.openjpa.jdbc.meta.strats.ElementEmbedValueHandler;
 import org.apache.openjpa.jdbc.meta.strats.EmbedFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.EmbeddedClassStrategy;
 import org.apache.openjpa.jdbc.meta.strats.FlatClassStrategy;
 import org.apache.openjpa.jdbc.meta.strats.FullClassStrategy;
+import org.apache.openjpa.jdbc.meta.strats.HandlerCollectionTableFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.HandlerFieldStrategy;
+import org.apache.openjpa.jdbc.meta.strats.HandlerHandlerMapTableFieldStrategy;
+import org.apache.openjpa.jdbc.meta.strats.HandlerRelationMapTableFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.ImmutableValueHandler;
 import org.apache.openjpa.jdbc.meta.strats.LobFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.MaxEmbeddedBlobFieldStrategy;
@@ -56,8 +60,10 @@
 import org.apache.openjpa.jdbc.meta.strats.RelationCollectionInverseKeyFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.RelationCollectionTableFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.RelationFieldStrategy;
+import org.apache.openjpa.jdbc.meta.strats.RelationHandlerMapTableFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.RelationMapInverseKeyFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.RelationMapTableFieldStrategy;
+import org.apache.openjpa.jdbc.meta.strats.RelationRelationMapTableFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.StateComparisonVersionStrategy;
 import org.apache.openjpa.jdbc.meta.strats.StringFieldStrategy;
 import org.apache.openjpa.jdbc.meta.strats.SubclassJoinDiscriminatorStrategy;
@@ -880,7 +886,9 @@
      */
     protected FieldStrategy handlerCollectionStrategy(FieldMapping field, 
         ValueHandler ehandler, boolean installHandlers) {
-        return null;
+        if (installHandlers)
+            field.getElementMapping().setHandler(ehandler);
+        return new HandlerCollectionTableFieldStrategy();
     }
 
     /**
@@ -890,7 +898,17 @@
     protected FieldStrategy handlerMapStrategy(FieldMapping field, 
         ValueHandler khandler, ValueHandler vhandler, boolean krel, 
         boolean vrel,  boolean installHandlers) {
-        return null;
+        if (installHandlers) {
+            field.getKeyMapping().setHandler(khandler);
+            field.getElementMapping().setHandler(vhandler);
+        }
+        if (!krel && !vrel)
+            return new HandlerHandlerMapTableFieldStrategy();
+        if (!krel && vrel)
+            return new HandlerRelationMapTableFieldStrategy();
+        if (krel && !vrel)
+            return new RelationHandlerMapTableFieldStrategy();
+        return new RelationRelationMapTableFieldStrategy();
     }
 
     /**
@@ -1064,6 +1082,8 @@
             case JavaTypes.OID:
                 return new ObjectIdValueHandler();
         }
+        if (val.isEmbeddedPC())
+            return new ElementEmbedValueHandler();
         return null;
     }
 

Added: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ElementEmbedValueHandler.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ElementEmbedValueHandler.java?rev=592917&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ElementEmbedValueHandler.java (added)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ElementEmbedValueHandler.java Wed Nov  7 13:46:09 2007
@@ -0,0 +1,183 @@
+/*
+ * 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.jdbc.meta.strats;
+
+import java.sql.*;
+import java.util.*;
+
+import org.apache.openjpa.lib.util.*;
+import org.apache.openjpa.kernel.*;
+import org.apache.openjpa.util.*;
+import org.apache.openjpa.jdbc.meta.*;
+import org.apache.openjpa.jdbc.kernel.*;
+import org.apache.openjpa.jdbc.schema.*;
+
+/**
+ * <p>Handler for embedded objects as elements of a collection or map.  For
+ * embedded objects as fields, use the more powerful
+ * {@link EmbedFieldStrategy}.</p>
+ *
+ * @author Abe White
+ * @since 0.4.0, 1.1.0
+ * @nojavadoc
+ */
+public class ElementEmbedValueHandler
+    extends EmbedValueHandler
+    implements RelationId {
+
+    private static final Localizer _loc = Localizer.forPackage
+        (ElementEmbedValueHandler.class);
+
+    private ValueMapping _vm = null;
+    private Column[] _cols = null;
+    private Object[] _args = null;
+    private int _nullIdx = -1;
+    private boolean _synthetic = false;
+
+    public Column[] map(ValueMapping vm, String name, ColumnIO io,
+        boolean adapt) {
+        LinkedList cols = new LinkedList();
+        LinkedList args = new LinkedList();
+        super.map(vm, name, io, adapt, cols, args);
+
+        ValueMappingInfo vinfo = vm.getValueInfo();
+        Column nullInd = vinfo.getNullIndicatorColumn(vm, name,
+            vm.getFieldMapping().getTable(), adapt);
+        if (nullInd != null)
+            vm.setColumns(new Column[]{ nullInd });
+
+        // record index of null indicator column and whether it is synthetic
+        if (nullInd != null) {
+            _nullIdx = cols.indexOf(nullInd);
+            if (_nullIdx == -1) {
+                cols.addFirst(nullInd);
+                args.addFirst(null);
+                _nullIdx = 0;
+                _synthetic = true;
+            }
+        }
+
+        _vm = vm;
+        _cols = (Column[]) cols.toArray(new Column[cols.size()]);
+        _args = args.toArray();
+        return _cols;
+    }
+
+    public boolean objectValueRequiresLoad(ValueMapping vm) {
+        return true;
+    }
+
+    public Object getResultArgument(ValueMapping vm) {
+        return _args;
+    }
+
+    public Object toDataStoreValue(ValueMapping vm, Object val,
+        JDBCStore store) {
+        OpenJPAStateManager em = store.getContext().getStateManager(val);
+        Object rval = null;
+        if (_cols.length > 1)
+            rval = new Object[_cols.length];
+
+        // set null indicator column
+        int idx = 0;
+        if (_synthetic) {
+            Object cval = ((EmbeddedClassStrategy) vm.getEmbeddedMapping().
+                getStrategy()).getNullIndicatorValue(em);
+            if (_cols.length == 1)
+                return cval;
+            ((Object[]) rval)[idx++] = cval;
+        }
+
+        return super.toDataStoreValue(em, vm, store, _cols, rval, idx);
+    }
+
+    public Object toObjectValue(ValueMapping vm, Object val,
+        OpenJPAStateManager sm, JDBCStore store, JDBCFetchConfiguration fetch)
+        throws SQLException {
+        if (sm == null)
+            throw new InvalidStateException(_loc.get("cant-project-owned",
+                vm));
+
+        // check null indicator first
+        if (_nullIdx != -1) {
+            Object nval;
+            if (_cols.length == 1)
+                nval = val;
+            else
+                nval = ((Object[]) val)[_nullIdx];
+            if (((EmbeddedClassStrategy) vm.getEmbeddedMapping().
+                getStrategy()).indicatesNull(nval))
+                return null;
+        }
+
+        // create embedded instance
+        OpenJPAStateManager em = store.getContext().embed(null, null, sm, vm);
+        int idx = (_synthetic) ? 1 : 0;
+        super.toObjectValue(em, vm, val, store, fetch, _cols, idx);
+
+        // after loading everything from result, load the rest of the
+        // configured fields
+        em.load(fetch);
+        return em.getManagedInstance();
+    }
+
+    /////////////////////////////
+    // RelationId implementation
+    /////////////////////////////
+
+    public Object toRelationDataStoreValue(OpenJPAStateManager sm, Column col) {
+        return toRelationDataStoreValue(sm, col, 0);
+    }
+
+    /**
+     * Recursive helper.
+     */
+    private Object toRelationDataStoreValue(OpenJPAStateManager sm, Column col,
+        int idx) {
+        FieldMapping field = findField(col, idx);
+        if (field == null)
+            throw new InternalException();
+
+        if (field.getHandler() instanceof RelationId)
+            return ((RelationId) field.getStrategy()).
+                toRelationDataStoreValue(sm, col);
+        if (field.getStrategy() instanceof RelationId)
+            return ((RelationId) field.getStrategy()).
+                toRelationDataStoreValue(sm, col);
+        return toRelationDataStoreValue(sm, col, field.getIndex() + 1);
+    }
+
+    /**
+     * Find the first field mapping that uses the given column starting with
+     * the given field index.
+     */
+    private FieldMapping findField(Column col, int idx) {
+        FieldMapping[] fms = _vm.getEmbeddedMapping().getFieldMappings();
+        Column[] cols;
+        for (int i = idx; i < fms.length; i++) {
+            if (fms[i].getManagement() != FieldMapping.MANAGE_PERSISTENT)
+                continue;
+            cols = ((Embeddable) fms[i]).getColumns();
+            for (int j = 0; j < cols.length; j++)
+                if (cols[j] == col)
+                    return fms[i];
+        }
+        return null;
+	}
+}

Added: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java?rev=592917&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java (added)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerCollectionTableFieldStrategy.java Wed Nov  7 13:46:09 2007
@@ -0,0 +1,250 @@
+/*
+ * 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.jdbc.meta.strats;
+
+import java.sql.*;
+import java.util.*;
+
+import org.apache.openjpa.lib.util.*;
+
+import org.apache.openjpa.kernel.*;
+import org.apache.openjpa.util.*;
+import org.apache.openjpa.meta.*;
+import org.apache.openjpa.jdbc.meta.*;
+import org.apache.openjpa.jdbc.kernel.*;
+import org.apache.openjpa.jdbc.schema.*;
+import org.apache.openjpa.jdbc.sql.*;
+
+/**
+ * <p>Mapping for a collection of values in a separate table controlled by a
+ * {@link ValueHandler}.</p>
+ *
+ * @author Abe White
+ * @since 0.4.0, 1.1.0
+ */
+public class HandlerCollectionTableFieldStrategy
+    extends StoreCollectionFieldStrategy
+    implements LRSCollectionFieldStrategy {
+
+    private static final Localizer _loc = Localizer.forPackage
+        (HandlerCollectionTableFieldStrategy.class);
+
+    private Column[] _cols = null;
+    private ColumnIO _io = null;
+    private boolean _load = false;
+    private boolean _lob = false;
+    private boolean _embed = false;
+
+    public FieldMapping getFieldMapping() {
+        return field;
+    }
+
+    public ClassMapping[] getIndependentElementMappings(boolean traverse) {
+        return ClassMapping.EMPTY_MAPPINGS;
+    }
+
+    public Column[] getElementColumns(ClassMapping elem) {
+        return _cols;
+    }
+
+    public ForeignKey getJoinForeignKey(ClassMapping elem) {
+        return field.getJoinForeignKey();
+    }
+
+    public void selectElement(Select sel, ClassMapping elem, JDBCStore store,
+        JDBCFetchConfiguration fetch, int eagerMode, Joins joins) {
+        sel.select(_cols, joins);
+    }
+
+    public Object loadElement(OpenJPAStateManager sm, JDBCStore store,
+        JDBCFetchConfiguration fetch, Result res, Joins joins)
+        throws SQLException {
+        return HandlerStrategies.loadObject(field.getElementMapping(),
+            sm, store, fetch, res, joins, _cols, _load);
+    }
+
+    protected Joins join(Joins joins, ClassMapping elem) {
+        return join(joins, false);
+    }
+
+    public Joins joinElementRelation(Joins joins, ClassMapping elem) {
+        return joinRelation(joins, false, false);
+    }
+
+    protected Proxy newLRSProxy() {
+        return new LRSProxyCollection(this);
+    }
+
+    public void map(boolean adapt) {
+        if (field.getTypeCode() != JavaTypes.COLLECTION
+            && field.getTypeCode() != JavaTypes.ARRAY)
+            throw new MetaDataException(_loc.get("not-coll", field));
+
+        assertNotMappedBy();
+        field.getValueInfo().assertNoSchemaComponents(field, !adapt);
+        field.getKeyMapping().getValueInfo().assertNoSchemaComponents
+            (field.getKey(), !adapt);
+
+        ValueMapping elem = field.getElementMapping();
+        if (elem.getHandler() == null)
+            throw new MetaDataException(_loc.get("no-handler", elem));
+
+        field.mapJoin(adapt, true);
+        _io = new ColumnIO();
+        _cols = HandlerStrategies.map(elem, "element", _io, adapt);
+
+        FieldMappingInfo finfo = field.getMappingInfo();
+        Column orderCol = finfo.getOrderColumn(field, field.getTable(), adapt);
+        field.setOrderColumn(orderCol);
+        field.setOrderColumnIO(finfo.getColumnIO());
+        field.mapPrimaryKey(adapt);
+    }
+
+    public void initialize() {
+        for (int i = 0; !_lob && i < _cols.length; i++)
+            _lob = _cols[i].isLob();
+
+        ValueMapping elem = field.getElementMapping();
+        _embed = elem.getEmbeddedMetaData() != null;
+        _load = elem.getHandler().objectValueRequiresLoad(elem);
+    }
+
+    public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+        throws SQLException {
+        insert(sm, store, rm, sm.fetchObject(field.getIndex()));
+    }
+
+    private void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm,
+        Object vals)
+        throws SQLException {
+        Collection coll;
+        if (field.getTypeCode() == JavaTypes.ARRAY)
+            coll = JavaTypes.toList(vals, field.getElement().getType(),
+                false);
+        else
+            coll = (Collection) vals;
+        if (coll == null || coll.isEmpty())
+            return;
+
+        Row row = rm.getSecondaryRow(field.getTable(), Row.ACTION_INSERT);
+        row.setForeignKey(field.getJoinForeignKey(), field.getJoinColumnIO(),
+            sm);
+
+        ValueMapping elem = field.getElementMapping();
+        Column order = field.getOrderColumn();
+        boolean setOrder = field.getOrderColumnIO().isInsertable(order, false);
+        int idx = 0;
+        for (Iterator itr = coll.iterator(); itr.hasNext(); idx++) {
+            HandlerStrategies.set(elem, itr.next(), store, row, _cols,
+                _io, true);
+            if (setOrder)
+                row.setInt(order, idx);
+            rm.flushSecondaryRow(row);
+        }
+    }
+
+    public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+        throws SQLException {
+        Object obj = sm.fetchObject(field.getIndex());
+        ChangeTracker ct = null;
+        if (obj instanceof Proxy) {
+            Proxy proxy = (Proxy) obj;
+            if (Proxies.isOwner(proxy, sm, field.getIndex()))
+                ct = proxy.getChangeTracker();
+        }
+
+        // if no fine-grained change tracking then just delete and reinsert
+        if (ct == null || !ct.isTracking()) {
+            delete(sm, store, rm);
+            insert(sm, store, rm, obj);
+            return;
+        }
+
+        // delete the removes
+        ValueMapping elem = field.getElementMapping();
+        Collection rem = ct.getRemoved();
+        if (!rem.isEmpty()) {
+            Row delRow = rm.getSecondaryRow(field.getTable(),
+                Row.ACTION_DELETE);
+            delRow.whereForeignKey(field.getJoinForeignKey(), sm);
+            for (Iterator itr = rem.iterator(); itr.hasNext();) {
+                HandlerStrategies.where(elem, itr.next(), store, delRow,
+                    _cols);
+                rm.flushSecondaryRow(delRow);
+            }
+        }
+
+        // insert the adds
+        Collection add = ct.getAdded();
+        if (!add.isEmpty()) {
+            Row addRow = rm.getSecondaryRow(field.getTable(),
+                Row.ACTION_INSERT);
+            addRow.setForeignKey(field.getJoinForeignKey(),
+                field.getJoinColumnIO(), sm);
+
+            int seq = ct.getNextSequence();
+            Column order = field.getOrderColumn();
+            boolean setOrder = field.getOrderColumnIO().isInsertable(order,
+                false);
+            for (Iterator itr = add.iterator(); itr.hasNext(); seq++) {
+                HandlerStrategies.set(elem, itr.next(), store, addRow, _cols,
+                    _io, true);
+                if (setOrder)
+                    addRow.setInt(order, seq);
+                rm.flushSecondaryRow(addRow);
+            }
+            if (order != null)
+                ct.setNextSequence(seq);
+        }
+    }
+
+    public void delete(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+        throws SQLException {
+        Row row = rm.getAllRows(field.getTable(), Row.ACTION_DELETE);
+        row.whereForeignKey(field.getJoinForeignKey(), sm);
+        rm.flushAllRows(row);
+    }
+
+    public int supportsSelect(Select sel, int type, OpenJPAStateManager sm,
+        JDBCStore store, JDBCFetchConfiguration fetch) {
+        // can't do any combined select with lobs, since they don't allow
+        // select distinct.  cant select eager parallel on embedded, because
+        // during parallel result processing the owning sm won't be available
+        // for each elem
+        if (_lob || (_embed && type == Select.EAGER_PARALLEL))
+            return 0;
+        return super.supportsSelect(sel, type, sm, store, fetch);
+    }
+
+    public Object toDataStoreValue(Object val, JDBCStore store) {
+        return HandlerStrategies.toDataStoreValue(field.getElementMapping(),
+            val, _cols, store);
+    }
+
+    public Joins join(Joins joins, boolean forceOuter) {
+        return field.join(joins, forceOuter, true);
+    }
+
+    public Joins joinRelation(Joins joins, boolean forceOuter,
+        boolean traverse) {
+        if (traverse)
+            HandlerStrategies.assertJoinable(field.getElementMapping());
+        return joins;
+    }
+}

Added: 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=592917&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerHandlerMapTableFieldStrategy.java (added)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerHandlerMapTableFieldStrategy.java Wed Nov  7 13:46:09 2007
@@ -0,0 +1,243 @@
+/*
+ * 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.jdbc.meta.strats;
+
+import java.sql.*;
+import java.util.*;
+
+import org.apache.openjpa.jdbc.kernel.*;
+import org.apache.openjpa.jdbc.meta.*;
+import org.apache.openjpa.jdbc.schema.*;
+import org.apache.openjpa.jdbc.sql.*;
+import org.apache.openjpa.kernel.*;
+import org.apache.openjpa.lib.util.*;
+import org.apache.openjpa.util.*;
+
+/**
+ * Mapping for a map of keys and values both controlled by
+ * {@link ValueHandler}s.
+ *
+ * @author Abe White
+ * @since 0.4.0, 1.1.0
+ */
+public class HandlerHandlerMapTableFieldStrategy
+    extends MapTableFieldStrategy {
+
+    private static final Localizer _loc = Localizer.forPackage
+        (HandlerHandlerMapTableFieldStrategy.class);
+
+    private Column[] _kcols = null;
+    private ColumnIO _kio = null;
+    private boolean _kload = false;
+    private Column[] _vcols = null;
+    private ColumnIO _vio = null;
+    private boolean _vload = false;
+
+    public Column[] getKeyColumns(ClassMapping cls) {
+        return _kcols;
+    }
+
+    public Column[] getValueColumns(ClassMapping cls) {
+        return _vcols;
+    }
+
+    public void selectKey(Select sel, ClassMapping cls, OpenJPAStateManager sm,
+        JDBCStore store, JDBCFetchConfiguration fetch, Joins joins) {
+        sel.select(_kcols, joins);
+    }
+
+    public void selectValue(Select sel, ClassMapping cls,
+        OpenJPAStateManager sm,
+        JDBCStore store, JDBCFetchConfiguration fetch, Joins joins) {
+        sel.select(_vcols, joins);
+    }
+
+    public Result[] getResults(OpenJPAStateManager sm, JDBCStore store,
+        JDBCFetchConfiguration fetch, int eagerMode, Joins[] joins, boolean lrs)
+        throws SQLException {
+        Select sel = store.getSQLFactory().newSelect();
+        sel.setLRS(lrs);
+        sel.select(_kcols);
+        sel.select(_vcols);
+        sel.whereForeignKey(field.getJoinForeignKey(), sm.getObjectId(),
+            field.getDefiningMapping(), store);
+        Result res = sel.execute(store, fetch);
+        return new Result[]{ res, res };
+    }
+
+    public Object loadKey(OpenJPAStateManager sm, JDBCStore store,
+        JDBCFetchConfiguration fetch, Result res, Joins joins)
+        throws SQLException {
+        return HandlerStrategies.loadObject(field.getKeyMapping(),
+            sm, store, fetch, res, joins, _kcols, _kload);
+    }
+
+    public Object loadValue(OpenJPAStateManager sm, JDBCStore store,
+        JDBCFetchConfiguration fetch, Result res, Joins joins)
+        throws SQLException {
+        return HandlerStrategies.loadObject(field.getElementMapping(),
+            sm, store, fetch, res, joins, _vcols, _vload);
+    }
+
+    public void map(boolean adapt) {
+        super.map(adapt);
+
+        ValueMapping key = field.getKeyMapping();
+        if (key.getHandler() == null)
+            throw new MetaDataException(_loc.get("no-handler", key));
+        ValueMapping val = field.getElementMapping();
+        if (val.getHandler() == null)
+            throw new MetaDataException(_loc.get("no-handler", val));
+        assertNotMappedBy();
+
+        field.mapJoin(adapt, true);
+        _kio = new ColumnIO();
+        _kcols = HandlerStrategies.map(key, "key", _kio, adapt);
+        _vio = new ColumnIO();
+        _vcols = HandlerStrategies.map(val, "value", _vio, adapt);
+        field.mapPrimaryKey(adapt);
+    }
+
+    public void initialize() {
+        _kload = field.getKeyMapping().getHandler().
+            objectValueRequiresLoad(field.getKeyMapping());
+        _vload = field.getElementMapping().getHandler().
+            objectValueRequiresLoad(field.getElementMapping());
+    }
+
+    public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+        throws SQLException {
+        insert(sm, store, rm, (Map) sm.fetchObject(field.getIndex()));
+    }
+
+    private void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm,
+        Map map)
+        throws SQLException {
+        if (map == null || map.isEmpty())
+            return;
+
+        Row row = rm.getSecondaryRow(field.getTable(), Row.ACTION_INSERT);
+        row.setForeignKey(field.getJoinForeignKey(), field.getJoinColumnIO(),
+            sm);
+
+        ValueMapping key = field.getKeyMapping();
+        ValueMapping val = field.getElementMapping();
+        Map.Entry entry;
+        for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) {
+            entry = (Map.Entry) itr.next();
+            HandlerStrategies.set(key, entry.getKey(), store, row, _kcols,
+                _kio, true);
+            HandlerStrategies.set(val, entry.getValue(), store, row, _vcols,
+                _vio, true);
+            rm.flushSecondaryRow(row);
+        }
+    }
+
+    public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+        throws SQLException {
+        Map map = (Map) sm.fetchObject(field.getIndex());
+        ChangeTracker ct = null;
+        if (map instanceof Proxy) {
+            Proxy proxy = (Proxy) map;
+            if (Proxies.isOwner(proxy, sm, field.getIndex()))
+                ct = proxy.getChangeTracker();
+        }
+
+        // if no fine-grained change tracking then just delete and reinsert
+        if (ct == null || !ct.isTracking()) {
+            delete(sm, store, rm);
+            insert(sm, store, rm, map);
+            return;
+        }
+
+        // delete the removes
+        ValueMapping key = field.getKeyMapping();
+        Collection rem = ct.getRemoved();
+        if (!rem.isEmpty()) {
+            Row delRow = rm.getSecondaryRow(field.getTable(),
+                Row.ACTION_DELETE);
+            delRow.whereForeignKey(field.getJoinForeignKey(), sm);
+            for (Iterator itr = rem.iterator(); itr.hasNext();) {
+                HandlerStrategies.where(key, itr.next(), store, delRow,
+                    _kcols);
+                rm.flushSecondaryRow(delRow);
+            }
+        }
+
+        // insert the adds
+        ValueMapping val = field.getElementMapping();
+        Collection add = ct.getAdded();
+        Object mkey;
+        if (!add.isEmpty()) {
+            Row 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();
+                HandlerStrategies.set(key, mkey, store, addRow, _kcols,
+                    _kio, true);
+                HandlerStrategies.set(val, map.get(mkey), store, addRow,
+                    _vcols, _vio, true);
+                rm.flushSecondaryRow(addRow);
+            }
+        }
+
+        // update the changes
+        Collection change = ct.getChanged();
+        if (!change.isEmpty()) {
+            Row changeRow = rm.getSecondaryRow(field.getTable(),
+                Row.ACTION_UPDATE);
+            changeRow.whereForeignKey(field.getJoinForeignKey(), sm);
+
+            for (Iterator itr = change.iterator(); itr.hasNext();) {
+                mkey = itr.next();
+                HandlerStrategies.where(key, mkey, store, changeRow, _kcols);
+                HandlerStrategies.set(val, map.get(mkey), store, changeRow,
+                    _vcols, _vio, true);
+                rm.flushSecondaryRow(changeRow);
+            }
+        }
+    }
+
+    public Object toDataStoreValue(Object val, JDBCStore store) {
+        return HandlerStrategies.toDataStoreValue(field.getElementMapping(),
+            val, _vcols, store);
+    }
+
+    public Object toKeyDataStoreValue(Object val, JDBCStore store) {
+        return HandlerStrategies.toDataStoreValue(field.getKeyMapping(), val,
+            _kcols, store);
+    }
+
+    public Joins joinRelation(Joins joins, boolean forceOuter,
+        boolean traverse) {
+        if (traverse)
+            HandlerStrategies.assertJoinable(field.getElementMapping());
+        return joins;
+    }
+
+    public Joins joinKeyRelation(Joins joins, boolean forceOuter,
+        boolean traverse) {
+        if (traverse)
+            HandlerStrategies.assertJoinable(field.getKeyMapping());
+        return joins;
+    }
+}

Added: 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=592917&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerRelationMapTableFieldStrategy.java (added)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/HandlerRelationMapTableFieldStrategy.java Wed Nov  7 13:46:09 2007
@@ -0,0 +1,313 @@
+/*
+ * 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.jdbc.meta.strats;
+
+import java.sql.*;
+import java.util.*;
+
+import org.apache.openjpa.lib.util.*;
+import org.apache.openjpa.meta.*;
+import org.apache.openjpa.kernel.*;
+import org.apache.openjpa.util.*;
+import org.apache.openjpa.jdbc.meta.*;
+import org.apache.openjpa.jdbc.kernel.*;
+import org.apache.openjpa.jdbc.schema.*;
+import org.apache.openjpa.jdbc.sql.*;
+
+/**
+ * <p>Mapping for a map whose keys are controlled by a {@link ValueHandler}
+ * and whose values are relations to other persistent objects.</p>
+ *
+ * @author Abe White
+ * @since 0.4.0, 1.1.0
+ */
+public class HandlerRelationMapTableFieldStrategy
+    extends MapTableFieldStrategy {
+
+    private static final Localizer _loc = Localizer.forPackage
+        (HandlerRelationMapTableFieldStrategy.class);
+
+    private Column[] _kcols = null;
+    private ColumnIO _kio = null;
+    private boolean _kload = false;
+
+    public Column[] getKeyColumns(ClassMapping cls) {
+        return _kcols;
+    }
+
+    public Column[] getValueColumns(ClassMapping cls) {
+        return field.getElementMapping().getColumns();
+    }
+
+    public void selectKey(Select sel, ClassMapping key, OpenJPAStateManager sm,
+        JDBCStore store, JDBCFetchConfiguration fetch, Joins joins) {
+        sel.select(_kcols, joins);
+    }
+
+    public void selectValue(Select sel, ClassMapping val,
+        OpenJPAStateManager sm, JDBCStore store, JDBCFetchConfiguration fetch, 
+        Joins joins) {
+        sel.select(val, field.getElementMapping().getSelectSubclasses(),
+            store, fetch, JDBCFetchConfiguration.EAGER_NONE, joins);
+    }
+
+    public Result[] getResults(final OpenJPAStateManager sm,
+        final JDBCStore store, final JDBCFetchConfiguration fetch,
+        final int eagerMode, final Joins[] resJoins, boolean lrs)
+        throws SQLException {
+        ValueMapping elem = field.getElementMapping();
+        final ClassMapping[] vals = elem.getIndependentTypeMappings();
+        Union union = store.getSQLFactory().newUnion(vals.length);
+        if (fetch.getSubclassFetchMode(elem.getTypeMapping())
+            != JDBCFetchConfiguration.EAGER_JOIN)
+            union.abortUnion();
+        union.setLRS(lrs);
+        union.select(new Union.Selector() {
+            public void select(Select sel, int idx) {
+                sel.select(_kcols);
+                sel.whereForeignKey(field.getJoinForeignKey(),
+                    sm.getObjectId(), field.getDefiningMapping(), store);
+
+                Joins joins = joinValueRelation(sel.newJoins(), vals[idx]);
+                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;
+            }
+        });
+        Result res = union.execute(store, fetch);
+        return new Result[]{ res, res };
+    }
+
+    public Object loadKey(OpenJPAStateManager sm, JDBCStore store,
+        JDBCFetchConfiguration fetch, Result res, Joins joins)
+        throws SQLException {
+        return HandlerStrategies.loadObject(field.getKeyMapping(),
+            sm, store, fetch, res, joins, _kcols, _kload);
+    }
+
+    public Object loadValue(OpenJPAStateManager sm, JDBCStore store,
+        JDBCFetchConfiguration fetch, Result res, Joins joins)
+        throws SQLException {
+        ClassMapping val = res.getBaseMapping();
+        if (val == null)
+            val = field.getElementMapping().getIndependentTypeMappings()[0];
+        return res.load(val, store, fetch, joins);
+    }
+
+    public Joins joinValueRelation(Joins joins, ClassMapping val) {
+        ValueMapping vm = field.getElementMapping();
+        return joins.joinRelation(field.getName(), vm.getForeignKey(val), val,
+            vm.getSelectSubclasses(), false, false);
+    }
+
+    public void map(boolean adapt) {
+        super.map(adapt);
+
+        ValueMapping key = field.getKeyMapping();
+        if (key.getHandler() == null)
+            throw new MetaDataException(_loc.get("no-handler", key));
+        ValueMapping val = field.getElementMapping();
+        if (val.getTypeCode() != JavaTypes.PC || val.isEmbeddedPC())
+            throw new MetaDataException(_loc.get("not-relation", val));
+        assertNotMappedBy();
+
+        field.mapJoin(adapt, true);
+        _kio = new ColumnIO();
+        _kcols = HandlerStrategies.map(key, "key", _kio, adapt);
+
+        if (val.getTypeMapping().isMapped()) {
+            ValueMappingInfo vinfo = val.getValueInfo();
+            ForeignKey fk = vinfo.getTypeJoin(val, "value", false, adapt);
+            val.setForeignKey(fk);
+            val.setColumnIO(vinfo.getColumnIO());
+        } else
+            RelationStrategies.mapRelationToUnmappedPC(val, "value", adapt);
+
+        val.mapConstraints("value", adapt);
+        field.mapPrimaryKey(adapt);
+    }
+
+    public void initialize() {
+        _kload = field.getKeyMapping().getHandler().
+            objectValueRequiresLoad(field.getKeyMapping());
+    }
+
+    public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+        throws SQLException {
+        insert(sm, store, rm, (Map) sm.fetchObject(field.getIndex()));
+    }
+
+    private void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm,
+        Map map)
+        throws SQLException {
+        if (map == null || map.isEmpty())
+            return;
+
+        Row 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 = store.getContext();
+        OpenJPAStateManager valsm;
+        Map.Entry entry;
+        for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) {
+            entry = (Map.Entry) itr.next();
+            HandlerStrategies.set(key, entry.getKey(), store, row, _kcols,
+                _kio, true);
+            valsm = RelationStrategies.getStateManager(entry.getValue(),
+                ctx);
+            val.setForeignKey(row, valsm);
+            rm.flushSecondaryRow(row);
+        }
+    }
+
+    public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+        throws SQLException {
+        Map map = (Map) sm.fetchObject(field.getIndex());
+        ChangeTracker ct = null;
+        if (map instanceof Proxy) {
+            Proxy proxy = (Proxy) map;
+            if (Proxies.isOwner(proxy, sm, field.getIndex()))
+                ct = proxy.getChangeTracker();
+        }
+
+        // if no fine-grained change tracking then just delete and reinsert
+        if (ct == null || !ct.isTracking()) {
+            delete(sm, store, rm);
+            insert(sm, store, rm, map);
+            return;
+        }
+
+        ValueMapping key = field.getKeyMapping();
+        ValueMapping val = field.getElementMapping();
+        StoreContext ctx = store.getContext();
+        OpenJPAStateManager valsm;
+
+        // update the changes; note that we have to model changes as
+        // delete-then-insert if we have a foreign key action, because
+        // secondary row updates aren't part of the constraint graph
+        Collection change = ct.getChanged();
+        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);
+
+            for (Iterator itr = change.iterator(); itr.hasNext();) {
+                mkey = itr.next();
+                HandlerStrategies.where(key, mkey, store, changeRow, _kcols);
+                valsm = RelationStrategies.getStateManager(map.get(mkey), ctx);
+                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);
+
+            for (Iterator itr = rem.iterator(); itr.hasNext();) {
+                HandlerStrategies.where(key, itr.next(), store, delRow,
+                    _kcols);
+                rm.flushSecondaryRow(delRow);
+            }
+            if (!canChange && !change.isEmpty()) {
+                for (Iterator itr = change.iterator(); itr.hasNext();) {
+                    HandlerStrategies.where(key, itr.next(), store, delRow,
+                        _kcols);
+                    rm.flushSecondaryRow(delRow);
+                }
+            }
+        }
+
+        // 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);
+
+            for (Iterator itr = add.iterator(); itr.hasNext();) {
+                mkey = itr.next();
+                HandlerStrategies.set(key, mkey, store, addRow, _kcols,
+                    _kio, true);
+                valsm = RelationStrategies.getStateManager(map.get(mkey), ctx);
+                val.setForeignKey(addRow, valsm);
+                rm.flushSecondaryRow(addRow);
+            }
+            if (!canChange && !change.isEmpty()) {
+                for (Iterator itr = change.iterator(); itr.hasNext();) {
+                    mkey = itr.next();
+                    HandlerStrategies.set(key, mkey, store, addRow, _kcols,
+                        _kio, true);
+                    valsm = RelationStrategies.getStateManager(map.get(mkey),
+                        ctx);
+                    val.setForeignKey(addRow, valsm);
+                    rm.flushSecondaryRow(addRow);
+                }
+            }
+        }
+    }
+
+    public Joins joinRelation(Joins joins, boolean forceOuter,
+        boolean traverse) {
+        ValueMapping val = field.getElementMapping();
+        ClassMapping[] clss = val.getIndependentTypeMappings();
+        if (clss.length != 1) {
+            if (traverse)
+                throw RelationStrategies.unjoinable(val);
+            return joins;
+        }
+        if (forceOuter)
+            return joins.outerJoinRelation(field.getName(),
+                val.getForeignKey(clss[0]), clss[0], val.getSelectSubclasses(),
+                false, false);
+        return joins.joinRelation(field.getName(),
+            val.getForeignKey(clss[0]), clss[0], val.getSelectSubclasses(), 
+            false, false);
+    }
+
+    public Joins joinKeyRelation(Joins joins, boolean forceOuter,
+        boolean traverse) {
+        if (traverse)
+            HandlerStrategies.assertJoinable(field.getKeyMapping());
+        return joins;
+    }
+
+    public Object toDataStoreValue(Object val, JDBCStore store) {
+        return RelationStrategies.toDataStoreValue(field.getElementMapping(),
+            val, store);
+    }
+
+    public Object toKeyDataStoreValue(Object val, JDBCStore store) {
+        return HandlerStrategies.toDataStoreValue(field.getKeyMapping(), val,
+            _kcols, store);
+    }
+}

Added: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationHandlerMapTableFieldStrategy.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationHandlerMapTableFieldStrategy.java?rev=592917&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationHandlerMapTableFieldStrategy.java (added)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationHandlerMapTableFieldStrategy.java Wed Nov  7 13:46:09 2007
@@ -0,0 +1,290 @@
+/*
+ * 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.jdbc.meta.strats;
+
+import java.sql.*;
+import java.util.*;
+
+import org.apache.openjpa.lib.util.*;
+import org.apache.openjpa.meta.*;
+import org.apache.openjpa.kernel.*;
+import org.apache.openjpa.util.*;
+import org.apache.openjpa.jdbc.meta.*;
+import org.apache.openjpa.jdbc.kernel.*;
+import org.apache.openjpa.jdbc.schema.*;
+import org.apache.openjpa.jdbc.sql.*;
+
+/**
+ * <p>Mapping for a map whose keys are relations to other persistent objects
+ * and whose values are controlled by a {@link ValueHandler}.</p>
+ *
+ * @author Abe White
+ * @since 0.4.0, 1.1.0
+ */
+public class RelationHandlerMapTableFieldStrategy
+    extends MapTableFieldStrategy {
+
+    private static final Localizer _loc = Localizer.forPackage
+        (RelationHandlerMapTableFieldStrategy.class);
+
+    private Column[] _vcols = null;
+    private ColumnIO _vio = null;
+    private boolean _vload = false;
+
+    public Column[] getKeyColumns(ClassMapping cls) {
+        return field.getKeyMapping().getColumns();
+    }
+
+    public Column[] getValueColumns(ClassMapping cls) {
+        return _vcols;
+    }
+
+    public void selectKey(Select sel, ClassMapping key, OpenJPAStateManager sm,
+        JDBCStore store, JDBCFetchConfiguration fetch, Joins joins) {
+        sel.select(key, field.getKeyMapping().getSelectSubclasses(),
+            store, fetch, JDBCFetchConfiguration.EAGER_NONE, joins);
+    }
+
+    public void selectValue(Select sel, ClassMapping val,
+        OpenJPAStateManager sm, JDBCStore store, JDBCFetchConfiguration fetch, 
+        Joins joins) {
+        sel.select(_vcols, joins);
+    }
+
+    public Result[] getResults(final OpenJPAStateManager sm,
+        final JDBCStore store, final JDBCFetchConfiguration fetch,
+        final int eagerMode, final Joins[] resJoins, boolean lrs)
+        throws SQLException {
+        ValueMapping key = field.getKeyMapping();
+        final ClassMapping[] keys = key.getIndependentTypeMappings();
+        Union union = store.getSQLFactory().newUnion(keys.length);
+        if (fetch.getSubclassFetchMode(key.getTypeMapping()) 
+            != JDBCFetchConfiguration.EAGER_JOIN)
+            union.abortUnion();
+        union.setLRS(lrs);
+        union.select(new Union.Selector() {
+            public void select(Select sel, int idx) {
+                sel.select(_vcols);
+                sel.whereForeignKey(field.getJoinForeignKey(),
+                    sm.getObjectId(), field.getDefiningMapping(), store);
+
+                Joins joins = joinKeyRelation(sel.newJoins(), keys[idx]);
+                sel.select(keys[idx], field.getKeyMapping().
+                    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[0] = joins;
+            }
+        });
+        Result res = union.execute(store, fetch);
+        return new Result[]{ res, res };
+    }
+
+    public Object loadKey(OpenJPAStateManager sm, JDBCStore store,
+        JDBCFetchConfiguration fetch, Result res, Joins joins)
+        throws SQLException {
+        ClassMapping key = res.getBaseMapping();
+        if (key == null)
+            key = field.getKeyMapping().getIndependentTypeMappings()[0];
+        return res.load(key, store, fetch, joins);
+    }
+
+    public Object loadValue(OpenJPAStateManager sm, JDBCStore store,
+        JDBCFetchConfiguration fetch, Result res, Joins joins)
+        throws SQLException {
+        return HandlerStrategies.loadObject(field.getElementMapping(),
+            sm, store, fetch, res, joins, _vcols, _vload);
+    }
+
+    public Joins joinKeyRelation(Joins joins, ClassMapping key) {
+        ValueMapping vm = field.getKeyMapping();
+        return joins.joinRelation(field.getName(), vm.getForeignKey(key), key,
+            vm.getSelectSubclasses(), false, false);
+    }
+
+    public void map(boolean adapt) {
+        super.map(adapt);
+
+        ValueMapping key = field.getKeyMapping();
+        if (key.getTypeCode() != JavaTypes.PC || key.isEmbeddedPC())
+            throw new MetaDataException(_loc.get("not-relation", key));
+        ValueMapping val = field.getElementMapping();
+        if (val.getHandler() == null)
+            throw new MetaDataException(_loc.get("no-handler", val));
+        assertNotMappedBy();
+
+        field.mapJoin(adapt, true);
+        _vio = new ColumnIO();
+        _vcols = HandlerStrategies.map(val, "value", _vio, adapt);
+
+        if (key.getTypeMapping().isMapped()) {
+            ValueMappingInfo vinfo = key.getValueInfo();
+            ForeignKey fk = vinfo.getTypeJoin(key, "key", false, adapt);
+            key.setForeignKey(fk);
+            key.setColumnIO(vinfo.getColumnIO());
+        } else
+            RelationStrategies.mapRelationToUnmappedPC(key, "key", adapt);
+
+        key.mapConstraints("key", adapt);
+        field.mapPrimaryKey(adapt);
+    }
+
+    public void initialize() {
+        _vload = field.getElementMapping().getHandler().
+            objectValueRequiresLoad(field.getElementMapping());
+    }
+
+    public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+        throws SQLException {
+        insert(sm, store, rm, (Map) sm.fetchObject(field.getIndex()));
+    }
+
+    private void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm,
+        Map map)
+        throws SQLException {
+        if (map == null || map.isEmpty())
+            return;
+
+        Row row = rm.getSecondaryRow(field.getTable(), Row.ACTION_INSERT);
+        row.setForeignKey(field.getJoinForeignKey(), field.getJoinColumnIO(),
+            sm);
+
+        ValueMapping val = field.getElementMapping();
+        ValueMapping key = field.getKeyMapping();
+        StoreContext ctx = store.getContext();
+        OpenJPAStateManager keysm;
+        Map.Entry entry;
+        for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) {
+            entry = (Map.Entry) itr.next();
+            keysm = RelationStrategies.getStateManager(entry.getKey(), ctx);
+            key.setForeignKey(row, keysm);
+            HandlerStrategies.set(val, entry.getValue(), store, row, _vcols,
+                _vio, true);
+            rm.flushSecondaryRow(row);
+        }
+    }
+
+    public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+        throws SQLException {
+        Map map = (Map) sm.fetchObject(field.getIndex());
+        ChangeTracker ct = null;
+        if (map instanceof Proxy) {
+            Proxy proxy = (Proxy) map;
+            if (Proxies.isOwner(proxy, sm, field.getIndex()))
+                ct = proxy.getChangeTracker();
+        }
+
+        // if no fine-grained change tracking then just delete and reinsert
+        if (ct == null || !ct.isTracking()) {
+            delete(sm, store, rm);
+            insert(sm, store, rm, map);
+            return;
+        }
+
+        // delete the removes
+        ValueMapping key = field.getKeyMapping();
+        StoreContext ctx = store.getContext();
+        Collection rem = ct.getRemoved();
+        OpenJPAStateManager keysm;
+        if (!rem.isEmpty()) {
+            Row 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);
+            }
+        }
+
+        // insert the adds
+        ValueMapping val = field.getElementMapping();
+        Collection add = ct.getAdded();
+        Object mkey;
+        if (!add.isEmpty()) {
+            Row 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();
+                keysm = RelationStrategies.getStateManager(mkey, ctx);
+                key.setForeignKey(addRow, keysm);
+                HandlerStrategies.set(val, map.get(mkey), store, addRow,
+                    _vcols, _vio, true);
+                rm.flushSecondaryRow(addRow);
+            }
+        }
+
+        // update the changes
+        Collection change = ct.getChanged();
+        if (!change.isEmpty()) {
+            Row changeRow = rm.getSecondaryRow(field.getTable(),
+                Row.ACTION_UPDATE);
+            changeRow.whereForeignKey(field.getJoinForeignKey(), sm);
+
+            for (Iterator itr = change.iterator(); itr.hasNext();) {
+                mkey = itr.next();
+                keysm = RelationStrategies.getStateManager(mkey, ctx);
+                key.whereForeignKey(changeRow, keysm);
+                HandlerStrategies.set(val, map.get(mkey), store, changeRow,
+                    _vcols, _vio, true);
+                rm.flushSecondaryRow(changeRow);
+            }
+        }
+    }
+
+    public Joins joinRelation(Joins joins, boolean forceOuter,
+        boolean traverse) {
+        if (traverse)
+            HandlerStrategies.assertJoinable(field.getElementMapping());
+        return joins;
+    }
+
+    public Joins joinKeyRelation(Joins joins, boolean forceOuter,
+        boolean traverse) {
+        ValueMapping key = field.getKeyMapping();
+        ClassMapping[] clss = key.getIndependentTypeMappings();
+        if (clss.length != 1) {
+            if (traverse)
+                throw RelationStrategies.unjoinable(field.getKeyMapping());
+            return joins;
+        }
+        if (forceOuter)
+            return joins.outerJoinRelation(field.getName(),
+                key.getForeignKey(clss[0]), clss[0], key.getSelectSubclasses(),
+                false, false);
+        return joins.joinRelation(field.getName(),
+            key.getForeignKey(clss[0]), clss[0], key.getSelectSubclasses(),
+            false, false);
+    }
+
+    public Object toDataStoreValue(Object val, JDBCStore store) {
+        return HandlerStrategies.toDataStoreValue(field.getElementMapping(),
+            val, _vcols, store);
+    }
+
+    public Object toKeyDataStoreValue(Object val, JDBCStore store) {
+        return RelationStrategies.toDataStoreValue(field.getKeyMapping(), val,
+            store);
+    }
+}

Added: 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=592917&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationRelationMapTableFieldStrategy.java (added)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationRelationMapTableFieldStrategy.java Wed Nov  7 13:46:09 2007
@@ -0,0 +1,375 @@
+/*
+ * 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.jdbc.meta.strats;
+
+import java.sql.*;
+import java.util.*;
+
+import org.apache.openjpa.lib.util.*;
+import org.apache.openjpa.meta.*;
+import org.apache.openjpa.kernel.*;
+import org.apache.openjpa.util.*;
+import org.apache.openjpa.jdbc.meta.*;
+import org.apache.openjpa.jdbc.kernel.*;
+import org.apache.openjpa.jdbc.schema.*;
+import org.apache.openjpa.jdbc.sql.*;
+
+/**
+ * <p>Mapping for a map whose keys and values are both relations to other
+ * persistent objects.</p>
+ *
+ * @author Abe White
+ * @since 0.4.0, 1.1.0
+ */
+public class RelationRelationMapTableFieldStrategy
+    extends MapTableFieldStrategy {
+
+    private static final Localizer _loc = Localizer.forPackage
+        (RelationRelationMapTableFieldStrategy.class);
+
+    private String _keyRelationName = null;
+
+    public Column[] getKeyColumns(ClassMapping cls) {
+        return field.getKeyMapping().getColumns();
+    }
+
+    public Column[] getValueColumns(ClassMapping cls) {
+        return field.getElementMapping().getColumns();
+    }
+
+    public void selectKey(Select sel, ClassMapping key, OpenJPAStateManager sm,
+        JDBCStore store, JDBCFetchConfiguration fetch, Joins joins) {
+        sel.select(key, field.getKeyMapping().getSelectSubclasses(),
+            store, fetch, JDBCFetchConfiguration.EAGER_NONE, joins);
+    }
+
+    public void selectValue(Select sel, ClassMapping val,
+        OpenJPAStateManager sm, JDBCStore store, JDBCFetchConfiguration fetch, 
+        Joins joins) {
+        sel.select(val, field.getElementMapping().getSelectSubclasses(),
+            store, fetch, JDBCFetchConfiguration.EAGER_NONE, joins);
+    }
+
+    public Result[] getResults(final OpenJPAStateManager sm,
+        final JDBCStore store, final JDBCFetchConfiguration fetch,
+        final int eagerMode, final Joins[] resJoins, boolean lrs)
+        throws SQLException {
+        ValueMapping key = field.getKeyMapping();
+        final ClassMapping[] keys = key.getIndependentTypeMappings();
+        Union kunion = store.getSQLFactory().newUnion(keys.length);
+        if (fetch.getSubclassFetchMode(key.getTypeMapping())
+            != JDBCFetchConfiguration.EAGER_JOIN)
+            kunion.abortUnion();
+        kunion.setLRS(lrs);
+        kunion.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
+                Joins joins = joinKeyRelation(sel.newJoins(), keys[idx]);
+                sel.orderBy(field.getKeyMapping().getColumns(), true, true);
+                sel.select(keys[idx], field.getKeyMapping().
+                    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[0] = joins;
+            }
+        });
+
+        ValueMapping val = field.getElementMapping();
+        final ClassMapping[] vals = val.getIndependentTypeMappings();
+        Union vunion = store.getSQLFactory().newUnion(vals.length);
+        if (fetch.getSubclassFetchMode(val.getTypeMapping())
+            != JDBCFetchConfiguration.EAGER_JOIN)
+            vunion.abortUnion();
+        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
+                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;
+            }
+        });
+
+        Result kres = null;
+        Result vres = null;
+        try {
+            kres = kunion.execute(store, fetch);
+            vres = vunion.execute(store, fetch);
+            return new Result[]{ kres, vres };
+        } catch (SQLException se) {
+            if (kres != null)
+                kres.close();
+            if (vres != null)
+                vres.close();
+            throw se;
+        }
+    }
+
+    public Object loadKey(OpenJPAStateManager sm, JDBCStore store,
+        JDBCFetchConfiguration fetch, Result res, Joins joins)
+        throws SQLException {
+        ClassMapping key = res.getBaseMapping();
+        if (key == null)
+            key = field.getKeyMapping().getIndependentTypeMappings()[0];
+        return res.load(key, store, fetch, joins);
+    }
+
+    public Object loadValue(OpenJPAStateManager sm, JDBCStore store,
+        JDBCFetchConfiguration fetch, Result res, Joins joins)
+        throws SQLException {
+        ClassMapping val = res.getBaseMapping();
+        if (val == null)
+            val = field.getElementMapping().getIndependentTypeMappings()[0];
+        return res.load(val, store, fetch, joins);
+    }
+
+    public Joins joinKeyRelation(Joins joins, ClassMapping key) {
+        ValueMapping vm = field.getKeyMapping();
+        return joins.joinRelation(_keyRelationName, vm.getForeignKey(key), key,
+            vm.getSelectSubclasses(), false, false);
+    }
+
+    public Joins joinValueRelation(Joins joins, ClassMapping val) {
+        ValueMapping vm = field.getElementMapping();
+        return joins.joinRelation(field.getName(), vm.getForeignKey(val), val,
+            vm.getSelectSubclasses(), false, false);
+    }
+
+    public void map(boolean adapt) {
+        super.map(adapt);
+
+        ValueMapping key = field.getKeyMapping();
+        if (key.getTypeCode() != JavaTypes.PC || key.isEmbeddedPC())
+            throw new MetaDataException(_loc.get("not-relation", key));
+        ValueMapping val = field.getElementMapping();
+        if (val.getTypeCode() != JavaTypes.PC || val.isEmbeddedPC())
+            throw new MetaDataException(_loc.get("not-relation", val));
+        assertNotMappedBy();
+
+        field.mapJoin(adapt, true);
+        mapTypeJoin(key, "key", adapt);
+        mapTypeJoin(val, "value", adapt);
+
+        field.mapPrimaryKey(adapt);
+    }
+
+    /**
+     * Map the given value's join to its persistent type.
+     */
+    private void mapTypeJoin(ValueMapping vm, String name, boolean adapt) {
+        if (vm.getTypeMapping().isMapped()) {
+            ValueMappingInfo vinfo = vm.getValueInfo();
+            ForeignKey fk = vinfo.getTypeJoin(vm, name, false, adapt);
+            vm.setForeignKey(fk);
+            vm.setColumnIO(vinfo.getColumnIO());
+        } else
+            RelationStrategies.mapRelationToUnmappedPC(vm, name, adapt);
+        vm.mapConstraints(name, adapt);
+    }
+
+    public void initialize() {
+        _keyRelationName = field.getName() + ":key";
+    }
+
+    public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+        throws SQLException {
+        insert(sm, rm, (Map) sm.fetchObject(field.getIndex()));
+    }
+
+    private void insert(OpenJPAStateManager sm, RowManager rm, Map map)
+        throws SQLException {
+        if (map == null || map.isEmpty())
+            return;
+
+        Row 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();
+        OpenJPAStateManager keysm, valsm;
+        Map.Entry entry;
+        for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) {
+            entry = (Map.Entry) itr.next();
+            keysm = RelationStrategies.getStateManager(entry.getKey(), ctx);
+            valsm = RelationStrategies.getStateManager(entry.getValue(), ctx);
+            key.setForeignKey(row, keysm);
+            val.setForeignKey(row, valsm);
+            rm.flushSecondaryRow(row);
+        }
+    }
+
+    public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+        throws SQLException {
+        Map map = (Map) sm.fetchObject(field.getIndex());
+        ChangeTracker ct = null;
+        if (map instanceof Proxy) {
+            Proxy proxy = (Proxy) map;
+            if (Proxies.isOwner(proxy, sm, field.getIndex()))
+                ct = proxy.getChangeTracker();
+        }
+
+        // if no fine-grained change tracking then just delete and reinsert
+        if (ct == null || !ct.isTracking()) {
+            delete(sm, store, rm);
+            insert(sm, rm, map);
+            return;
+        }
+
+        ValueMapping key = field.getKeyMapping();
+        ValueMapping val = field.getElementMapping();
+        StoreContext ctx = store.getContext();
+        OpenJPAStateManager keysm, valsm;
+
+        // update the changes; note that we have to model changes as
+        // delete-then-insert if we have a foreign key action, because
+        // secondary row updates aren't part of the constraint graph
+        Collection change = ct.getChanged();
+        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);
+
+            for (Iterator itr = change.iterator(); itr.hasNext();) {
+                mkey = itr.next();
+                keysm = RelationStrategies.getStateManager(mkey, ctx);
+                valsm = RelationStrategies.getStateManager(map.get(mkey), ctx);
+                key.whereForeignKey(changeRow, keysm);
+                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);
+
+            for (Iterator itr = rem.iterator(); itr.hasNext();) {
+                keysm = RelationStrategies.getStateManager(itr.next(), 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);
+                }
+            }
+        }
+
+        // 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);
+
+            for (Iterator itr = add.iterator(); itr.hasNext();) {
+                mkey = itr.next();
+                keysm = RelationStrategies.getStateManager(mkey, ctx);
+                valsm = RelationStrategies.getStateManager(map.get(mkey), ctx);
+                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();
+                    keysm = RelationStrategies.getStateManager(mkey, ctx);
+                    valsm = RelationStrategies.getStateManager(map.get(mkey),
+                        ctx);
+                    key.setForeignKey(addRow, keysm);
+                    val.setForeignKey(addRow, valsm);
+                    rm.flushSecondaryRow(addRow);
+                }
+            }
+        }
+    }
+
+    public Joins joinRelation(Joins joins, boolean forceOuter,
+        boolean traverse) {
+        ValueMapping val = field.getElementMapping();
+        ClassMapping[] clss = val.getIndependentTypeMappings();
+        if (clss.length != 1) {
+            if (traverse)
+                throw RelationStrategies.unjoinable(val);
+            return joins;
+        }
+        if (forceOuter)
+            return joins.outerJoinRelation(field.getName(),
+                val.getForeignKey(clss[0]), clss[0], val.getSelectSubclasses(),
+                false, false);
+        return joins.joinRelation(field.getName(),
+            val.getForeignKey(clss[0]), clss[0], val.getSelectSubclasses(),
+            false, false);
+    }
+
+    public Joins joinKeyRelation(Joins joins, boolean forceOuter,
+        boolean traverse) {
+        ValueMapping key = field.getKeyMapping();
+        ClassMapping[] clss = key.getIndependentTypeMappings();
+        if (clss.length != 1) {
+            if (traverse)
+                throw RelationStrategies.unjoinable(key);
+            return joins;
+        }
+        if (forceOuter)
+            return joins.outerJoinRelation(field.getName(),
+                key.getForeignKey(clss[0]), clss[0], key.getSelectSubclasses(),
+                false, false);
+        return joins.joinRelation(_keyRelationName,
+            key.getForeignKey(clss[0]), clss[0], key.getSelectSubclasses(), 
+            false, false);
+    }
+
+    public Object toDataStoreValue(Object val, JDBCStore store) {
+        return RelationStrategies.toDataStoreValue(field.getElementMapping(),
+            val, store);
+    }
+
+    public Object toKeyDataStoreValue(Object val, JDBCStore store) {
+        return RelationStrategies.toDataStoreValue(field.getKeyMapping(),
+            val, store);
+    }
+}



Mime
View raw message