001: /*
002: * Copyright 2004 (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: InverseMapStore.java,v 1.6 2004/01/18 03:01:06 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.FieldMetaData;
017: import com.triactive.jdo.model.MapMetaData;
018: import java.lang.reflect.Field;
019: import java.util.Collection;
020: import java.util.Iterator;
021: import javax.jdo.JDOUserException;
022:
023: class InverseMapStore extends AbstractMapStore {
024: private final ClassBaseTable valueTable;
025: private final int ownerFieldNumber;
026: private final int keyFieldNumber;
027:
028: public InverseMapStore(FieldMetaData fmd, StoreManager storeMgr) {
029: this .storeMgr = storeMgr;
030:
031: dba = storeMgr.getDatabaseAdapter();
032:
033: Field field = fmd.getField();
034: ClassMetaData cmd = fmd.getClassMetaData();
035: MapMetaData mmd = fmd.getMapMetaData();
036: if (mmd == null)
037: throw new JDOUserException("No map metadata found in "
038: + fmd);
039:
040: keyType = mmd.getKeyType();
041: valueType = mmd.getValueType();
042:
043: FieldMetaData vofmd = mmd.getOwnerField();
044: FieldMetaData vkfmd = mmd.getKeyField();
045:
046: FieldMetaData omd = vofmd.getOwnedByMap();
047: if (omd == null || !omd.equals(fmd))
048: throw new InvalidMetaDataRelationshipException(fmd,
049: "owner-field", vofmd, "map-field");
050: if (vkfmd == null)
051: throw new ClassDefinitionException(
052: "Missing map \"key-field\" in " + fmd);
053:
054: String ownerFieldName = vofmd.getName();
055: String keyFieldName = vkfmd.getName();
056:
057: ClassMetaData vmd = vofmd.getClassMetaData();
058:
059: ownerFieldNumber = vmd.getAbsoluteFieldNumber(ownerFieldName);
060: keyFieldNumber = vmd.getAbsoluteFieldNumber(keyFieldName);
061:
062: valueTable = storeMgr.getClassBaseTable(valueType);
063:
064: if (!valueTable.isFieldPersistent(ownerFieldNumber))
065: throw new JDOUserException(
066: "Non-persistent field type for map owner field: "
067: + field.getDeclaringClass().getName() + "."
068: + field.getName());
069: if (!valueTable.isFieldPersistent(keyFieldNumber))
070: throw new JDOUserException(
071: "Non-persistent field type for map key field: "
072: + field.getDeclaringClass().getName() + "."
073: + field.getName());
074:
075: Mapping m = valueTable.getFieldMapping(ownerFieldNumber);
076: if (!(m instanceof ColumnMapping))
077: throw new JDOUserException(
078: "Invalid field type for map owner field: "
079: + field.getDeclaringClass().getName() + "."
080: + field.getName());
081:
082: ownerMapping = (ColumnMapping) m;
083:
084: m = valueTable.getFieldMapping(keyFieldNumber);
085: if (!(m instanceof ColumnMapping))
086: throw new JDOUserException(
087: "Invalid field type for map key field: "
088: + field.getDeclaringClass().getName() + "."
089: + field.getName());
090:
091: keyMapping = (ColumnMapping) m;
092: valueMapping = (ColumnMapping) dba.getMapping(valueType);
093:
094: keysAreEmbedded = !(keyMapping instanceof OIDMapping);
095: valuesAreEmbedded = false;
096:
097: ownerColumn = ownerMapping.getColumn();
098: keyColumn = keyMapping.getColumn();
099: valueColumn = valueTable.getIDMapping().getColumn();
100:
101: loadStmt = "SELECT " + keyColumn.getName() + ','
102: + valueColumn.getName() + " FROM "
103: + valueTable.getName() + " WHERE "
104: + ownerColumn.getName() + " = ?";
105: getStmt = "SELECT " + valueColumn.getName() + " FROM "
106: + valueTable.getName() + " WHERE "
107: + ownerColumn.getName() + " = ? AND "
108: + keyColumn.getName() + " = ?";
109: sizeStmt = "SELECT COUNT(*)" + " FROM " + valueTable.getName()
110: + " WHERE " + ownerColumn.getName() + " = ?";
111: containsValueStmt = "SELECT " + ownerColumn.getName()
112: + " FROM " + valueTable.getName() + " WHERE "
113: + ownerColumn.getName() + " = ? AND "
114: + valueColumn.getName() + " = ?";
115: containsEntryStmt = "SELECT " + ownerColumn.getName()
116: + " FROM " + valueTable.getName() + " WHERE "
117: + ownerColumn.getName() + " = ? AND "
118: + keyColumn.getName() + " = ? AND "
119: + valueColumn.getName() + " = ?";
120:
121: if (keyColumn.isNullable()) {
122: String excludeNullKeys = " AND " + keyColumn.getName()
123: + " IS NOT NULL";
124:
125: loadStmt += excludeNullKeys;
126: sizeStmt += excludeNullKeys;
127: containsValueStmt += excludeNullKeys;
128: }
129: }
130:
131: public boolean allowsNullValues() {
132: return false;
133: }
134:
135: public Object put(StateManager sm, Object newKey, Object newValue) {
136: validateKeyForWriting(sm, newKey);
137:
138: /*
139: * See if there's an existing value having the key. If so, remove that
140: * object from the map by nulling out its key field.
141: */
142: Object oldValue = get(sm, newKey);
143:
144: if (oldValue != newValue) {
145: if (oldValue != null)
146: removeValue(sm, newKey, oldValue);
147:
148: PersistenceManager pm = sm.getPersistenceManager();
149: StateManager vsm = pm.findStateManager(newValue);
150:
151: /*
152: * If the value object is currently unmanaged (Transient) it first
153: * has to transition to a TransientClean state so we can modify its
154: * fields, then to TransientDirty as we modify the owner/key fields.
155: * If the subsequent makePersistent() attempt fails the object will
156: * be restored on rollback but will be left in a managed state
157: * (TransientClean).
158: */
159: if (vsm == null) {
160: pm.makeTransactional(newValue);
161: vsm = pm.findStateManager(newValue);
162: }
163:
164: /*
165: * "Put" the new value in the map by updating its owner/key fields
166: * to the appropriate values. This should result in no actual
167: * database activity if the object was already persistent and the
168: * fields were already set to the right values.
169: */
170: Object oldOwner = vsm.getField(ownerFieldNumber);
171: Object oldKey = vsm.getField(keyFieldNumber);
172: Object newOwner = sm.getObject();
173: vsm.setField(ownerFieldNumber, oldOwner, newOwner);
174: vsm.setField(keyFieldNumber, oldKey, newKey);
175: pm.makePersistent(newValue);
176: }
177:
178: return oldValue;
179: }
180:
181: public Object remove(StateManager sm, Object key) {
182: Object oldValue = get(sm, key);
183:
184: if (oldValue != null)
185: removeValue(sm, key, oldValue);
186:
187: return oldValue;
188: }
189:
190: private void removeValue(StateManager sm, Object key,
191: Object oldValue) {
192: PersistenceManager pm = sm.getPersistenceManager();
193:
194: if (keyColumn.isNullable()) {
195: StateManager vsm = pm.findStateManager(oldValue);
196: vsm.setField(keyFieldNumber, key, null);
197: } else
198: pm.deletePersistent(oldValue);
199: }
200:
201: public boolean removeEntry(StateManager sm, Object key, Object value) {
202: if (!validateValueForReading(sm, value))
203: return false;
204:
205: PersistenceManager pm = sm.getPersistenceManager();
206: StateManager vsm = pm.findStateManager(value);
207: Object actualKey = vsm.getField(keyFieldNumber);
208:
209: if (!key.equals(actualKey))
210: return false;
211:
212: if (keyColumn.isNullable())
213: vsm.setField(keyFieldNumber, key, null);
214: else
215: pm.deletePersistent(value);
216:
217: return true;
218: }
219:
220: public void clear(StateManager sm) {
221: PersistenceManager pm = sm.getPersistenceManager();
222: Collection values = load(sm).values();
223:
224: if (keyColumn.isNullable()) {
225: Iterator i = values.iterator();
226:
227: while (i.hasNext()) {
228: StateManager vsm = pm.findStateManager(i.next());
229: vsm.setField(keyFieldNumber, vsm
230: .getField(keyFieldNumber), null);
231: }
232: } else
233: pm.deletePersistentAll(values);
234: }
235: }
|