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: ClassBaseTable.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.sql.Connection;
019: import java.sql.SQLException;
020: import java.util.ArrayList;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.List;
024: import javax.jdo.Extent;
025: import javax.jdo.JDOFatalInternalException;
026: import javax.jdo.JDOUnsupportedOptionException;
027:
028: /**
029: * A database base table that serves as backing for a persistence-capable Java
030: * class.
031: *
032: * @author <a href="mailto:mmartin5@austin.rr.com">Mike Martin</a>
033: * @version $Revision: 1.6 $
034: */
035:
036: public class ClassBaseTable extends JDOBaseTable implements ClassTable {
037: private final ClassMetaData cmd;
038: private final Class clazz;
039:
040: private ClassBaseTable super table;
041: private ColumnMapping idMapping;
042: private Mapping[] fieldMappings;
043:
044: ClassBaseTable(TableMetadata tmd, ClassMetaData cmd,
045: StoreManager storeMgr) {
046: super (tmd, storeMgr);
047:
048: this .cmd = cmd;
049:
050: clazz = cmd.getPCClass();
051:
052: switch (cmd.getIdentityType()) {
053: case ClassMetaData.NO_IDENTITY:
054: throw new JDOUnsupportedOptionException(
055: "No identity not supported: " + clazz.getName());
056:
057: case ClassMetaData.APPLICATION_IDENTITY:
058: throw new JDOUnsupportedOptionException(
059: "Application identity not supported: "
060: + clazz.getName());
061:
062: case ClassMetaData.DATASTORE_IDENTITY:
063: break;
064:
065: default:
066: throw new JDOFatalInternalException(
067: "Invalid identity type on class " + clazz.getName());
068: }
069: }
070:
071: public void initialize() {
072: assertIsUninitialized();
073:
074: Class super class = cmd.getPCSuperclass();
075: super table = super class == null ? null : storeMgr
076: .getClassBaseTable(super class);
077:
078: Column idColumn = newColumn(OID.class, name, Role.NONE)
079: .setPrimaryKeyPart();
080:
081: idMapping = dba.getMapping(idColumn);
082:
083: int fieldCount = cmd.getFieldCount();
084: fieldMappings = new Mapping[fieldCount];
085:
086: for (int relativeFieldNumber = 0; relativeFieldNumber < fieldCount; ++relativeFieldNumber) {
087: FieldMetaData fmd = cmd
088: .getFieldRelative(relativeFieldNumber);
089:
090: switch (fmd.getPersistenceModifier()) {
091: case FieldMetaData.PERSISTENCE_MODIFIER_NONE:
092: default:
093: throw new JDOFatalInternalException(
094: "Invalid persistence modifier on field "
095: + fmd.getName());
096:
097: case FieldMetaData.PERSISTENCE_MODIFIER_TRANSACTIONAL:
098: break;
099:
100: case FieldMetaData.PERSISTENCE_MODIFIER_PERSISTENT:
101: fieldMappings[relativeFieldNumber] = dba.getMapping(
102: this , relativeFieldNumber);
103: break;
104: }
105: }
106:
107: state = TABLE_STATE_INITIALIZED;
108: }
109:
110: public Class getType() {
111: return clazz;
112: }
113:
114: public ClassMetaData getClassMetaData() {
115: return cmd;
116: }
117:
118: protected List getExpectedForeignKeys() {
119: List foreignKeys = super .getExpectedForeignKeys();
120:
121: if (super table != null)
122: foreignKeys.add(0, new ForeignKey(idMapping.getColumn(),
123: super table, false));
124:
125: return foreignKeys;
126: }
127:
128: protected List getSQLCreateStatements() {
129: List stmts = super .getSQLCreateStatements();
130:
131: stmts.addAll(getSQLAddUniqueConstraintsStatements());
132:
133: return stmts;
134: }
135:
136: private List getSQLAddUniqueConstraintsStatements() {
137: ArrayList stmts = new ArrayList();
138: HashMap candidateKeysByMapField = new HashMap();
139: int fieldCount = cmd.getFieldCount();
140:
141: for (int relativeFieldNumber = 0; relativeFieldNumber < fieldCount; ++relativeFieldNumber) {
142: FieldMetaData fmd = cmd
143: .getFieldRelative(relativeFieldNumber);
144: FieldMetaData mfmd = fmd.getOwnedByMap();
145:
146: if (mfmd != null) {
147: MapMetaData mmd = mfmd.getMapMetaData();
148:
149: if (mmd.isInverseMap()) {
150: FieldMetaData omd = mmd.getOwnerField();
151: FieldMetaData kmd = mmd.getKeyField();
152:
153: if (omd == null || !omd.equals(fmd))
154: throw new InvalidMetaDataRelationshipException(
155: fmd, "map-field", mfmd, "owner-field");
156: if (kmd == null)
157: throw new ClassDefinitionException(
158: "Missing map \"key-field\" in " + mfmd);
159:
160: Mapping om = getFieldMapping(omd.getName());
161: Mapping km = getFieldMapping(kmd.getName());
162:
163: if (!(om instanceof ColumnMapping))
164: throw new ClassDefinitionException(
165: "Invalid \"owner-field\" type: "
166: + om.getType());
167: if (!(km instanceof ColumnMapping))
168: throw new ClassDefinitionException(
169: "Invalid \"key-field\" type: "
170: + km.getType());
171:
172: Column ownerCol = ((ColumnMapping) om).getColumn();
173: Column keyCol = ((ColumnMapping) km).getColumn();
174:
175: if (dba.supportsNullsInCandidateKeys()
176: || (!ownerCol.isNullable() && !keyCol
177: .isNullable())) {
178: CandidateKey ck = new CandidateKey(this );
179:
180: ck.addColumn(ownerCol);
181: ck.addColumn(keyCol);
182:
183: if (candidateKeysByMapField.put(mfmd, ck) != null)
184: throw new ClassDefinitionException(
185: "Duplicate map owner field pointing to "
186: + mfmd);
187: }
188: }
189: }
190: }
191:
192: Iterator cks = candidateKeysByMapField.values().iterator();
193: int ckNum = 0;
194:
195: while (cks.hasNext()) {
196: CandidateKeyIdentifier ckName = new CandidateKeyIdentifier(
197: this , ++ckNum);
198:
199: stmts.add(dba.getAddCandidateKeyStatement(ckName,
200: (CandidateKey) cks.next()));
201: }
202:
203: return stmts;
204: }
205:
206: public Column newColumn(int relativeFieldNumber) {
207: FieldMetaData fmd = cmd.getFieldRelative(relativeFieldNumber);
208:
209: if (fmd.isPrimaryKeyPart())
210: throw new JDOUnsupportedOptionException(
211: "Primary key part fields not supported, class = "
212: + clazz.getName() + ", field = "
213: + fmd.getName());
214:
215: Class type = fmd.getType();
216: Column col = newColumn(type, fmd.getName());
217:
218: col.setOptions(fmd);
219:
220: if (fmd.getNullValueHandling() != FieldMetaData.NULL_VALUE_EXCEPTION
221: && !type.isPrimitive())
222: col.setNullable();
223:
224: return col;
225: }
226:
227: public ClassBaseTable getSupertable() {
228: assertIsInitialized();
229:
230: return super table;
231: }
232:
233: public ColumnMapping getIDMapping() {
234: assertIsInitialized();
235:
236: return idMapping;
237: }
238:
239: // Must handle superclass fields.
240: public boolean isFieldPersistent(int fieldNumber) {
241: assertIsInitialized();
242:
243: int ifc = cmd.getInheritedFieldCount();
244:
245: if (fieldNumber < ifc)
246: return super table.isFieldPersistent(fieldNumber);
247: else
248: return fieldMappings[fieldNumber - ifc] != null;
249: }
250:
251: // Must handle superclass fields.
252: public Mapping getFieldMapping(int fieldNumber) {
253: assertIsInitialized();
254:
255: int ifc = cmd.getInheritedFieldCount();
256:
257: if (fieldNumber < ifc)
258: return super table.getFieldMapping(fieldNumber);
259: else {
260: Mapping m = fieldMappings[fieldNumber - ifc];
261:
262: if (m == null)
263: throw new NoSuchPersistentFieldException(clazz,
264: fieldNumber);
265:
266: return m;
267: }
268: }
269:
270: // Must handle superclass fields.
271: public Mapping getFieldMapping(String fieldName) {
272: assertIsInitialized();
273:
274: Mapping m = null;
275: ClassBaseTable cbt = this ;
276:
277: do {
278: int rfn = cbt.cmd.getRelativeFieldNumber(fieldName);
279:
280: if (rfn >= 0) {
281: m = cbt.fieldMappings[rfn];
282: break;
283: }
284:
285: cbt = cbt.super table;
286: } while (cbt != null);
287:
288: if (m == null)
289: throw new NoSuchPersistentFieldException(clazz, fieldName);
290:
291: return m;
292: }
293:
294: private void assertPCClass(StateManager sm) {
295: Class c = sm.getObject().getClass();
296:
297: if (!clazz.isAssignableFrom(c))
298: throw new JDOFatalInternalException("Table class = "
299: + clazz + ", object class = " + c);
300: }
301:
302: public Extent newExtent(PersistenceManager pm, boolean subclasses) {
303: assertIsValidated();
304:
305: return new ClassBaseTableExtent(pm, this , subclasses);
306: }
307:
308: public void insert(StateManager sm) {
309: assertIsValidated();
310: assertPCClass(sm);
311:
312: if (super table != null)
313: super table.insert(sm);
314:
315: InsertRequest ir = storeMgr.getInsertRequest(this );
316:
317: ir.execute(sm);
318: }
319:
320: public void lookup(StateManager sm) {
321: assertIsValidated();
322: assertPCClass(sm);
323:
324: LookupRequest lr = storeMgr.getLookupRequest(this );
325:
326: lr.execute(sm);
327: }
328:
329: public void fetch(StateManager sm, int[] fieldNumbers) {
330: assertIsValidated();
331: assertPCClass(sm);
332:
333: if (super table != null)
334: super table.fetch(sm, fieldNumbers);
335:
336: FetchRequest fr = storeMgr.getFetchRequest(this , fieldNumbers);
337:
338: fr.execute(sm);
339: }
340:
341: public void update(StateManager sm, int[] fieldNumbers) {
342: assertIsValidated();
343: assertPCClass(sm);
344:
345: if (super table != null)
346: super table.update(sm, fieldNumbers);
347:
348: UpdateRequest ur = storeMgr
349: .getUpdateRequest(this , fieldNumbers);
350:
351: ur.execute(sm);
352: }
353:
354: public void delete(StateManager sm) {
355: assertIsValidated();
356: assertPCClass(sm);
357:
358: DeleteRequest dr = storeMgr.getDeleteRequest(this);
359:
360: dr.execute(sm);
361:
362: if (supertable != null)
363: supertable.delete(sm);
364: }
365: }
|