001 package org.apache.ojb.broker.core;
002
003 /* Copyright 2003-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 import java.lang.reflect.Constructor;
019 import java.util.Collection;
020 import java.util.Collections;
021 import java.util.Enumeration;
022 import java.util.HashSet;
023 import java.util.Iterator;
024 import java.util.List;
025 import java.util.Set;
026
027 import org.apache.commons.lang.ObjectUtils;
028 import org.apache.ojb.broker.Identity;
029 import org.apache.ojb.broker.IdentityFactory;
030 import org.apache.ojb.broker.ManageableCollection;
031 import org.apache.ojb.broker.MtoNImplementor;
032 import org.apache.ojb.broker.OptimisticLockException;
033 import org.apache.ojb.broker.PBKey;
034 import org.apache.ojb.broker.PBState;
035 import org.apache.ojb.broker.PersistenceBrokerException;
036 import org.apache.ojb.broker.TransactionAbortedException;
037 import org.apache.ojb.broker.TransactionInProgressException;
038 import org.apache.ojb.broker.TransactionNotInProgressException;
039 import org.apache.ojb.broker.accesslayer.ChainingIterator;
040 import org.apache.ojb.broker.accesslayer.ConnectionManagerFactory;
041 import org.apache.ojb.broker.accesslayer.ConnectionManagerIF;
042 import org.apache.ojb.broker.accesslayer.JdbcAccess;
043 import org.apache.ojb.broker.accesslayer.JdbcAccessFactory;
044 import org.apache.ojb.broker.accesslayer.OJBIterator;
045 import org.apache.ojb.broker.accesslayer.PagingIterator;
046 import org.apache.ojb.broker.accesslayer.PkEnumeration;
047 import org.apache.ojb.broker.accesslayer.RelationshipPrefetcherFactory;
048 import org.apache.ojb.broker.accesslayer.StatementManagerFactory;
049 import org.apache.ojb.broker.accesslayer.StatementManagerIF;
050 import org.apache.ojb.broker.accesslayer.sql.SqlGenerator;
051 import org.apache.ojb.broker.accesslayer.sql.SqlGeneratorFactory;
052 import org.apache.ojb.broker.cache.MaterializationCache;
053 import org.apache.ojb.broker.cache.ObjectCache;
054 import org.apache.ojb.broker.cache.ObjectCacheFactory;
055 import org.apache.ojb.broker.cache.ObjectCacheInternal;
056 import org.apache.ojb.broker.core.proxy.AbstractProxyFactory;
057 import org.apache.ojb.broker.core.proxy.CollectionProxy;
058 import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
059 import org.apache.ojb.broker.core.proxy.IndirectionHandler;
060 import org.apache.ojb.broker.core.proxy.ProxyFactory;
061 import org.apache.ojb.broker.core.proxy.VirtualProxy;
062 import org.apache.ojb.broker.metadata.ClassDescriptor;
063 import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
064 import org.apache.ojb.broker.metadata.CollectionDescriptor;
065 import org.apache.ojb.broker.metadata.DescriptorRepository;
066 import org.apache.ojb.broker.metadata.FieldDescriptor;
067 import org.apache.ojb.broker.metadata.MetadataManager;
068 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
069 import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
070 import org.apache.ojb.broker.query.Query;
071 import org.apache.ojb.broker.query.QueryByIdentity;
072 import org.apache.ojb.broker.query.QueryBySQL;
073 import org.apache.ojb.broker.util.BrokerHelper;
074 import org.apache.ojb.broker.util.IdentityArrayList;
075 import org.apache.ojb.broker.util.ObjectModification;
076 import org.apache.ojb.broker.util.logging.Logger;
077 import org.apache.ojb.broker.util.logging.LoggerFactory;
078 import org.apache.ojb.broker.util.sequence.SequenceManager;
079 import org.apache.ojb.broker.util.sequence.SequenceManagerFactory;
080
081 /**
082 * The PersistenceBrokerImpl is an implementation of the PersistenceBroker
083 * Interface that specifies a persistence mechanism for Java objects.
084 * This Concrete implementation provides an object relational mapping
085 * and allows to store and retrieve arbitrary objects in/from relational
086 * databases accessed by JDBC.
087 *
088 * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
089 * @author <a href="mailto:leandro@ibnetwork.com.br">Leandro Rodrigo Saad Cruz<a>
090 * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird<a>
091 * @author <a href="mailto:jbraeuchi@gmx.ch">Jakob Braeuchi</a>
092 *
093 * @version $Id: PersistenceBrokerImpl.java,v 1.4 2008-07-08 15:53:48 sfheise Exp $
094 */
095 public class PersistenceBrokerImpl extends PersistenceBrokerAbstractImpl implements PBState
096 {
097 private Logger logger = LoggerFactory.getLogger(PersistenceBrokerImpl.class);
098
099 protected PersistenceBrokerFactoryIF pbf;
100 protected BrokerHelper brokerHelper;
101 protected MtoNBroker mtoNBroker;
102 protected QueryReferenceBroker referencesBroker;
103
104 /**
105 * signs if this broker was closed
106 */
107 private boolean isClosed;
108 /**
109 * Reflects the transaction status of this instance.
110 */
111 private boolean inTransaction;
112 /**
113 * Flag indicate that this PB instance is handled
114 * in a managed environment.
115 */
116 private boolean managed;
117 private MaterializationCache objectCache;
118 /**
119 * m_DbAccess is used to do all Jdbc related work: connecting, executing...
120 */
121 private JdbcAccess dbAccess;
122 /**
123 * holds mapping information for all classes to be treated by PersistenceBroker
124 */
125 private DescriptorRepository descriptorRepository = null;
126 private ConnectionManagerIF connectionManager = null;
127 private SequenceManager sequenceManager = null;
128 private StatementManagerIF statementManager = null;
129 private SqlGenerator sqlGenerator;
130 private IdentityFactory identityFactory;
131 private RelationshipPrefetcherFactory relationshipPrefetcherFactory;
132 private ProxyFactory proxyFactory;
133 private PBKey pbKey;
134
135 /**
136 * List of objects being stored now, allows to avoid infinite
137 * recursion storeCollections -> storeReferences -> storeCollections...
138 *
139 */
140 /*
141 we use an object identity based List to compare objects to prevent problems
142 with user implemented equals/hashCode methods of persistence capable objects
143 (e.g. objects are equals but PK fields not)
144 */
145 private List nowStoring = new IdentityArrayList();
146
147 /**
148 * Lists for object registration during delete operations.
149 * We reuse these list to avoid excessive object creation.
150 * @see #clearRegistrationLists
151 */
152 /*
153 arminw: list was cleared before delete method end. Internal we only
154 call doDelete(...) method. Same procedure as 'nowStoring'
155
156 we use an object identity based List to compare objects to prevent problems
157 with user implemented equals/hashCode methods of persistence capable objects
158 (e.g. objects are equals but PK fields not)
159 */
160 private List markedForDelete = new IdentityArrayList();
161
162 /**
163 * The set of identities of all deleted objects during current transaction
164 */
165 /*
166 olegnitz: this is the only way I know that solves the following problem
167 of batch mode: if one does store() after delete() for the same OID,
168 the broker checks whether the given OID exists in database to decide
169 which action to do: INSERT or UPDATE. If the preceding DELETE statement is
170 still in batch (not executed yet), then the OID exists in database so
171 the broker does UPDATE. Due the the following set of deleted OIDs
172 the broker will know that it should do INSERT.
173 */
174 private Set deletedDuringTransaction = new HashSet();
175
176 /**
177 * Constructor used by {@link PersistenceBrokerFactoryIF} implementation.
178 */
179 public PersistenceBrokerImpl(PBKey key, PersistenceBrokerFactoryIF pbf)
180 {
181 refresh();
182 if(key == null) throw new PersistenceBrokerException("Could not instantiate broker with PBKey 'null'");
183 this.pbf = pbf;
184 this.pbKey = key;
185 /*
186 be careful when changing initializing order
187 */
188 brokerHelper = new BrokerHelper(this);
189 connectionManager = ConnectionManagerFactory.getInstance().createConnectionManager(this);
190 /*
191 TODO: find better solution
192 MaterializationCache is a interim solution help to solve the problem of not full
193 materialized object reads by concurrent threads and will be replaced when
194 the new real two-level cache was introduced
195 */
196 objectCache = ObjectCacheFactory.getInstance().createObjectCache(this);
197 sequenceManager = SequenceManagerFactory.getSequenceManager(this);
198 dbAccess = JdbcAccessFactory.getInstance().createJdbcAccess(this);
199 statementManager = StatementManagerFactory.getInstance().createStatementManager(this);
200 sqlGenerator = SqlGeneratorFactory.getInstance().createSqlGenerator(
201 connectionManager.getSupportedPlatform());
202 mtoNBroker = new MtoNBroker(this);
203 referencesBroker = new QueryReferenceBroker(this);
204 identityFactory = new IdentityFactoryImpl(this);
205 relationshipPrefetcherFactory = new RelationshipPrefetcherFactory(this);
206 proxyFactory = AbstractProxyFactory.getProxyFactory();
207 }
208
209 public MaterializationCache getInternalCache()
210 {
211 return objectCache;
212 }
213
214 public IdentityFactory serviceIdentity()
215 {
216 return this.identityFactory;
217 }
218
219 public SqlGenerator serviceSqlGenerator()
220 {
221 return this.sqlGenerator;
222 }
223
224 public StatementManagerIF serviceStatementManager()
225 {
226 return statementManager;
227 }
228
229 public JdbcAccess serviceJdbcAccess()
230 {
231 return dbAccess;
232 }
233
234 public ConnectionManagerIF serviceConnectionManager()
235 {
236 return connectionManager;
237 }
238
239 public SequenceManager serviceSequenceManager()
240 {
241 return this.sequenceManager;
242 }
243
244 public BrokerHelper serviceBrokerHelper()
245 {
246 return this.brokerHelper;
247 }
248
249 public ObjectCache serviceObjectCache()
250 {
251 return this.objectCache;
252 }
253
254 public QueryReferenceBroker getReferenceBroker()
255 {
256 return this.referencesBroker;
257 }
258
259 public RelationshipPrefetcherFactory getRelationshipPrefetcherFactory()
260 {
261 return relationshipPrefetcherFactory;
262 }
263
264 public boolean isClosed()
265 {
266 return this.isClosed;
267 }
268
269 public void setClosed(boolean closed)
270 {
271 // When lookup the PB instance from pool method setClosed(false)
272 // was called before returning instance from pool, in this case
273 // OJB have to refresh the instance.
274 if(!closed)
275 {
276 refresh();
277 }
278 this.isClosed = closed;
279 }
280
281 /**
282 * If <em>true</em> this instance is handled by a managed
283 * environment - registered within a JTA transaction.
284 */
285 public boolean isManaged()
286 {
287 return managed;
288 }
289
290 /**
291 * Set <em>true</em> if this instance is registered within a
292 * JTA transaction. On {@link #close()} call this flag was reset
293 * to <em>false</em> automatic.
294 */
295 public void setManaged(boolean managed)
296 {
297 this.managed = managed;
298 }
299
300 /**
301 * Lookup the current {@link DescriptorRepository} for
302 * this class. This method is responsible to keep this
303 * PB instance in sync with {@link MetadataManager}.
304 */
305 public void refresh()
306 {
307 // guarantee that refreshed use initial status
308 setInTransaction(false);
309 this.descriptorRepository = MetadataManager.getInstance().getRepository();
310 }
311
312 /**
313 * Release all resources used by this
314 * class - CAUTION: No further operations can be
315 * done with this instance after calling this method.
316 */
317 public void destroy()
318 {
319 removeAllListeners();
320 if (connectionManager != null)
321 {
322 if(connectionManager.isInLocalTransaction())
323 {
324 connectionManager.localRollback();
325 }
326 connectionManager.releaseConnection();
327 }
328 this.setClosed(true);
329
330 this.descriptorRepository = null;
331 this.pbKey = null;
332 this.pbf = null;
333 this.connectionManager = null;
334 this.dbAccess = null;
335 this.objectCache = null;
336 this.sequenceManager = null;
337 this.sqlGenerator = null;
338 this.statementManager = null;
339 }
340
341 public PBKey getPBKey()
342 {
343 return pbKey;
344 }
345
346 public void setPBKey(PBKey key)
347 {
348 this.pbKey = key;
349 }
350
351 /**
352 * @see org.apache.ojb.broker.PersistenceBroker#close()
353 */
354 public boolean close()
355 {
356 /**
357 * MBAIRD: if we call close on a broker that is in a transaction,
358 * we should just abort whatever it's doing.
359 */
360 if (isInTransaction())
361 {
362 logger.error("Broker is still in PB-transaction, do automatic abort before close!");
363 abortTransaction();
364 }
365 if (logger.isDebugEnabled())
366 {
367 logger.debug("PB.close was called: " + this);
368 }
369 try
370 {
371 fireBrokerEvent(BEFORE_CLOSE_EVENT);
372 clearRegistrationLists();
373 referencesBroker.removePrefetchingListeners();
374 if (connectionManager != null)
375 {
376 connectionManager.releaseConnection();
377 /*
378 arminw:
379 set batch mode explicit to 'false'. Using
380
381 connectionManager.setBatchMode(
382 connectionManager.getConnectionDescriptor().getBatchMode());
383
384 cause many unexpected junit failures/errors when running
385 test suite with batch-mode 'true' setting.
386 */
387 connectionManager.setBatchMode(false);
388 }
389 }
390 finally
391 {
392 // reset flag indicating use in managed environment
393 setManaged(false);
394 // free current used DescriptorRepository reference
395 descriptorRepository = null;
396 removeAllListeners();
397 this.setClosed(true);
398 }
399 return true;
400 }
401
402 /**
403 * Abort and close the transaction.
404 * Calling abort abandons all persistent object modifications and releases the
405 * associated locks.
406 * If transaction is not in progress a TransactionNotInProgressException is thrown
407 */
408 public synchronized void abortTransaction() throws TransactionNotInProgressException
409 {
410 if(isInTransaction())
411 {
412 fireBrokerEvent(BEFORE_ROLLBACK_EVENT);
413 setInTransaction(false);
414 clearRegistrationLists();
415 referencesBroker.removePrefetchingListeners();
416 /*
417 arminw:
418 check if we in local tx, before do local rollback
419 Necessary, because ConnectionManager may do a rollback by itself
420 or in managed environments the used connection is already be closed
421 */
422 if(connectionManager.isInLocalTransaction()) this.connectionManager.localRollback();
423 fireBrokerEvent(AFTER_ROLLBACK_EVENT);
424 }
425 }
426
427 /**
428 * begin a transaction against the underlying RDBMS.
429 * Calling <code>beginTransaction</code> multiple times,
430 * without an intervening call to <code>commitTransaction</code> or <code>abortTransaction</code>,
431 * causes the exception <code>TransactionInProgressException</code> to be thrown
432 * on the second and subsequent calls.
433 */
434 public synchronized void beginTransaction() throws TransactionInProgressException, TransactionAbortedException
435 {
436 if (isInTransaction())
437 {
438 throw new TransactionInProgressException("PersistenceBroker is already in transaction");
439 }
440 fireBrokerEvent(BEFORE_BEGIN_EVENT);
441 setInTransaction(true);
442 this.connectionManager.localBegin();
443 fireBrokerEvent(AFTER_BEGIN_EVENT);
444 }
445
446 /**
447 * Commit and close the transaction.
448 * Calling <code>commit</code> commits to the database all
449 * UPDATE, INSERT and DELETE statements called within the transaction and
450 * releases any locks held by the transaction.
451 * If beginTransaction() has not been called before a
452 * TransactionNotInProgressException exception is thrown.
453 * If the transaction cannot be commited a TransactionAbortedException exception is thrown.
454 */
455 public synchronized void commitTransaction() throws TransactionNotInProgressException, TransactionAbortedException
456 {
457 if (!isInTransaction())
458 {
459 throw new TransactionNotInProgressException("PersistenceBroker is NOT in transaction, can't commit");
460 }
461 fireBrokerEvent(BEFORE_COMMIT_EVENT);
462 setInTransaction(false);
463 clearRegistrationLists();
464 referencesBroker.removePrefetchingListeners();
465 /*
466 arminw:
467 In managed environments it should be possible to close a used connection before
468 the tx was commited, thus it will be possible that the PB instance is in PB-tx, but
469 the connection is already closed. To avoid problems check if CM is in local tx before
470 do the CM.commit call
471 */
472 if(connectionManager.isInLocalTransaction())
473 {
474 this.connectionManager.localCommit();
475 }
476 fireBrokerEvent(AFTER_COMMIT_EVENT);
477 }
478
479 /**
480 * Deletes the concrete representation of the specified object in the underlying
481 * persistence system. This method is intended for use in top-level api or
482 * by internal calls.
483 *
484 * @param obj The object to delete.
485 * @param ignoreReferences With this flag the automatic deletion/unlinking
486 * of references can be suppressed (independent of the used auto-delete setting in metadata),
487 * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
488 * these kind of reference (descriptor) will always be performed. If <em>true</em>
489 * all "normal" referenced objects will be ignored, only the specified object is handled.
490 * @throws PersistenceBrokerException
491 */
492 public void delete(Object obj, boolean ignoreReferences) throws PersistenceBrokerException
493 {
494 if(isTxCheck() && !isInTransaction())
495 {
496 if(logger.isEnabledFor(Logger.ERROR))
497 {
498 String msg = "No running PB-tx found. Please, only delete objects in context of a PB-transaction" +
499 " to avoid side-effects - e.g. when rollback of complex objects.";
500 try
501 {
502 throw new Exception("** Delete object without active PersistenceBroker transaction **");
503 }
504 catch(Exception e)
505 {
506 logger.error(msg, e);
507 }
508 }
509 }
510 try
511 {
512 doDelete(obj, ignoreReferences);
513 }
514 finally
515 {
516 markedForDelete.clear();
517 }
518 }
519
520 /**
521 * @see org.apache.ojb.broker.PersistenceBroker#delete
522 */
523 public void delete(Object obj) throws PersistenceBrokerException
524 {
525 delete(obj, false);
526 }
527
528 /**
529 * do delete given object. Should be used by all intern classes to delete
530 * objects.
531 */
532 private void doDelete(Object obj, boolean ignoreReferences) throws PersistenceBrokerException
533 {
534 //logger.info("DELETING " + obj);
535 // object is not null
536 if (obj != null)
537 {
538 obj = getProxyFactory().getRealObject(obj);
539 /**
540 * Kuali Foundation modification -- 8/24/2007
541 */
542 if ( obj == null ) return;
543 /**
544 * End of Kuali Foundation modification
545 */
546 /**
547 * MBAIRD
548 * 1. if we are marked for delete already, avoid recursing on this object
549 *
550 * arminw:
551 * use object instead Identity object in markedForDelete List,
552 * because using objects we get a better performance. I can't find
553 * side-effects in doing so.
554 */
555 if (markedForDelete.contains(obj))
556 {
557 return;
558 }
559
560 ClassDescriptor cld = getClassDescriptor(obj.getClass());
561 //BRJ: check for valid pk
562 if (!serviceBrokerHelper().assertValidPkForDelete(cld, obj))
563 {
564 String msg = "Cannot delete object without valid PKs. " + obj;
565 logger.error(msg);
566 return;
567 }
568
569 /**
570 * MBAIRD
571 * 2. register object in markedForDelete map.
572 */
573 markedForDelete.add(obj);
574 Identity oid = serviceIdentity().buildIdentity(cld, obj);
575
576 // Invoke events on PersistenceBrokerAware instances and listeners
577 BEFORE_DELETE_EVENT.setTarget(obj);
578 fireBrokerEvent(BEFORE_DELETE_EVENT);
579 BEFORE_DELETE_EVENT.setTarget(null);
580
581 // now perform deletion
582 performDeletion(cld, obj, oid, ignoreReferences);
583
584 // Invoke events on PersistenceBrokerAware instances and listeners
585 AFTER_DELETE_EVENT.setTarget(obj);
586 fireBrokerEvent(AFTER_DELETE_EVENT);
587 AFTER_DELETE_EVENT.setTarget(null);
588
589 // let the connection manager to execute batch
590 connectionManager.executeBatchIfNecessary();
591 }
592 }
593
594 /**
595 * This method perform the delete of the specified object
596 * based on the {@link org.apache.ojb.broker.metadata.ClassDescriptor}.
597 */
598 private void performDeletion(final ClassDescriptor cld, final Object obj, final Identity oid, final boolean ignoreReferences) throws PersistenceBrokerException
599 {
600 // 1. delete dependend collections
601 if (!ignoreReferences && cld.getCollectionDescriptors().size() > 0)
602 {
603 deleteCollections(obj, cld.getCollectionDescriptors());
604 }
605 // 2. delete object from directly mapped table
606 try
607 {
608 dbAccess.executeDelete(cld, obj); // use obj not oid to delete, BRJ
609 }
610 catch(OptimisticLockException e)
611 {
612 // ensure that the outdated object be removed from cache
613 objectCache.remove(oid);
614 throw e;
615 }
616
617 // 3. Add OID to the set of deleted objects
618 deletedDuringTransaction.add(oid);
619
620 // 4. delete dependend upon objects last to avoid FK violations
621 if (cld.getObjectReferenceDescriptors().size() > 0)
622 {
623 deleteReferences(cld, obj, oid, ignoreReferences);
624 }
625 // remove obj from the object cache:
626 objectCache.remove(oid);
627 }
628
629 /**
630 * Extent aware Delete by Query
631 * @param query
632 * @param cld
633 * @throws PersistenceBrokerException
634 */
635 private void deleteByQuery(Query query, ClassDescriptor cld) throws PersistenceBrokerException
636 {
637 if (logger.isDebugEnabled())
638 {
639 logger.debug("deleteByQuery " + cld.getClassNameOfObject() + ", " + query);
640 }
641
642 if (query instanceof QueryBySQL)
643 {
644 String sql = ((QueryBySQL) query).getSql();
645 this.dbAccess.executeUpdateSQL(sql, cld);
646 }
647 else
648 {
649 // if query is Identity based transform it to a criteria based query first
650 if (query instanceof QueryByIdentity)
651 {
652 QueryByIdentity qbi = (QueryByIdentity) query;
653 Object oid = qbi.getExampleObject();
654 // make sure it's an Identity
655 if (!(oid instanceof Identity))
656 {
657 oid = serviceIdentity().buildIdentity(oid);
658 }
659 query = referencesBroker.getPKQuery((Identity) oid);
660 }
661
662 if (!cld.isInterface())
663 {
664 this.dbAccess.executeDelete(query, cld);
665 }
666
667 // if class is an extent, we have to delete all extent classes too
668 String lastUsedTable = cld.getFullTableName();
669 if (cld.isExtent())
670 {
671 Iterator extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator();
672
673 while (extents.hasNext())
674 {
675 ClassDescriptor extCld = (ClassDescriptor) extents.next();
676
677 // read same table only once
678 if (!extCld.getFullTableName().equals(lastUsedTable))
679 {
680 lastUsedTable = extCld.getFullTableName();
681 this.dbAccess.executeDelete(query, extCld);
682 }
683 }
684 }
685
686 }
687 }
688
689 /**
690 * @see org.apache.ojb.broker.PersistenceBroker#deleteByQuery(Query)
691 */
692 public void deleteByQuery(Query query) throws PersistenceBrokerException
693 {
694 ClassDescriptor cld = getClassDescriptor(query.getSearchClass());
695 deleteByQuery(query, cld);
696 }
697
698 /**
699 * Deletes references that <b>obj</b> points to.
700 * All objects which we have a FK poiting to (Via ReferenceDescriptors)
701 * will be deleted if auto-delete is true <b>AND</b>
702 * the member field containing the object reference is NOT null.
703 *
704 * @param obj Object which we will delete references for
705 * @param listRds list of ObjectRederenceDescriptors
706 * @param ignoreReferences With this flag the automatic deletion/unlinking
707 * of references can be suppressed (independent of the used auto-delete setting in metadata),
708 * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
709 * these kind of reference (descriptor) will always be performed.
710 * @throws PersistenceBrokerException if some goes wrong - please see the error message for details
711 */
712 private void deleteReferences(ClassDescriptor cld, Object obj, Identity oid, boolean ignoreReferences) throws PersistenceBrokerException
713 {
714 List listRds = cld.getObjectReferenceDescriptors();
715 // get all members of obj that are references and delete them
716 Iterator i = listRds.iterator();
717 while (i.hasNext())
718 {
719 ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i.next();
720 if ((!ignoreReferences && rds.getCascadingDelete() == ObjectReferenceDescriptor.CASCADE_OBJECT)
721 || rds.isSuperReferenceDescriptor())
722 {
723 Object referencedObject = rds.getPersistentField().get(obj);
724 if (referencedObject != null)
725 {
726 if(rds.isSuperReferenceDescriptor())
727 {
728 ClassDescriptor base = cld.getSuperClassDescriptor();
729 /*
730 arminw: If "table-per-subclass" inheritance is used we have to
731 guarantee that all super-class table entries are deleted too.
732 Thus we have to perform the recursive deletion of all super-class
733 table entries.
734 */
735 performDeletion(base, referencedObject, oid, ignoreReferences);
736 }
737 else
738 {
739 doDelete(referencedObject, ignoreReferences);
740 }
741 }
742 }
743 }
744 }
745
746 /**
747 * Deletes collections of objects poiting to <b>obj</b>.
748 * All object which have a FK poiting to this object (Via CollectionDescriptors)
749 * will be deleted if auto-delete is true <b>AND</b>
750 * the member field containing the object reference if NOT null.
751 *
752 * @param obj Object which we will delete collections for
753 * @param listCds list of ObjectReferenceDescriptors
754 * @throws PersistenceBrokerException if some goes wrong - please see the error message for details
755 */
756 private void deleteCollections(Object obj, List listCds) throws PersistenceBrokerException
757 {
758 // get all members of obj that are collections and delete all their elements
759 Iterator i = listCds.iterator();
760
761 while (i.hasNext())
762 {
763 CollectionDescriptor cds = (CollectionDescriptor) i.next();
764 if(cds.getCascadingDelete() != ObjectReferenceDescriptor.CASCADE_NONE)
765 {
766 if(cds.isMtoNRelation())
767 {
768 // if this is a m:n mapped table, remove entries from indirection table
769 mtoNBroker.deleteMtoNImplementor(cds, obj);
770 }
771 /*
772 if cascading delete is on, delete all referenced objects.
773 NOTE: User has to take care to populate all referenced objects before delete
774 the main object to avoid referential constraint violation
775 */
776 if (cds.getCascadingDelete() == ObjectReferenceDescriptor.CASCADE_OBJECT)
777 {
778 Object col = cds.getPersistentField().get(obj);
779 if (col != null)
780 {
781 Iterator colIterator = BrokerHelper.getCollectionIterator(col);
782 while (colIterator.hasNext())
783 {
784 doDelete(colIterator.next(), false);
785 }
786 }
787 }
788 }
789 }
790 }
791
792 /**
793 * Store an Object.
794 * @see org.apache.ojb.broker.PersistenceBroker#store(Object)
795 */
796 public void store(Object obj) throws PersistenceBrokerException
797 {
798 obj = extractObjectToStore(obj);
799 // only do something if obj != null
800 if(obj == null) return;
801
802 ClassDescriptor cld = getClassDescriptor(obj.getClass());
803 /*
804 if one of the PK fields was null, we assume the objects
805 was new and needs insert
806 */
807 boolean insert = serviceBrokerHelper().hasNullPKField(cld, obj);
808 Identity oid = serviceIdentity().buildIdentity(cld, obj);
809 /*
810 if PK values are set, lookup cache or db to see whether object
811 needs insert or update
812 */
813 if (!insert)
814 {
815 insert = objectCache.lookup(oid) == null
816 && !serviceBrokerHelper().doesExist(cld, oid, obj);
817 }
818 store(obj, oid, cld, insert);
819 }
820
821 /**
822 * Check if the given object is <code>null</code> or an unmaterialized proxy object - in
823 * both cases <code>null</code> will be returned, else the given object itself or the
824 * materialized proxy object will be returned.
825 */
826 private Object extractObjectToStore(Object obj)
827 {
828 Object result = obj;
829 // only do something if obj != null
830 if(result != null)
831 {
832 // ProxyObjects only have to be updated if their real
833 // subjects have been loaded
834 result = getProxyFactory().getRealObjectIfMaterialized(obj);
835 // null for unmaterialized Proxy
836 if (result == null)
837 {
838 if(logger.isDebugEnabled())
839 logger.debug("No materialized object could be found -> nothing to store," +
840 " object was " + ObjectUtils.identityToString(obj));
841 }
842 }
843 return result;
844 }
845
846 /**
847 * Method which start the real store work (insert or update)
848 * and is intended for use by top-level api or internal calls.
849 *
850 * @param obj The object to store.
851 * @param oid The {@link Identity} of the object to store.
852 * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the object.
853 * @param insert If <em>true</em> an insert operation will be performed, else update operation.
854 * @param ignoreReferences With this flag the automatic storing/linking
855 * of references can be suppressed (independent of the used auto-update setting in metadata),
856 * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
857 * these kind of reference (descriptor) will always be performed. If <em>true</em>
858 * all "normal" referenced objects will be ignored, only the specified object is handled.
859 */
860 public void store(Object obj, Identity oid, ClassDescriptor cld, boolean insert, boolean ignoreReferences)
861 {
862 if(obj == null || nowStoring.contains(obj))
863 {
864 return;
865 }
866
867 /*
868 if the object has been deleted during this transaction,
869 then we must insert it
870 */
871 //System.out.println("## insert: " +insert + " / deleted: " + deletedDuringTransaction);
872 if (!insert)
873 {
874 insert = deletedDuringTransaction.contains(oid);
875 }
876
877 //************************************************
878 // now store it:
879 if(isTxCheck() && !isInTransaction())
880 {
881 if(logger.isEnabledFor(Logger.ERROR))
882 {
883 try
884 {
885 throw new Exception("** Try to store object without active PersistenceBroker transaction **");
886 }
887 catch(Exception e)
888 {
889 logger.error("No running tx found, please only store in context of an PB-transaction" +
890 ", to avoid side-effects - e.g. when rollback of complex objects", e);
891 }
892 }
893 }
894 // Invoke events on PersistenceBrokerAware instances and listeners
895 if (insert)
896 {
897 BEFORE_STORE_EVENT.setTarget(obj);
898 fireBrokerEvent(BEFORE_STORE_EVENT);
899 BEFORE_STORE_EVENT.setTarget(null);
900 }
901 else
902 {
903 BEFORE_UPDATE_EVENT.setTarget(obj);
904 fireBrokerEvent(BEFORE_UPDATE_EVENT);
905 BEFORE_UPDATE_EVENT.setTarget(null);
906 }
907
908 try
909 {
910 nowStoring.add(obj);
911 storeToDb(obj, cld, oid, insert, ignoreReferences);
912 }
913 finally
914 {
915 // to optimize calls to DB don't remove already stored objects
916 nowStoring.remove(obj);
917 }
918
919
920 // Invoke events on PersistenceBrokerAware instances and listeners
921 if (insert)
922 {
923 AFTER_STORE_EVENT.setTarget(obj);
924 fireBrokerEvent(AFTER_STORE_EVENT);
925 AFTER_STORE_EVENT.setTarget(null);
926 }
927 else
928 {
929 AFTER_UPDATE_EVENT.setTarget(obj);
930 fireBrokerEvent(AFTER_UPDATE_EVENT);
931 AFTER_UPDATE_EVENT.setTarget(null);
932 }
933 // end of store operation
934 //************************************************
935
936 // if the object was stored, remove it from deleted set
937 if(deletedDuringTransaction.size() > 0) deletedDuringTransaction.remove(oid);
938
939 // let the connection manager to execute batch
940 connectionManager.executeBatchIfNecessary();
941 }
942
943 /**
944 * Internal used method which start the real store work.
945 */
946 protected void store(Object obj, Identity oid, ClassDescriptor cld, boolean insert)
947 {
948 store(obj, oid, cld, insert, false);
949 }
950
951 /**
952 * Store all object references that <b>obj</b> points to.
953 * All objects which we have a FK pointing to (Via ReferenceDescriptors) will be
954 * stored if auto-update is true <b>AND</b> the member field containing the object
955 * reference is NOT null.
956 * With flag <em>ignoreReferences</em> the storing/linking
957 * of references can be suppressed (independent of the used auto-update setting),
958 * except {@link org.apache.ojb.broker.metadata.SuperReferenceDescriptor}
959 * these kind of reference (descriptor) will always be performed.
960 *
961 * @param obj Object which we will store references for
962 */
963 private void storeReferences(Object obj, ClassDescriptor cld, boolean insert, boolean ignoreReferences)
964 {
965 // get all members of obj that are references and store them
966 Collection listRds = cld.getObjectReferenceDescriptors();
967 // return if nothing to do
968 if(listRds == null || listRds.size() == 0)
969 {
970 return;
971 }
972 Iterator i = listRds.iterator();
973 while (i.hasNext())
974 {
975 ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i.next();
976 /*
977 arminw: the super-references (used for table per subclass inheritance) must
978 be performed in any case. The "normal" 1:1 references can be ignored when
979 flag "ignoreReferences" is set
980 */
981 if((!ignoreReferences && rds.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE)
982 || rds.isSuperReferenceDescriptor())
983 {
984 storeAndLinkOneToOne(false, obj, cld, rds, insert);
985 }
986 }
987 }
988
989 /**
990 * Store/Link 1:1 reference.
991 *
992 * @param obj real object the reference starts
993 * @param rds {@link ObjectReferenceDescriptor} of the real object
994 * @param insert flag for insert operation
995 */
996 private void storeAndLinkOneToOne(boolean onlyLink, Object obj, ClassDescriptor cld,
997 ObjectReferenceDescriptor rds, boolean insert)
998 {
999 Object ref = rds.getPersistentField().get(obj);
1000 if (!onlyLink && rds.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
1001 {
1002 if(rds.isSuperReferenceDescriptor())
1003 {
1004 ClassDescriptor superCld = rds.getClassDescriptor().getSuperClassDescriptor();
1005 Identity oid = serviceIdentity().buildIdentity(superCld, ref);
1006 storeToDb(ref, superCld, oid, insert);
1007 }
1008 else store(ref);
1009 }
1010
1011 /**
1012 * Kuali Foundation modification -- 1/10/2008
1013 */
1014 ref = getProxyFactory().getRealObject(ref);
1015 /**
1016 * End of Kuali Foundation modification
1017 */
1018 link(obj, cld, rds, ref, insert);
1019 }
1020
1021 /**
1022 * Store/Link collections of objects poiting to <b>obj</b>.
1023 * More info please see comments in source.
1024 *
1025 * @param obj real object which we will store collections for
1026 * @throws PersistenceBrokerException if some goes wrong - please see the error message for details
1027 */
1028 private void storeCollections(Object obj, ClassDescriptor cld, boolean insert) throws PersistenceBrokerException
1029 {
1030 // get all members of obj that are collections and store all their elements
1031 Collection listCods = cld.getCollectionDescriptors();
1032 // return if nothing to do
1033 if (listCods.size() == 0)
1034 {
1035 return;
1036 }
1037 Iterator i = listCods.iterator();
1038 while (i.hasNext())
1039 {
1040 CollectionDescriptor cod = (CollectionDescriptor) i.next();
1041
1042 // if CASCADE_NONE was set, do nothing with referenced objects
1043 if (cod.getCascadingStore() != ObjectReferenceDescriptor.CASCADE_NONE)
1044 {
1045 Object referencedObjects = cod.getPersistentField().get(obj);
1046 if (cod.isMtoNRelation())
1047 {
1048 storeAndLinkMtoN(false, obj, cod, referencedObjects, insert);
1049 }
1050 else
1051 {
1052 storeAndLinkOneToMany(false, obj, cod, referencedObjects, insert);
1053 }
1054
1055 // BRJ: only when auto-update = object (CASCADE_OBJECT)
1056 //
1057 if ((cod.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
1058 && (referencedObjects instanceof ManageableCollection))
1059 {
1060 ((ManageableCollection) referencedObjects).afterStore(this);
1061 }
1062 }
1063 }
1064 }
1065
1066 /**
1067 * Store/Link m:n collection references.
1068 *
1069 * @param obj real object the reference starts
1070 * @param cod {@link CollectionDescriptor} of the real object
1071 * @param referencedObjects the referenced objects ({@link ManageableCollection} or Collection or Array) or null
1072 * @param insert flag for insert operation
1073 */
1074 private void storeAndLinkMtoN(boolean onlyLink, Object obj, CollectionDescriptor cod,
1075 Object referencedObjects, boolean insert)
1076 {
1077 /*
1078 - if the collection is a collectionproxy and it's not already loaded
1079 no need to perform an update on the referenced objects
1080 - on insert we link and insert the referenced objects, because the proxy
1081 collection maybe "inherited" from the object before the PK was replaced
1082 */
1083 if(insert || !(referencedObjects instanceof CollectionProxy
1084 && !((CollectionProxy) referencedObjects).isLoaded()))
1085 {
1086 // if referenced objects are null, assign empty list
1087 if(referencedObjects == null)
1088 {
1089 referencedObjects = Collections.EMPTY_LIST;
1090 }
1091 /*
1092 NOTE: Take care of referenced objects, they could be of type Collection or
1093 an Array or of type ManageableCollection, thus it is not guaranteed that we
1094 can cast to Collection!!!
1095
1096 if we store an object with m:n reference and no references could be
1097 found, we remove all entires of given object in indirection table
1098 */
1099 Iterator referencedObjectsIterator;
1100
1101 if(!onlyLink && cod.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
1102 {
1103 referencedObjectsIterator = BrokerHelper.getCollectionIterator(referencedObjects);
1104 while (referencedObjectsIterator.hasNext())
1105 {
1106 store(referencedObjectsIterator.next());
1107 }
1108 }
1109
1110 Collection existingMtoNKeys;
1111 if(!insert)
1112 {
1113 existingMtoNKeys = mtoNBroker.getMtoNImplementor(cod, obj);
1114 // we can't reuse iterator
1115 referencedObjectsIterator = BrokerHelper.getCollectionIterator(referencedObjects);
1116 // remove all entries in indirection table which not be part of referenced objects
1117 mtoNBroker.deleteMtoNImplementor(cod, obj, referencedObjectsIterator, existingMtoNKeys);
1118 }
1119 else
1120 {
1121 existingMtoNKeys = Collections.EMPTY_LIST;
1122 }
1123 // we can't reuse iterator
1124 referencedObjectsIterator = BrokerHelper.getCollectionIterator(referencedObjects);
1125 while (referencedObjectsIterator.hasNext())
1126 {
1127 Object refObj = referencedObjectsIterator.next();
1128 // Now store indirection record
1129 // BRJ: this could cause integrity problems because
1130 // obj may not be stored depending on auto-update
1131 mtoNBroker.storeMtoNImplementor(cod, obj, refObj, existingMtoNKeys);
1132 }
1133 }
1134 }
1135
1136 /**
1137 * Store/Link 1:n collection references.
1138 *
1139 * @param obj real object the reference starts
1140 * @param linkOnly if true the referenced objects will only be linked (FK set, no reference store).
1141 * Reference store setting in descriptor will be ignored in this case
1142 * @param cod {@link CollectionDescriptor} of the real object
1143 * @param referencedObjects the referenced objects ({@link ManageableCollection} or Collection or Array) or null
1144 * @param insert flag for insert operation
1145 */
1146 private void storeAndLinkOneToMany(boolean linkOnly, Object obj, CollectionDescriptor cod,
1147 Object referencedObjects, boolean insert)
1148 {
1149 if(referencedObjects == null)
1150 {
1151 return;
1152 }
1153 /*
1154 Only make sense to perform (link or/and store) real referenced objects
1155 or materialized collection proxy objects, because on unmaterialized collection
1156 nothing has changed.
1157
1158 - if the collection is a collectionproxy and it's not already loaded
1159 no need to perform an update on the referenced objects
1160 - on insert we link and insert the referenced objects, because the proxy
1161 collection maybe "inherited" from the object before the PK was replaced
1162 */
1163 if(insert || !(referencedObjects instanceof CollectionProxyDefaultImpl
1164 && !((CollectionProxyDefaultImpl) referencedObjects).isLoaded()))
1165 {
1166 Iterator it = BrokerHelper.getCollectionIterator(referencedObjects);
1167 Object refObj;
1168 while(it.hasNext())
1169 {
1170 refObj = it.next();
1171 /*
1172 TODO: Check this!
1173 arminw:
1174 When it's necessary to 'link' (set the FK) the 1:n reference objects?
1175 1. set FK in refObj if it is materialized
1176 2. if the referenced object is a proxy AND the main object needs insert
1177 we have to materialize the real object, because the user may move a collection
1178 of proxy objects from object A to new object B. In this case we have to replace the
1179 FK in the proxy object with new key of object B.
1180 */
1181 if(insert || getProxyFactory().isMaterialized(refObj))
1182 {
1183 ClassDescriptor refCld = getClassDescriptor(getProxyFactory().getRealClass(refObj));
1184 // get the real object before linking
1185 refObj = getProxyFactory().getRealObject(refObj);
1186 link(refObj, refCld, cod, obj, insert);
1187 // if enabled cascade store and not only link, store the refObj
1188 if(!linkOnly && cod.getCascadingStore() == ObjectReferenceDescriptor.CASCADE_OBJECT)
1189 {
1190 store(refObj);
1191 }
1192 }
1193 }
1194 }
1195 }
1196
1197 /**
1198 * Assign FK value to target object by reading PK values of referenced object.
1199 *
1200 * @param targetObject real (non-proxy) target object
1201 * @param cld {@link ClassDescriptor} of the real target object
1202 * @param rds An {@link ObjectReferenceDescriptor} or {@link CollectionDescriptor}
1203 * associated with the real object.
1204 * @param referencedObject referenced object or proxy
1205 * @param insert Show if "linking" is done while insert or update.
1206 */
1207 public void link(Object targetObject, ClassDescriptor cld, ObjectReferenceDescriptor rds, Object referencedObject, boolean insert)
1208 {
1209 // MBAIRD: we have 'disassociated' this object from the referenced object,
1210 // the object represented by the reference descriptor is now null, so set
1211 // the fk in the target object to null.
1212 // arminw: if an insert was done and ref object was null, we should allow
1213 // to pass FK fields of main object (maybe only the FK fields are set)
1214 if (referencedObject == null)
1215 {
1216 /*
1217 arminw:
1218 if update we set FK fields to 'null', because reference was disassociated
1219 We do nothing on insert, maybe only the FK fields of main object (without
1220 materialization of the reference object) are set by the user
1221 */
1222 if(!insert)
1223 {
1224 unlinkFK(targetObject, cld, rds);
1225 }
1226 }
1227 else
1228 {
1229 setFKField(targetObject, cld, rds, referencedObject);
1230 }
1231 }
1232
1233 /**
1234 * Unkink FK fields of target object.
1235 *
1236 * @param targetObject real (non-proxy) target object
1237 * @param cld {@link ClassDescriptor} of the real target object
1238 * @param rds An {@link ObjectReferenceDescriptor} or {@link CollectionDescriptor}
1239 * associated with the real object.
1240 */
1241 public void unlinkFK(Object targetObject, ClassDescriptor cld, ObjectReferenceDescriptor rds)
1242 {
1243 setFKField(targetObject, cld, rds, null);
1244 }
1245
1246 /**
1247 * Set the FK value on the target object, extracted from the referenced object. If the referenced object was
1248 * <i>null</i> the FK values were set to <i>null</i>, expect when the FK field was declared as PK.
1249 *
1250 * @param targetObject real (non-proxy) target object
1251 * @param cld {@link ClassDescriptor} of the real target object
1252 * @param rds An {@link ObjectReferenceDescriptor} or {@link CollectionDescriptor}
1253 * @param referencedObject The referenced object or <i>null</i>
1254 */
1255 private void setFKField(Object targetObject, ClassDescriptor cld, ObjectReferenceDescriptor rds, Object referencedObject)
1256 {
1257 ValueContainer[] refPkValues;
1258 FieldDescriptor fld;
1259 FieldDescriptor[] objFkFields = rds.getForeignKeyFieldDescriptors(cld);
1260 if (objFkFields == null)
1261 {
1262 throw new PersistenceBrokerException("No foreign key fields defined for class '"+cld.getClassNameOfObject()+"'");
1263 }
1264 if(referencedObject == null)
1265 {
1266 refPkValues = null;
1267 }
1268 else
1269 {
1270 Class refClass = proxyFactory.getRealClass(referencedObject);
1271 ClassDescriptor refCld = getClassDescriptor(refClass);
1272 refPkValues = brokerHelper.getKeyValues(refCld, referencedObject, false);
1273 }
1274 for (int i = 0; i < objFkFields.length; i++)
1275 {
1276 fld = objFkFields[i];
1277 /*
1278 arminw:
1279 we set the FK value when the extracted PK fields from the referenced object are not null at all
1280 or if null, the FK field was not a PK field of target object too.
1281 Should be ok, because the values of the extracted PK field values should never be null and never
1282 change, so it doesn't matter if the target field is a PK too.
1283 */
1284 if(refPkValues != null || !fld.isPrimaryKey())
1285 {
1286 fld.getPersistentField().set(targetObject, refPkValues != null ? refPkValues[i].getValue(): null);
1287 }
1288 }
1289 }
1290
1291 /**
1292 * Assign FK value of main object with PK values of the reference object.
1293 *
1294 * @param obj real object with reference (proxy) object (or real object with set FK values on insert)
1295 * @param cld {@link ClassDescriptor} of the real object
1296 * @param rds An {@link ObjectReferenceDescriptor} of real object.
1297 * @param insert Show if "linking" is done while insert or update.
1298 */
1299 public void linkOneToOne(Object obj, ClassDescriptor cld, ObjectReferenceDescriptor rds, boolean insert)
1300 {
1301 storeAndLinkOneToOne(true, obj, cld, rds, true);
1302 }
1303
1304 /**
1305 * Assign FK value to all n-side objects referenced by given object.
1306 *
1307 * @param obj real object with 1:n reference
1308 * @param cod {@link CollectionDescriptor} of referenced 1:n objects
1309 * @param insert flag signal insert operation, false signals update operation
1310 */
1311 public void linkOneToMany(Object obj, CollectionDescriptor cod, boolean insert)
1312 {
1313 Object referencedObjects = cod.getPersistentField().get(obj);
1314 storeAndLinkOneToMany(true, obj, cod,referencedObjects, insert);
1315 }
1316
1317 /**
1318 * Assign FK values and store entries in indirection table
1319 * for all objects referenced by given object.
1320 *
1321 * @param obj real object with 1:n reference
1322 * @param cod {@link CollectionDescriptor} of referenced 1:n objects
1323 * @param insert flag signal insert operation, false signals update operation
1324 */
1325 public void linkMtoN(Object obj, CollectionDescriptor cod, boolean insert)
1326 {
1327 Object referencedObjects = cod.getPersistentField().get(obj);
1328 storeAndLinkMtoN(true, obj, cod, referencedObjects, insert);
1329 }
1330
1331 public void unlinkXtoN(Object obj, CollectionDescriptor col)
1332 {
1333 if(col.isMtoNRelation())
1334 {
1335 // if this is a m:n mapped table, remove entries from indirection table
1336 mtoNBroker.deleteMtoNImplementor(col, obj);
1337 }
1338 else
1339 {
1340 Object collectionObject = col.getPersistentField().get(obj);
1341 if (collectionObject != null)
1342 {
1343 Iterator colIterator = BrokerHelper.getCollectionIterator(collectionObject);
1344 ClassDescriptor cld = null;
1345 while (colIterator.hasNext())
1346 {
1347 Object target = colIterator.next();
1348 if(cld == null) cld = getClassDescriptor(getProxyFactory().getRealClass(target));
1349 unlinkFK(target, cld, col);
1350 }
1351 }
1352 }
1353 }
1354
1355 /**
1356 * Retrieve all References (also Collection-attributes) of a given instance.
1357 * Loading is forced, even if the collection- and reference-descriptors differ.
1358 * @param pInstance the persistent instance to work with
1359 */
1360 public void retrieveAllReferences(Object pInstance) throws PersistenceBrokerException
1361 {
1362 if (logger.isDebugEnabled())
1363 {
1364 logger.debug("Manually retrieving all references for object " + serviceIdentity().buildIdentity(pInstance));
1365 }
1366 ClassDescriptor cld = getClassDescriptor(pInstance.getClass());
1367 getInternalCache().enableMaterializationCache();
1368 // to avoid problems with circular references, locally cache the current object instance
1369 Identity oid = serviceIdentity().buildIdentity(pInstance);
1370 // boolean needLocalRemove = false;
1371 if(getInternalCache().doLocalLookup(oid) == null)
1372 {
1373 getInternalCache().doInternalCache(oid, pInstance, MaterializationCache.TYPE_TEMP);
1374 // needLocalRemove = true;
1375 }
1376 try
1377 {
1378 referencesBroker.retrieveReferences(pInstance, cld, true);
1379 referencesBroker.retrieveCollections(pInstance, cld, true);
1380 // arminw: should no longer needed since we use TYPE_TEMP for this kind of objects
1381 // // do locally remove the object to avoid problems with object state detection (insert/update),
1382 // // because objects found in the cache detected as 'old' means 'update'
1383 // if(needLocalRemove) getInternalCache().doLocalRemove(oid);
1384 getInternalCache().disableMaterializationCache();
1385 }
1386 catch(RuntimeException e)
1387 {
1388 getInternalCache().doLocalClear();
1389 throw e;
1390 }
1391 }
1392
1393 /**
1394 * retrieve a single reference- or collection attribute
1395 * of a persistent instance.
1396 * @param pInstance the persistent instance
1397 * @param pAttributeName the name of the Attribute to load
1398 */
1399 public void retrieveReference(Object pInstance, String pAttributeName) throws PersistenceBrokerException
1400 {
1401 if (logger.isDebugEnabled())
1402 {
1403 logger.debug("Retrieving reference named ["+pAttributeName+"] on object of type ["+
1404 pInstance.getClass().getName()+"]");
1405 }
1406 ClassDescriptor cld = getClassDescriptor(pInstance.getClass());
1407 CollectionDescriptor cod = cld.getCollectionDescriptorByName(pAttributeName);
1408 getInternalCache().enableMaterializationCache();
1409 // to avoid problems with circular references, locally cache the current object instance
1410 Identity oid = serviceIdentity().buildIdentity(pInstance);
1411 boolean needLocalRemove = false;
1412 if(getInternalCache().doLocalLookup(oid) == null)
1413 {
1414 getInternalCache().doInternalCache(oid, pInstance, MaterializationCache.TYPE_TEMP);
1415 needLocalRemove = true;
1416 }
1417 try
1418 {
1419 if (cod != null)
1420 {
1421 referencesBroker.retrieveCollection(pInstance, cld, cod, true);
1422 }
1423 else
1424 {
1425 ObjectReferenceDescriptor ord = cld.getObjectReferenceDescriptorByName(pAttributeName);
1426 if (ord != null)
1427 {
1428 referencesBroker.retrieveReference(pInstance, cld, ord, true);
1429 }
1430 else
1431 {
1432 throw new PersistenceBrokerException("did not find attribute " + pAttributeName +
1433 " for class " + pInstance.getClass().getName());
1434 }
1435 }
1436 // do locally remove the object to avoid problems with object state detection (insert/update),
1437 // because objects found in the cache detected as 'old' means 'update'
1438 if(needLocalRemove) getInternalCache().doLocalRemove(oid);
1439 getInternalCache().disableMaterializationCache();
1440 }
1441 catch(RuntimeException e)
1442 {
1443 getInternalCache().doLocalClear();
1444 throw e;
1445 }
1446 }
1447
1448 /**
1449 * Check if the references of the specified object have enabled
1450 * the <em>refresh</em> attribute and refresh the reference if set <em>true</em>.
1451 *
1452 * @throws PersistenceBrokerException if there is a error refreshing collections or references
1453 * @param obj The object to check.
1454 * @param oid The {@link Identity} of the object.
1455 * @param cld The {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the object.
1456 */
1457 public void checkRefreshRelationships(Object obj, Identity oid, ClassDescriptor cld)
1458 {
1459 Iterator iter;
1460 CollectionDescriptor cds;
1461 ObjectReferenceDescriptor rds;
1462 // to avoid problems with circular references, locally cache the current object instance
1463 Object tmp = getInternalCache().doLocalLookup(oid);
1464 if(tmp != null && getInternalCache().isEnabledMaterialisationCache())
1465 {
1466 /*
1467 arminw: This should fix OJB-29, infinite loops on bidirectional 1:1 relations with
1468 refresh attribute 'true' for both references. OJB now assume that the object is already
1469 refreshed when it's cached in the materialisation cache
1470 */
1471 return;
1472 }
1473 try
1474 {
1475 getInternalCache().enableMaterializationCache();
1476 if(tmp == null)
1477 {
1478 getInternalCache().doInternalCache(oid, obj, MaterializationCache.TYPE_TEMP);
1479 }
1480 if(logger.isDebugEnabled()) logger.debug("Refresh relationships for " + oid);
1481 iter = cld.getCollectionDescriptors().iterator();
1482 while (iter.hasNext())
1483 {
1484 cds = (CollectionDescriptor) iter.next();
1485 if (cds.isRefresh())
1486 {
1487 referencesBroker.retrieveCollection(obj, cld, cds, false);
1488 }
1489 }
1490 iter = cld.getObjectReferenceDescriptors().iterator();
1491 while (iter.hasNext())
1492 {
1493 rds = (ObjectReferenceDescriptor) iter.next();
1494 if (rds.isRefresh())
1495 {
1496 referencesBroker.retrieveReference(obj, cld, rds, false);
1497 }
1498 }
1499 getInternalCache().disableMaterializationCache();
1500 }
1501 catch(RuntimeException e)
1502 {
1503 getInternalCache().doLocalClear();
1504 throw e;
1505 }
1506 }
1507
1508 /**
1509 * retrieve a collection of type collectionClass matching the Query query
1510 *
1511 * @see org.apache.ojb.broker.PersistenceBroker#getCollectionByQuery(Class, Query)
1512 */
1513 public ManageableCollection getCollectionByQuery(Class collectionClass, Query query)
1514 throws PersistenceBrokerException
1515 {
1516 return referencesBroker.getCollectionByQuery(collectionClass, query, false);
1517 }
1518
1519 /**
1520 * retrieve a collection of itemClass Objects matching the Query query
1521 */
1522 public Collection getCollectionByQuery(Query query) throws PersistenceBrokerException
1523 {
1524 return referencesBroker.getCollectionByQuery(query, false);
1525 }
1526
1527 /**
1528 * Retrieve an plain object (without populated references) by it's identity
1529 * from the database
1530 *
1531 * @param cld the real {@link org.apache.ojb.broker.metadata.ClassDescriptor} of the object to refresh
1532 * @param oid the {@link org.apache.ojb.broker.Identity} of the object
1533 * @return A new plain object read from the database or <em>null</em> if not found
1534 * @throws ClassNotPersistenceCapableException
1535 */
1536 private Object getPlainDBObject(ClassDescriptor cld, Identity oid) throws ClassNotPersistenceCapableException
1537 {
1538 Object newObj = null;
1539
1540 // Class is NOT an Interface: it has a directly mapped table and we lookup this table first:
1541 if (!cld.isInterface())
1542 {
1543 // 1. try to retrieve skalar fields from directly mapped table columns
1544 newObj = dbAccess.materializeObject(cld, oid);
1545 }
1546
1547 // if we did not find the object yet AND if the cld represents an Extent,
1548 // we can lookup all tables of the extent classes:
1549 if (newObj == null && cld.isExtent())
1550 {
1551 Iterator extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator();
1552
1553 while (extents.hasNext())
1554 {
1555 ClassDescriptor extCld = (ClassDescriptor) extents.next();
1556
1557 newObj = dbAccess.materializeObject(extCld, oid);
1558 if (newObj != null)
1559 {
1560 break;
1561 }
1562 }
1563 }
1564 return newObj;
1565 }
1566
1567
1568 /**
1569 * Retrieve an full materialized (dependent on the metadata settings)
1570 * object by it's identity from the database, as well as caching the
1571 * object
1572 *
1573 * @param oid The {@link org.apache.ojb.broker.Identity} of the object to for
1574 * @return A new object read from the database or <em>null</em> if not found
1575 * @throws ClassNotPersistenceCapableException
1576 */
1577 private Object getDBObject(Identity oid) throws ClassNotPersistenceCapableException
1578 {
1579 Class c = oid.getObjectsRealClass();
1580
1581 if (c == null)
1582 {
1583 logger.info("Real class for used Identity object is 'null', use top-level class instead");
1584 c = oid.getObjectsTopLevelClass();
1585 }
1586
1587 ClassDescriptor cld = getClassDescriptor(c);
1588 Object newObj = getPlainDBObject(cld, oid);
1589
1590 // loading references is useful only when the Object could be found in db:
1591 if (newObj != null)
1592 {
1593 if (oid.getObjectsRealClass() == null)
1594 {
1595 oid.setObjectsRealClass(newObj.getClass());
1596 }
1597
1598 /*
1599 * synchronize on newObj so the ODMG-layer can take a snapshot only of
1600 * fully cached (i.e. with all references + collections) objects
1601 */
1602 synchronized (newObj)
1603 {
1604 objectCache.enableMaterializationCache();
1605 try
1606 {
1607 // cache object immediately , so that references
1608 // can be established from referenced Objects back to this Object
1609 objectCache.doInternalCache(oid, newObj, ObjectCacheInternal.TYPE_NEW_MATERIALIZED);
1610
1611 /*
1612 * Chris Lewington: can cause problems with multiple objects
1613 * mapped to one table, as follows:
1614 *
1615 * if the class searched on does not match the retrieved
1616 * class, eg a search on an OID retrieves a row but it could
1617 * be a different class (OJB gets all column values),
1618 * then trying to resolve references will fail as the object
1619 * will not match the Class Descriptor.
1620 *
1621 * To be safe, get the descriptor of the retrieved object
1622 * BEFORE resolving refs
1623 */
1624 ClassDescriptor newObjCld = getClassDescriptor(newObj.getClass());
1625 // don't force loading of references:
1626 final boolean unforced = false;
1627
1628 // 2. retrieve non-skalar fields that contain objects retrievable from other tables
1629 referencesBroker.retrieveReferences(newObj, newObjCld, unforced);
1630 // 3. retrieve collection fields from foreign-key related tables:
1631 referencesBroker.retrieveCollections(newObj, newObjCld, unforced);
1632 objectCache.disableMaterializationCache();
1633 }
1634 catch(RuntimeException e)
1635 {
1636 objectCache.doLocalClear();
1637 throw e;
1638 }
1639 }
1640 }
1641
1642 return newObj;
1643 }
1644
1645 /**
1646 * returns an Iterator that iterates Objects of class c if calling the .next()
1647 * method. The Elements returned come from a SELECT ... WHERE Statement
1648 * that is defined by the Query query.
1649 * If itemProxy is null, no proxies are used.
1650 */
1651 public Iterator getIteratorByQuery(Query query) throws PersistenceBrokerException
1652 {
1653 Class itemClass = query.getSearchClass();
1654 ClassDescriptor cld = getClassDescriptor(itemClass);
1655 return getIteratorFromQuery(query, cld);
1656 }
1657
1658 /**
1659 * Get an extent aware Iterator based on the Query
1660 *
1661 * @param query
1662 * @param cld the ClassDescriptor
1663 * @return OJBIterator
1664 */
1665 protected OJBIterator getIteratorFromQuery(Query query, ClassDescriptor cld) throws PersistenceBrokerException
1666 {
1667 RsIteratorFactory factory = RsIteratorFactoryImpl.getInstance();
1668 OJBIterator result = getRsIteratorFromQuery(query, cld, factory);
1669
1670 if (query.usePaging())
1671 {
1672 result = new PagingIterator(result, query.getStartAtIndex(), query.getEndAtIndex());
1673 }
1674 return result;
1675 }
1676
1677 public Object getObjectByIdentity(Identity id) throws PersistenceBrokerException
1678 {
1679 objectCache.enableMaterializationCache();
1680 Object result = null;
1681 try
1682 {
1683 result = doGetObjectByIdentity(id);
1684 objectCache.disableMaterializationCache();
1685 }
1686 catch(RuntimeException e)
1687 {
1688 // catch runtime exc. to guarantee clearing of internal buffer on failure
1689 objectCache.doLocalClear();
1690 throw e;
1691 }
1692 return result;
1693 }
1694
1695 /**
1696 * Internal used method to retrieve object based on Identity.
1697 *
1698 * @param id
1699 * @return
1700 * @throws PersistenceBrokerException
1701 */
1702 public Object doGetObjectByIdentity(Identity id) throws PersistenceBrokerException
1703 {
1704 if (logger.isDebugEnabled()) logger.debug("getObjectByIdentity " + id);
1705
1706 // check if object is present in ObjectCache:
1707 Object obj = objectCache.lookup(id);
1708 // only perform a db lookup if necessary (object not cached yet)
1709 if (obj == null)
1710 {
1711 obj = getDBObject(id);
1712 }
1713 else
1714 {
1715 ClassDescriptor cld = getClassDescriptor(obj.getClass());
1716 // if specified in the ClassDescriptor the instance must be refreshed
1717 if (cld.isAlwaysRefresh())
1718 {
1719 refreshInstance(obj, id, cld);
1720 }
1721 // now refresh all references
1722 checkRefreshRelationships(obj, id, cld);
1723 }
1724
1725 // Invoke events on PersistenceBrokerAware instances and listeners
1726 AFTER_LOOKUP_EVENT.setTarget(obj);
1727 fireBrokerEvent(AFTER_LOOKUP_EVENT);
1728 AFTER_LOOKUP_EVENT.setTarget(null);
1729
1730 //logger.info("RETRIEVING object " + obj);
1731 return obj;
1732 }
1733
1734 /**
1735 * refresh all primitive typed attributes of a cached instance
1736 * with the current values from the database.
1737 * refreshing of reference and collection attributes is not done
1738 * here.
1739 * @param cachedInstance the cached instance to be refreshed
1740 * @param oid the Identity of the cached instance
1741 * @param cld the ClassDescriptor of cachedInstance
1742 */
1743 private void refreshInstance(Object cachedInstance, Identity oid, ClassDescriptor cld)
1744 {
1745 // read in fresh copy from the db, but do not cache it
1746 Object freshInstance = getPlainDBObject(cld, oid);
1747
1748 // update all primitive typed attributes
1749 FieldDescriptor[] fields = cld.getFieldDescriptions();
1750 FieldDescriptor fmd;
1751 PersistentField fld;
1752 for (int i = 0; i < fields.length; i++)
1753 {
1754 fmd = fields[i];
1755 fld = fmd.getPersistentField();
1756 fld.set(cachedInstance, fld.get(freshInstance));
1757 }
1758 }
1759
1760 /**
1761 * retrieve an Object by query
1762 * I.e perform a SELECT ... FROM ... WHERE ... in an RDBMS
1763 */
1764 public Object getObjectByQuery(Query query) throws PersistenceBrokerException
1765 {
1766 Object result = null;
1767 if (query instanceof QueryByIdentity)
1768 {
1769 // example obj may be an entity or an Identity
1770 Object obj = query.getExampleObject();
1771 if (obj instanceof Identity)
1772 {
1773 Identity oid = (Identity) obj;
1774 result = getObjectByIdentity(oid);
1775 }
1776 else
1777 {
1778 // TODO: This workaround doesn't allow 'null' for PK fields
1779 if (!serviceBrokerHelper().hasNullPKField(getClassDescriptor(obj.getClass()), obj))
1780 {
1781 Identity oid = serviceIdentity().buildIdentity(obj);
1782 result = getObjectByIdentity(oid);
1783 }
1784 }
1785 }
1786 else
1787 {
1788 Class itemClass = query.getSearchClass();
1789 ClassDescriptor cld = getClassDescriptor(itemClass);
1790 /*
1791 use OJB intern Iterator, thus we are able to close used
1792 resources instantly
1793 */
1794 OJBIterator it = getIteratorFromQuery(query, cld);
1795 /*
1796 arminw:
1797 patch by Andre Clute, instead of taking the first found result
1798 try to get the first found none null result.
1799 He wrote:
1800 I have a situation where an item with a certain criteria is in my
1801 database twice -- once deleted, and then a non-deleted version of it.
1802 When I do a PB.getObjectByQuery(), the RsIterator get's both results
1803 from the database, but the first row is the deleted row, so my RowReader
1804 filters it out, and do not get the right result.
1805 */
1806 try
1807 {
1808 while (result==null && it.hasNext())
1809 {
1810 result = it.next();
1811 }
1812 } // make sure that we close the used resources
1813 finally
1814 {
1815 if(it != null) it.releaseDbResources();
1816 }
1817 }
1818 return result;
1819 }
1820
1821 /**
1822 * returns an Enumeration of PrimaryKey Objects for objects of class DataClass.
1823 * The Elements returned come from a SELECT ... WHERE Statement
1824 * that is defined by the fields and their coresponding values of listFields
1825 * and listValues.
1826 * Useful for EJB Finder Methods...
1827 * @param primaryKeyClass the pk class for the searched objects
1828 * @param query the query
1829 */
1830 public Enumeration getPKEnumerationByQuery(Class primaryKeyClass, Query query) throws PersistenceBrokerException
1831 {
1832 if (logger.isDebugEnabled()) logger.debug("getPKEnumerationByQuery " + query);
1833
1834 query.setFetchSize(1);
1835 ClassDescriptor cld = getClassDescriptor(query.getSearchClass());
1836 return new PkEnumeration(query, cld, primaryKeyClass, this);
1837 }
1838
1839 /**
1840 * Makes object obj persistent in the underlying persistence system.
1841 * E.G. by INSERT INTO ... or UPDATE ... in an RDBMS.
1842 * The ObjectModification parameter can be used to determine whether INSERT or update is to be used.
1843 * This functionality is typically called from transaction managers, that
1844 * track which objects have to be stored. If the object is an unmaterialized
1845 * proxy the method return immediately.
1846 */
1847 public void store(Object obj, ObjectModification mod) throws PersistenceBrokerException
1848 {
1849 obj = extractObjectToStore(obj);
1850 // null for unmaterialized Proxy
1851 if (obj == null)
1852 {
1853 return;
1854 }
1855
1856 ClassDescriptor cld = getClassDescriptor(obj.getClass());
1857 // this call ensures that all autoincremented primary key attributes are filled
1858 Identity oid = serviceIdentity().buildIdentity(cld, obj);
1859 // select flag for insert / update selection by checking the ObjectModification
1860 if (mod.needsInsert())
1861 {
1862 store(obj, oid, cld, true);
1863 }
1864 else if (mod.needsUpdate())
1865 {
1866 store(obj, oid, cld, false);
1867 }
1868 /*
1869 arminw
1870 TODO: Why we need this behaviour? What about 1:1 relations?
1871 */
1872 else
1873 {
1874 // just store 1:n and m:n associations
1875 storeCollections(obj, cld, mod.needsInsert());
1876 }
1877 }
1878
1879 /**
1880 * I pulled this out of internal store so that when doing multiple table
1881 * inheritance, i can recurse this function.
1882 *
1883 * @param obj
1884 * @param cld
1885 * @param oid BRJ: what is it good for ???
1886 * @param insert
1887 * @param ignoreReferences
1888 */
1889 private void storeToDb(Object obj, ClassDescriptor cld, Identity oid, boolean insert, boolean ignoreReferences)
1890 {
1891 // 1. link and store 1:1 references
1892 storeReferences(obj, cld, insert, ignoreReferences);
1893
1894 Object[] pkValues = oid.getPrimaryKeyValues();
1895 if (!serviceBrokerHelper().assertValidPksForStore(cld.getPkFields(), pkValues))
1896 {
1897 // BRJ: fk values may be part of pk, but the are not known during
1898 // creation of Identity. so we have to get them here
1899 pkValues = serviceBrokerHelper().getKeyValues(cld, obj);
1900 if (!serviceBrokerHelper().assertValidPksForStore(cld.getPkFields(), pkValues))
1901 {
1902 String append = insert ? " on insert" : " on update" ;
1903 throw new PersistenceBrokerException("assertValidPkFields failed for Object of type: " + cld.getClassNameOfObject() + append);
1904 }
1905 }
1906
1907 // get super class cld then store it with the object
1908 /*
1909 now for multiple table inheritance
1910 1. store super classes, topmost parent first
1911 2. go down through heirarchy until current class
1912 3. todo: store to full extent?
1913
1914 // arminw: TODO: The extend-attribute feature dosn't work, should we remove this stuff?
1915 This if-clause will go up the inheritance heirarchy to store all the super classes.
1916 The id for the top most super class will be the id for all the subclasses too
1917 */
1918 if(cld.getSuperClass() != null)
1919 {
1920
1921 ClassDescriptor superCld = getDescriptorRepository().getDescriptorFor(cld.getSuperClass());
1922 storeToDb(obj, superCld, oid, insert);
1923 // arminw: why this?? I comment out this section
1924 // storeCollections(obj, cld.getCollectionDescriptors(), insert);
1925 }
1926
1927 // 2. store primitive typed attributes (Or is THIS step 3 ?)
1928 // if obj not present in db use INSERT
1929 if (insert)
1930 {
1931 dbAccess.executeInsert(cld, obj);
1932 if(oid.isTransient())
1933 {
1934 // Create a new Identity based on the current set of primary key values.
1935 oid = serviceIdentity().buildIdentity(cld, obj);
1936 }
1937 }
1938 // else use UPDATE
1939 else
1940 {
1941 try
1942 {
1943 dbAccess.executeUpdate(cld, obj);
1944 }
1945 catch(OptimisticLockException e)
1946 {
1947 // ensure that the outdated object be removed from cache
1948 objectCache.remove(oid);
1949 throw e;
1950 }
1951 }
1952 // cache object for symmetry with getObjectByXXX()
1953 // Add the object to the cache.
1954 objectCache.doInternalCache(oid, obj, ObjectCacheInternal.TYPE_WRITE);
1955 // 3. store 1:n and m:n associations
1956 if(!ignoreReferences) storeCollections(obj, cld, insert);
1957 }
1958
1959 /**
1960 * I pulled this out of internal store so that when doing multiple table
1961 * inheritance, i can recurse this function.
1962 *
1963 * @param obj
1964 * @param cld
1965 * @param oid BRJ: what is it good for ???
1966 * @param insert
1967 */
1968 private void storeToDb(Object obj, ClassDescriptor cld, Identity oid, boolean insert)
1969 {
1970 storeToDb(obj, cld, oid, insert, false);
1971 }
1972
1973 /**
1974 * returns true if the broker is currently running a transaction.
1975 * @return boolean
1976 */
1977 public boolean isInTransaction()
1978 {
1979 // return this.connectionManager.isInLocalTransaction();
1980 return inTransaction;
1981 }
1982
1983 public void setInTransaction(boolean inTransaction)
1984 {
1985 this.inTransaction = inTransaction;
1986 }
1987
1988 /**
1989 * @see org.apache.ojb.broker.PersistenceBroker#removeFromCache
1990 */
1991 public void removeFromCache(Object objectOrIdentity) throws PersistenceBrokerException
1992 {
1993 Identity identity;
1994 if (objectOrIdentity instanceof Identity)
1995 {
1996 identity = (Identity)objectOrIdentity;
1997 }
1998 else
1999 {
2000 identity = serviceIdentity().buildIdentity(objectOrIdentity);
2001 }
2002 objectCache.remove(identity);
2003 }
2004
2005 /**
2006 * returns a ClassDescriptor for the persistence capable class clazz.
2007 * throws a PersistenceBrokerException if clazz is not persistence capable,
2008 * i.e. if clazz is not defined in the DescriptorRepository.
2009 */
2010 public ClassDescriptor getClassDescriptor(Class clazz) throws PersistenceBrokerException
2011 {
2012 return descriptorRepository.getDescriptorFor(clazz);
2013 }
2014
2015 public boolean hasClassDescriptor(Class clazz)
2016 {
2017 return descriptorRepository.hasDescriptorFor(clazz);
2018 }
2019
2020 /**
2021 * clears the brokers internal cache.
2022 * removing is recursive. That is referenced Objects are also
2023 * removed from the cache, if the auto-retrieve flag is set
2024 * for obj.getClass() in the metadata repository.
2025 *
2026 */
2027 public void clearCache() throws PersistenceBrokerException
2028 {
2029 objectCache.clear();
2030 }
2031
2032 /**
2033 * @see org.apache.ojb.broker.PersistenceBroker#getTopLevelClass
2034 */
2035 public Class getTopLevelClass(Class clazz) throws PersistenceBrokerException
2036 {
2037 try
2038 {
2039 return descriptorRepository.getTopLevelClass(clazz);
2040 }
2041 catch (ClassNotPersistenceCapableException e)
2042 {
2043 throw new PersistenceBrokerException(e);
2044 }
2045 }
2046
2047 /**
2048 * @see org.apache.ojb.broker.PersistenceBroker#getCount(Query)
2049 */
2050 public int getCount(Query query) throws PersistenceBrokerException
2051 {
2052 Query countQuery = serviceBrokerHelper().getCountQuery(query);
2053 Iterator iter;
2054 int result = 0;
2055
2056 if (logger.isDebugEnabled()) logger.debug("getCount " + countQuery.getSearchClass() + ", " + countQuery);
2057
2058 iter = getReportQueryIteratorByQuery(countQuery);
2059 try
2060 {
2061 while (iter.hasNext())
2062 {
2063 Object[] row = (Object[]) iter.next();
2064 result += ((Number) row[0]).intValue();
2065 }
2066 }
2067 finally
2068 {
2069 if (iter instanceof OJBIterator)
2070 {
2071 ((OJBIterator) iter).releaseDbResources();
2072 }
2073 }
2074
2075 return result;
2076 }
2077
2078 /**
2079 * Get an Iterator based on the ReportQuery
2080 *
2081 * @param query
2082 * @return Iterator
2083 */
2084 public Iterator getReportQueryIteratorByQuery(Query query) throws PersistenceBrokerException
2085 {
2086 ClassDescriptor cld = getClassDescriptor(query.getSearchClass());
2087 return getReportQueryIteratorFromQuery(query, cld);
2088 }
2089
2090 /**
2091 * Get an extent aware RsIterator based on the Query
2092 *
2093 * @param query
2094 * @param cld
2095 * @param factory the Factory for the RsIterator
2096 * @return OJBIterator
2097 */
2098 private OJBIterator getRsIteratorFromQuery(Query query, ClassDescriptor cld, RsIteratorFactory factory)
2099 throws PersistenceBrokerException
2100 {
2101 query.setFetchSize(1);
2102 if (query instanceof QueryBySQL)
2103 {
2104 if(logger.isDebugEnabled()) logger.debug("Creating SQL-RsIterator for class ["+cld.getClassNameOfObject()+"]");
2105 return factory.createRsIterator((QueryBySQL) query, cld, this);
2106 }
2107
2108 if (!cld.isExtent() || !query.getWithExtents())
2109 {
2110 // no extents just use the plain vanilla RsIterator
2111 if(logger.isDebugEnabled()) logger.debug("Creating RsIterator for class ["+cld.getClassNameOfObject()+"]");
2112
2113 return factory.createRsIterator(query, cld, this);
2114 }
2115
2116 if(logger.isDebugEnabled()) logger.debug("Creating ChainingIterator for class ["+cld.getClassNameOfObject()+"]");
2117
2118 ChainingIterator chainingIter = new ChainingIterator();
2119
2120 // BRJ: add base class iterator
2121 if (!cld.isInterface())
2122 {
2123 if(logger.isDebugEnabled()) logger.debug("Adding RsIterator for class ["+cld.getClassNameOfObject()+"] to ChainingIterator");
2124
2125 chainingIter.addIterator(factory.createRsIterator(query, cld, this));
2126 }
2127
2128 Iterator extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator();
2129 while (extents.hasNext())
2130 {
2131 ClassDescriptor extCld = (ClassDescriptor) extents.next();
2132
2133 // read same table only once
2134 if (chainingIter.containsIteratorForTable(extCld.getFullTableName()))
2135 {
2136 if(logger.isDebugEnabled()) logger.debug("Skipping class ["+extCld.getClassNameOfObject()+"]");
2137 }
2138 else
2139 {
2140 if(logger.isDebugEnabled()) logger.debug("Adding RsIterator of class ["+extCld.getClassNameOfObject()+"] to ChainingIterator");
2141
2142 // add the iterator to the chaining iterator.
2143 chainingIter.addIterator(factory.createRsIterator(query, extCld, this));
2144 }
2145 }
2146
2147 return chainingIter;
2148 }
2149
2150 /**
2151 * Get an extent aware Iterator based on the ReportQuery
2152 *
2153 * @param query
2154 * @param cld
2155 * @return OJBIterator
2156 */
2157 private OJBIterator getReportQueryIteratorFromQuery(Query query, ClassDescriptor cld) throws PersistenceBrokerException
2158 {
2159 RsIteratorFactory factory = ReportRsIteratorFactoryImpl.getInstance();
2160 OJBIterator result = getRsIteratorFromQuery(query, cld, factory);
2161
2162 if (query.usePaging())
2163 {
2164 result = new PagingIterator(result, query.getStartAtIndex(), query.getEndAtIndex());
2165 }
2166
2167 return result;
2168 }
2169
2170 /**
2171 * @see org.odbms.ObjectContainer#query()
2172 */
2173 public org.odbms.Query query()
2174 {
2175 return new org.apache.ojb.soda.QueryImpl(this);
2176 }
2177
2178 /**
2179 * @return DescriptorRepository
2180 */
2181 public DescriptorRepository getDescriptorRepository()
2182 {
2183 return descriptorRepository;
2184 }
2185
2186 protected void finalize()
2187 {
2188 if (!isClosed)
2189 {
2190 close();
2191 }
2192 }
2193
2194 /**
2195 * clean up the maps for reuse by the next transaction.
2196 */
2197 private void clearRegistrationLists()
2198 {
2199 nowStoring.clear();
2200 objectCache.doLocalClear();
2201 deletedDuringTransaction.clear();
2202 /*
2203 arminw:
2204 for better performance I don't register MtoNBroker as listner,
2205 so use this method to reset on commit/rollback
2206 */
2207 mtoNBroker.reset();
2208 }
2209
2210
2211
2212 /**
2213 * @see org.apache.ojb.broker.PersistenceBroker#deleteMtoNImplementor
2214 */
2215 public void deleteMtoNImplementor(MtoNImplementor m2nImpl) throws PersistenceBrokerException
2216 {
2217 mtoNBroker.deleteMtoNImplementor(m2nImpl);
2218 }
2219
2220 /**
2221 * @see org.apache.ojb.broker.PersistenceBroker#addMtoNImplementor
2222 */
2223 public void addMtoNImplementor(MtoNImplementor m2n) throws PersistenceBrokerException
2224 {
2225 mtoNBroker.storeMtoNImplementor(m2n);
2226 }
2227
2228 public ProxyFactory getProxyFactory() {
2229 return proxyFactory;
2230 }
2231
2232 /**
2233 * Creates a proxy instance.
2234 *
2235 * @param baseClassForProxy The base class that the Proxy should extend. For dynamic Proxies, the method of
2236 * generation is dependent on the ProxyFactory implementation.
2237 * @param realSubjectsIdentity The identity of the subject
2238 * @return An instance of the proxy subclass
2239 * @throws PersistenceBrokerException If there is an error creating the proxy object
2240 */
2241 public Object createProxy(Class baseClassForProxy, Identity realSubjectsIdentity)
2242 {
2243 try
2244 {
2245 // the invocation handler manages all delegation stuff
2246 IndirectionHandler handler = getProxyFactory().createIndirectionHandler(pbKey, realSubjectsIdentity);
2247
2248 // the proxy simply provides the interface of the real subject
2249 if (VirtualProxy.class.isAssignableFrom(baseClassForProxy))
2250 {
2251 Constructor constructor = baseClassForProxy.getDeclaredConstructor(new Class[]{ IndirectionHandler.class });
2252 return constructor.newInstance(new Object[]{ handler });
2253 }
2254 else
2255 {
2256 return getProxyFactory().createProxy(baseClassForProxy,handler);
2257 }
2258
2259
2260 }
2261 catch (Exception ex)
2262 {
2263 throw new PersistenceBrokerException("Unable to create proxy using class:"+baseClassForProxy.getName(), ex);
2264 }
2265 }
2266
2267
2268 }