001: /*
002: * Copyright 2002 (C) TJDO.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the TJDO License version 1.0.
006: * See the terms of the TJDO License in the documentation provided with this software.
007: *
008: * $Id: InverseSetStore.java,v 1.9 2004/02/01 18:22:42 jackknifebarber Exp $
009: */
010:
011: package com.triactive.jdo.store;
012:
013: import com.triactive.jdo.PersistenceManager;
014: import com.triactive.jdo.StateManager;
015: import com.triactive.jdo.model.ClassMetaData;
016: import com.triactive.jdo.model.CollectionMetaData;
017: import com.triactive.jdo.model.FieldMetaData;
018: import com.triactive.jdo.util.IntArrayList;
019: import java.lang.reflect.Field;
020: import java.util.Collection;
021: import java.util.Iterator;
022: import javax.jdo.JDOUserException;
023:
024: class InverseSetStore extends AbstractSetStore {
025: private final int ownerFieldNumber;
026:
027: public InverseSetStore(FieldMetaData fmd, StoreManager storeMgr) {
028: this .storeMgr = storeMgr;
029:
030: dba = storeMgr.getDatabaseAdapter();
031:
032: Field field = fmd.getField();
033: ClassMetaData cmd = fmd.getClassMetaData();
034: CollectionMetaData colmd = fmd.getCollectionMetaData();
035: if (colmd == null)
036: throw new JDOUserException(
037: "No collection metadata found in " + fmd);
038:
039: elementType = colmd.getElementType();
040: elementsAreEmbedded = false;
041:
042: FieldMetaData eofmd = colmd.getOwnerField();
043:
044: FieldMetaData omd = eofmd.getOwnedByCollection();
045: if (omd == null || !omd.equals(fmd))
046: throw new InvalidMetaDataRelationshipException(fmd,
047: "owner-field", eofmd, "collection-field");
048:
049: String ownerFieldName = eofmd.getName();
050:
051: ClassMetaData emd = eofmd.getClassMetaData();
052: ownerFieldNumber = emd.getAbsoluteFieldNumber(ownerFieldName);
053:
054: ClassBaseTable elementTable = storeMgr
055: .getClassBaseTable(elementType);
056:
057: if (!elementTable.isFieldPersistent(ownerFieldNumber))
058: throw new JDOUserException(
059: "Non-persistent field type for collection owner field: "
060: + field.getDeclaringClass().getName() + "."
061: + field.getName());
062:
063: Mapping m = elementTable.getFieldMapping(ownerFieldNumber);
064: if (!(m instanceof ColumnMapping)
065: || m instanceof PostWriteProcessing)
066: throw new JDOUserException(
067: "Invalid field type for collection owner field: "
068: + field.getDeclaringClass().getName() + "."
069: + field.getName());
070:
071: ownerMapping = (ColumnMapping) m;
072: elementMapping = (ColumnMapping) dba.getMapping(elementType);
073:
074: setTable = elementTable;
075: setName = "inverseSet";
076:
077: ownerColumn = ownerMapping.getColumn();
078: elementColumn = elementTable.getIDMapping().getColumn();
079:
080: loadStmt = "SELECT " + elementColumn.getName() + " FROM "
081: + elementTable.getName() + " WHERE "
082: + ownerColumn.getName() + " = ?";
083: sizeStmt = "SELECT COUNT(" + elementColumn.getName() + ")"
084: + " FROM " + elementTable.getName() + " WHERE "
085: + ownerColumn.getName() + " = ?";
086: containsStmt = "SELECT " + elementColumn.getName() + " FROM "
087: + elementTable.getName() + " WHERE "
088: + ownerColumn.getName() + " = ? AND "
089: + elementColumn.getName() + " = ?";
090: addStmt = null;
091: removeStmt = null;
092: clearStmt = null;
093:
094: /*
095: * Setup the prefetch field info.
096: */
097: int fieldCount = emd.getInheritedFieldCount()
098: + emd.getFieldCount();
099: IntArrayList colfn = new IntArrayList(fieldCount);
100: ColumnMapping[] colfm = new ColumnMapping[fieldCount];
101:
102: boolean[] defaultFetchGroupFields = emd
103: .getDefaultFetchGroupFieldFlags();
104:
105: for (int i = 0; i < fieldCount; ++i) {
106: if (defaultFetchGroupFields[i]) {
107: m = elementTable.getFieldMapping(i);
108:
109: if (m instanceof ColumnMapping) {
110: colfn.add(i);
111: colfm[i] = (ColumnMapping) m;
112: }
113: }
114: }
115:
116: if (colfn.isEmpty()) {
117: prefetchFieldNumbers = null;
118: prefetchFieldMappings = null;
119: } else {
120: prefetchFieldNumbers = colfn.toArray();
121: prefetchFieldMappings = colfm;
122: }
123: }
124:
125: public QueryStatement newQueryStatement(StateManager sm,
126: Class candidateClass) {
127: if (!elementType.isAssignableFrom(candidateClass))
128: throw new IncompatibleQueryElementTypeException(
129: elementType, candidateClass);
130:
131: ClassBaseTable candidateTable = storeMgr
132: .getClassBaseTable(candidateClass);
133: QueryStatement stmt = dba.newQueryStatement(candidateTable);
134:
135: SQLExpression ownerExpr = new ObjectExpression(stmt, stmt
136: .getColumn(ownerColumn));
137: SQLExpression ownerVal = ownerMapping.newSQLLiteral(stmt, sm
138: .getObject());
139:
140: stmt.andCondition(ownerExpr.eq(ownerVal));
141:
142: stmt.select(elementColumn);
143:
144: return stmt;
145: }
146:
147: public QueryStatement.QueryColumn joinElementsTo(
148: QueryStatement stmt,
149: QueryStatement.QueryColumn ownerIDColumn,
150: SQLIdentifier setRangeVar, Class filteredElementType,
151: SQLIdentifier elementRangeVar) {
152: if (!elementType.isAssignableFrom(filteredElementType))
153: throw new IncompatibleQueryElementTypeException(
154: elementType, filteredElementType);
155:
156: /* Join the element table on the owner ID column. */
157: ClassBaseTable filteredElementTable = storeMgr
158: .getClassBaseTable(filteredElementType);
159:
160: stmt.newTableExpression(filteredElementTable, elementRangeVar);
161: stmt.innerJoin(ownerIDColumn, stmt.getColumn(elementRangeVar,
162: ownerColumn));
163:
164: Column elementTableIDColumn = filteredElementTable
165: .getIDMapping().getColumn();
166:
167: return stmt.getColumn(elementRangeVar, elementTableIDColumn);
168: }
169:
170: public boolean add(StateManager sm, Object element) {
171: PersistenceManager pm = sm.getPersistenceManager();
172: StateManager esm = pm.findStateManager(element);
173:
174: /*
175: * If the object is currently unmanaged (Transient) it first has to
176: * transition to a TransientClean state so we can modify its fields,
177: * then to TransientDirty as we modify the owner field. If the
178: * subsequent makePersistent() attempt fails the object will be restored
179: * on rollback but will be left in a managed state (TransientClean).
180: */
181: if (esm == null) {
182: pm.makeTransactional(element);
183: esm = pm.findStateManager(element);
184: }
185:
186: boolean wasPersistent = esm
187: .isPersistent((javax.jdo.spi.PersistenceCapable) element);
188:
189: /*
190: * "Add" the new value to the set by updating its owner field to the
191: * appropriate value. This should result in no actual database
192: * activity if the element was already persistent and the field was
193: * already set to the right value.
194: */
195: Object oldOwner = esm.getField(ownerFieldNumber);
196: Object newOwner = sm.getObject();
197: esm.setField(ownerFieldNumber, oldOwner, newOwner);
198: pm.makePersistent(element);
199:
200: return !wasPersistent || oldOwner != newOwner;
201: }
202:
203: public boolean remove(StateManager sm, Object element) {
204: if (!validateElementForReading(sm, element))
205: return false;
206:
207: PersistenceManager pm = sm.getPersistenceManager();
208: StateManager esm = pm.findStateManager(element);
209:
210: Object oldOwner = esm.getField(ownerFieldNumber);
211:
212: if (oldOwner != sm.getObject())
213: return false;
214: else {
215: if (ownerColumn.isNullable())
216: esm.setField(ownerFieldNumber, oldOwner, null);
217: else
218: pm.deletePersistent(element);
219:
220: return true;
221: }
222: }
223:
224: public void clear(StateManager sm) {
225: PersistenceManager pm = sm.getPersistenceManager();
226: Collection elements = load(sm);
227:
228: if (ownerColumn.isNullable()) {
229: Iterator i = elements.iterator();
230:
231: while (i.hasNext()) {
232: StateManager esm = pm.findStateManager(i.next());
233: esm.setField(ownerFieldNumber, esm
234: .getField(ownerFieldNumber), null);
235: }
236: } else
237: pm.deletePersistentAll(elements);
238: }
239: }
|