001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.jdbc.meta.strats;
020:
021: import java.sql.SQLException;
022: import java.util.Collection;
023: import java.util.Iterator;
024:
025: import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
026: import org.apache.openjpa.jdbc.kernel.JDBCStore;
027: import org.apache.openjpa.jdbc.meta.ClassMapping;
028: import org.apache.openjpa.jdbc.meta.FieldMapping;
029: import org.apache.openjpa.jdbc.meta.FieldMappingInfo;
030: import org.apache.openjpa.jdbc.meta.ValueMapping;
031: import org.apache.openjpa.jdbc.meta.ValueMappingInfo;
032: import org.apache.openjpa.jdbc.schema.Column;
033: import org.apache.openjpa.jdbc.schema.ForeignKey;
034: import org.apache.openjpa.jdbc.sql.Joins;
035: import org.apache.openjpa.jdbc.sql.Result;
036: import org.apache.openjpa.jdbc.sql.Row;
037: import org.apache.openjpa.jdbc.sql.RowManager;
038: import org.apache.openjpa.jdbc.sql.Select;
039: import org.apache.openjpa.kernel.OpenJPAStateManager;
040: import org.apache.openjpa.kernel.StoreContext;
041: import org.apache.openjpa.lib.util.Localizer;
042: import org.apache.openjpa.meta.JavaTypes;
043: import org.apache.openjpa.util.ChangeTracker;
044: import org.apache.openjpa.util.MetaDataException;
045: import org.apache.openjpa.util.Proxies;
046: import org.apache.openjpa.util.Proxy;
047:
048: /**
049: * Maps a set of related objects through an association table.
050: *
051: * @author Abe White
052: */
053: public abstract class RelationToManyTableFieldStrategy extends
054: StoreCollectionFieldStrategy {
055:
056: private static final Localizer _loc = Localizer
057: .forPackage(RelationToManyTableFieldStrategy.class);
058:
059: protected ClassMapping[] getIndependentElementMappings(
060: boolean traverse) {
061: return (traverse) ? field.getElementMapping()
062: .getIndependentTypeMappings()
063: : ClassMapping.EMPTY_MAPPINGS;
064: }
065:
066: protected ForeignKey getJoinForeignKey(ClassMapping elem) {
067: return field.getJoinForeignKey();
068: }
069:
070: protected void selectElement(Select sel, ClassMapping elem,
071: JDBCStore store, JDBCFetchConfiguration fetch,
072: int eagerMode, Joins joins) {
073: sel.select(elem, field.getElementMapping()
074: .getSelectSubclasses(), store, fetch, eagerMode, joins);
075: }
076:
077: protected Object loadElement(OpenJPAStateManager sm,
078: JDBCStore store, JDBCFetchConfiguration fetch, Result res,
079: Joins joins) throws SQLException {
080: ClassMapping elem = res.getBaseMapping();
081: if (elem == null)
082: elem = field.getElementMapping()
083: .getIndependentTypeMappings()[0];
084: return res.load(elem, store, fetch, joins);
085: }
086:
087: protected Joins join(Joins joins, ClassMapping elem) {
088: return join(joins, false);
089: }
090:
091: protected Joins joinElementRelation(Joins joins, ClassMapping elem) {
092: ValueMapping vm = field.getElementMapping();
093: return joins.joinRelation(field.getName(), vm
094: .getForeignKey(elem), elem, vm.getSelectSubclasses(),
095: false, false);
096: }
097:
098: public void map(boolean adapt) {
099: field.getValueInfo().assertNoSchemaComponents(field, !adapt);
100: field.getKeyMapping().getValueInfo().assertNoSchemaComponents(
101: field.getKey(), !adapt);
102:
103: ValueMapping elem = field.getElementMapping();
104: if (elem.getTypeCode() != JavaTypes.PC || elem.isEmbeddedPC())
105: throw new MetaDataException(_loc.get("not-elem-relation",
106: field));
107:
108: // check for named inverse
109: FieldMapping mapped = field.getMappedByMapping();
110: ValueMappingInfo vinfo = elem.getValueInfo();
111: boolean criteria = vinfo.getUseClassCriteria();
112: if (mapped != null) {
113: if (mapped.getElement().getTypeCode() != JavaTypes.PC)
114: throw new MetaDataException(_loc.get(
115: "not-inv-relation-coll", field, mapped));
116: field.getMappingInfo().assertNoSchemaComponents(field,
117: !adapt);
118: vinfo.assertNoSchemaComponents(elem, !adapt);
119: mapped.resolve(mapped.MODE_META | mapped.MODE_MAPPING);
120:
121: if (!mapped.isMapped() || mapped.isSerialized())
122: throw new MetaDataException(_loc.get(
123: "mapped-by-unmapped", field, mapped));
124:
125: field.setJoinForeignKey(mapped.getElementMapping()
126: .getForeignKey(field.getDefiningMapping()));
127: elem.setForeignKey(mapped.getJoinForeignKey());
128: elem.setUseClassCriteria(criteria);
129: field.setOrderColumn(mapped.getOrderColumn());
130: return;
131: }
132:
133: field.mapJoin(adapt, true);
134: if (elem.getTypeMapping().isMapped()) {
135: ForeignKey fk = vinfo.getTypeJoin(elem, "element", false,
136: adapt);
137: elem.setForeignKey(fk);
138: elem.setColumnIO(vinfo.getColumnIO());
139: } else
140: RelationStrategies.mapRelationToUnmappedPC(elem, "element",
141: adapt);
142: elem.setUseClassCriteria(criteria);
143: elem.mapConstraints("element", adapt);
144:
145: FieldMappingInfo finfo = field.getMappingInfo();
146: Column orderCol = finfo.getOrderColumn(field, field.getTable(),
147: adapt);
148: field.setOrderColumn(orderCol);
149: field.setOrderColumnIO(finfo.getColumnIO());
150: field.mapPrimaryKey(adapt);
151: }
152:
153: public void insert(OpenJPAStateManager sm, JDBCStore store,
154: RowManager rm) throws SQLException {
155: if (field.getMappedBy() == null)
156: insert(sm, rm, sm.fetchObject(field.getIndex()));
157: }
158:
159: private void insert(OpenJPAStateManager sm, RowManager rm,
160: Object vals) throws SQLException {
161: Collection coll = toCollection(vals);
162: if (coll == null || coll.isEmpty())
163: return;
164:
165: Row row = rm.getSecondaryRow(field.getTable(),
166: Row.ACTION_INSERT);
167: row.setForeignKey(field.getJoinForeignKey(), field
168: .getJoinColumnIO(), sm);
169:
170: ValueMapping elem = field.getElementMapping();
171: StoreContext ctx = sm.getContext();
172: Column order = field.getOrderColumn();
173: boolean setOrder = field.getOrderColumnIO().isInsertable(order,
174: false);
175: int idx = 0;
176: OpenJPAStateManager esm;
177: for (Iterator itr = coll.iterator(); itr.hasNext(); idx++) {
178: esm = RelationStrategies.getStateManager(itr.next(), ctx);
179: elem.setForeignKey(row, esm);
180: if (setOrder)
181: row.setInt(order, idx);
182: rm.flushSecondaryRow(row);
183: }
184: }
185:
186: public void update(OpenJPAStateManager sm, JDBCStore store,
187: RowManager rm) throws SQLException {
188: if (field.getMappedBy() != null)
189: return;
190:
191: Object obj = sm.fetchObject(field.getIndex());
192: ChangeTracker ct = null;
193: if (obj instanceof Proxy) {
194: Proxy proxy = (Proxy) obj;
195: if (Proxies.isOwner(proxy, sm, field.getIndex()))
196: ct = proxy.getChangeTracker();
197: }
198:
199: // if no fine-grained change tracking then just delete and reinsert
200: if (ct == null || !ct.isTracking()) {
201: delete(sm, store, rm);
202: insert(sm, rm, obj);
203: return;
204: }
205:
206: StoreContext ctx = store.getContext();
207: ValueMapping elem = field.getElementMapping();
208: OpenJPAStateManager esm;
209:
210: // delete the removes
211: Collection rem = ct.getRemoved();
212: if (!rem.isEmpty()) {
213: Row delRow = rm.getSecondaryRow(field.getTable(),
214: Row.ACTION_DELETE);
215: delRow.whereForeignKey(field.getJoinForeignKey(), sm);
216:
217: for (Iterator itr = rem.iterator(); itr.hasNext();) {
218: esm = RelationStrategies.getStateManager(itr.next(),
219: ctx);
220: elem.whereForeignKey(delRow, esm);
221: rm.flushSecondaryRow(delRow);
222: }
223: }
224:
225: // insert the adds
226: Collection add = ct.getAdded();
227: if (!add.isEmpty()) {
228: Row addRow = rm.getSecondaryRow(field.getTable(),
229: Row.ACTION_INSERT);
230: addRow.setForeignKey(field.getJoinForeignKey(), field
231: .getJoinColumnIO(), sm);
232:
233: int seq = ct.getNextSequence();
234: Column order = field.getOrderColumn();
235: boolean setOrder = field.getOrderColumnIO().isInsertable(
236: order, false);
237: for (Iterator itr = add.iterator(); itr.hasNext(); seq++) {
238: esm = RelationStrategies.getStateManager(itr.next(),
239: ctx);
240: elem.setForeignKey(addRow, esm);
241: if (setOrder)
242: addRow.setInt(order, seq);
243: rm.flushSecondaryRow(addRow);
244: }
245: if (order != null)
246: ct.setNextSequence(seq);
247: }
248: }
249:
250: public void delete(OpenJPAStateManager sm, JDBCStore store,
251: RowManager rm) throws SQLException {
252: Row row = rm.getAllRows(field.getTable(), Row.ACTION_DELETE);
253: row.whereForeignKey(field.getJoinForeignKey(), sm);
254: rm.flushAllRows(row);
255: }
256:
257: public Object toDataStoreValue(Object val, JDBCStore store) {
258: return RelationStrategies.toDataStoreValue(field
259: .getElementMapping(), val, store);
260: }
261:
262: public Joins join(Joins joins, boolean forceOuter) {
263: return field.join(joins, forceOuter, true);
264: }
265:
266: public Joins joinRelation(Joins joins, boolean forceOuter,
267: boolean traverse) {
268: ValueMapping elem = field.getElementMapping();
269: ClassMapping[] clss = elem.getIndependentTypeMappings();
270: if (clss.length != 1) {
271: if (traverse)
272: throw RelationStrategies.unjoinable(elem);
273: return joins;
274: }
275: if (forceOuter)
276: return joins.outerJoinRelation(field.getName(), elem
277: .getForeignKey(clss[0]), clss[0], elem
278: .getSelectSubclasses(), false, false);
279: return joins.joinRelation(field.getName(), elem
280: .getForeignKey(clss[0]), clss[0], elem
281: .getSelectSubclasses(), false, false);
282: }
283: }
|