001: /**********************************************************************
002: Copyright (c) 2007 Andy Jefferson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015: Contributors:
016: ...
017: **********************************************************************/package org.jpox.store;
018:
019: import java.util.HashMap;
020: import java.util.Map;
021:
022: import org.jpox.ClassLoaderResolver;
023: import org.jpox.ObjectManager;
024: import org.jpox.ObjectManagerFactoryImpl;
025: import org.jpox.StateManager;
026: import org.jpox.exceptions.JPOXDataStoreException;
027: import org.jpox.exceptions.JPOXException;
028: import org.jpox.exceptions.JPOXObjectNotFoundException;
029: import org.jpox.exceptions.JPOXUserException;
030: import org.jpox.metadata.AbstractClassMetaData;
031: import org.jpox.metadata.AbstractMemberMetaData;
032: import org.jpox.metadata.InheritanceStrategy;
033: import org.jpox.store.exceptions.DatastorePermissionException;
034: import org.jpox.store.fieldmanager.FieldManager;
035: import org.jpox.store.mapping.JavaTypeMapping;
036: import org.jpox.store.mapping.MappingManager;
037: import org.jpox.util.JPOXLogger;
038: import org.jpox.util.StringUtils;
039:
040: /**
041: * Manager for a datastore that has a schema and maps classes to associated objects in the datastore.
042: * Datastores such as RDBMS will extend this type of StoreManager.
043: * <p>
044: * In a "mapped" datastore, a class is associated with a DatastoreClass. Similarly a field of a class is associated
045: * with a DatastoreField. Where a relation is stored separately this is associated with a DatastoreContainerObject.
046: * In an RDBMS datastore this will be
047: * <ul>
048: * <li>class <-> table</li>
049: * <li>field <-> column</li>
050: * <li>relation <-> join-table / foreign-key</li>
051: * </ul>
052: * </p>
053: * <p>
054: * This type of StoreManager allows creation/validation of the schema. A schema is split into
055: * "tables", "columns", "constraints" currently. The PMF/EMF allows specification of a set of properties
056: * that will apply to this type of datastore.
057: * </p>
058: *
059: * @version $Revision: 1.11 $
060: */
061: public abstract class MappedStoreManager extends StoreManager {
062: /** Whether to auto create any tables. */
063: protected final boolean autoCreateTables;
064:
065: /** Whether to auto create any columns that are missing. */
066: protected final boolean autoCreateColumns;
067:
068: /** Whether to auto create any constraints */
069: protected final boolean autoCreateConstraints;
070:
071: /** Whether to warn only when any errors occur on auto-create. */
072: protected final boolean autoCreateWarnOnError;
073:
074: /** Whether to validate any tables */
075: protected final boolean validateTables;
076:
077: /** Whether to validate any columns */
078: protected final boolean validateColumns;
079:
080: /** Whether to validate any constraints */
081: protected final boolean validateConstraints;
082:
083: /** Whether to check if table/view exists */
084: protected final boolean checkExistTablesOrViews;
085:
086: /**
087: * Map of all managed datastore containers (tables) keyed by the datastore identifier.
088: * Only currently used for storing SequenceTable.
089: */
090: protected Map datastoreContainerByIdentifier = new HashMap();
091:
092: /**
093: * Constructor. Stores the basic information required for the datastore management.
094: * @param key Key for this StoreManager
095: * @param clr the ClassLoaderResolver
096: * @param omf The corresponding ObjectManagerFactory.
097: * @see StoreManagerFactory
098: */
099: protected MappedStoreManager(String key, ClassLoaderResolver clr,
100: ObjectManagerFactoryImpl omf) {
101: super (key, clr, omf);
102:
103: autoCreateColumns = omf.getPersistenceConfiguration()
104: .getAutoCreateColumns();
105: autoCreateTables = omf.getPersistenceConfiguration()
106: .getAutoCreateTables();
107: autoCreateConstraints = omf.getPersistenceConfiguration()
108: .getAutoCreateConstraints();
109: autoCreateWarnOnError = omf.getPersistenceConfiguration()
110: .getAutoCreateWarnOnError();
111:
112: validateTables = omf.getPersistenceConfiguration()
113: .getValidateTables();
114: validateColumns = omf.getPersistenceConfiguration()
115: .getValidateColumns();
116: validateConstraints = omf.getPersistenceConfiguration()
117: .getValidateConstraints();
118:
119: checkExistTablesOrViews = omf.getPersistenceConfiguration()
120: .getCheckExistTablesOrViews();
121: }
122:
123: /**
124: * Gets the MappingManager to use for this store.
125: * @return Returns the MappingManager.
126: */
127: public MappingManager getMappingManager() {
128: return dba.getMappingManager();
129: }
130:
131: /**
132: * Method returning whether the datastore has a "datastore class" equivalent that the StoreManager
133: * uses to represent it. For datastores like RDBMS this will return "true" since we have a "table".
134: * For object datastores this is typically "false" since the objects are stored in some internal form
135: * not visible via its API.
136: * @return Whether datastore classes are used by the StoreManager
137: */
138: public boolean usesDatastoreClass() {
139: return true;
140: }
141:
142: /**
143: * Inserts a persistent object into the database.
144: * @param sm The state manager of the object to be inserted.
145: * @throws JPOXDataStoreException when an error occurs in the datastore communication
146: */
147: public void insertObject(StateManager sm) {
148: if (readOnlyDatastore) {
149: if (readOnlyDatastoreAction.equalsIgnoreCase("EXCEPTION")) {
150: throw new DatastorePermissionException(LOCALISER.msg(
151: "032004", StringUtils.toJVMIDString(sm
152: .getObject())));
153: } else {
154: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
155: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
156: "032005", StringUtils.toJVMIDString(sm
157: .getObject())));
158: }
159: return;
160: }
161: }
162:
163: ClassLoaderResolver clr = sm.getObjectManager()
164: .getClassLoaderResolver();
165: String className = sm.getObject().getClass().getName();
166: DatastoreClass dc = getDatastoreClass(className, clr);
167: if (dc == null) {
168: AbstractClassMetaData cmd = getMetaDataManager()
169: .getMetaDataForClass(className, clr);
170: if (cmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.SUBCLASS_TABLE) {
171: throw new JPOXUserException(LOCALISER.msg("032013",
172: className));
173: }
174: throw new JPOXException(LOCALISER.msg("032014", className,
175: cmd.getInheritanceMetaData().getStrategyValue()))
176: .setFatal();
177: }
178: if (storeManagerRuntime != null) {
179: storeManagerRuntime.incrementInsertCount();
180: }
181: dc.insert(sm);
182: }
183:
184: /**
185: * Fetches a persistent object from the database.
186: * @param sm The state manager of the object to be fetched.
187: * @param fieldNumbers The numbers of the fields to be fetched.
188: * @throws JPOXObjectNotFoundException if the object doesnt exist
189: * @throws JPOXDataStoreException when an error occurs in the datastore communication
190: */
191: public void fetchObject(StateManager sm, int fieldNumbers[]) {
192: AbstractMemberMetaData[] fmds = null;
193: if (fieldNumbers != null && fieldNumbers.length > 0) {
194: // Convert the field numbers for this class into their metadata for the table
195: fmds = new AbstractMemberMetaData[fieldNumbers.length];
196: for (int i = 0; i < fmds.length; i++) {
197: fmds[i] = sm.getClassMetaData()
198: .getMetaDataForManagedMemberAtAbsolutePosition(
199: fieldNumbers[i]);
200: }
201:
202: if (sm.getPcObjectType() != StateManager.PC) {
203: StringBuffer str = new StringBuffer();
204: for (int i = 0; i < fmds.length; i++) {
205: if (i > 0) {
206: str.append(',');
207: }
208: str.append(fmds[i].getName());
209: }
210: JPOXLogger.PERSISTENCE.info("Request to load fields \""
211: + str.toString() + "\" of class "
212: + sm.getClassMetaData().getFullClassName()
213: + " but object is embedded, so ignored");
214: } else {
215: if (storeManagerRuntime != null) {
216: storeManagerRuntime.incrementFetchCount();
217: }
218: getDatastoreClass(sm.getObject().getClass().getName(),
219: sm.getObjectManager().getClassLoaderResolver())
220: .fetch(sm, fmds);
221: }
222: }
223: }
224:
225: /**
226: * Updates a persistent object in the database.
227: * @param sm The state manager of the object to be updated.
228: * @param fieldNumbers The numbers of the fields to be updated.
229: * @throws JPOXDataStoreException when an error occurs in the datastore communication
230: */
231: public void updateObject(StateManager sm, int fieldNumbers[]) {
232: if (readOnlyDatastore) {
233: if (readOnlyDatastoreAction.equalsIgnoreCase("EXCEPTION")) {
234: throw new DatastorePermissionException(LOCALISER.msg(
235: "032006", StringUtils.toJVMIDString(sm
236: .getObject())));
237: } else {
238: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
239: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
240: "032007", StringUtils.toJVMIDString(sm
241: .getObject())));
242: }
243: return;
244: }
245: }
246:
247: AbstractMemberMetaData[] fmds = null;
248: if (fieldNumbers != null && fieldNumbers.length > 0) {
249: // Convert the field numbers for this class into their metadata for the table
250: fmds = new AbstractMemberMetaData[fieldNumbers.length];
251: for (int i = 0; i < fmds.length; i++) {
252: fmds[i] = sm.getClassMetaData()
253: .getMetaDataForManagedMemberAtAbsolutePosition(
254: fieldNumbers[i]);
255: }
256:
257: if (storeManagerRuntime != null) {
258: storeManagerRuntime.incrementUpdateCount();
259: }
260: getDatastoreClass(sm.getObject().getClass().getName(),
261: sm.getObjectManager().getClassLoaderResolver())
262: .update(sm, fmds);
263: }
264: }
265:
266: /**
267: * Deletes a persistent object from the database.
268: * @param sm The state manager of the object to be deleted.
269: * @throws JPOXDataStoreException when an error occurs in the datastore communication
270: */
271: public void deleteObject(StateManager sm) {
272: if (readOnlyDatastore) {
273: if (readOnlyDatastoreAction.equalsIgnoreCase("EXCEPTION")) {
274: throw new DatastorePermissionException(LOCALISER.msg(
275: "032008", StringUtils.toJVMIDString(sm
276: .getObject())));
277: } else {
278: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
279: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
280: "032009", StringUtils.toJVMIDString(sm
281: .getObject())));
282: }
283: return;
284: }
285: }
286:
287: if (storeManagerRuntime != null) {
288: storeManagerRuntime.incrementDeleteCount();
289: }
290: getDatastoreClass(sm.getObject().getClass().getName(),
291: sm.getObjectManager().getClassLoaderResolver()).delete(
292: sm);
293: }
294:
295: /**
296: * Locates this object in the datastore.
297: * @param sm The StateManager for the object to be found
298: * @throws JPOXObjectNotFoundException if the object doesnt exist
299: * @throws JPOXDataStoreException when an error occurs in the datastore communication
300: */
301: public void locateObject(StateManager sm) {
302: getDatastoreClass(sm.getObject().getClass().getName(),
303: sm.getObjectManager().getClassLoaderResolver()).locate(
304: sm);
305: }
306:
307: /**
308: * Method to return a persistable object with the specified id. Optional operation for StoreManagers.
309: * Should return a (at least) hollow PersistenceCapable object if the store manager supports the operation.
310: * If the StoreManager is managing the in-memory object instantiation (as part of co-managing the object lifecycle
311: * in general), then the StoreManager has to create the object during this call (if it is not already created).
312: * Most relational databases leave the in-memory object instantion to JPOX Core, but some object databases may manage
313: * the in-memory object instantion, effectively preventing JPOX Core of doing this.
314: * <p>
315: * StoreManager implementations may simply return null, indicating that they leave the object instantiate to JPOX.
316: * Other implementations may instantiate the object in question (whether the implementation may trust that the object
317: * is not already instantiated has still to be determined). If an implementation believes that an object with the
318: * given ID should exist, but in fact does not exist, then the implementation should throw a RuntimeException.
319: * It should not silently return null in this case.
320: * </p>
321: * @param om the ObjectManager which will manage the object
322: * @param id the id of the object in question.
323: * @return a persistable object with a valid object state (for example: hollow) or null,
324: * indicating that the implementation leaves the instantiation work to JPOX.
325: */
326: public Object findObject(ObjectManager om, Object id) {
327: return null;
328: }
329:
330: /**
331: * Method to create a new fetch statement for the passed table.
332: * @param table The table to fetch from
333: * @return The fetch statement
334: */
335: public abstract FetchStatement getFetchStatement(
336: DatastoreContainerObject table);
337:
338: /**
339: * Called by Mapping objects to request the creation of a DatastoreObject (table).
340: * @param fmd The field metadata describing the field.
341: * @param clr The ClassLoaderResolver
342: * @return The DatastoreContainerObject
343: */
344: public abstract DatastoreContainerObject newJoinDatastoreContainerObject(
345: AbstractMemberMetaData fmd, ClassLoaderResolver clr);
346:
347: /**
348: * Utility to return all StoreData for a Datastore Container identifier.
349: * Returns StoreData with this table identifier and where the class is the owner of the table.
350: * @param tableIdentifier Identifier for the table
351: * @return The StoreData for this table (if managed).
352: */
353: public synchronized TableStoreData[] getStoreDataForDatastoreContainerObject(
354: DatastoreIdentifier tableIdentifier) {
355: return storeDataMgr
356: .getStoreDataForDatastoreContainerObject(tableIdentifier);
357: }
358:
359: /**
360: * Returns the datastore container (table) for the specified field.
361: * Returns 'null' if the field is not (yet) known to the store manager.
362: * @param fmd The metadata for the field.
363: * @return The corresponding datastore container, or 'null'.
364: */
365: public synchronized DatastoreContainerObject getDatastoreContainerObject(
366: AbstractMemberMetaData fmd) {
367: StoreData sd = storeDataMgr.get(fmd);
368: if (sd != null && sd instanceof TableStoreData) {
369: return ((TableStoreData) sd).getDatastoreContainerObject();
370: } else {
371: return null;
372: }
373: }
374:
375: /**
376: * Method to add a datastore container to the managed datastore classes
377: * @param table The datastore container
378: */
379: public void addDatastoreContainer(DatastoreContainerObject table) {
380: if (table != null
381: && datastoreContainerByIdentifier.get(table
382: .getIdentifier()) == null) {
383: datastoreContainerByIdentifier.put(table, table
384: .getIdentifier());
385: }
386: }
387:
388: /**
389: * Method to create the necessary datastore columns for a reference field.
390: * @param m (Java) Mapping for the field
391: * @param table The datastore container where the datastore columns will be created
392: * @param fmd MetaData for the field/property
393: * @param clr ClassLoader resolver
394: * @param embedded Whether the field is embedded
395: */
396: public abstract void createDatastoreColumnsForReferenceField(
397: JavaTypeMapping m, DatastoreContainerObject table,
398: AbstractMemberMetaData fmd, ClassLoaderResolver clr,
399: boolean embedded);
400:
401: /**
402: * Method to create the necessary datastore columns for a field using subclass-table inheritance.
403: * @param m (Java) Mapping for the field
404: * @param table The datastore container where the datastore columns will be created
405: * @param fmd MetaData for the field/property
406: * @param clr ClassLoader resolver
407: */
408: public abstract void createDatastoreColumnsForFieldUsingSubclassTable(
409: JavaTypeMapping m, DatastoreContainerObject table,
410: AbstractMemberMetaData fmd, ClassLoaderResolver clr);
411:
412: /**
413: * Accessor for a FieldManager capable of processing the results of a query.
414: * @param sm StateManager for the object
415: * @param rs The results
416: * @param stmtExprIndx Statement expression indices for results -> fields mapping
417: * @return The FieldManager
418: */
419: public abstract FieldManager getFieldManagerForResultProcessing(
420: StateManager sm, Object rs,
421: StatementExpressionIndex[] stmtExprIndx);
422: }
|