0001: package org.apache.ojb.broker.core;
0002:
0003: /* Copyright 2003-2005 The Apache Software Foundation
0004: *
0005: * Licensed under the Apache License, Version 2.0 (the "License");
0006: * you may not use this file except in compliance with the License.
0007: * You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: import java.lang.reflect.Constructor;
0019: import java.util.Collection;
0020: import java.util.Collections;
0021: import java.util.Enumeration;
0022: import java.util.HashSet;
0023: import java.util.Iterator;
0024: import java.util.List;
0025: import java.util.Set;
0026:
0027: import org.apache.commons.lang.ObjectUtils;
0028: import org.apache.ojb.broker.Identity;
0029: import org.apache.ojb.broker.IdentityFactory;
0030: import org.apache.ojb.broker.ManageableCollection;
0031: import org.apache.ojb.broker.MtoNImplementor;
0032: import org.apache.ojb.broker.OptimisticLockException;
0033: import org.apache.ojb.broker.PBKey;
0034: import org.apache.ojb.broker.PBState;
0035: import org.apache.ojb.broker.PersistenceBrokerException;
0036: import org.apache.ojb.broker.TransactionAbortedException;
0037: import org.apache.ojb.broker.TransactionInProgressException;
0038: import org.apache.ojb.broker.TransactionNotInProgressException;
0039: import org.apache.ojb.broker.accesslayer.ChainingIterator;
0040: import org.apache.ojb.broker.accesslayer.ConnectionManagerFactory;
0041: import org.apache.ojb.broker.accesslayer.ConnectionManagerIF;
0042: import org.apache.ojb.broker.accesslayer.JdbcAccess;
0043: import org.apache.ojb.broker.accesslayer.JdbcAccessFactory;
0044: import org.apache.ojb.broker.accesslayer.OJBIterator;
0045: import org.apache.ojb.broker.accesslayer.PagingIterator;
0046: import org.apache.ojb.broker.accesslayer.PkEnumeration;
0047: import org.apache.ojb.broker.accesslayer.RelationshipPrefetcherFactory;
0048: import org.apache.ojb.broker.accesslayer.StatementManagerFactory;
0049: import org.apache.ojb.broker.accesslayer.StatementManagerIF;
0050: import org.apache.ojb.broker.accesslayer.sql.SqlGenerator;
0051: import org.apache.ojb.broker.accesslayer.sql.SqlGeneratorFactory;
0052: import org.apache.ojb.broker.cache.MaterializationCache;
0053: import org.apache.ojb.broker.cache.ObjectCache;
0054: import org.apache.ojb.broker.cache.ObjectCacheFactory;
0055: import org.apache.ojb.broker.cache.ObjectCacheInternal;
0056: import org.apache.ojb.broker.core.proxy.AbstractProxyFactory;
0057: import org.apache.ojb.broker.core.proxy.CollectionProxy;
0058: import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
0059: import org.apache.ojb.broker.core.proxy.IndirectionHandler;
0060: import org.apache.ojb.broker.core.proxy.ProxyFactory;
0061: import org.apache.ojb.broker.core.proxy.VirtualProxy;
0062: import org.apache.ojb.broker.metadata.ClassDescriptor;
0063: import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
0064: import org.apache.ojb.broker.metadata.CollectionDescriptor;
0065: import org.apache.ojb.broker.metadata.DescriptorRepository;
0066: import org.apache.ojb.broker.metadata.FieldDescriptor;
0067: import org.apache.ojb.broker.metadata.MetadataManager;
0068: import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
0069: import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
0070: import org.apache.ojb.broker.query.Query;
0071: import org.apache.ojb.broker.query.QueryByIdentity;
0072: import org.apache.ojb.broker.query.QueryBySQL;
0073: import org.apache.ojb.broker.util.BrokerHelper;
0074: import org.apache.ojb.broker.util.IdentityArrayList;
0075: import org.apache.ojb.broker.util.ObjectModification;
0076: import org.apache.ojb.broker.util.logging.Logger;
0077: import org.apache.ojb.broker.util.logging.LoggerFactory;
0078: import org.apache.ojb.broker.util.sequence.SequenceManager;
0079: import org.apache.ojb.broker.util.sequence.SequenceManagerFactory;
0080:
0081: /**
0082: * The PersistenceBrokerImpl is an implementation of the PersistenceBroker
0083: * Interface that specifies a persistence mechanism for Java objects.
0084: * This Concrete implementation provides an object relational mapping
0085: * and allows to store and retrieve arbitrary objects in/from relational
0086: * databases accessed by JDBC.
0087: *
0088: * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
0089: * @author <a href="mailto:leandro@ibnetwork.com.br">Leandro Rodrigo Saad Cruz<a>
0090: * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird<a>
0091: * @author <a href="mailto:jbraeuchi@gmx.ch">Jakob Braeuchi</a>
0092: *
0093: * @version $Id: PersistenceBrokerImpl.java,v 1.83.2.30 2005/12/13 18:21:23 arminw Exp $
0094: */
0095: public class PersistenceBrokerImpl extends
0096: PersistenceBrokerAbstractImpl implements PBState {
0097: private Logger logger = LoggerFactory
0098: .getLogger(PersistenceBrokerImpl.class);
0099:
0100: protected PersistenceBrokerFactoryIF pbf;
0101: protected BrokerHelper brokerHelper;
0102: protected MtoNBroker mtoNBroker;
0103: protected QueryReferenceBroker referencesBroker;
0104:
0105: /**
0106: * signs if this broker was closed
0107: */
0108: private boolean isClosed;
0109: /**
0110: * Reflects the transaction status of this instance.
0111: */
0112: private boolean inTransaction;
0113: /**
0114: * Flag indicate that this PB instance is handled
0115: * in a managed environment.
0116: */
0117: private boolean managed;
0118: private MaterializationCache objectCache;
0119: /**
0120: * m_DbAccess is used to do all Jdbc related work: connecting, executing...
0121: */
0122: private JdbcAccess dbAccess;
0123: /**
0124: * holds mapping information for all classes to be treated by PersistenceBroker
0125: */
0126: private DescriptorRepository descriptorRepository = null;
0127: private ConnectionManagerIF connectionManager = null;
0128: private SequenceManager sequenceManager = null;
0129: private StatementManagerIF statementManager = null;
0130: private SqlGenerator sqlGenerator;
0131: private IdentityFactory identityFactory;
0132: private RelationshipPrefetcherFactory relationshipPrefetcherFactory;
0133: private ProxyFactory proxyFactory;
0134: private PBKey pbKey;
0135:
0136: /**
0137: * List of objects being stored now, allows to avoid infinite
0138: * recursion storeCollections -> storeReferences -> storeCollections...
0139: *
0140: */
0141: /*
0142: we use an object identity based List to compare objects to prevent problems
0143: with user implemented equals/hashCode methods of persistence capable objects
0144: (e.g. objects are equals but PK fields not)
0145: */
0146: private List nowStoring = new IdentityArrayList();
0147:
0148: /**
0149: * Lists for object registration during delete operations.
0150: * We reuse these list to avoid excessive object creation.
0151: * @see #clearRegistrationLists
0152: */
0153: /*
0154: arminw: list was cleared before delete method end. Internal we only
0155: call doDelete(...) method. Same procedure as 'nowStoring'
0156:
0157: we use an object identity based List to compare objects to prevent problems
0158: with user implemented equals/hashCode methods of persistence capable objects
0159: (e.g. objects are equals but PK fields not)
0160: */
0161: private List markedForDelete = new IdentityArrayList();
0162:
0163: /**
0164: * The set of identities of all deleted objects during current transaction
0165: */
0166: /*
0167: olegnitz: this is the only way I know that solves the following problem
0168: of batch mode: if one does store() after delete() for the same OID,
0169: the broker checks whether the given OID exists in database to decide
0170: which action to do: INSERT or UPDATE. If the preceding DELETE statement is
0171: still in batch (not executed yet), then the OID exists in database so
0172: the broker does UPDATE. Due the the following set of deleted OIDs
0173: the broker will know that it should do INSERT.
0174: */
0175: private Set deletedDuringTransaction = new HashSet();
0176:
0177: /**
0178: * Constructor used by {@link PersistenceBrokerFactoryIF} implementation.
0179: */
0180: public PersistenceBrokerImpl(PBKey key,
0181: PersistenceBrokerFactoryIF pbf) {
0182: refresh();
0183: if (key == null)
0184: throw new PersistenceBrokerException(
0185: "Could not instantiate broker with PBKey 'null'");
0186: this .pbf = pbf;
0187: this .pbKey = key;
0188: /*
0189: be careful when changing initializing order
0190: */
0191: brokerHelper = new BrokerHelper(this );
0192: connectionManager = ConnectionManagerFactory.getInstance()
0193: .createConnectionManager(this );
0194: /*
0195: TODO: find better solution
0196: MaterializationCache is a interim solution help to solve the problem of not full
0197: materialized object reads by concurrent threads and will be replaced when
0198: the new real two-level cache was introduced
0199: */
0200: objectCache = ObjectCacheFactory.getInstance()
0201: .createObjectCache(this );
0202: sequenceManager = SequenceManagerFactory
0203: .getSequenceManager(this );
0204: dbAccess = JdbcAccessFactory.getInstance().createJdbcAccess(
0205: this );
0206: statementManager = StatementManagerFactory.getInstance()
0207: .createStatementManager(this );
0208: sqlGenerator = SqlGeneratorFactory.getInstance()
0209: .createSqlGenerator(
0210: connectionManager.getSupportedPlatform());
0211: mtoNBroker = new MtoNBroker(this );
0212: referencesBroker = new QueryReferenceBroker(this );
0213: identityFactory = new IdentityFactoryImpl(this );
0214: relationshipPrefetcherFactory = new RelationshipPrefetcherFactory(
0215: this );
0216: proxyFactory = AbstractProxyFactory.getProxyFactory();
0217: }
0218:
0219: public MaterializationCache getInternalCache() {
0220: return objectCache;
0221: }
0222:
0223: public IdentityFactory serviceIdentity() {
0224: return this .identityFactory;
0225: }
0226:
0227: public SqlGenerator serviceSqlGenerator() {
0228: return this .sqlGenerator;
0229: }
0230:
0231: public StatementManagerIF serviceStatementManager() {
0232: return statementManager;
0233: }
0234:
0235: public JdbcAccess serviceJdbcAccess() {
0236: return dbAccess;
0237: }
0238:
0239: public ConnectionManagerIF serviceConnectionManager() {
0240: return connectionManager;
0241: }
0242:
0243: public SequenceManager serviceSequenceManager() {
0244: return this .sequenceManager;
0245: }
0246:
0247: public BrokerHelper serviceBrokerHelper() {
0248: return this .brokerHelper;
0249: }
0250:
0251: public ObjectCache serviceObjectCache() {
0252: return this .objectCache;
0253: }
0254:
0255: public QueryReferenceBroker getReferenceBroker() {
0256: return this .referencesBroker;
0257: }
0258:
0259: public RelationshipPrefetcherFactory getRelationshipPrefetcherFactory() {
0260: return relationshipPrefetcherFactory;
0261: }
0262:
0263: public boolean isClosed() {
0264: return this .isClosed;
0265: }
0266:
0267: public void setClosed(boolean closed) {
0268: // When lookup the PB instance from pool method setClosed(false)
0269: // was called before returning instance from pool, in this case
0270: // OJB have to refresh the instance.
0271: if (!closed) {
0272: refresh();
0273: }
0274: this .isClosed = closed;
0275: }
0276:
0277: /**
0278: * If <em>true</em> this instance is handled by a managed
0279: * environment - registered within a JTA transaction.
0280: */
0281: public boolean isManaged() {
0282: return managed;
0283: }
0284:
0285: /**
0286: * Set <em>true</em> if this instance is registered within a
0287: * JTA transaction. On {@link #close()} call this flag was reset
0288: * to <em>false</em> automatic.
0289: */
0290: public void setManaged(boolean managed) {
0291: this .managed = managed;
0292: }
0293:
0294: /**
0295: * Lookup the current {@link DescriptorRepository} for
0296: * this class. This method is responsible to keep this
0297: * PB instance in sync with {@link MetadataManager}.
0298: */
0299: public void refresh() {
0300: // guarantee that refreshed use initial status
0301: setInTransaction(false);
0302: this .descriptorRepository = MetadataManager.getInstance()
0303: .getRepository();
0304: }
0305:
0306: /**
0307: * Release all resources used by this
0308: * class - CAUTION: No further operations can be
0309: * done with this instance after calling this method.
0310: */
0311: public void destroy() {
0312: removeAllListeners();
0313: if (connectionManager != null) {
0314: if (connectionManager.isInLocalTransaction()) {
0315: connectionManager.localRollback();
0316: }
0317: connectionManager.releaseConnection();
0318: }
0319: this .setClosed(true);
0320:
0321: this .descriptorRepository = null;
0322: this .pbKey = null;
0323: this .pbf = null;
0324: this .connectionManager = null;
0325: this .dbAccess = null;
0326: this .objectCache = null;
0327: this .sequenceManager = null;
0328: this .sqlGenerator = null;
0329: this .statementManager = null;
0330: }
0331:
0332: public PBKey getPBKey() {
0333: return pbKey;
0334: }
0335:
0336: public void setPBKey(PBKey key) {
0337: this .pbKey = key;
0338: }
0339:
0340: /**
0341: * @see org.apache.ojb.broker.PersistenceBroker#close()
0342: */
0343: public boolean close() {
0344: /**
0345: * MBAIRD: if we call close on a broker that is in a transaction,
0346: * we should just abort whatever it's doing.
0347: */
0348: if (isInTransaction()) {
0349: logger
0350: .error("Broker is still in PB-transaction, do automatic abort before close!");
0351: abortTransaction();
0352: }
0353: if (logger.isDebugEnabled()) {
0354: logger.debug("PB.close was called: " + this );
0355: }
0356: try {
0357: fireBrokerEvent(BEFORE_CLOSE_EVENT);
0358: clearRegistrationLists();
0359: referencesBroker.removePrefetchingListeners();
0360: if (connectionManager != null) {
0361: connectionManager.releaseConnection();
0362: /*
0363: arminw:
0364: set batch mode explicit to 'false'. Using
0365:
0366: connectionManager.setBatchMode(
0367: connectionManager.getConnectionDescriptor().getBatchMode());
0368:
0369: cause many unexpected junit failures/errors when running
0370: test suite with batch-mode 'true' setting.
0371: */
0372: connectionManager.setBatchMode(false);
0373: }
0374: } finally {
0375: // reset flag indicating use in managed environment
0376: setManaged(false);
0377: // free current used DescriptorRepository reference
0378: descriptorRepository = null;
0379: removeAllListeners();
0380: this .setClosed(true);
0381: }
0382: return true;
0383: }
0384:
0385: /**
0386: * Abort and close the transaction.
0387: * Calling abort abandons all persistent object modifications and releases the
0388: * associated locks.
0389: * If transaction is not in progress a TransactionNotInProgressException is thrown
0390: */
0391: public synchronized void abortTransaction()
0392: throws TransactionNotInProgressException {
0393: if (isInTransaction()) {
0394: fireBrokerEvent(BEFORE_ROLLBACK_EVENT);
0395: setInTransaction(false);
0396: clearRegistrationLists();
0397: referencesBroker.removePrefetchingListeners();
0398: /*
0399: arminw:
0400: check if we in local tx, before do local rollback
0401: Necessary, because ConnectionManager may do a rollback by itself
0402: or in managed environments the used connection is already be closed
0403: */
0404: if (connectionManager.isInLocalTransaction())
0405: this .connectionManager.localRollback();
0406: fireBrokerEvent(AFTER_ROLLBACK_EVENT);
0407: }
0408: }
0409:
0410: /**
0411: * begin a transaction against the underlying RDBMS.
0412: * Calling <code>beginTransaction</code> multiple times,
0413: * without an intervening call to <code>commitTransaction</code> or <code>abortTransaction</code>,
0414: * causes the exception <code>TransactionInProgressException</code> to be thrown
0415: * on the second and subsequent calls.
0416: */
0417: public synchronized void beginTransaction()
0418: throws TransactionInProgressException,
0419: TransactionAbortedException {
0420: if (isInTransaction()) {
0421: throw new TransactionInProgressException(
0422: "PersistenceBroker is already in transaction");
0423: }
0424: fireBrokerEvent(BEFORE_BEGIN_EVENT);
0425: setInTransaction(true);
0426: this .connectionManager.localBegin();
0427: fireBrokerEvent(AFTER_BEGIN_EVENT);
0428: }
0429:
0430: /**
0431: * Commit and close the transaction.
0432: * Calling <code>commit</code> commits to the database all
0433: * UPDATE, INSERT and DELETE statements called within the transaction and
0434: * releases any locks held by the transaction.
0435: * If beginTransaction() has not been called before a
0436: * TransactionNotInProgressException exception is thrown.
0437: * If the transaction cannot be commited a TransactionAbortedException exception is thrown.
0438: */
0439: public synchronized void commitTransaction()
0440: throws TransactionNotInProgressException,
0441: TransactionAbortedException {
0442: if (!isInTransaction()) {
0443: throw new TransactionNotInProgressException(
0444: "PersistenceBroker is NOT in transaction, can't commit");
0445: }
0446: fireBrokerEvent(BEFORE_COMMIT_EVENT);
0447: setInTransaction(false);
0448: clearRegistrationLists();
0449: referencesBroker.removePrefetchingListeners();
0450: /*
0451: arminw:
0452: In managed environments it should be possible to close a used connection before
0453: the tx was commited, thus it will be possible that the PB instance is in PB-tx, but
0454: the connection is already closed. To avoid problems check if CM is in local tx before
0455: do the CM.commit call
0456: */
0457: if (connectionManager.isInLocalTransaction()) {
0458: this .connectionManager.localCommit();
0459: }
0460: fireBrokerEvent(AFTER_COMMIT_EVENT);
0461: }
0462:
0463: /**
0464: * Deletes the concrete representation of the specified object in the underlying
0465: * persistence system. This method is intended for use in top-level api or
0466: * by internal calls.
0467: *
0468: * @param obj The object to delete.
0469: * @param ignoreReferences With this flag the automatic deletion/unlinking
0470: * of references can be suppressed (independent of the used auto-delete setting in metadata),
0471: * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
0472: * these kind of reference (descriptor) will always be performed. If <em>true</em>
0473: * all "normal" referenced objects will be ignored, only the specified object is handled.
0474: * @throws PersistenceBrokerException
0475: */
0476: public void delete(Object obj, boolean ignoreReferences)
0477: throws PersistenceBrokerException {
0478: if (isTxCheck() && !isInTransaction()) {
0479: if (logger.isEnabledFor(Logger.ERROR)) {
0480: String msg = "No running PB-tx found. Please, only delete objects in context of a PB-transaction"
0481: + " to avoid side-effects - e.g. when rollback of complex objects.";
0482: try {
0483: throw new Exception(
0484: "** Delete object without active PersistenceBroker transaction **");
0485: } catch (Exception e) {
0486: logger.error(msg, e);
0487: }
0488: }
0489: }
0490: try {
0491: doDelete(obj, ignoreReferences);
0492: } finally {
0493: markedForDelete.clear();
0494: }
0495: }
0496:
0497: /**
0498: * @see org.apache.ojb.broker.PersistenceBroker#delete
0499: */
0500: public void delete(Object obj) throws PersistenceBrokerException {
0501: delete(obj, false);
0502: }
0503:
0504: /**
0505: * do delete given object. Should be used by all intern classes to delete
0506: * objects.
0507: */
0508: private void doDelete(Object obj, boolean ignoreReferences)
0509: throws PersistenceBrokerException {
0510: //logger.info("DELETING " + obj);
0511: // object is not null
0512: if (obj != null) {
0513: obj = getProxyFactory().getRealObject(obj);
0514: /**
0515: * MBAIRD
0516: * 1. if we are marked for delete already, avoid recursing on this object
0517: *
0518: * arminw:
0519: * use object instead Identity object in markedForDelete List,
0520: * because using objects we get a better performance. I can't find
0521: * side-effects in doing so.
0522: */
0523: if (markedForDelete.contains(obj)) {
0524: return;
0525: }
0526:
0527: ClassDescriptor cld = getClassDescriptor(obj.getClass());
0528: //BRJ: check for valid pk
0529: if (!serviceBrokerHelper().assertValidPkForDelete(cld, obj)) {
0530: String msg = "Cannot delete object without valid PKs. "
0531: + obj;
0532: logger.error(msg);
0533: return;
0534: }
0535:
0536: /**
0537: * MBAIRD
0538: * 2. register object in markedForDelete map.
0539: */
0540: markedForDelete.add(obj);
0541: Identity oid = serviceIdentity().buildIdentity(cld, obj);
0542:
0543: // Invoke events on PersistenceBrokerAware instances and listeners
0544: BEFORE_DELETE_EVENT.setTarget(obj);
0545: fireBrokerEvent(BEFORE_DELETE_EVENT);
0546: BEFORE_DELETE_EVENT.setTarget(null);
0547:
0548: // 1. delete dependend collections
0549: if (!ignoreReferences
0550: && cld.getCollectionDescriptors().size() > 0) {
0551: deleteCollections(obj, cld.getCollectionDescriptors());
0552: }
0553: // 2. delete object from directly mapped table
0554: try {
0555: dbAccess.executeDelete(cld, obj); // use obj not oid to delete, BRJ
0556: } catch (OptimisticLockException e) {
0557: // ensure that the outdated object be removed from cache
0558: objectCache.remove(oid);
0559: throw e;
0560: }
0561:
0562: // 3. Add OID to the set of deleted objects
0563: deletedDuringTransaction.add(oid);
0564:
0565: // 4. delete dependend upon objects last to avoid FK violations
0566: if (cld.getObjectReferenceDescriptors().size() > 0) {
0567: deleteReferences(obj, cld
0568: .getObjectReferenceDescriptors(),
0569: ignoreReferences);
0570: }
0571: // remove obj from the object cache:
0572: objectCache.remove(oid);
0573:
0574: // Invoke events on PersistenceBrokerAware instances and listeners
0575: AFTER_DELETE_EVENT.setTarget(obj);
0576: fireBrokerEvent(AFTER_DELETE_EVENT);
0577: AFTER_DELETE_EVENT.setTarget(null);
0578:
0579: // let the connection manager to execute batch
0580: connectionManager.executeBatchIfNecessary();
0581: }
0582: }
0583:
0584: /**
0585: * Extent aware Delete by Query
0586: * @param query
0587: * @param cld
0588: * @throws PersistenceBrokerException
0589: */
0590: private void deleteByQuery(Query query, ClassDescriptor cld)
0591: throws PersistenceBrokerException {
0592: if (logger.isDebugEnabled()) {
0593: logger.debug("deleteByQuery " + cld.getClassNameOfObject()
0594: + ", " + query);
0595: }
0596:
0597: if (query instanceof QueryBySQL) {
0598: String sql = ((QueryBySQL) query).getSql();
0599: this .dbAccess.executeUpdateSQL(sql, cld);
0600: } else {
0601: // if query is Identity based transform it to a criteria based query first
0602: if (query instanceof QueryByIdentity) {
0603: QueryByIdentity qbi = (QueryByIdentity) query;
0604: Object oid = qbi.getExampleObject();
0605: // make sure it's an Identity
0606: if (!(oid instanceof Identity)) {
0607: oid = serviceIdentity().buildIdentity(oid);
0608: }
0609: query = referencesBroker.getPKQuery((Identity) oid);
0610: }
0611:
0612: if (!cld.isInterface()) {
0613: this .dbAccess.executeDelete(query, cld);
0614: }
0615:
0616: // if class is an extent, we have to delete all extent classes too
0617: String lastUsedTable = cld.getFullTableName();
0618: if (cld.isExtent()) {
0619: Iterator extents = getDescriptorRepository()
0620: .getAllConcreteSubclassDescriptors(cld)
0621: .iterator();
0622:
0623: while (extents.hasNext()) {
0624: ClassDescriptor extCld = (ClassDescriptor) extents
0625: .next();
0626:
0627: // read same table only once
0628: if (!extCld.getFullTableName()
0629: .equals(lastUsedTable)) {
0630: lastUsedTable = extCld.getFullTableName();
0631: this .dbAccess.executeDelete(query, extCld);
0632: }
0633: }
0634: }
0635:
0636: }
0637: }
0638:
0639: /**
0640: * @see org.apache.ojb.broker.PersistenceBroker#deleteByQuery(Query)
0641: */
0642: public void deleteByQuery(Query query)
0643: throws PersistenceBrokerException {
0644: ClassDescriptor cld = getClassDescriptor(query.getSearchClass());
0645: deleteByQuery(query, cld);
0646: }
0647:
0648: /**
0649: * Deletes references that <b>obj</b> points to.
0650: * All objects which we have a FK poiting to (Via ReferenceDescriptors)
0651: * will be deleted if auto-delete is true <b>AND</b>
0652: * the member field containing the object reference is NOT null.
0653: *
0654: * @param obj Object which we will delete references for
0655: * @param listRds list of ObjectRederenceDescriptors
0656: * @param ignoreReferences With this flag the automatic deletion/unlinking
0657: * of references can be suppressed (independent of the used auto-delete setting in metadata),
0658: * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
0659: * these kind of reference (descriptor) will always be performed.
0660: * @throws PersistenceBrokerException if some goes wrong - please see the error message for details
0661: */
0662: private void deleteReferences(Object obj, List listRds,
0663: boolean ignoreReferences) throws PersistenceBrokerException {
0664: // get all members of obj that are references and delete them
0665: Iterator i = listRds.iterator();
0666: while (i.hasNext()) {
0667: ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i
0668: .next();
0669: if ((!ignoreReferences && rds.getCascadingDelete() == ObjectReferenceDescriptor.CASCADE_OBJECT)
0670: || rds.isSuperReferenceDescriptor()) {
0671: Object referencedObject = rds.getPersistentField().get(
0672: obj);
0673: if (referencedObject != null) {
0674: doDelete(referencedObject, ignoreReferences);
0675: }
0676: }
0677: }
0678: }
0679:
0680: /**
0681: * Deletes collections of objects poiting to <b>obj</b>.
0682: * All object which have a FK poiting to this object (Via CollectionDescriptors)
0683: * will be deleted if auto-delete is true <b>AND</b>
0684: * the member field containing the object reference if NOT null.
0685: *
0686: * @param obj Object which we will delete collections for
0687: * @param listCds list of ObjectReferenceDescriptors
0688: * @throws PersistenceBrokerException if some goes wrong - please see the error message for details
0689: */
0690: private void deleteCollections(Object obj, List listCds)
0691: throws PersistenceBrokerException {
0692: // get all members of obj that are collections and delete all their elements
0693: Iterator i = listCds.iterator();
0694:
0695: while (i.hasNext()) {
0696: CollectionDescriptor cds = (CollectionDescriptor) i.next();
0697: if (cds.getCascadingDelete() != ObjectReferenceDescriptor.CASCADE_NONE) {
0698: if (cds.isMtoNRelation()) {
0699: // if this is a m:n mapped table, remove entries from indirection table
0700: mtoNBroker.deleteMtoNImplementor(cds, obj);
0701: }
0702: /*
0703: if cascading delete is on, delete all referenced objects.
0704: NOTE: User has to take care to populate all referenced objects before delete
0705: the main object to avoid referential constraint violation
0706: */
0707: if (cds.getCascadingDelete() == ObjectReferenceDescriptor.CASCADE_OBJECT) {
0708: Object col = cds.getPersistentField().get(obj);
0709: if (col != null) {
0710: Iterator colIterator = BrokerHelper
0711: .getCollectionIterator(col);
0712: while (colIterator.hasNext()) {
0713: doDelete(colIterator.next(), false);
0714: }
0715: }
0716: }
0717: }
0718: }
0719: }
0720:
0721: /**
0722: * Store an Object.
0723: * @see org.apache.ojb.broker.PersistenceBroker#store(Object)
0724: */
0725: public void store(Object obj) throws PersistenceBrokerException {
0726: obj = extractObjectToStore(obj);
0727: // only do something if obj != null
0728: if (obj == null)
0729: return;
0730:
0731: ClassDescriptor cld = getClassDescriptor(obj.getClass());
0732: /*
0733: if one of the PK fields was null, we assume the objects
0734: was new and needs insert
0735: */
0736: boolean insert = serviceBrokerHelper().hasNullPKField(cld, obj);
0737: Identity oid = serviceIdentity().buildIdentity(cld, obj);
0738: /*
0739: if PK values are set, lookup cache or db to see whether object
0740: needs insert or update
0741: */
0742: if (!insert) {
0743: insert = objectCache.lookup(oid) == null
0744: && !serviceBrokerHelper().doesExist(cld, oid, obj);
0745: }
0746: store(obj, oid, cld, insert);
0747: }
0748:
0749: /**
0750: * Check if the given object is <code>null</code> or an unmaterialized proxy object - in
0751: * both cases <code>null</code> will be returned, else the given object itself or the
0752: * materialized proxy object will be returned.
0753: */
0754: private Object extractObjectToStore(Object obj) {
0755: Object result = obj;
0756: // only do something if obj != null
0757: if (result != null) {
0758: // ProxyObjects only have to be updated if their real
0759: // subjects have been loaded
0760: result = getProxyFactory().getRealObjectIfMaterialized(obj);
0761: // null for unmaterialized Proxy
0762: if (result == null) {
0763: if (logger.isDebugEnabled())
0764: logger
0765: .debug("No materialized object could be found -> nothing to store,"
0766: + " object was "
0767: + ObjectUtils.identityToString(obj));
0768: }
0769: }
0770: return result;
0771: }
0772:
0773: /**
0774: * Method which start the real store work (insert or update)
0775: * and is intended for use by top-level api or internal calls.
0776: *
0777: * @param obj The object to store.
0778: * @param oid The {@link Identity} of the object to store.
0779: * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the object.
0780: * @param insert If <em>true</em> an insert operation will be performed, else update operation.
0781: * @param ignoreReferences With this flag the automatic storing/linking
0782: * of references can be suppressed (independent of the used auto-update setting in metadata),
0783: * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
0784: * these kind of reference (descriptor) will always be performed. If <em>true</em>
0785: * all "normal" referenced objects will be ignored, only the specified object is handled.
0786: */
0787: public void store(Object obj, Identity oid, ClassDescriptor cld,
0788: boolean insert, boolean ignoreReferences) {
0789: if (obj == null || nowStoring.contains(obj)) {
0790: return;
0791: }
0792:
0793: /*
0794: if the object has been deleted during this transaction,
0795: then we must insert it
0796: */
0797: //System.out.println("## insert: " +insert + " / deleted: " + deletedDuringTransaction);
0798: if (!insert) {
0799: insert = deletedDuringTransaction.contains(oid);
0800: }
0801:
0802: //************************************************
0803: // now store it:
0804: if (isTxCheck() && !isInTransaction()) {
0805: if (logger.isEnabledFor(Logger.ERROR)) {
0806: try {
0807: throw new Exception(
0808: "** Try to store object without active PersistenceBroker transaction **");
0809: } catch (Exception e) {
0810: logger
0811: .error(
0812: "No running tx found, please only store in context of an PB-transaction"
0813: + ", to avoid side-effects - e.g. when rollback of complex objects",
0814: e);
0815: }
0816: }
0817: }
0818: // Invoke events on PersistenceBrokerAware instances and listeners
0819: if (insert) {
0820: BEFORE_STORE_EVENT.setTarget(obj);
0821: fireBrokerEvent(BEFORE_STORE_EVENT);
0822: BEFORE_STORE_EVENT.setTarget(null);
0823: } else {
0824: BEFORE_UPDATE_EVENT.setTarget(obj);
0825: fireBrokerEvent(BEFORE_UPDATE_EVENT);
0826: BEFORE_UPDATE_EVENT.setTarget(null);
0827: }
0828:
0829: try {
0830: nowStoring.add(obj);
0831: storeToDb(obj, cld, oid, insert, ignoreReferences);
0832: } finally {
0833: // to optimize calls to DB don't remove already stored objects
0834: nowStoring.remove(obj);
0835: }
0836:
0837: // Invoke events on PersistenceBrokerAware instances and listeners
0838: if (insert) {
0839: AFTER_STORE_EVENT.setTarget(obj);
0840: fireBrokerEvent(AFTER_STORE_EVENT);
0841: AFTER_STORE_EVENT.setTarget(null);
0842: } else {
0843: AFTER_UPDATE_EVENT.setTarget(obj);
0844: fireBrokerEvent(AFTER_UPDATE_EVENT);
0845: AFTER_UPDATE_EVENT.setTarget(null);
0846: }
0847: // end of store operation
0848: //************************************************
0849:
0850: // if the object was stored, remove it from deleted set
0851: if (deletedDuringTransaction.size() > 0)
0852: deletedDuringTransaction.remove(oid);
0853:
0854: // let the connection manager to execute batch
0855: connectionManager.executeBatchIfNecessary();
0856: }
0857:
0858: /**
0859: * Internal used method which start the real store work.
0860: */
0861: protected void store(Object obj, Identity oid, ClassDescriptor cld,
0862: boolean insert) {
0863: store(obj, oid, cld, insert, false);
0864: }
0865:
0866: /**
0867: * Store all object references that <b>obj</b> points to.
0868: * All objects which we have a FK pointing to (Via ReferenceDescriptors) will be
0869: * stored if auto-update is true <b>AND</b> the member field containing the object
0870: * reference is NOT null.
0871: * With flag <em>ignoreReferences</em> the storing/linking
0872: * of references can be suppressed (independent of the used auto-update setting),
0873: * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
0874: * these kind of reference (descriptor) will always be performed.
0875: *
0876: * @param obj Object which we will store references for
0877: */
0878: private void storeReferences(Object obj, ClassDescriptor cld,
0879: boolean insert, boolean ignoreReferences) {
0880: // get all members of obj that are references and store them
0881: Collection listRds = cld.getObjectReferenceDescriptors();
0882: // return if nothing to do
0883: if (listRds == null || listRds.size() == 0) {
0884: return;
0885: }
0886: Iterator i = listRds.iterator();
0887: while (i.hasNext()) {
0888: ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i
0889: .next();
0890: /*
0891: arminw: the super-references (used for table per subclass inheritance) must
0892: be performed in any case. The "normal" 1:1 references can be ignored when
0893: flag "ignoreReferences" is set
0894: */
0895: if ((!ignoreReferences && rds.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE)
0896: || rds.isSuperReferenceDescriptor()) {
0897: storeAndLinkOneToOne(false, obj, cld, rds, insert);
0898: }
0899: }
0900: }
0901:
0902: /**
0903: * Store/Link 1:1 reference.
0904: *
0905: * @param obj real object the reference starts
0906: * @param rds {@link ObjectReferenceDescriptor} of the real object
0907: * @param insert flag for insert operation
0908: */
0909: private void storeAndLinkOneToOne(boolean onlyLink, Object obj,
0910: ClassDescriptor cld, ObjectReferenceDescriptor rds,
0911: boolean insert) {
0912: Object ref = rds.getPersistentField().get(obj);
0913: if (!onlyLink
0914: && rds.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT) {
0915: if (rds.isSuperReferenceDescriptor()) {
0916: ClassDescriptor super Cld = rds.getClassDescriptor()
0917: .getSuperClassDescriptor();
0918: Identity oid = serviceIdentity().buildIdentity(
0919: super Cld, ref);
0920: storeToDb(ref, super Cld, oid, insert);
0921: } else
0922: store(ref);
0923: }
0924: link(obj, cld, rds, ref, insert);
0925: }
0926:
0927: /**
0928: * Store/Link collections of objects poiting to <b>obj</b>.
0929: * More info please see comments in source.
0930: *
0931: * @param obj real object which we will store collections for
0932: * @throws PersistenceBrokerException if some goes wrong - please see the error message for details
0933: */
0934: private void storeCollections(Object obj, ClassDescriptor cld,
0935: boolean insert) throws PersistenceBrokerException {
0936: // get all members of obj that are collections and store all their elements
0937: Collection listCods = cld.getCollectionDescriptors();
0938: // return if nothing to do
0939: if (listCods.size() == 0) {
0940: return;
0941: }
0942: Iterator i = listCods.iterator();
0943: while (i.hasNext()) {
0944: CollectionDescriptor cod = (CollectionDescriptor) i.next();
0945:
0946: // if CASCADE_NONE was set, do nothing with referenced objects
0947: if (cod.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE) {
0948: Object referencedObjects = cod.getPersistentField()
0949: .get(obj);
0950: if (cod.isMtoNRelation()) {
0951: storeAndLinkMtoN(false, obj, cod,
0952: referencedObjects, insert);
0953: } else {
0954: storeAndLinkOneToMany(false, obj, cod,
0955: referencedObjects, insert);
0956: }
0957:
0958: // BRJ: only when auto-update = object (CASCADE_OBJECT)
0959: //
0960: if ((cod.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
0961: && (referencedObjects instanceof ManageableCollection)) {
0962: ((ManageableCollection) referencedObjects)
0963: .afterStore(this );
0964: }
0965: }
0966: }
0967: }
0968:
0969: /**
0970: * Store/Link m:n collection references.
0971: *
0972: * @param obj real object the reference starts
0973: * @param cod {@link CollectionDescriptor} of the real object
0974: * @param referencedObjects the referenced objects ({@link ManageableCollection} or Collection or Array) or null
0975: * @param insert flag for insert operation
0976: */
0977: private void storeAndLinkMtoN(boolean onlyLink, Object obj,
0978: CollectionDescriptor cod, Object referencedObjects,
0979: boolean insert) {
0980: /*
0981: - if the collection is a collectionproxy and it's not already loaded
0982: no need to perform an update on the referenced objects
0983: - on insert we link and insert the referenced objects, because the proxy
0984: collection maybe "inherited" from the object before the PK was replaced
0985: */
0986: if (insert
0987: || !(referencedObjects instanceof CollectionProxy && !((CollectionProxy) referencedObjects)
0988: .isLoaded())) {
0989: // if referenced objects are null, assign empty list
0990: if (referencedObjects == null) {
0991: referencedObjects = Collections.EMPTY_LIST;
0992: }
0993: /*
0994: NOTE: Take care of referenced objects, they could be of type Collection or
0995: an Array or of type ManageableCollection, thus it is not guaranteed that we
0996: can cast to Collection!!!
0997:
0998: if we store an object with m:n reference and no references could be
0999: found, we remove all entires of given object in indirection table
1000: */
1001: Iterator referencedObjectsIterator;
1002:
1003: if (!onlyLink
1004: && cod.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT) {
1005: referencedObjectsIterator = BrokerHelper
1006: .getCollectionIterator(referencedObjects);
1007: while (referencedObjectsIterator.hasNext()) {
1008: store(referencedObjectsIterator.next());
1009: }
1010: }
1011:
1012: Collection existingMtoNKeys;
1013: if (!insert) {
1014: existingMtoNKeys = mtoNBroker.getMtoNImplementor(cod,
1015: obj);
1016: // we can't reuse iterator
1017: referencedObjectsIterator = BrokerHelper
1018: .getCollectionIterator(referencedObjects);
1019: // remove all entries in indirection table which not be part of referenced objects
1020: mtoNBroker.deleteMtoNImplementor(cod, obj,
1021: referencedObjectsIterator, existingMtoNKeys);
1022: } else {
1023: existingMtoNKeys = Collections.EMPTY_LIST;
1024: }
1025: // we can't reuse iterator
1026: referencedObjectsIterator = BrokerHelper
1027: .getCollectionIterator(referencedObjects);
1028: while (referencedObjectsIterator.hasNext()) {
1029: Object refObj = referencedObjectsIterator.next();
1030: // Now store indirection record
1031: // BRJ: this could cause integrity problems because
1032: // obj may not be stored depending on auto-update
1033: mtoNBroker.storeMtoNImplementor(cod, obj, refObj,
1034: existingMtoNKeys);
1035: }
1036: }
1037: }
1038:
1039: /**
1040: * Store/Link 1:n collection references.
1041: *
1042: * @param obj real object the reference starts
1043: * @param linkOnly if true the referenced objects will only be linked (FK set, no reference store).
1044: * Reference store setting in descriptor will be ignored in this case
1045: * @param cod {@link CollectionDescriptor} of the real object
1046: * @param referencedObjects the referenced objects ({@link ManageableCollection} or Collection or Array) or null
1047: * @param insert flag for insert operation
1048: */
1049: private void storeAndLinkOneToMany(boolean linkOnly, Object obj,
1050: CollectionDescriptor cod, Object referencedObjects,
1051: boolean insert) {
1052: if (referencedObjects == null) {
1053: return;
1054: }
1055: /*
1056: Only make sense to perform (link or/and store) real referenced objects
1057: or materialized collection proxy objects, because on unmaterialized collection
1058: nothing has changed.
1059:
1060: - if the collection is a collectionproxy and it's not already loaded
1061: no need to perform an update on the referenced objects
1062: - on insert we link and insert the referenced objects, because the proxy
1063: collection maybe "inherited" from the object before the PK was replaced
1064: */
1065: if (insert
1066: || !(referencedObjects instanceof CollectionProxyDefaultImpl && !((CollectionProxyDefaultImpl) referencedObjects)
1067: .isLoaded())) {
1068: Iterator it = BrokerHelper
1069: .getCollectionIterator(referencedObjects);
1070: Object refObj;
1071: while (it.hasNext()) {
1072: refObj = it.next();
1073: /*
1074: TODO: Check this!
1075: arminw:
1076: When it's necessary to 'link' (set the FK) the 1:n reference objects?
1077: 1. set FK in refObj if it is materialized
1078: 2. if the referenced object is a proxy AND the main object needs insert
1079: we have to materialize the real object, because the user may move a collection
1080: of proxy objects from object A to new object B. In this case we have to replace the
1081: FK in the proxy object with new key of object B.
1082: */
1083: if (insert || getProxyFactory().isMaterialized(refObj)) {
1084: ClassDescriptor refCld = getClassDescriptor(getProxyFactory()
1085: .getRealClass(refObj));
1086: // get the real object before linking
1087: refObj = getProxyFactory().getRealObject(refObj);
1088: link(refObj, refCld, cod, obj, insert);
1089: // if enabled cascade store and not only link, store the refObj
1090: if (!linkOnly
1091: && cod.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT) {
1092: store(refObj);
1093: }
1094: }
1095: }
1096: }
1097: }
1098:
1099: /**
1100: * Assign FK value to target object by reading PK values of referenced object.
1101: *
1102: * @param targetObject real (non-proxy) target object
1103: * @param cld {@link ClassDescriptor} of the real target object
1104: * @param rds An {@link ObjectReferenceDescriptor} or {@link CollectionDescriptor}
1105: * associated with the real object.
1106: * @param referencedObject referenced object or proxy
1107: * @param insert Show if "linking" is done while insert or update.
1108: */
1109: public void link(Object targetObject, ClassDescriptor cld,
1110: ObjectReferenceDescriptor rds, Object referencedObject,
1111: boolean insert) {
1112: // MBAIRD: we have 'disassociated' this object from the referenced object,
1113: // the object represented by the reference descriptor is now null, so set
1114: // the fk in the target object to null.
1115: // arminw: if an insert was done and ref object was null, we should allow
1116: // to pass FK fields of main object (maybe only the FK fields are set)
1117: if (referencedObject == null) {
1118: /*
1119: arminw:
1120: if update we set FK fields to 'null', because reference was disassociated
1121: We do nothing on insert, maybe only the FK fields of main object (without
1122: materialization of the reference object) are set by the user
1123: */
1124: if (!insert) {
1125: unlinkFK(targetObject, cld, rds);
1126: }
1127: } else {
1128: setFKField(targetObject, cld, rds, referencedObject);
1129: }
1130: }
1131:
1132: /**
1133: * Unkink FK fields of target object.
1134: *
1135: * @param targetObject real (non-proxy) target object
1136: * @param cld {@link ClassDescriptor} of the real target object
1137: * @param rds An {@link ObjectReferenceDescriptor} or {@link CollectionDescriptor}
1138: * associated with the real object.
1139: */
1140: public void unlinkFK(Object targetObject, ClassDescriptor cld,
1141: ObjectReferenceDescriptor rds) {
1142: setFKField(targetObject, cld, rds, null);
1143: }
1144:
1145: /**
1146: * Set the FK value on the target object, extracted from the referenced object. If the referenced object was
1147: * <i>null</i> the FK values were set to <i>null</i>, expect when the FK field was declared as PK.
1148: *
1149: * @param targetObject real (non-proxy) target object
1150: * @param cld {@link ClassDescriptor} of the real target object
1151: * @param rds An {@link ObjectReferenceDescriptor} or {@link CollectionDescriptor}
1152: * @param referencedObject The referenced object or <i>null</i>
1153: */
1154: private void setFKField(Object targetObject, ClassDescriptor cld,
1155: ObjectReferenceDescriptor rds, Object referencedObject) {
1156: ValueContainer[] refPkValues;
1157: FieldDescriptor fld;
1158: FieldDescriptor[] objFkFields = rds
1159: .getForeignKeyFieldDescriptors(cld);
1160: if (objFkFields == null) {
1161: throw new PersistenceBrokerException(
1162: "No foreign key fields defined for class '"
1163: + cld.getClassNameOfObject() + "'");
1164: }
1165: if (referencedObject == null) {
1166: refPkValues = null;
1167: } else {
1168: Class refClass = proxyFactory
1169: .getRealClass(referencedObject);
1170: ClassDescriptor refCld = getClassDescriptor(refClass);
1171: refPkValues = brokerHelper.getKeyValues(refCld,
1172: referencedObject, false);
1173: }
1174: for (int i = 0; i < objFkFields.length; i++) {
1175: fld = objFkFields[i];
1176: /*
1177: arminw:
1178: we set the FK value when the extracted PK fields from the referenced object are not null at all
1179: or if null, the FK field was not a PK field of target object too.
1180: Should be ok, because the values of the extracted PK field values should never be null and never
1181: change, so it doesn't matter if the target field is a PK too.
1182: */
1183: if (refPkValues != null || !fld.isPrimaryKey()) {
1184: fld.getPersistentField().set(
1185: targetObject,
1186: refPkValues != null ? refPkValues[i].getValue()
1187: : null);
1188: }
1189: }
1190: }
1191:
1192: /**
1193: * Assign FK value of main object with PK values of the reference object.
1194: *
1195: * @param obj real object with reference (proxy) object (or real object with set FK values on insert)
1196: * @param cld {@link ClassDescriptor} of the real object
1197: * @param rds An {@link ObjectReferenceDescriptor} of real object.
1198: * @param insert Show if "linking" is done while insert or update.
1199: */
1200: public void linkOneToOne(Object obj, ClassDescriptor cld,
1201: ObjectReferenceDescriptor rds, boolean insert) {
1202: storeAndLinkOneToOne(true, obj, cld, rds, true);
1203: }
1204:
1205: /**
1206: * Assign FK value to all n-side objects referenced by given object.
1207: *
1208: * @param obj real object with 1:n reference
1209: * @param cod {@link CollectionDescriptor} of referenced 1:n objects
1210: * @param insert flag signal insert operation, false signals update operation
1211: */
1212: public void linkOneToMany(Object obj, CollectionDescriptor cod,
1213: boolean insert) {
1214: Object referencedObjects = cod.getPersistentField().get(obj);
1215: storeAndLinkOneToMany(true, obj, cod, referencedObjects, insert);
1216: }
1217:
1218: /**
1219: * Assign FK values and store entries in indirection table
1220: * for all objects referenced by given object.
1221: *
1222: * @param obj real object with 1:n reference
1223: * @param cod {@link CollectionDescriptor} of referenced 1:n objects
1224: * @param insert flag signal insert operation, false signals update operation
1225: */
1226: public void linkMtoN(Object obj, CollectionDescriptor cod,
1227: boolean insert) {
1228: Object referencedObjects = cod.getPersistentField().get(obj);
1229: storeAndLinkMtoN(true, obj, cod, referencedObjects, insert);
1230: }
1231:
1232: public void unlinkXtoN(Object obj, CollectionDescriptor col) {
1233: if (col.isMtoNRelation()) {
1234: // if this is a m:n mapped table, remove entries from indirection table
1235: mtoNBroker.deleteMtoNImplementor(col, obj);
1236: } else {
1237: Object collectionObject = col.getPersistentField().get(obj);
1238: if (collectionObject != null) {
1239: Iterator colIterator = BrokerHelper
1240: .getCollectionIterator(collectionObject);
1241: ClassDescriptor cld = null;
1242: while (colIterator.hasNext()) {
1243: Object target = colIterator.next();
1244: if (cld == null)
1245: cld = getClassDescriptor(getProxyFactory()
1246: .getRealClass(target));
1247: unlinkFK(target, cld, col);
1248: }
1249: }
1250: }
1251: }
1252:
1253: /**
1254: * Retrieve all References (also Collection-attributes) of a given instance.
1255: * Loading is forced, even if the collection- and reference-descriptors differ.
1256: * @param pInstance the persistent instance to work with
1257: */
1258: public void retrieveAllReferences(Object pInstance)
1259: throws PersistenceBrokerException {
1260: if (logger.isDebugEnabled()) {
1261: logger
1262: .debug("Manually retrieving all references for object "
1263: + serviceIdentity()
1264: .buildIdentity(pInstance));
1265: }
1266: ClassDescriptor cld = getClassDescriptor(pInstance.getClass());
1267: getInternalCache().enableMaterializationCache();
1268: // to avoid problems with circular references, locally cache the current object instance
1269: Identity oid = serviceIdentity().buildIdentity(pInstance);
1270: // boolean needLocalRemove = false;
1271: if (getInternalCache().doLocalLookup(oid) == null) {
1272: getInternalCache().doInternalCache(oid, pInstance,
1273: MaterializationCache.TYPE_TEMP);
1274: // needLocalRemove = true;
1275: }
1276: try {
1277: referencesBroker.retrieveReferences(pInstance, cld, true);
1278: referencesBroker.retrieveCollections(pInstance, cld, true);
1279: // arminw: should no longer needed since we use TYPE_TEMP for this kind of objects
1280: // // do locally remove the object to avoid problems with object state detection (insert/update),
1281: // // because objects found in the cache detected as 'old' means 'update'
1282: // if(needLocalRemove) getInternalCache().doLocalRemove(oid);
1283: getInternalCache().disableMaterializationCache();
1284: } catch (RuntimeException e) {
1285: getInternalCache().doLocalClear();
1286: throw e;
1287: }
1288: }
1289:
1290: /**
1291: * retrieve a single reference- or collection attribute
1292: * of a persistent instance.
1293: * @param pInstance the persistent instance
1294: * @param pAttributeName the name of the Attribute to load
1295: */
1296: public void retrieveReference(Object pInstance,
1297: String pAttributeName) throws PersistenceBrokerException {
1298: if (logger.isDebugEnabled()) {
1299: logger.debug("Retrieving reference named ["
1300: + pAttributeName + "] on object of type ["
1301: + pInstance.getClass().getName() + "]");
1302: }
1303: ClassDescriptor cld = getClassDescriptor(pInstance.getClass());
1304: CollectionDescriptor cod = cld
1305: .getCollectionDescriptorByName(pAttributeName);
1306: getInternalCache().enableMaterializationCache();
1307: // to avoid problems with circular references, locally cache the current object instance
1308: Identity oid = serviceIdentity().buildIdentity(pInstance);
1309: boolean needLocalRemove = false;
1310: if (getInternalCache().doLocalLookup(oid) == null) {
1311: getInternalCache().doInternalCache(oid, pInstance,
1312: MaterializationCache.TYPE_TEMP);
1313: needLocalRemove = true;
1314: }
1315: try {
1316: if (cod != null) {
1317: referencesBroker.retrieveCollection(pInstance, cld,
1318: cod, true);
1319: } else {
1320: ObjectReferenceDescriptor ord = cld
1321: .getObjectReferenceDescriptorByName(pAttributeName);
1322: if (ord != null) {
1323: referencesBroker.retrieveReference(pInstance, cld,
1324: ord, true);
1325: } else {
1326: throw new PersistenceBrokerException(
1327: "did not find attribute " + pAttributeName
1328: + " for class "
1329: + pInstance.getClass().getName());
1330: }
1331: }
1332: // do locally remove the object to avoid problems with object state detection (insert/update),
1333: // because objects found in the cache detected as 'old' means 'update'
1334: if (needLocalRemove)
1335: getInternalCache().doLocalRemove(oid);
1336: getInternalCache().disableMaterializationCache();
1337: } catch (RuntimeException e) {
1338: getInternalCache().doLocalClear();
1339: throw e;
1340: }
1341: }
1342:
1343: /**
1344: * Check if the references of the specified object have enabled
1345: * the <em>refresh</em> attribute and refresh the reference if set <em>true</em>.
1346: *
1347: * @throws PersistenceBrokerException if there is a error refreshing collections or references
1348: * @param obj The object to check.
1349: * @param oid The {@link Identity} of the object.
1350: * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the object.
1351: */
1352: public void checkRefreshRelationships(Object obj, Identity oid,
1353: ClassDescriptor cld) {
1354: Iterator iter;
1355: CollectionDescriptor cds;
1356: ObjectReferenceDescriptor rds;
1357: // to avoid problems with circular references, locally cache the current object instance
1358: Object tmp = getInternalCache().doLocalLookup(oid);
1359: if (tmp != null
1360: && getInternalCache().isEnabledMaterialisationCache()) {
1361: /*
1362: arminw: This should fix OJB-29, infinite loops on bidirectional 1:1 relations with
1363: refresh attribute 'true' for both references. OJB now assume that the object is already
1364: refreshed when it's cached in the materialisation cache
1365: */
1366: return;
1367: }
1368: try {
1369: getInternalCache().enableMaterializationCache();
1370: if (tmp == null) {
1371: getInternalCache().doInternalCache(oid, obj,
1372: MaterializationCache.TYPE_TEMP);
1373: }
1374: if (logger.isDebugEnabled())
1375: logger.debug("Refresh relationships for " + oid);
1376: iter = cld.getCollectionDescriptors().iterator();
1377: while (iter.hasNext()) {
1378: cds = (CollectionDescriptor) iter.next();
1379: if (cds.isRefresh()) {
1380: referencesBroker.retrieveCollection(obj, cld, cds,
1381: false);
1382: }
1383: }
1384: iter = cld.getObjectReferenceDescriptors().iterator();
1385: while (iter.hasNext()) {
1386: rds = (ObjectReferenceDescriptor) iter.next();
1387: if (rds.isRefresh()) {
1388: referencesBroker.retrieveReference(obj, cld, rds,
1389: false);
1390: }
1391: }
1392: getInternalCache().disableMaterializationCache();
1393: } catch (RuntimeException e) {
1394: getInternalCache().doLocalClear();
1395: throw e;
1396: }
1397: }
1398:
1399: /**
1400: * retrieve a collection of type collectionClass matching the Query query
1401: *
1402: * @see org.apache.ojb.broker.PersistenceBroker#getCollectionByQuery(Class, Query)
1403: */
1404: public ManageableCollection getCollectionByQuery(
1405: Class collectionClass, Query query)
1406: throws PersistenceBrokerException {
1407: return referencesBroker.getCollectionByQuery(collectionClass,
1408: query, false);
1409: }
1410:
1411: /**
1412: * retrieve a collection of itemClass Objects matching the Query query
1413: */
1414: public Collection getCollectionByQuery(Query query)
1415: throws PersistenceBrokerException {
1416: return referencesBroker.getCollectionByQuery(query, false);
1417: }
1418:
1419: /**
1420: * Retrieve an plain object (without populated references) by it's identity
1421: * from the database
1422: *
1423: * @param cld the real {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the object to refresh
1424: * @param oid the {@link org.apache.ojb.broker.Identity} of the object
1425: * @return A new plain object read from the database or <em>null</em> if not found
1426: * @throws ClassNotPersistenceCapableException
1427: */
1428: private Object getPlainDBObject(ClassDescriptor cld, Identity oid)
1429: throws ClassNotPersistenceCapableException {
1430: Object newObj = null;
1431:
1432: // Class is NOT an Interface: it has a directly mapped table and we lookup this table first:
1433: if (!cld.isInterface()) {
1434: // 1. try to retrieve skalar fields from directly mapped table columns
1435: newObj = dbAccess.materializeObject(cld, oid);
1436: }
1437:
1438: // if we did not find the object yet AND if the cld represents an Extent,
1439: // we can lookup all tables of the extent classes:
1440: if (newObj == null && cld.isExtent()) {
1441: Iterator extents = getDescriptorRepository()
1442: .getAllConcreteSubclassDescriptors(cld).iterator();
1443:
1444: while (extents.hasNext()) {
1445: ClassDescriptor extCld = (ClassDescriptor) extents
1446: .next();
1447:
1448: newObj = dbAccess.materializeObject(extCld, oid);
1449: if (newObj != null) {
1450: break;
1451: }
1452: }
1453: }
1454: return newObj;
1455: }
1456:
1457: /**
1458: * Retrieve an full materialized (dependent on the metadata settings)
1459: * object by it's identity from the database, as well as caching the
1460: * object
1461: *
1462: * @param oid The {@link org.apache.ojb.broker.Identity} of the object to for
1463: * @return A new object read from the database or <em>null</em> if not found
1464: * @throws ClassNotPersistenceCapableException
1465: */
1466: private Object getDBObject(Identity oid)
1467: throws ClassNotPersistenceCapableException {
1468: Class c = oid.getObjectsRealClass();
1469:
1470: if (c == null) {
1471: logger
1472: .info("Real class for used Identity object is 'null', use top-level class instead");
1473: c = oid.getObjectsTopLevelClass();
1474: }
1475:
1476: ClassDescriptor cld = getClassDescriptor(c);
1477: Object newObj = getPlainDBObject(cld, oid);
1478:
1479: // loading references is useful only when the Object could be found in db:
1480: if (newObj != null) {
1481: if (oid.getObjectsRealClass() == null) {
1482: oid.setObjectsRealClass(newObj.getClass());
1483: }
1484:
1485: /*
1486: * synchronize on newObj so the ODMG-layer can take a snapshot only of
1487: * fully cached (i.e. with all references + collections) objects
1488: */
1489: synchronized (newObj) {
1490: objectCache.enableMaterializationCache();
1491: try {
1492: // cache object immediately , so that references
1493: // can be established from referenced Objects back to this Object
1494: objectCache.doInternalCache(oid, newObj,
1495: ObjectCacheInternal.TYPE_NEW_MATERIALIZED);
1496:
1497: /*
1498: * Chris Lewington: can cause problems with multiple objects
1499: * mapped to one table, as follows:
1500: *
1501: * if the class searched on does not match the retrieved
1502: * class, eg a search on an OID retrieves a row but it could
1503: * be a different class (OJB gets all column values),
1504: * then trying to resolve references will fail as the object
1505: * will not match the Class Descriptor.
1506: *
1507: * To be safe, get the descriptor of the retrieved object
1508: * BEFORE resolving refs
1509: */
1510: ClassDescriptor newObjCld = getClassDescriptor(newObj
1511: .getClass());
1512: // don't force loading of references:
1513: final boolean unforced = false;
1514:
1515: // 2. retrieve non-skalar fields that contain objects retrievable from other tables
1516: referencesBroker.retrieveReferences(newObj,
1517: newObjCld, unforced);
1518: // 3. retrieve collection fields from foreign-key related tables:
1519: referencesBroker.retrieveCollections(newObj,
1520: newObjCld, unforced);
1521: objectCache.disableMaterializationCache();
1522: } catch (RuntimeException e) {
1523: objectCache.doLocalClear();
1524: throw e;
1525: }
1526: }
1527: }
1528:
1529: return newObj;
1530: }
1531:
1532: /**
1533: * returns an Iterator that iterates Objects of class c if calling the .next()
1534: * method. The Elements returned come from a SELECT ... WHERE Statement
1535: * that is defined by the Query query.
1536: * If itemProxy is null, no proxies are used.
1537: */
1538: public Iterator getIteratorByQuery(Query query)
1539: throws PersistenceBrokerException {
1540: Class itemClass = query.getSearchClass();
1541: ClassDescriptor cld = getClassDescriptor(itemClass);
1542: return getIteratorFromQuery(query, cld);
1543: }
1544:
1545: /**
1546: * Get an extent aware Iterator based on the Query
1547: *
1548: * @param query
1549: * @param cld the ClassDescriptor
1550: * @return OJBIterator
1551: */
1552: protected OJBIterator getIteratorFromQuery(Query query,
1553: ClassDescriptor cld) throws PersistenceBrokerException {
1554: RsIteratorFactory factory = RsIteratorFactoryImpl.getInstance();
1555: OJBIterator result = getRsIteratorFromQuery(query, cld, factory);
1556:
1557: if (query.usePaging()) {
1558: result = new PagingIterator(result,
1559: query.getStartAtIndex(), query.getEndAtIndex());
1560: }
1561: return result;
1562: }
1563:
1564: public Object getObjectByIdentity(Identity id)
1565: throws PersistenceBrokerException {
1566: objectCache.enableMaterializationCache();
1567: Object result = null;
1568: try {
1569: result = doGetObjectByIdentity(id);
1570: objectCache.disableMaterializationCache();
1571: } catch (RuntimeException e) {
1572: // catch runtime exc. to guarantee clearing of internal buffer on failure
1573: objectCache.doLocalClear();
1574: throw e;
1575: }
1576: return result;
1577: }
1578:
1579: /**
1580: * Internal used method to retrieve object based on Identity.
1581: *
1582: * @param id
1583: * @return
1584: * @throws PersistenceBrokerException
1585: */
1586: public Object doGetObjectByIdentity(Identity id)
1587: throws PersistenceBrokerException {
1588: if (logger.isDebugEnabled())
1589: logger.debug("getObjectByIdentity " + id);
1590:
1591: // check if object is present in ObjectCache:
1592: Object obj = objectCache.lookup(id);
1593: // only perform a db lookup if necessary (object not cached yet)
1594: if (obj == null) {
1595: obj = getDBObject(id);
1596: } else {
1597: ClassDescriptor cld = getClassDescriptor(obj.getClass());
1598: // if specified in the ClassDescriptor the instance must be refreshed
1599: if (cld.isAlwaysRefresh()) {
1600: refreshInstance(obj, id, cld);
1601: }
1602: // now refresh all references
1603: checkRefreshRelationships(obj, id, cld);
1604: }
1605:
1606: // Invoke events on PersistenceBrokerAware instances and listeners
1607: AFTER_LOOKUP_EVENT.setTarget(obj);
1608: fireBrokerEvent(AFTER_LOOKUP_EVENT);
1609: AFTER_LOOKUP_EVENT.setTarget(null);
1610:
1611: //logger.info("RETRIEVING object " + obj);
1612: return obj;
1613: }
1614:
1615: /**
1616: * refresh all primitive typed attributes of a cached instance
1617: * with the current values from the database.
1618: * refreshing of reference and collection attributes is not done
1619: * here.
1620: * @param cachedInstance the cached instance to be refreshed
1621: * @param oid the Identity of the cached instance
1622: * @param cld the ClassDescriptor of cachedInstance
1623: */
1624: private void refreshInstance(Object cachedInstance, Identity oid,
1625: ClassDescriptor cld) {
1626: // read in fresh copy from the db, but do not cache it
1627: Object freshInstance = getPlainDBObject(cld, oid);
1628:
1629: // update all primitive typed attributes
1630: FieldDescriptor[] fields = cld.getFieldDescriptions();
1631: FieldDescriptor fmd;
1632: PersistentField fld;
1633: for (int i = 0; i < fields.length; i++) {
1634: fmd = fields[i];
1635: fld = fmd.getPersistentField();
1636: fld.set(cachedInstance, fld.get(freshInstance));
1637: }
1638: }
1639:
1640: /**
1641: * retrieve an Object by query
1642: * I.e perform a SELECT ... FROM ... WHERE ... in an RDBMS
1643: */
1644: public Object getObjectByQuery(Query query)
1645: throws PersistenceBrokerException {
1646: Object result = null;
1647: if (query instanceof QueryByIdentity) {
1648: // example obj may be an entity or an Identity
1649: Object obj = query.getExampleObject();
1650: if (obj instanceof Identity) {
1651: Identity oid = (Identity) obj;
1652: result = getObjectByIdentity(oid);
1653: } else {
1654: // TODO: This workaround doesn't allow 'null' for PK fields
1655: if (!serviceBrokerHelper().hasNullPKField(
1656: getClassDescriptor(obj.getClass()), obj)) {
1657: Identity oid = serviceIdentity().buildIdentity(obj);
1658: result = getObjectByIdentity(oid);
1659: }
1660: }
1661: } else {
1662: Class itemClass = query.getSearchClass();
1663: ClassDescriptor cld = getClassDescriptor(itemClass);
1664: /*
1665: use OJB intern Iterator, thus we are able to close used
1666: resources instantly
1667: */
1668: OJBIterator it = getIteratorFromQuery(query, cld);
1669: /*
1670: arminw:
1671: patch by Andre Clute, instead of taking the first found result
1672: try to get the first found none null result.
1673: He wrote:
1674: I have a situation where an item with a certain criteria is in my
1675: database twice -- once deleted, and then a non-deleted version of it.
1676: When I do a PB.getObjectByQuery(), the RsIterator get's both results
1677: from the database, but the first row is the deleted row, so my RowReader
1678: filters it out, and do not get the right result.
1679: */
1680: try {
1681: while (result == null && it.hasNext()) {
1682: result = it.next();
1683: }
1684: } // make sure that we close the used resources
1685: finally {
1686: if (it != null)
1687: it.releaseDbResources();
1688: }
1689: }
1690: return result;
1691: }
1692:
1693: /**
1694: * returns an Enumeration of PrimaryKey Objects for objects of class DataClass.
1695: * The Elements returned come from a SELECT ... WHERE Statement
1696: * that is defined by the fields and their coresponding values of listFields
1697: * and listValues.
1698: * Useful for EJB Finder Methods...
1699: * @param primaryKeyClass the pk class for the searched objects
1700: * @param query the query
1701: */
1702: public Enumeration getPKEnumerationByQuery(Class primaryKeyClass,
1703: Query query) throws PersistenceBrokerException {
1704: if (logger.isDebugEnabled())
1705: logger.debug("getPKEnumerationByQuery " + query);
1706:
1707: query.setFetchSize(1);
1708: ClassDescriptor cld = getClassDescriptor(query.getSearchClass());
1709: return new PkEnumeration(query, cld, primaryKeyClass, this );
1710: }
1711:
1712: /**
1713: * Makes object obj persistent in the underlying persistence system.
1714: * E.G. by INSERT INTO ... or UPDATE ... in an RDBMS.
1715: * The ObjectModification parameter can be used to determine whether INSERT or update is to be used.
1716: * This functionality is typically called from transaction managers, that
1717: * track which objects have to be stored. If the object is an unmaterialized
1718: * proxy the method return immediately.
1719: */
1720: public void store(Object obj, ObjectModification mod)
1721: throws PersistenceBrokerException {
1722: obj = extractObjectToStore(obj);
1723: // null for unmaterialized Proxy
1724: if (obj == null) {
1725: return;
1726: }
1727:
1728: ClassDescriptor cld = getClassDescriptor(obj.getClass());
1729: // this call ensures that all autoincremented primary key attributes are filled
1730: Identity oid = serviceIdentity().buildIdentity(cld, obj);
1731: // select flag for insert / update selection by checking the ObjectModification
1732: if (mod.needsInsert()) {
1733: store(obj, oid, cld, true);
1734: } else if (mod.needsUpdate()) {
1735: store(obj, oid, cld, false);
1736: }
1737: /*
1738: arminw
1739: TODO: Why we need this behaviour? What about 1:1 relations?
1740: */
1741: else {
1742: // just store 1:n and m:n associations
1743: storeCollections(obj, cld, mod.needsInsert());
1744: }
1745: }
1746:
1747: /**
1748: * I pulled this out of internal store so that when doing multiple table
1749: * inheritance, i can recurse this function.
1750: *
1751: * @param obj
1752: * @param cld
1753: * @param oid BRJ: what is it good for ???
1754: * @param insert
1755: * @param ignoreReferences
1756: */
1757: private void storeToDb(Object obj, ClassDescriptor cld,
1758: Identity oid, boolean insert, boolean ignoreReferences) {
1759: // 1. link and store 1:1 references
1760: storeReferences(obj, cld, insert, ignoreReferences);
1761:
1762: Object[] pkValues = oid.getPrimaryKeyValues();
1763: if (!serviceBrokerHelper().assertValidPksForStore(
1764: cld.getPkFields(), pkValues)) {
1765: // BRJ: fk values may be part of pk, but the are not known during
1766: // creation of Identity. so we have to get them here
1767: pkValues = serviceBrokerHelper().getKeyValues(cld, obj);
1768: if (!serviceBrokerHelper().assertValidPksForStore(
1769: cld.getPkFields(), pkValues)) {
1770: String append = insert ? " on insert" : " on update";
1771: throw new PersistenceBrokerException(
1772: "assertValidPkFields failed for Object of type: "
1773: + cld.getClassNameOfObject() + append);
1774: }
1775: }
1776:
1777: // get super class cld then store it with the object
1778: /*
1779: now for multiple table inheritance
1780: 1. store super classes, topmost parent first
1781: 2. go down through heirarchy until current class
1782: 3. todo: store to full extent?
1783:
1784: // arminw: TODO: The extend-attribute feature dosn't work, should we remove this stuff?
1785: This if-clause will go up the inheritance heirarchy to store all the super classes.
1786: The id for the top most super class will be the id for all the subclasses too
1787: */
1788: if (cld.getSuperClass() != null) {
1789:
1790: ClassDescriptor super Cld = getDescriptorRepository()
1791: .getDescriptorFor(cld.getSuperClass());
1792: storeToDb(obj, super Cld, oid, insert);
1793: // arminw: why this?? I comment out this section
1794: // storeCollections(obj, cld.getCollectionDescriptors(), insert);
1795: }
1796:
1797: // 2. store primitive typed attributes (Or is THIS step 3 ?)
1798: // if obj not present in db use INSERT
1799: if (insert) {
1800: dbAccess.executeInsert(cld, obj);
1801: if (oid.isTransient()) {
1802: // Create a new Identity based on the current set of primary key values.
1803: oid = serviceIdentity().buildIdentity(cld, obj);
1804: }
1805: }
1806: // else use UPDATE
1807: else {
1808: try {
1809: dbAccess.executeUpdate(cld, obj);
1810: } catch (OptimisticLockException e) {
1811: // ensure that the outdated object be removed from cache
1812: objectCache.remove(oid);
1813: throw e;
1814: }
1815: }
1816: // cache object for symmetry with getObjectByXXX()
1817: // Add the object to the cache.
1818: objectCache.doInternalCache(oid, obj,
1819: ObjectCacheInternal.TYPE_WRITE);
1820: // 3. store 1:n and m:n associations
1821: if (!ignoreReferences)
1822: storeCollections(obj, cld, insert);
1823: }
1824:
1825: /**
1826: * I pulled this out of internal store so that when doing multiple table
1827: * inheritance, i can recurse this function.
1828: *
1829: * @param obj
1830: * @param cld
1831: * @param oid BRJ: what is it good for ???
1832: * @param insert
1833: */
1834: private void storeToDb(Object obj, ClassDescriptor cld,
1835: Identity oid, boolean insert) {
1836: storeToDb(obj, cld, oid, insert, false);
1837: }
1838:
1839: /**
1840: * returns true if the broker is currently running a transaction.
1841: * @return boolean
1842: */
1843: public boolean isInTransaction() {
1844: // return this.connectionManager.isInLocalTransaction();
1845: return inTransaction;
1846: }
1847:
1848: public void setInTransaction(boolean inTransaction) {
1849: this .inTransaction = inTransaction;
1850: }
1851:
1852: /**
1853: * @see org.apache.ojb.broker.PersistenceBroker#removeFromCache
1854: */
1855: public void removeFromCache(Object objectOrIdentity)
1856: throws PersistenceBrokerException {
1857: Identity identity;
1858: if (objectOrIdentity instanceof Identity) {
1859: identity = (Identity) objectOrIdentity;
1860: } else {
1861: identity = serviceIdentity()
1862: .buildIdentity(objectOrIdentity);
1863: }
1864: objectCache.remove(identity);
1865: }
1866:
1867: /**
1868: * returns a ClassDescriptor for the persistence capable class clazz.
1869: * throws a PersistenceBrokerException if clazz is not persistence capable,
1870: * i.e. if clazz is not defined in the DescriptorRepository.
1871: */
1872: public ClassDescriptor getClassDescriptor(Class clazz)
1873: throws PersistenceBrokerException {
1874: return descriptorRepository.getDescriptorFor(clazz);
1875: }
1876:
1877: public boolean hasClassDescriptor(Class clazz) {
1878: return descriptorRepository.hasDescriptorFor(clazz);
1879: }
1880:
1881: /**
1882: * clears the brokers internal cache.
1883: * removing is recursive. That is referenced Objects are also
1884: * removed from the cache, if the auto-retrieve flag is set
1885: * for obj.getClass() in the metadata repository.
1886: *
1887: */
1888: public void clearCache() throws PersistenceBrokerException {
1889: objectCache.clear();
1890: }
1891:
1892: /**
1893: * @see org.apache.ojb.broker.PersistenceBroker#getTopLevelClass
1894: */
1895: public Class getTopLevelClass(Class clazz)
1896: throws PersistenceBrokerException {
1897: try {
1898: return descriptorRepository.getTopLevelClass(clazz);
1899: } catch (ClassNotPersistenceCapableException e) {
1900: throw new PersistenceBrokerException(e);
1901: }
1902: }
1903:
1904: /**
1905: * @see org.apache.ojb.broker.PersistenceBroker#getCount(Query)
1906: */
1907: public int getCount(Query query) throws PersistenceBrokerException {
1908: Query countQuery = serviceBrokerHelper().getCountQuery(query);
1909: Iterator iter;
1910: int result = 0;
1911:
1912: if (logger.isDebugEnabled())
1913: logger.debug("getCount " + countQuery.getSearchClass()
1914: + ", " + countQuery);
1915:
1916: iter = getReportQueryIteratorByQuery(countQuery);
1917: try {
1918: while (iter.hasNext()) {
1919: Object[] row = (Object[]) iter.next();
1920: result += ((Number) row[0]).intValue();
1921: }
1922: } finally {
1923: if (iter instanceof OJBIterator) {
1924: ((OJBIterator) iter).releaseDbResources();
1925: }
1926: }
1927:
1928: return result;
1929: }
1930:
1931: /**
1932: * Get an Iterator based on the ReportQuery
1933: *
1934: * @param query
1935: * @return Iterator
1936: */
1937: public Iterator getReportQueryIteratorByQuery(Query query)
1938: throws PersistenceBrokerException {
1939: ClassDescriptor cld = getClassDescriptor(query.getSearchClass());
1940: return getReportQueryIteratorFromQuery(query, cld);
1941: }
1942:
1943: /**
1944: * Get an extent aware RsIterator based on the Query
1945: *
1946: * @param query
1947: * @param cld
1948: * @param factory the Factory for the RsIterator
1949: * @return OJBIterator
1950: */
1951: private OJBIterator getRsIteratorFromQuery(Query query,
1952: ClassDescriptor cld, RsIteratorFactory factory)
1953: throws PersistenceBrokerException {
1954: query.setFetchSize(1);
1955: if (query instanceof QueryBySQL) {
1956: if (logger.isDebugEnabled())
1957: logger.debug("Creating SQL-RsIterator for class ["
1958: + cld.getClassNameOfObject() + "]");
1959: return factory.createRsIterator((QueryBySQL) query, cld,
1960: this );
1961: }
1962:
1963: if (!cld.isExtent() || !query.getWithExtents()) {
1964: // no extents just use the plain vanilla RsIterator
1965: if (logger.isDebugEnabled())
1966: logger.debug("Creating RsIterator for class ["
1967: + cld.getClassNameOfObject() + "]");
1968:
1969: return factory.createRsIterator(query, cld, this );
1970: }
1971:
1972: if (logger.isDebugEnabled())
1973: logger.debug("Creating ChainingIterator for class ["
1974: + cld.getClassNameOfObject() + "]");
1975:
1976: ChainingIterator chainingIter = new ChainingIterator();
1977:
1978: // BRJ: add base class iterator
1979: if (!cld.isInterface()) {
1980: if (logger.isDebugEnabled())
1981: logger.debug("Adding RsIterator for class ["
1982: + cld.getClassNameOfObject()
1983: + "] to ChainingIterator");
1984:
1985: chainingIter.addIterator(factory.createRsIterator(query,
1986: cld, this ));
1987: }
1988:
1989: Iterator extents = getDescriptorRepository()
1990: .getAllConcreteSubclassDescriptors(cld).iterator();
1991: while (extents.hasNext()) {
1992: ClassDescriptor extCld = (ClassDescriptor) extents.next();
1993:
1994: // read same table only once
1995: if (chainingIter.containsIteratorForTable(extCld
1996: .getFullTableName())) {
1997: if (logger.isDebugEnabled())
1998: logger.debug("Skipping class ["
1999: + extCld.getClassNameOfObject() + "]");
2000: } else {
2001: if (logger.isDebugEnabled())
2002: logger.debug("Adding RsIterator of class ["
2003: + extCld.getClassNameOfObject()
2004: + "] to ChainingIterator");
2005:
2006: // add the iterator to the chaining iterator.
2007: chainingIter.addIterator(factory.createRsIterator(
2008: query, extCld, this ));
2009: }
2010: }
2011:
2012: return chainingIter;
2013: }
2014:
2015: /**
2016: * Get an extent aware Iterator based on the ReportQuery
2017: *
2018: * @param query
2019: * @param cld
2020: * @return OJBIterator
2021: */
2022: private OJBIterator getReportQueryIteratorFromQuery(Query query,
2023: ClassDescriptor cld) throws PersistenceBrokerException {
2024: RsIteratorFactory factory = ReportRsIteratorFactoryImpl
2025: .getInstance();
2026: OJBIterator result = getRsIteratorFromQuery(query, cld, factory);
2027:
2028: if (query.usePaging()) {
2029: result = new PagingIterator(result,
2030: query.getStartAtIndex(), query.getEndAtIndex());
2031: }
2032:
2033: return result;
2034: }
2035:
2036: /**
2037: * @see org.odbms.ObjectContainer#query()
2038: */
2039: public org.odbms.Query query() {
2040: return new org.apache.ojb.soda.QueryImpl(this );
2041: }
2042:
2043: /**
2044: * @return DescriptorRepository
2045: */
2046: public DescriptorRepository getDescriptorRepository() {
2047: return descriptorRepository;
2048: }
2049:
2050: protected void finalize() {
2051: if (!isClosed) {
2052: close();
2053: }
2054: }
2055:
2056: /**
2057: * clean up the maps for reuse by the next transaction.
2058: */
2059: private void clearRegistrationLists() {
2060: nowStoring.clear();
2061: objectCache.doLocalClear();
2062: deletedDuringTransaction.clear();
2063: /*
2064: arminw:
2065: for better performance I don't register MtoNBroker as listner,
2066: so use this method to reset on commit/rollback
2067: */
2068: mtoNBroker.reset();
2069: }
2070:
2071: /**
2072: * @see org.apache.ojb.broker.PersistenceBroker#deleteMtoNImplementor
2073: */
2074: public void deleteMtoNImplementor(MtoNImplementor m2nImpl)
2075: throws PersistenceBrokerException {
2076: mtoNBroker.deleteMtoNImplementor(m2nImpl);
2077: }
2078:
2079: /**
2080: * @see org.apache.ojb.broker.PersistenceBroker#addMtoNImplementor
2081: */
2082: public void addMtoNImplementor(MtoNImplementor m2n)
2083: throws PersistenceBrokerException {
2084: mtoNBroker.storeMtoNImplementor(m2n);
2085: }
2086:
2087: public ProxyFactory getProxyFactory() {
2088: return proxyFactory;
2089: }
2090:
2091: /**
2092: * Creates a proxy instance.
2093: *
2094: * @param baseClassForProxy The base class that the Proxy should extend. For dynamic Proxies, the method of
2095: * generation is dependent on the ProxyFactory implementation.
2096: * @param realSubjectsIdentity The identity of the subject
2097: * @return An instance of the proxy subclass
2098: * @throws PersistenceBrokerException If there is an error creating the proxy object
2099: */
2100: public Object createProxy(Class baseClassForProxy,
2101: Identity realSubjectsIdentity) {
2102: try {
2103: // the invocation handler manages all delegation stuff
2104: IndirectionHandler handler = getProxyFactory()
2105: .createIndirectionHandler(pbKey,
2106: realSubjectsIdentity);
2107:
2108: // the proxy simply provides the interface of the real subject
2109: if (VirtualProxy.class.isAssignableFrom(baseClassForProxy)) {
2110: Constructor constructor = baseClassForProxy
2111: .getDeclaredConstructor(new Class[] { IndirectionHandler.class });
2112: return constructor
2113: .newInstance(new Object[] { handler });
2114: } else {
2115: return getProxyFactory().createProxy(baseClassForProxy,
2116: handler);
2117: }
2118:
2119: } catch (Exception ex) {
2120: throw new PersistenceBrokerException(
2121: "Unable to create proxy using class:"
2122: + baseClassForProxy.getName(), ex);
2123: }
2124: }
2125:
2126: }
|