package org.apache.ojb.broker.core; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Iterator; import java.util.List; import java.util.LinkedList; import org.apache.ojb.broker.Identity; import org.apache.ojb.broker.ManageableCollection; import org.apache.ojb.broker.PBKey; import org.apache.ojb.broker.PersistenceBrokerException; import org.apache.ojb.broker.accesslayer.JdbcAccess; import org.apache.ojb.broker.accesslayer.OJBIterator; import org.apache.ojb.broker.accesslayer.PagingIterator; import org.apache.ojb.broker.accesslayer.RsIterator; import org.apache.ojb.broker.accesslayer.RsQueryObject; import org.apache.ojb.broker.accesslayer.ChainingIterator; import org.apache.ojb.broker.query.Criteria; import org.apache.ojb.broker.query.Query; import org.apache.ojb.broker.query.QueryBySQL; import org.apache.ojb.broker.query.QueryFactory; import org.apache.ojb.broker.metadata.ClassDescriptor; import org.apache.ojb.broker.metadata.CollectionDescriptor; import org.apache.ojb.broker.metadata.FieldDescriptor; import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor; import org.apache.ojb.broker.util.ClassHelper; import pt.ist.fenixframework.pstm.ojb.FenixJdbcAccessImpl; import pt.ist.fenixframework.pstm.AbstractDomainObject; import pt.ist.fenixframework.pstm.DomainObjectAllocator; public class FenixPersistenceBroker extends PersistenceBrokerImpl { public FenixPersistenceBroker(PBKey key, PersistenceBrokerFactoryIF pbf) { super(key, pbf); } // copied and adapted from PersistenceBrokerImpl.doGetObjectByIdentity and getDBObject methods @Override public Object doGetObjectByIdentity(Identity oid) throws PersistenceBrokerException { Class c = oid.getObjectsRealClass(); if (c == null) { c = oid.getObjectsTopLevelClass(); } ClassDescriptor cld = getClassDescriptor(c); JdbcAccess dbAccess = serviceJdbcAccess(); Object newObj = dbAccess.materializeObject(cld, oid); // if we did not find the object yet AND if the cld represents an Extent, // we can lookup all tables of the extent classes: if (newObj == null && cld.isExtent()) { Iterator extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator(); while (extents.hasNext()) { ClassDescriptor extCld = (ClassDescriptor) extents.next(); newObj = dbAccess.materializeObject(extCld, oid); if (newObj != null) { break; } } } if (newObj != null) { if (oid.getObjectsRealClass() == null) { oid.setObjectsRealClass(newObj.getClass()); } } return newObj; } // copied and adapted from PersistenceBrokerImpl's // "retrieveReference" and QueryReferenceBroker's // "retrieveReference", "getReferencedObjectIdentity" and // "getReferencedObject" methods @Override public void retrieveReference(Object obj, String pAttributeName) throws PersistenceBrokerException { // In the new version of the fenix-framework, where we use OIDs allover as foreign keys // this method is no longer called for retrieving references to single objects // (those described by an ObjectReferenceDescriptor). // Now, this method should be used only for retrieving collections. // // So, the code here is the merging of the original // PersistenceBrokerImpl.retrieveReference with the // QueryReferenceBroker.retrieveCollection method, changed to // use OIDs rather the primary keys (which remain to be, // still, the idInternal, to create the SQL queries. ClassDescriptor cld = getClassDescriptor(obj.getClass()); CollectionDescriptor cds = cld.getCollectionDescriptorByName(pAttributeName); if (cds == null) { throw new PersistenceBrokerException("In the Fenix Framework retrieveReference should be called only for collections"); } // this collection type will be used: Class collectionClass = cds.getCollectionClass(); Query fkQuery = getFKQuery((AbstractDomainObject)obj, cld, cds); ManageableCollection result = referencesBroker.getCollectionByQuery(collectionClass, fkQuery, false); cds.getPersistentField().set(obj, result); } // this method results from the merging and simplification of the // getFKQuery, getFKQueryMtoN, and getFKQuery1toN methods that // exist in OJB's QueryReferenceBroker class. private Query getFKQuery(AbstractDomainObject obj, ClassDescriptor cld, CollectionDescriptor cod) { if (cod.isMtoNRelation()) { // each of the following arrays have one element only Object[] thisClassFks = cod.getFksToThisClass(); Object[] itemClassFks = cod.getFksToItemClass(); String table = cod.getIndirectionTable(); Criteria criteria = new Criteria(); criteria.addColumnEqualTo(table + "." + thisClassFks[0], obj.getOid()); criteria.addColumnEqualToField(table + "." + itemClassFks[0], "OID"); ClassDescriptor refCld = getClassDescriptor(cod.getItemClass()); return QueryFactory.newQuery(refCld.getClassOfObject(), table, criteria); } else { ClassDescriptor refCld = getClassDescriptor(cod.getItemClass()); // the following array will have only one element FieldDescriptor[] fields = cod.getForeignKeyFieldDescriptors(refCld); Criteria criteria = new Criteria(); criteria.addEqualTo(fields[0].getAttributeName(), obj.getOid()); return QueryFactory.newQuery(refCld.getClassOfObject(), criteria); } } // copied from PersistenceBrokerImpl, to change the RsIteratorFactory used protected OJBIterator getIteratorFromQuery(Query query, ClassDescriptor cld) throws PersistenceBrokerException { RsIteratorFactory factory = FenixRsIteratorFactory.getInstance(); OJBIterator result = getRsIteratorFromQuery(query, cld, factory); if (query.usePaging()) { result = new PagingIterator(result, query.getStartAtIndex(), query.getEndAtIndex()); } return result; } // verbatim copy from PersistenceBrokerImpl because the method was private there... private OJBIterator getRsIteratorFromQuery(Query query, ClassDescriptor cld, RsIteratorFactory factory) throws PersistenceBrokerException { if (query instanceof QueryBySQL) { return factory.createRsIterator((QueryBySQL) query, cld, this); } if (!cld.isExtent() || !query.getWithExtents()) { // no extents just use the plain vanilla RsIterator return factory.createRsIterator(query, cld, this); } ChainingIterator chainingIter = new ChainingIterator(); List<String> tablesRead = new LinkedList<String>(); // BRJ: add base class iterator if (!cld.isInterface()) { chainingIter.addIterator(factory.createRsIterator(query, cld, this)); tablesRead.add(cld.getFullTableName()); } Iterator extents = getDescriptorRepository().getAllConcreteSubclassDescriptors(cld).iterator(); while (extents.hasNext()) { ClassDescriptor extCld = (ClassDescriptor) extents.next(); // read same table only once // JC: the following (original) test did not work when the // previously added iterator was empty (something that is // tested when the iterator is added to the chaining // iterator), because in that case the iterator added is // simply ignored. So, the following test will return // false and, thus, make repeated queries to the same // table when all the classes are mapped to the same // table. Instead, we keep the table names used so far in // the tablesRead list and check it here. //if (chainingIter.containsIteratorForTable(extCld.getFullTableName())) if (tablesRead.contains(extCld.getFullTableName())) { } else { // add the iterator to the chaining iterator. chainingIter.addIterator(factory.createRsIterator(query, extCld, this)); tablesRead.add(extCld.getFullTableName()); } } return chainingIter; } static class FenixRsIteratorFactory extends RsIteratorFactoryImpl { private static RsIteratorFactory instance; synchronized static RsIteratorFactory getInstance() { if (instance == null) { instance = new FenixRsIteratorFactory(); } return instance; } public RsIterator createRsIterator(Query query, ClassDescriptor cld, PersistenceBrokerImpl broker) { return new FenixRsIterator(RsQueryObject.get(cld, query), broker); } } static class FenixRsIterator extends RsIterator { FenixRsIterator(RsQueryObject queryObject, PersistenceBrokerImpl broker) { super(queryObject, broker); } protected Object getObjectFromResultSet() throws PersistenceBrokerException { ClassDescriptor cld = getQueryObject().getClassDescriptor(); if (cld.getFactoryClass() != DomainObjectAllocator.class) { return super.getObjectFromResultSet(); } else { ResultSet rs = getRsAndStmt().m_rs; return FenixJdbcAccessImpl.readObjectFromRs(rs); } } } }