You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2006/07/19 23:35:07 UTC
svn commit: r423615 [23/44] - in /incubator/openjpa/trunk: ./
openjpa-jdbc-5/ openjpa-jdbc-5/src/ openjpa-jdbc-5/src/main/
openjpa-jdbc-5/src/main/java/ openjpa-jdbc-5/src/main/java/org/
openjpa-jdbc-5/src/main/java/org/apache/ openjpa-jdbc-5/src/main/...
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,731 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
+import org.apache.openjpa.jdbc.kernel.JDBCFetchState;
+import org.apache.openjpa.jdbc.kernel.JDBCStore;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.Embeddable;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.meta.MappingInfo;
+import org.apache.openjpa.jdbc.meta.ValueMapping;
+import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.ColumnIO;
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.jdbc.schema.Table;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.jdbc.sql.Joins;
+import org.apache.openjpa.jdbc.sql.Result;
+import org.apache.openjpa.jdbc.sql.Row;
+import org.apache.openjpa.jdbc.sql.RowManager;
+import org.apache.openjpa.jdbc.sql.SQLBuffer;
+import org.apache.openjpa.jdbc.sql.Select;
+import org.apache.openjpa.jdbc.sql.SelectExecutor;
+import org.apache.openjpa.jdbc.sql.Union;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.util.ApplicationIds;
+import org.apache.openjpa.util.MetaDataException;
+import org.apache.openjpa.util.OpenJPAId;
+
+/**
+ * Mapping for a single-valued relation to another entity.
+ *
+ * @author Abe White
+ * @since 4.0
+ */
+public class RelationFieldStrategy
+ extends AbstractFieldStrategy
+ implements Embeddable {
+
+ private static final Localizer _loc = Localizer.forPackage
+ (RelationFieldStrategy.class);
+
+ private Boolean _fkOid = null;
+
+ public void map(boolean adapt) {
+ if (field.getTypeCode() != JavaTypes.PC || field.isEmbeddedPC())
+ throw new MetaDataException(_loc.get("not-relation", field));
+
+ field.getKeyMapping().getValueInfo().assertNoSchemaComponents
+ (field.getKey(), !adapt);
+ field.getElementMapping().getValueInfo().assertNoSchemaComponents
+ (field.getElement(), !adapt);
+ boolean criteria = field.getValueInfo().getUseClassCriteria();
+
+ // check for named inverse
+ FieldMapping mapped = field.getMappedByMapping();
+ if (mapped != null) {
+ field.getMappingInfo().assertNoSchemaComponents(field, !adapt);
+ field.getValueInfo().assertNoSchemaComponents(field, !adapt);
+ mapped.resolve(mapped.MODE_META | mapped.MODE_MAPPING);
+
+ if (!mapped.getDefiningMapping().isMapped())
+ throw new MetaDataException(_loc.get("mapped-by-unmapped",
+ field, mapped));
+
+ if (mapped.getTypeCode() == JavaTypes.PC) {
+ if (mapped.getJoinDirection() == mapped.JOIN_FORWARD) {
+ field.setJoinDirection(field.JOIN_INVERSE);
+ field.setColumns(mapped.getDefiningMapping().
+ getPrimaryKeyColumns());
+ } else if (isTypeUnjoinedSubclass(mapped))
+ throw new MetaDataException(_loc.get
+ ("mapped-inverse-unjoined", field.getName(),
+ field.getDefiningMapping(), mapped));
+
+ field.setForeignKey(mapped.getForeignKey
+ (field.getDefiningMapping()));
+ } else if (mapped.getElement().getTypeCode() == JavaTypes.PC) {
+ if (isTypeUnjoinedSubclass(mapped.getElementMapping()))
+ throw new MetaDataException(_loc.get
+ ("mapped-inverse-unjoined", field.getName(),
+ field.getDefiningMapping(), mapped));
+
+ // warn the user about making the collection side the owner
+ Log log = field.getRepository().getLog();
+ if (log.isInfoEnabled())
+ log.info(_loc.get("coll-owner", field, mapped));
+ field.setForeignKey(mapped.getElementMapping().
+ getForeignKey());
+ } else
+ throw new MetaDataException(_loc.get("not-inv-relation",
+ field, mapped));
+
+ field.setUseClassCriteria(criteria);
+ return;
+ }
+
+ // this is necessary to support openjpa 3 mappings, which didn't
+ // differentiate between secondary table joins and relations built
+ // around an inverse key: check to see if we're mapped as a secondary
+ // table join but we're in the table of the related type, and if so
+ // switch our join mapping info to our value mapping info
+ String tableName = field.getMappingInfo().getTableName();
+ Table table = field.getTypeMapping().getTable();
+ ValueMappingInfo vinfo = field.getValueInfo();
+ if (tableName != null && table != null
+ && (tableName.equalsIgnoreCase(table.getName())
+ || tableName.equalsIgnoreCase(table.getFullName()))) {
+ vinfo.setJoinDirection(MappingInfo.JOIN_INVERSE);
+ vinfo.setColumns(field.getMappingInfo().getColumns());
+ field.getMappingInfo().setTableName(null);
+ field.getMappingInfo().setColumns(null);
+ }
+
+ field.mapJoin(adapt, false);
+ if (field.getTypeMapping().isMapped()) {
+ ForeignKey fk = vinfo.getTypeJoin(field, field.getName(), true,
+ adapt);
+ field.setForeignKey(fk);
+ field.setColumnIO(vinfo.getColumnIO());
+ if (vinfo.getJoinDirection() == vinfo.JOIN_INVERSE)
+ field.setJoinDirection(field.JOIN_INVERSE);
+ } else
+ RelationStrategies.mapRelationToUnmappedPC(field, field.getName(),
+ adapt);
+
+ field.setUseClassCriteria(criteria);
+ field.mapConstraints(field.getName(), adapt);
+ field.mapPrimaryKey(adapt);
+ }
+
+ /**
+ * Return whether our defining mapping is an unjoined subclass of
+ * the type of the given value.
+ */
+ private boolean isTypeUnjoinedSubclass(ValueMapping mapped) {
+ ClassMapping def = field.getDefiningMapping();
+ for (; def != null; def = def.getJoinablePCSuperclassMapping())
+ if (def == mapped.getTypeMapping())
+ return false;
+ return true;
+ }
+
+ public void initialize() {
+ field.setUsesIntermediate(true);
+
+ ForeignKey fk = field.getForeignKey();
+ if (fk == null)
+ _fkOid = Boolean.TRUE;
+ else if (field.getJoinDirection() != FieldMapping.JOIN_INVERSE)
+ _fkOid = field.getTypeMapping().isForeignKeyObjectId(fk);
+ }
+
+ public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+ throws SQLException {
+ if (field.getMappedBy() != null)
+ return;
+
+ OpenJPAStateManager rel = RelationStrategies.getStateManager
+ (sm.fetchObjectField(field.getIndex()), store.getContext());
+ if (field.getJoinDirection() == field.JOIN_INVERSE)
+ updateInverse(sm, rel, store, rm, sm);
+ else {
+ Row row = field.getRow(sm, store, rm, Row.ACTION_INSERT);
+ if (row != null)
+ field.setForeignKey(row, rel);
+ }
+ }
+
+ public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+ throws SQLException {
+ if (field.getMappedBy() != null)
+ return;
+
+ Object relObj = sm.fetchObjectField(field.getIndex());
+ OpenJPAStateManager rel = RelationStrategies.getStateManager
+ (sm.fetchObjectField(field.getIndex()), store.getContext());
+
+ if (field.getJoinDirection() == field.JOIN_INVERSE) {
+ nullInverse(sm, rm);
+ updateInverse(sm, rel, store, rm, sm);
+ } else {
+ Row row = field.getRow(sm, store, rm, Row.ACTION_UPDATE);
+ if (row != null)
+ field.setForeignKey(row, rel);
+ }
+ }
+
+ public void delete(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+ throws SQLException {
+ if (field.getMappedBy() != null)
+ return;
+
+ if (field.getJoinDirection() == field.JOIN_INVERSE) {
+ if (sm.getLoaded().get(field.getIndex())) {
+ OpenJPAStateManager rel = RelationStrategies.getStateManager(sm.
+ fetchObjectField(field.getIndex()), store.getContext());
+ updateInverse(sm, rel, store, rm, null);
+ } else
+ nullInverse(sm, rm);
+ } else {
+ field.deleteRow(sm, store, rm);
+
+ // if our foreign key has a delete action, we need to set the
+ // related object so constraints can be evaluated
+ OpenJPAStateManager rel = RelationStrategies.getStateManager
+ (sm.fetchObjectField(field.getIndex()), store.getContext());
+ if (rel != null) {
+ ForeignKey fk = field.getForeignKey((ClassMapping)
+ rel.getMetaData());
+ if (fk.getDeleteAction() == ForeignKey.ACTION_RESTRICT) {
+ Row row = field.getRow(sm, store, rm, Row.ACTION_DELETE);
+ row.setForeignKey(fk, null, rel);
+ }
+ }
+ }
+ }
+
+ /**
+ * Null inverse relations that reference the given object.
+ */
+ private void nullInverse(OpenJPAStateManager sm, RowManager rm)
+ throws SQLException {
+ ForeignKey fk = field.getForeignKey();
+ ColumnIO io = field.getColumnIO();
+ if (!io.isAnyUpdatable(fk, true))
+ return;
+
+ if (field.getIndependentTypeMappings().length != 1)
+ throw RelationStrategies.uninversable(field);
+
+ Row row = rm.getAllRows(fk.getTable(), Row.ACTION_UPDATE);
+ row.setForeignKey(fk, io, null);
+ row.whereForeignKey(fk, sm);
+ rm.flushAllRows(row);
+ }
+
+ /**
+ * This method updates the inverse columns of our relation
+ * with the given object.
+ */
+ private void updateInverse(OpenJPAStateManager sm, OpenJPAStateManager rel,
+ JDBCStore store, RowManager rm, OpenJPAStateManager sm2)
+ throws SQLException {
+ // nothing to do if inverse is null or about to be deleted
+ //### should we throw an exception if the inverse is null?
+ if (rel == null || rel.isDeleted())
+ return;
+
+ ForeignKey fk = field.getForeignKey();
+ ColumnIO io = field.getColumnIO();
+
+ int action;
+ if (rel.isNew() && !rel.isFlushed()) {
+ if (sm2 == null || !io.isAnyInsertable(fk, false))
+ return;
+ action = Row.ACTION_INSERT;
+ } else {
+ if (!io.isAnyUpdatable(fk, sm2 == null))
+ return;
+ action = Row.ACTION_UPDATE;
+ }
+
+ if (field.getIndependentTypeMappings().length != 1)
+ throw RelationStrategies.uninversable(field);
+
+ // get the row for the inverse object; the row might be in a secondary
+ // table if there is a field controlling the foreign key
+ Row row = null;
+ FieldMapping[] invs = field.getInverseMappings();
+ for (int i = 0; i < invs.length; i++) {
+ if (invs[i].getMappedByMetaData() == field
+ && invs[i].getTypeCode() == JavaTypes.PC) {
+ row = invs[i].getRow(rel, store, rm, action);
+ break;
+ }
+ }
+ ClassMapping relMapping = field.getTypeMapping();
+ if (row == null)
+ row = rm.getRow(relMapping.getTable(), action, rel, true);
+
+ // if this is an update, this might be the only mod to the row, so
+ // make sure the where condition is set
+ if (action == Row.ACTION_UPDATE
+ && row.getTable() == relMapping.getTable())
+ row.wherePrimaryKey(rel);
+
+ // update the inverse pointer with our oid value
+ row.setForeignKey(fk, io, sm2);
+ }
+
+ public int supportsSelect(Select sel, int type, OpenJPAStateManager sm,
+ JDBCStore store, JDBCFetchConfiguration fetch) {
+ if (type == Select.TYPE_JOINLESS)
+ return (field.getJoinDirection() != field.JOIN_INVERSE
+ && sel.isSelected(field.getTable())) ? 1 : 0;
+ if (type == Select.TYPE_TWO_PART)
+ return 1;
+
+ // already cached?
+ if (sm != null) {
+ Object oid = sm.getIntermediate(field.getIndex());
+ if (store.getContext().findCached(oid, null) != null)
+ return 0;
+ }
+
+ ClassMapping[] clss = field.getIndependentTypeMappings();
+ switch (type) {
+ case Select.EAGER_PARALLEL:
+ return clss.length;
+ case Select.EAGER_OUTER:
+ return (clss.length == 1 && store.getDBDictionary().canOuterJoin
+ (sel.getJoinSyntax(), field.getForeignKey(clss[0]))) ? 1 :
+ 0;
+ case Select.EAGER_INNER:
+ return (clss.length == 1) ? 1 : 0;
+ default:
+ return 0;
+ }
+ }
+
+ public void selectEagerParallel(SelectExecutor sel,
+ final OpenJPAStateManager sm, final JDBCStore store,
+ final JDBCFetchState fetchState, final int eagerMode) {
+ final ClassMapping[] clss = field.getIndependentTypeMappings();
+ if (!(sel instanceof Union))
+ selectEagerParallel((Select) sel, clss[0], store, fetchState,
+ eagerMode);
+ else {
+ Union union = (Union) sel;
+ if (fetchState.getJDBCFetchConfiguration().getSubclassFetchMode
+ (field.getTypeMapping()) != JDBCFetchConfiguration.EAGER_JOIN)
+ union.abortUnion();
+ union.select(new Union.Selector() {
+ public void select(Select sel, int idx) {
+ selectEagerParallel(sel, clss[idx], store, fetchState,
+ eagerMode);
+ }
+ });
+ }
+ }
+
+ /**
+ * Perform an eager parallel select.
+ */
+ private void selectEagerParallel(Select sel, ClassMapping cls,
+ JDBCStore store, JDBCFetchState fetchState, int eagerMode) {
+ sel.selectPrimaryKey(field.getDefiningMapping());
+ // set a variable name that does not conflict with any in the query;
+ // using a variable guarantees that the selected data will use different
+ // aliases and joins than any existing WHERE conditions on this field
+ // that might otherwise limit the relations that match
+ Joins joins = sel.newJoins().setVariable("*");
+ eagerJoin(joins, cls, true);
+ sel.select(cls, field.getSelectSubclasses(), store, fetchState,
+ eagerMode, joins);
+ }
+
+ public void selectEagerJoin(Select sel, OpenJPAStateManager sm,
+ JDBCStore store, JDBCFetchState fetchState, int eagerMode) {
+ // limit the eager mode to single on recursive eager fetching b/c
+ // at this point the select has been modified and an attempt to
+ // clone it for a to-many eager select can result in a clone that
+ // produces invalid SQL
+ ClassMapping cls = field.getIndependentTypeMappings()[0];
+ sel.select(cls, field.getSelectSubclasses(), store, fetchState,
+ JDBCFetchConfiguration.EAGER_JOIN,
+ eagerJoin(sel.newJoins(), cls, false));
+ }
+
+ /**
+ * Add the joins needed to select/load eager data.
+ */
+ private Joins eagerJoin(Joins joins, ClassMapping cls, boolean forceInner) {
+ boolean inverse = field.getJoinDirection() == field.JOIN_INVERSE;
+ if (!inverse)
+ joins = join(joins, false);
+
+ // and join into relation
+ ForeignKey fk = field.getForeignKey(cls);
+ if (!forceInner && field.getNullValue() != FieldMapping.NULL_EXCEPTION)
+ return joins.outerJoinRelation(field.getName(), fk, inverse, false);
+ return joins.joinRelation(field.getName(), fk, inverse, false);
+ }
+
+ public int select(Select sel, OpenJPAStateManager sm, JDBCStore store,
+ JDBCFetchState fetchState, int eagerMode) {
+ if (field.getJoinDirection() == field.JOIN_INVERSE)
+ return -1;
+ // already cached oid?
+ if (sm != null && sm.getIntermediate(field.getIndex()) != null)
+ return -1;
+ if (!Boolean.TRUE.equals(_fkOid))
+ return -1;
+ sel.select(field.getColumns(), field.join(sel));
+ return 0;
+ }
+
+ public Object loadEagerParallel(OpenJPAStateManager sm, JDBCStore store,
+ JDBCFetchState fetchState, Object res)
+ throws SQLException {
+ // process batched results if we haven't already
+ Map rels;
+ if (res instanceof Result)
+ rels = processEagerParallelResult(sm, store, fetchState,
+ (Result) res);
+ else
+ rels = (Map) res;
+
+ // store object for this oid in instance
+ sm.storeObject(field.getIndex(), rels.remove(sm.getObjectId()));
+ return rels;
+ }
+
+ /**
+ * Process the given batched result.
+ */
+ private Map processEagerParallelResult(OpenJPAStateManager sm,
+ JDBCStore store, JDBCFetchState fetchState, Result res)
+ throws SQLException {
+ // do same joins as for load
+ //### cheat: we know typical result joins only care about the relation
+ //### path; thus we can ignore different mappings
+ ClassMapping[] clss = field.getIndependentTypeMappings();
+ Joins joins = res.newJoins().setVariable("*");
+ eagerJoin(joins, clss[0], true);
+
+ Map rels = new HashMap();
+ ClassMapping owner = field.getDefiningMapping();
+ ClassMapping cls;
+ Object oid;
+ while (res.next()) {
+ cls = res.getBaseMapping();
+ if (cls == null)
+ cls = clss[0];
+ oid = owner.getObjectId(store, res, null, true, null);
+ rels.put(oid, res.load(cls, store, fetchState, joins));
+ }
+ res.close();
+
+ return rels;
+ }
+
+ public void loadEagerJoin(OpenJPAStateManager sm, JDBCStore store,
+ JDBCFetchState fetchState, Result res)
+ throws SQLException {
+ ClassMapping cls = field.getIndependentTypeMappings()[0];
+ sm.storeObject(field.getIndex(), res.load(cls, store, fetchState,
+ eagerJoin(res.newJoins(), cls, false)));
+ }
+
+ public void load(OpenJPAStateManager sm, JDBCStore store,
+ JDBCFetchState fetchState, Result res)
+ throws SQLException {
+ if (field.getJoinDirection() == field.JOIN_INVERSE)
+ return;
+ // cached oid?
+ if (sm != null && sm.getIntermediate(field.getIndex()) != null)
+ return;
+ if (!Boolean.TRUE.equals(_fkOid))
+ return;
+ if (!res.containsAll(field.getColumns()))
+ return;
+
+ // get the related object's oid
+ ClassMapping relMapping = field.getTypeMapping();
+ Object oid = null;
+ if (relMapping.isMapped()) {
+ oid = relMapping.getObjectId(store, res, field.getForeignKey(),
+ field.getPolymorphic() != ValueMapping.POLY_FALSE, null);
+ } else {
+ Column[] cols = field.getColumns();
+ if (relMapping.getIdentityType() == ClassMapping.ID_DATASTORE) {
+ long id = res.getLong(cols[0]);
+ if (!res.wasNull())
+ oid = store.newDataStoreId(id, relMapping, true);
+ } else // application id
+ {
+ if (cols.length == 1) {
+ Object val = res.getObject(cols[0], null, null);
+ if (val != null)
+ oid = ApplicationIds.fromPKValues(new Object[]{ val },
+ relMapping);
+ } else {
+ Object[] vals = new Object[cols.length];
+ for (int i = 0; i < cols.length; i++) {
+ vals[i] = res.getObject(cols[i], null, null);
+ if (vals[i] == null)
+ break;
+ if (i == cols.length - 1)
+ oid = ApplicationIds.fromPKValues(vals, relMapping);
+ }
+ }
+ }
+ }
+
+ if (oid == null)
+ sm.storeObject(field.getIndex(), null);
+ else
+ sm.setIntermediate(field.getIndex(), oid);
+ }
+
+ public void load(final OpenJPAStateManager sm, final JDBCStore store,
+ final JDBCFetchState fetchState)
+ throws SQLException {
+ final JDBCFetchConfiguration fetch =
+ fetchState.getJDBCFetchConfiguration();
+ // check for cached oid value, or load oid if no way to join
+ if (Boolean.TRUE.equals(_fkOid)) {
+ Object oid = sm.getIntermediate(field.getIndex());
+ if (oid != null) {
+ Object val = store.find(oid, field, fetchState);
+ sm.storeObject(field.getIndex(), val);
+ return;
+ }
+ }
+
+ final ClassMapping[] rels = field.getIndependentTypeMappings();
+ final int subs = field.getSelectSubclasses();
+ final Joins[] resJoins = new Joins[rels.length];
+
+ // select related mapping columns; joining from the related type
+ // back to our fk table if not an inverse mapping (in which case we
+ // can just make sure the inverse cols == our pk values)
+ Union union = store.getSQLFactory().newUnion(rels.length);
+ union.setSingleResult(true);
+ if (fetch.getSubclassFetchMode(field.getTypeMapping())
+ != JDBCFetchConfiguration.EAGER_JOIN)
+ union.abortUnion();
+ union.select(new Union.Selector() {
+ public void select(Select sel, int idx) {
+ if (field.getJoinDirection() == field.JOIN_INVERSE)
+ sel.whereForeignKey(field.getForeignKey(rels[idx]),
+ sm.getObjectId(), field.getDefiningMapping(), store);
+ else {
+ resJoins[idx] = sel.newJoins().joinRelation
+ (field.getName(), field.getForeignKey(rels[idx]),
+ false, false);
+ field.wherePrimaryKey(sel, sm, store);
+ }
+ sel.select(rels[idx], subs, store, fetchState,
+ fetch.EAGER_JOIN, resJoins[idx]);
+ }
+ });
+
+ Result res = union.execute(store, fetch);
+ try {
+ Object val = null;
+ if (res.next())
+ val = res.load(rels[res.indexOf()], store, fetchState,
+ resJoins[res.indexOf()]);
+ sm.storeObject(field.getIndex(), val);
+ } finally {
+ res.close();
+ }
+ }
+
+ public Object toDataStoreValue(Object val, JDBCStore store) {
+ return RelationStrategies.toDataStoreValue(field, val, store);
+ }
+
+ public void appendIsNull(SQLBuffer sql, Select sel, Joins joins) {
+ // if no inverse, just join to mapping's table (usually a no-op
+ // because it'll be in the primary table) and see if fk cols are null;
+ // if inverse, then we have to do a sub-select to see if any inverse
+ // objects point back to this field's owner
+ if (field.getJoinDirection() != field.JOIN_INVERSE) {
+ //### probably need some sort of subselect here on fk constants
+ joins = join(joins, false);
+ Column[] cols = field.getColumns();
+ if (cols.length == 0)
+ sql.append("1 <> 1");
+ else
+ sql.append(sel.getColumnAlias(cols[0], joins)).
+ append(" IS ").appendValue(null, cols[0]);
+ } else
+ testInverseNull(sql, sel, joins, true);
+ }
+
+ public void appendIsNotNull(SQLBuffer sql, Select sel, Joins joins) {
+ // if no inverse, just join to mapping's table (usually a no-op
+ // because it'll be in the primary table) and see if fk cols aren't
+ // null; if inverse, then we have to do a sub-select to see if any
+ // inverse objects point back to this field's owner
+ if (field.getJoinDirection() != field.JOIN_INVERSE) {
+ //### probably need some sort of subselect here on fk constants
+ joins = join(joins, false);
+ Column[] cols = field.getColumns();
+ if (cols.length == 0)
+ sql.append("1 = 1");
+ else
+ sql.append(sel.getColumnAlias(cols[0], joins)).
+ append(" IS NOT ").appendValue(null, cols[0]);
+ } else
+ testInverseNull(sql, sel, joins, false);
+ }
+
+ /**
+ * Append SQL for a sub-select testing whether an inverse object exists
+ * for this relation.
+ */
+ private void testInverseNull(SQLBuffer sql, Select sel, Joins joins,
+ boolean empty) {
+ DBDictionary dict = field.getMappingRepository().getDBDictionary();
+ dict.assertSupport(dict.supportsSubselect, "SupportsSubselect");
+
+ if (field.getIndependentTypeMappings().length != 1)
+ throw RelationStrategies.uninversable(field);
+
+ if (empty)
+ sql.append("0 = ");
+ else
+ sql.append("0 < ");
+
+ ForeignKey fk = field.getForeignKey();
+ ContainerFieldStrategy.appendJoinCount(sql, sel, joins, dict, field,
+ fk);
+ }
+
+ public Joins join(Joins joins, boolean forceOuter) {
+ // if we're not in an inverse object table join normally, otherwise
+ // already traversed the relation; just join back to owner table
+ if (field.getJoinDirection() != field.JOIN_INVERSE)
+ return field.join(joins, forceOuter, false);
+ if (field.getIndependentTypeMappings().length != 1)
+ throw RelationStrategies.uninversable(field);
+ if (forceOuter)
+ return joins.outerJoinRelation(field.getName(),
+ field.getForeignKey(), true, false);
+ return joins.joinRelation(field.getName(), field.getForeignKey(),
+ true, false);
+ }
+
+ public Joins joinRelation(Joins joins, boolean forceOuter,
+ boolean traverse) {
+ // if this is an inverse mapping it's already joined to the relation
+ if (field.getJoinDirection() == field.JOIN_INVERSE)
+ return joins;
+ ClassMapping[] clss = field.getIndependentTypeMappings();
+ if (clss.length != 1) {
+ if (traverse)
+ throw RelationStrategies.unjoinable(field);
+ return joins;
+ }
+ if (forceOuter)
+ return joins.outerJoinRelation(field.getName(),
+ field.getForeignKey(clss[0]), false, false);
+ return joins.joinRelation(field.getName(),
+ field.getForeignKey(clss[0]), false, false);
+ }
+
+ /////////////////////////////
+ // Embeddable implementation
+ /////////////////////////////
+
+ public Column[] getColumns() {
+ return field.getColumns();
+ }
+
+ public ColumnIO getColumnIO() {
+ return field.getColumnIO();
+ }
+
+ public Object[] getResultArguments() {
+ return null;
+ }
+
+ public Object toEmbeddedDataStoreValue(Object val, JDBCStore store) {
+ return toDataStoreValue(val, store);
+ }
+
+ public Object toEmbeddedObjectValue(Object val) {
+ return UNSUPPORTED;
+ }
+
+ public void loadEmbedded(OpenJPAStateManager sm, JDBCStore store,
+ JDBCFetchState fetchState, Object val)
+ throws SQLException {
+ ClassMapping relMapping = field.getTypeMapping();
+ Object oid;
+ if (val == null)
+ oid = null;
+ else if (relMapping.getIdentityType() == ClassMapping.ID_DATASTORE)
+ oid = store.newDataStoreId(((Number) val).longValue(), relMapping,
+ field.getPolymorphic() != ValueMapping.POLY_FALSE);
+ else {
+ Object[] pks = (getColumns().length == 1) ? new Object[]{ val }
+ : (Object[]) val;
+ boolean nulls = true;
+ for (int i = 0; nulls && i < pks.length; i++)
+ nulls = pks[i] == null;
+ if (nulls)
+ oid = null;
+ else {
+ oid = ApplicationIds.fromPKValues(pks, relMapping);
+ if (field.getPolymorphic() == ValueMapping.POLY_FALSE
+ && oid instanceof OpenJPAId) {
+ ((OpenJPAId) oid).setManagedInstanceType(relMapping.
+ getDescribedType());
+ }
+ }
+ }
+
+ if (oid == null)
+ sm.storeObject(field.getIndex(), null);
+ else
+ sm.setIntermediate(field.getIndex(), oid);
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationFieldStrategy.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationMapInverseKeyFieldStrategy.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationMapInverseKeyFieldStrategy.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationMapInverseKeyFieldStrategy.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationMapInverseKeyFieldStrategy.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.SQLException;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
+import org.apache.openjpa.jdbc.kernel.JDBCFetchState;
+import org.apache.openjpa.jdbc.kernel.JDBCStore;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.meta.ValueMapping;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.jdbc.sql.Joins;
+import org.apache.openjpa.jdbc.sql.Result;
+import org.apache.openjpa.jdbc.sql.Select;
+import org.apache.openjpa.jdbc.sql.Union;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.util.InternalException;
+import org.apache.openjpa.util.MetaDataException;
+import org.apache.openjpa.util.Proxy;
+
+/**
+ * Uses an inverse foreign key in the table of the map value to determine
+ * map values. Derives map keys from a field in each value.
+ *
+ * @author Abe White
+ */
+public class RelationMapInverseKeyFieldStrategy
+ extends RelationToManyInverseKeyFieldStrategy
+ implements LRSMapFieldStrategy {
+
+ private static final Localizer _loc = Localizer.forPackage
+ (RelationMapInverseKeyFieldStrategy.class);
+
+ public FieldMapping getFieldMapping() {
+ return field;
+ }
+
+ public ClassMapping[] getIndependentKeyMappings(boolean traverse) {
+ return ClassMapping.EMPTY_MAPPINGS;
+ }
+
+ public ClassMapping[] getIndependentValueMappings(boolean traverse) {
+ return getIndependentElementMappings(traverse);
+ }
+
+ public Column[] getKeyColumns(ClassMapping cls) {
+ return cls.getFieldMapping(field.getKey().
+ getValueMappedByMetaData().getIndex()).getColumns();
+ }
+
+ public Column[] getValueColumns(ClassMapping cls) {
+ return cls.getPrimaryKeyColumns();
+ }
+
+ public ForeignKey getJoinForeignKey(ClassMapping cls) {
+ return super.getJoinForeignKey(cls);
+ }
+
+ public void selectKey(Select sel, ClassMapping key, OpenJPAStateManager sm,
+ JDBCStore store, JDBCFetchState fetchState, Joins joins) {
+ throw new InternalException();
+ }
+
+ public Object loadKey(OpenJPAStateManager sm, JDBCStore store,
+ JDBCFetchState fetchState, Result res, Joins joins)
+ throws SQLException {
+ throw new InternalException();
+ }
+
+ public Object deriveKey(JDBCStore store, Object value) {
+ OpenJPAStateManager sm = RelationStrategies.getStateManager(value,
+ store.getContext());
+ return (sm == null) ? null : sm.fetchField(field.getKey().
+ getValueMappedByMetaData().getIndex(), false);
+ }
+
+ public Object deriveValue(JDBCStore store, Object key) {
+ return null;
+ }
+
+ public void selectValue(Select sel, ClassMapping val,
+ OpenJPAStateManager sm,
+ JDBCStore store, JDBCFetchState fetchState, Joins joins) {
+ selectElement(sel, val, store, fetchState,
+ JDBCFetchConfiguration.EAGER_NONE, joins);
+ }
+
+ public Object loadValue(OpenJPAStateManager sm, JDBCStore store,
+ JDBCFetchState fetchState, Result res, Joins joins)
+ throws SQLException {
+ return loadElement(sm, store, fetchState, res, joins);
+ }
+
+ public Result[] getResults(final OpenJPAStateManager sm,
+ final JDBCStore store, final JDBCFetchState fetchState,
+ final int eagerMode, final Joins[] joins, boolean lrs)
+ throws SQLException {
+ ValueMapping val = field.getElementMapping();
+ final ClassMapping[] vals = val.getIndependentTypeMappings();
+ Union union = store.getSQLFactory().newUnion(vals.length);
+ JDBCFetchConfiguration fetch = fetchState.getJDBCFetchConfiguration();
+ if (fetch.getSubclassFetchMode(val.getTypeMapping())
+ != JDBCFetchConfiguration.EAGER_JOIN)
+ union.abortUnion();
+ union.setLRS(lrs);
+ union.select(new Union.Selector() {
+ public void select(Select sel, int idx) {
+ joins[1] = selectAll(sel, vals[idx], sm, store, fetchState,
+ eagerMode);
+ }
+ });
+ Result res = union.execute(store,
+ fetchState.getJDBCFetchConfiguration());
+ return new Result[]{ res, res };
+ }
+
+ public Joins joinKeyRelation(Joins joins, ClassMapping key) {
+ return joins;
+ }
+
+ public Joins joinValueRelation(Joins joins, ClassMapping val) {
+ return joinElementRelation(joins, val);
+ }
+
+ protected Proxy newLRSProxy(OpenJPAConfiguration conf) {
+ return new LRSProxyMap(this, conf);
+ }
+
+ protected void add(JDBCStore store, Object coll, Object obj) {
+ if (obj != null)
+ ((Map) coll).put(deriveKey(store, obj), obj);
+ }
+
+ protected Collection toCollection(Object val) {
+ return (val == null) ? null : ((Map) val).values();
+ }
+
+ public void map(boolean adapt) {
+ if (field.getTypeCode() != JavaTypes.MAP)
+ throw new MetaDataException(_loc.get("not-map", field));
+ if (field.getKey().getValueMappedBy() == null)
+ throw new MetaDataException(_loc.get("not-mapped-by-key", field));
+ super.map(adapt);
+ }
+
+ public Joins joinKey(Joins joins, boolean forceOuter) {
+ return joinRelation(join(joins, forceOuter), forceOuter, false);
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationMapInverseKeyFieldStrategy.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationMapTableFieldStrategy.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationMapTableFieldStrategy.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationMapTableFieldStrategy.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationMapTableFieldStrategy.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.SQLException;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
+import org.apache.openjpa.jdbc.kernel.JDBCFetchState;
+import org.apache.openjpa.jdbc.kernel.JDBCStore;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.meta.ValueMapping;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.jdbc.sql.Joins;
+import org.apache.openjpa.jdbc.sql.Result;
+import org.apache.openjpa.jdbc.sql.Select;
+import org.apache.openjpa.jdbc.sql.Union;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.util.InternalException;
+import org.apache.openjpa.util.MetaDataException;
+import org.apache.openjpa.util.Proxy;
+
+/**
+ * Uses an association table to hold map values. Derives map keys from
+ * a field in each value.
+ *
+ * @author Abe White
+ */
+public class RelationMapTableFieldStrategy
+ extends RelationToManyTableFieldStrategy
+ implements LRSMapFieldStrategy {
+
+ private static final Localizer _loc = Localizer.forPackage
+ (RelationMapTableFieldStrategy.class);
+
+ public FieldMapping getFieldMapping() {
+ return field;
+ }
+
+ public ClassMapping[] getIndependentKeyMappings(boolean traverse) {
+ return ClassMapping.EMPTY_MAPPINGS;
+ }
+
+ public ClassMapping[] getIndependentValueMappings(boolean traverse) {
+ return getIndependentElementMappings(traverse);
+ }
+
+ public ForeignKey getJoinForeignKey(ClassMapping cls) {
+ return super.getJoinForeignKey(cls);
+ }
+
+ public Column[] getKeyColumns(ClassMapping cls) {
+ return cls.getFieldMapping(field.getKey().
+ getValueMappedByMetaData().getIndex()).getColumns();
+ }
+
+ public Column[] getValueColumns(ClassMapping cls) {
+ return field.getElementMapping().getColumns();
+ }
+
+ public void selectKey(Select sel, ClassMapping key, OpenJPAStateManager sm,
+ JDBCStore store, JDBCFetchState fetchState, Joins joins) {
+ throw new InternalException();
+ }
+
+ public Object loadKey(OpenJPAStateManager sm, JDBCStore store,
+ JDBCFetchState fetchState, Result res, Joins joins)
+ throws SQLException {
+ throw new InternalException();
+ }
+
+ public Object deriveKey(JDBCStore store, Object value) {
+ OpenJPAStateManager sm = RelationStrategies.getStateManager(value,
+ store.getContext());
+ return (sm == null) ? null : sm.fetchField(field.getKey().
+ getValueMappedByMetaData().getIndex(), false);
+ }
+
+ public Object deriveValue(JDBCStore store, Object key) {
+ return null;
+ }
+
+ public void selectValue(Select sel, ClassMapping val,
+ OpenJPAStateManager sm,
+ JDBCStore store, JDBCFetchState fetchState, Joins joins) {
+ selectElement(sel, val, store, fetchState,
+ JDBCFetchConfiguration.EAGER_NONE, joins);
+ }
+
+ public Object loadValue(OpenJPAStateManager sm, JDBCStore store,
+ JDBCFetchState fetchState, Result res, Joins joins)
+ throws SQLException {
+ return loadElement(sm, store, fetchState, res, joins);
+ }
+
+ public Result[] getResults(final OpenJPAStateManager sm,
+ final JDBCStore store, final JDBCFetchState fetchState,
+ final int eagerMode, final Joins[] joins, boolean lrs)
+ throws SQLException {
+ ValueMapping val = field.getElementMapping();
+ final ClassMapping[] vals = val.getIndependentTypeMappings();
+ Union union = store.getSQLFactory().newUnion(vals.length);
+ JDBCFetchConfiguration fetch = fetchState.getJDBCFetchConfiguration();
+ if (fetch.getSubclassFetchMode(val.getTypeMapping())
+ != JDBCFetchConfiguration.EAGER_JOIN)
+ union.abortUnion();
+ union.setLRS(lrs);
+ union.select(new Union.Selector() {
+ public void select(Select sel, int idx) {
+ joins[1] = selectAll(sel, vals[idx], sm, store, fetchState,
+ eagerMode);
+ }
+ });
+ Result res = union.execute(store,
+ fetchState.getJDBCFetchConfiguration());
+ return new Result[]{ res, res };
+ }
+
+ public Joins joinKeyRelation(Joins joins, ClassMapping key) {
+ return joins;
+ }
+
+ public Joins joinValueRelation(Joins joins, ClassMapping val) {
+ return joinElementRelation(joins, val);
+ }
+
+ protected Proxy newLRSProxy(OpenJPAConfiguration conf) {
+ return new LRSProxyMap(this, conf);
+ }
+
+ protected void add(JDBCStore store, Object coll, Object obj) {
+ if (obj != null)
+ ((Map) coll).put(deriveKey(store, obj), obj);
+ }
+
+ protected Collection toCollection(Object val) {
+ return (val == null) ? null : ((Map) val).values();
+ }
+
+ public void map(boolean adapt) {
+ if (field.getTypeCode() != JavaTypes.MAP)
+ throw new MetaDataException(_loc.get("not-map", field));
+ if (field.getKey().getValueMappedBy() == null)
+ throw new MetaDataException(_loc.get("not-mapped-by-key", field));
+ super.map(adapt);
+ }
+
+ public Joins joinKey(Joins joins, boolean forceOuter) {
+ return joinRelation(join(joins, forceOuter), forceOuter, false);
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationMapTableFieldStrategy.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationStrategies.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationStrategies.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationStrategies.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationStrategies.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.util.List;
+
+import org.apache.openjpa.jdbc.kernel.JDBCStore;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.meta.FieldStrategy;
+import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
+import org.apache.openjpa.jdbc.meta.ValueMapping;
+import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.kernel.DetachedValueStateManager;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.kernel.StoreContext;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.util.MetaDataException;
+
+/**
+ * Helper methods for relation mappings.
+ *
+ * @author Abe White
+ */
+public class RelationStrategies {
+
+ private static final Localizer _loc = Localizer.forPackage
+ (RelationStrategies.class);
+
+ /**
+ * Return an exception indicating that we cannot join to the given relation.
+ */
+ public static MetaDataException unjoinable(ValueMapping vm) {
+ return new MetaDataException(_loc.get("cant-join", vm));
+ }
+
+ /**
+ * Return an exception indicating that the relation cannot be loaded
+ * because it has independent subclasses and does not represent a full oid.
+ */
+ public static MetaDataException unloadable(ValueMapping vm) {
+ return new MetaDataException(_loc.get("cant-load", vm));
+ }
+
+ /**
+ * Return an exception indicating that the relation is invalid
+ * because it has is based on an inverse foreign key and has independent
+ * subclasses.
+ */
+ public static MetaDataException uninversable(ValueMapping vm) {
+ return new MetaDataException(_loc.get("cant-inverse", vm));
+ }
+
+ /**
+ * Return the given object as its foreign key values.
+ *
+ * @see FieldStrategy#toDataStoreValue
+ */
+ public static Object toDataStoreValue(ValueMapping vm, Object val,
+ JDBCStore store) {
+ ClassMapping rel;
+ if (val == null || val.getClass() == vm.getType())
+ rel = vm.getTypeMapping(); // common case
+ else
+ rel = vm.getMappingRepository().getMapping(val.getClass(),
+ store.getContext().getClassLoader(), true);
+
+ Column[] cols;
+ if (vm.getJoinDirection() == ValueMapping.JOIN_INVERSE)
+ cols = rel.getPrimaryKeyColumns();
+ else
+ cols = vm.getForeignKey(rel).getPrimaryKeyColumns();
+ return rel.toDataStoreValue(val, cols, store);
+ }
+
+ /**
+ * Map a logical foreign key to an unmapped base class relation.
+ */
+ public static void mapRelationToUnmappedPC(ValueMapping vm,
+ String name, boolean adapt) {
+ if (vm.getTypeMapping().getIdentityType() == ClassMapping.ID_UNKNOWN)
+ throw new MetaDataException(_loc.get("rel-to-unknownid", vm));
+
+ ValueMappingInfo vinfo = vm.getValueInfo();
+ Column[] tmplates = newUnmappedPCTemplateColumns(vm, name);
+ vm.setColumns(vinfo.getColumns(vm, name, tmplates,
+ vm.getFieldMapping().getTable(), adapt));
+ vm.setColumnIO(vinfo.getColumnIO());
+ }
+
+ /**
+ * Create template columns for a logical foreign key to an unmapped base
+ * class relation.
+ */
+ private static Column[] newUnmappedPCTemplateColumns(ValueMapping vm,
+ String name) {
+ ClassMapping rel = vm.getTypeMapping();
+ if (rel.getIdentityType() == ClassMapping.ID_DATASTORE) {
+ Column col = new Column();
+ col.setName(name);
+ col.setJavaType(JavaTypes.LONG);
+ col.setRelationId(true);
+ return new Column[]{ col };
+ }
+
+ FieldMapping[] pks = rel.getPrimaryKeyFieldMappings();
+ Column[] cols = new Column[pks.length];
+ for (int i = 0; i < pks.length; i++) {
+ cols[i] = mapPrimaryKey(vm, pks[i]);
+ if (cols.length == 1)
+ cols[i].setName(name);
+ else if (cols[i].getName() == null)
+ cols[i].setName(name + "_" + pks[i].getName());
+ else
+ cols[i].setName(name + "_" + cols[i].getName());
+ cols[i].setTargetField(pks[i].getName());
+ cols[i].setRelationId(true);
+ }
+ return cols;
+ }
+
+ /**
+ * Create a default column for the given primary key field. Uses the
+ * user's raw mapping info if given. Only supports simple field types.
+ * The column name will be set to the name of the related primary key
+ * column, if any.
+ */
+ private static Column mapPrimaryKey(ValueMapping vm, FieldMapping pk) {
+ List cols = pk.getValueInfo().getColumns();
+ if (cols.size() > 1)
+ throw new MetaDataException(_loc.get("bad-unmapped-rel", vm, pk));
+
+ Column tmplate = null;
+ if (cols.size() == 1)
+ tmplate = (Column) cols.get(0);
+
+ Column col = new Column();
+ switch (pk.getTypeCode()) {
+ case JavaTypes.BOOLEAN:
+ case JavaTypes.BOOLEAN_OBJ:
+ case JavaTypes.BYTE:
+ case JavaTypes.BYTE_OBJ:
+ case JavaTypes.CHAR:
+ case JavaTypes.CHAR_OBJ:
+ case JavaTypes.DOUBLE:
+ case JavaTypes.DOUBLE_OBJ:
+ case JavaTypes.FLOAT:
+ case JavaTypes.FLOAT_OBJ:
+ case JavaTypes.INT:
+ case JavaTypes.INT_OBJ:
+ case JavaTypes.LONG:
+ case JavaTypes.LONG_OBJ:
+ case JavaTypes.NUMBER:
+ case JavaTypes.SHORT:
+ case JavaTypes.SHORT_OBJ:
+ case JavaTypes.STRING:
+ case JavaTypes.BIGINTEGER:
+ case JavaTypes.BIGDECIMAL:
+ col.setJavaType(pk.getTypeCode());
+ break;
+ case JavaTypes.DATE:
+ col.setJavaType(JavaSQLTypes.getDateTypeCode(pk.getType()));
+ break;
+ default:
+ throw new MetaDataException(
+ _loc.get("bad-unmapped-rel", vm, pk));
+ }
+
+ if (tmplate != null) {
+ col.setName(tmplate.getName());
+ col.setType(tmplate.getType());
+ col.setTypeName(tmplate.getTypeName());
+ col.setSize(tmplate.getSize());
+ col.setDecimalDigits(tmplate.getDecimalDigits());
+ }
+ return col;
+ }
+
+ /**
+ * Return the state manager for the given instance, using a detached
+ * state manager if the instnace is not managed.
+ */
+ public static OpenJPAStateManager getStateManager(Object obj,
+ StoreContext ctx) {
+ if (obj == null)
+ return null;
+ OpenJPAStateManager sm = ctx.getStateManager(obj);
+ if (sm == null) // must be detached
+ return new DetachedValueStateManager(obj, ctx);
+ return sm;
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationStrategies.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.SQLException;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.openjpa.jdbc.kernel.JDBCFetchState;
+import org.apache.openjpa.jdbc.kernel.JDBCStore;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.meta.FieldMappingInfo;
+import org.apache.openjpa.jdbc.meta.ValueMapping;
+import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.ColumnIO;
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.jdbc.sql.Joins;
+import org.apache.openjpa.jdbc.sql.Result;
+import org.apache.openjpa.jdbc.sql.Row;
+import org.apache.openjpa.jdbc.sql.RowManager;
+import org.apache.openjpa.jdbc.sql.Select;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.kernel.StoreContext;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.util.ChangeTracker;
+import org.apache.openjpa.util.MetaDataException;
+import org.apache.openjpa.util.Proxies;
+import org.apache.openjpa.util.Proxy;
+
+/**
+ * Maps a relation to a set of other objects using an inverse
+ * foreign key in the related object table.
+ *
+ * @author Abe White
+ */
+public abstract class RelationToManyInverseKeyFieldStrategy
+ extends StoreCollectionFieldStrategy {
+
+ private static final Localizer _loc = Localizer.forPackage
+ (RelationToManyInverseKeyFieldStrategy.class);
+
+ private boolean _orderInsert = false;
+ private boolean _orderUpdate = false;
+
+ protected ClassMapping[] getIndependentElementMappings(boolean traverse) {
+ return field.getElementMapping().getIndependentTypeMappings();
+ }
+
+ protected ForeignKey getJoinForeignKey(ClassMapping elem) {
+ return field.getElementMapping().getForeignKey(elem);
+ }
+
+ protected void selectElement(Select sel, ClassMapping elem,
+ JDBCStore store, JDBCFetchState fetchState, int eagerMode,
+ Joins joins) {
+ sel.select(elem, field.getElementMapping().getSelectSubclasses(),
+ store, fetchState, eagerMode, joins);
+ }
+
+ protected Object loadElement(OpenJPAStateManager sm, JDBCStore store,
+ JDBCFetchState fetchState, Result res, Joins joins)
+ throws SQLException {
+ ClassMapping elem = res.getBaseMapping();
+ if (elem == null)
+ elem = field.getElementMapping().getIndependentTypeMappings()[0];
+ return res.load(elem, store, fetchState, joins);
+ }
+
+ protected Joins join(Joins joins, ClassMapping elem) {
+ return joins.joinRelation(field.getName(),
+ field.getElementMapping().getForeignKey(elem), true, true);
+ }
+
+ protected Joins joinElementRelation(Joins joins, ClassMapping elem) {
+ return joinRelation(joins, false, false);
+ }
+
+ public void map(boolean adapt) {
+ field.getValueInfo().assertNoSchemaComponents(field, !adapt);
+ field.getKeyMapping().getValueInfo().assertNoSchemaComponents
+ (field.getKey(), !adapt);
+
+ ValueMapping elem = field.getElementMapping();
+ if (elem.getTypeCode() != JavaTypes.PC || elem.isEmbeddedPC()
+ || !elem.getTypeMapping().isMapped())
+ throw new MetaDataException(_loc.get("not-elem-relation", field));
+
+ // check for named inverse
+ FieldMapping mapped = field.getMappedByMapping();
+ FieldMappingInfo finfo = field.getMappingInfo();
+ ValueMappingInfo vinfo = elem.getValueInfo();
+ boolean criteria = vinfo.getUseClassCriteria();
+ if (mapped != null) {
+ mapped.resolve(mapped.MODE_META | mapped.MODE_MAPPING);
+ if (!(mapped.getStrategy()instanceof RelationFieldStrategy))
+ throw new MetaDataException(_loc.get("not-inv-relation",
+ field, mapped));
+ vinfo.assertNoSchemaComponents(elem, !adapt);
+ elem.setForeignKey(mapped.getForeignKey
+ (field.getDefiningMapping()));
+ elem.setColumns(mapped.getDefiningMapping().
+ getPrimaryKeyColumns());
+ elem.setJoinDirection(ValueMapping.JOIN_EXPECTED_INVERSE);
+ elem.setUseClassCriteria(criteria);
+
+ field.setOrderColumn(finfo.getOrderColumn(field,
+ mapped.getForeignKey().getTable(), adapt));
+ field.setOrderColumnIO(finfo.getColumnIO());
+ return;
+ }
+
+ // map inverse foreign key in related table
+ ForeignKey fk = vinfo.getInverseTypeJoin(elem, field.getName(), adapt);
+ elem.setForeignKey(fk);
+ elem.setColumnIO(vinfo.getColumnIO());
+ elem.setColumns(elem.getTypeMapping().getPrimaryKeyColumns());
+ elem.setJoinDirection(ValueMapping.JOIN_EXPECTED_INVERSE);
+ elem.setUseClassCriteria(criteria);
+ elem.mapConstraints(field.getName(), adapt);
+
+ field.setOrderColumn(finfo.getOrderColumn(field, fk.getTable(),
+ adapt));
+ field.setOrderColumnIO(finfo.getColumnIO());
+ }
+
+ public void initialize() {
+ Column order = field.getOrderColumn();
+ _orderInsert = field.getOrderColumnIO().isInsertable(order, false);
+ _orderUpdate = field.getOrderColumnIO().isUpdatable(order, false);
+
+ ValueMapping elem = field.getElementMapping();
+ Log log = field.getRepository().getLog();
+ if (field.getMappedBy() == null
+ && elem.getUseClassCriteria() && log.isWarnEnabled()) {
+ ForeignKey fk = elem.getForeignKey();
+ if (elem.getColumnIO().isAnyUpdatable(fk, false))
+ log.warn(_loc.get("class-crit-owner", field));
+ }
+ }
+
+ public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+ throws SQLException {
+ if (field.getMappedBy() == null || _orderInsert || _orderUpdate)
+ insert(sm, rm, sm.fetchObject(field.getIndex()));
+ }
+
+ private void insert(OpenJPAStateManager sm, RowManager rm, Object vals)
+ throws SQLException {
+ if (field.getMappedBy() != null && !_orderInsert && !_orderUpdate)
+ return;
+ Collection coll = toCollection(vals);
+ if (coll == null || coll.isEmpty())
+ return;
+
+ ClassMapping rel = field.getElementMapping().getTypeMapping();
+ int idx = 0;
+ for (Iterator itr = coll.iterator(); itr.hasNext(); idx++)
+ updateInverse(sm.getContext(), itr.next(), rel, rm, sm, idx);
+ }
+
+ public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+ throws SQLException {
+ if (field.getMappedBy() != null && !_orderInsert && !_orderUpdate)
+ return;
+
+ 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, rm, obj);
+ return;
+ }
+
+ // null inverse columns for deletes and update them with our oid for
+ // inserts
+ ClassMapping rel = field.getElementMapping().getTypeMapping();
+ StoreContext ctx = store.getContext();
+ if (field.getMappedBy() == null) {
+ Collection rem = ct.getRemoved();
+ for (Iterator itr = rem.iterator(); itr.hasNext();)
+ updateInverse(ctx, itr.next(), rel, rm, null, 0);
+ }
+
+ Collection add = ct.getAdded();
+ int seq = ct.getNextSequence();
+ for (Iterator itr = add.iterator(); itr.hasNext(); seq++)
+ updateInverse(ctx, itr.next(), rel, rm, sm, seq);
+ if (field.getOrderColumn() != null)
+ ct.setNextSequence(seq);
+ }
+
+ public void delete(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+ throws SQLException {
+ if (field.getMappedBy() != null
+ || field.getElementMapping().getUseClassCriteria())
+ return;
+
+ ValueMapping elem = field.getElementMapping();
+ ColumnIO io = elem.getColumnIO();
+ ForeignKey fk = elem.getForeignKey();
+ if (!io.isAnyUpdatable(fk, true))
+ return;
+
+ // null any existing inverse columns that refer to this obj
+ assertInversable();
+ Row row = rm.getAllRows(fk.getTable(), Row.ACTION_UPDATE);
+ row.setForeignKey(fk, io, null);
+ row.whereForeignKey(fk, sm);
+ rm.flushAllRows(row);
+ }
+
+ /**
+ * This method updates the inverse columns of a 1-M related object
+ * with the given oid.
+ */
+ private void updateInverse(StoreContext ctx, Object inverse,
+ ClassMapping rel, RowManager rm, OpenJPAStateManager sm, int idx)
+ throws SQLException {
+ OpenJPAStateManager invsm = RelationStrategies.getStateManager(inverse,
+ ctx);
+ if (invsm == null || invsm.isDeleted())
+ return;
+
+ ValueMapping elem = field.getElementMapping();
+ ForeignKey fk = elem.getForeignKey();
+ ColumnIO io = elem.getColumnIO();
+ Column order = field.getOrderColumn();
+
+ int action;
+ boolean writeable;
+ boolean orderWriteable;
+ if (invsm.isNew() && !invsm.isFlushed()) {
+ // no need to null inverse columns of new instance
+ if (sm == null)
+ return;
+ writeable = io.isAnyInsertable(fk, false);
+ orderWriteable = _orderInsert;
+ action = Row.ACTION_INSERT;
+ } else {
+ writeable = io.isAnyUpdatable(fk, sm == null);
+ orderWriteable = field.getOrderColumnIO().isUpdatable
+ (order, sm == null);
+ action = Row.ACTION_UPDATE;
+ }
+ if (!writeable && !orderWriteable)
+ return;
+
+ assertInversable();
+
+ // if this is an update, this might be the only mod to the row, so
+ // make sure the where condition is set
+ Row row = rm.getRow(fk.getTable(), action, invsm, true);
+ if (action == Row.ACTION_UPDATE)
+ row.wherePrimaryKey(invsm);
+
+ // update the inverse pointer with our oid value
+ if (writeable)
+ row.setForeignKey(fk, io, sm);
+ if (orderWriteable)
+ row.setInt(order, idx);
+ }
+
+ public Object toDataStoreValue(Object val, JDBCStore store) {
+ ClassMapping cm = field.getElementMapping().getTypeMapping();
+ return cm.toDataStoreValue(val, cm.getPrimaryKeyColumns(), store);
+ }
+
+ public Joins join(Joins joins, boolean forceOuter) {
+ ValueMapping elem = field.getElementMapping();
+ ClassMapping[] clss = elem.getIndependentTypeMappings();
+ if (clss.length != 1)
+ throw RelationStrategies.unjoinable(elem);
+ if (forceOuter)
+ return joins.outerJoinRelation(field.getName(),
+ elem.getForeignKey(clss[0]), true, true);
+ return joins.joinRelation(field.getName(),
+ elem.getForeignKey(clss[0]), true, true);
+ }
+
+ private void assertInversable() {
+ ValueMapping elem = field.getElementMapping();
+ if (elem.getIndependentTypeMappings().length != 1)
+ throw RelationStrategies.uninversable(elem);
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyTableFieldStrategy.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyTableFieldStrategy.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyTableFieldStrategy.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyTableFieldStrategy.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.SQLException;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.openjpa.jdbc.kernel.JDBCFetchState;
+import org.apache.openjpa.jdbc.kernel.JDBCStore;
+import org.apache.openjpa.jdbc.meta.ClassMapping;
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.meta.FieldMappingInfo;
+import org.apache.openjpa.jdbc.meta.ValueMapping;
+import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
+import org.apache.openjpa.jdbc.schema.Column;
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.jdbc.sql.Joins;
+import org.apache.openjpa.jdbc.sql.Result;
+import org.apache.openjpa.jdbc.sql.Row;
+import org.apache.openjpa.jdbc.sql.RowManager;
+import org.apache.openjpa.jdbc.sql.Select;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.kernel.StoreContext;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.util.ChangeTracker;
+import org.apache.openjpa.util.MetaDataException;
+import org.apache.openjpa.util.Proxies;
+import org.apache.openjpa.util.Proxy;
+
+/**
+ * Maps a set of related objects through an association table.
+ *
+ * @author Abe White
+ */
+public abstract class RelationToManyTableFieldStrategy
+ extends StoreCollectionFieldStrategy {
+
+ private static final Localizer _loc = Localizer.forPackage
+ (RelationToManyTableFieldStrategy.class);
+
+ private Boolean _fkOid = null;
+
+ protected ClassMapping[] getIndependentElementMappings(boolean traverse) {
+ return (traverse)
+ ? field.getElementMapping().getIndependentTypeMappings()
+ : ClassMapping.EMPTY_MAPPINGS;
+ }
+
+ protected ForeignKey getJoinForeignKey(ClassMapping elem) {
+ return field.getJoinForeignKey();
+ }
+
+ protected void selectElement(Select sel, ClassMapping elem,
+ JDBCStore store, JDBCFetchState fetchState, int eagerMode,
+ Joins joins) {
+ sel.select(elem, field.getElementMapping().getSelectSubclasses(),
+ store, fetchState, eagerMode, joins);
+ }
+
+ protected Object loadElement(OpenJPAStateManager sm, JDBCStore store,
+ JDBCFetchState fetchState, Result res, Joins joins)
+ throws SQLException {
+ ClassMapping elem = res.getBaseMapping();
+ if (elem == null)
+ elem = field.getElementMapping().getIndependentTypeMappings()[0];
+ return res.load(elem, store, fetchState, joins);
+ }
+
+ protected Joins join(Joins joins, ClassMapping elem) {
+ return join(joins, false);
+ }
+
+ protected Joins joinElementRelation(Joins joins, ClassMapping elem) {
+ return joins.joinRelation(field.getName(), field.getElementMapping().
+ getForeignKey(elem), false, false);
+ }
+
+ public void map(boolean adapt) {
+ field.getValueInfo().assertNoSchemaComponents(field, !adapt);
+ field.getKeyMapping().getValueInfo().assertNoSchemaComponents
+ (field.getKey(), !adapt);
+
+ ValueMapping elem = field.getElementMapping();
+ if (elem.getTypeCode() != JavaTypes.PC || elem.isEmbeddedPC())
+ throw new MetaDataException(_loc.get("not-elem-relation", field));
+
+ // check for named inverse
+ FieldMapping mapped = field.getMappedByMapping();
+ ValueMappingInfo vinfo = elem.getValueInfo();
+ boolean criteria = vinfo.getUseClassCriteria();
+ if (mapped != null) {
+ if (mapped.getElement().getTypeCode() != JavaTypes.PC)
+ throw new MetaDataException(_loc.get("not-inv-relation-coll",
+ field, mapped));
+ field.getMappingInfo().assertNoSchemaComponents(field, !adapt);
+ vinfo.assertNoSchemaComponents(elem, !adapt);
+ mapped.resolve(mapped.MODE_META | mapped.MODE_MAPPING);
+
+ if (!mapped.getDefiningMapping().isMapped())
+ throw new MetaDataException(_loc.get("mapped-by-unmapped",
+ field, mapped));
+
+ field.setJoinForeignKey(mapped.getElementMapping().
+ getForeignKey(field.getDefiningMapping()));
+ elem.setForeignKey(mapped.getJoinForeignKey());
+ elem.setUseClassCriteria(criteria);
+ field.setOrderColumn(mapped.getOrderColumn());
+ return;
+ }
+
+ field.mapJoin(adapt, true);
+ if (elem.getTypeMapping().isMapped()) {
+ ForeignKey fk = vinfo.getTypeJoin(elem, "element", false, adapt);
+ elem.setForeignKey(fk);
+ elem.setColumnIO(vinfo.getColumnIO());
+ } else
+ RelationStrategies.mapRelationToUnmappedPC(elem, "element", adapt);
+ elem.setUseClassCriteria(criteria);
+ elem.mapConstraints("element", 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() {
+ ValueMapping elem = field.getElementMapping();
+ ForeignKey fk = elem.getForeignKey();
+ if (fk == null)
+ _fkOid = Boolean.TRUE;
+ else
+ _fkOid = elem.getTypeMapping().isForeignKeyObjectId(fk);
+ }
+
+ public void insert(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+ throws SQLException {
+ if (field.getMappedBy() == null)
+ insert(sm, rm, sm.fetchObject(field.getIndex()));
+ }
+
+ private void insert(OpenJPAStateManager sm, RowManager rm, Object vals)
+ throws SQLException {
+ Collection coll = toCollection(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();
+ StoreContext ctx = sm.getContext();
+ Column order = field.getOrderColumn();
+ boolean setOrder = field.getOrderColumnIO().isInsertable(order, false);
+ int idx = 0;
+ OpenJPAStateManager esm;
+ for (Iterator itr = coll.iterator(); itr.hasNext(); idx++) {
+ esm = RelationStrategies.getStateManager(itr.next(), ctx);
+ elem.setForeignKey(row, esm);
+ if (setOrder)
+ row.setInt(order, idx);
+ rm.flushSecondaryRow(row);
+ }
+ }
+
+ public void update(OpenJPAStateManager sm, JDBCStore store, RowManager rm)
+ throws SQLException {
+ if (field.getMappedBy() != null)
+ return;
+
+ 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, rm, obj);
+ return;
+ }
+
+ StoreContext ctx = store.getContext();
+ ValueMapping elem = field.getElementMapping();
+ OpenJPAStateManager esm;
+
+ // delete the removes
+ 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();) {
+ esm = RelationStrategies.getStateManager(itr.next(), ctx);
+ elem.whereForeignKey(delRow, esm);
+ 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++) {
+ esm = RelationStrategies.getStateManager(itr.next(), ctx);
+ elem.setForeignKey(addRow, esm);
+ 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 Object toDataStoreValue(Object val, JDBCStore store) {
+ return RelationStrategies.toDataStoreValue(field.getElementMapping(),
+ val, store);
+ }
+
+ public Joins join(Joins joins, boolean forceOuter) {
+ return field.join(joins, forceOuter, true);
+ }
+
+ public Joins joinRelation(Joins joins, boolean forceOuter,
+ boolean traverse) {
+ ValueMapping elem = field.getElementMapping();
+ ClassMapping[] clss = elem.getIndependentTypeMappings();
+ if (clss.length != 1) {
+ if (traverse)
+ throw RelationStrategies.unjoinable(elem);
+ return joins;
+ }
+ if (forceOuter)
+ return joins.outerJoinRelation(field.getName(),
+ elem.getForeignKey(clss[0]), false, false);
+ return joins.joinRelation(field.getName(),
+ elem.getForeignKey(clss[0]), false, false);
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyTableFieldStrategy.java
------------------------------------------------------------------------------
svn:executable = *