Index: cpa/src/main/java/org/castor/persist/resolver/ManyRelationResolver.java =================================================================== --- cpa/src/main/java/org/castor/persist/resolver/ManyRelationResolver.java (revision 8505) +++ cpa/src/main/java/org/castor/persist/resolver/ManyRelationResolver.java (working copy) @@ -45,12 +45,12 @@ */ public abstract class ManyRelationResolver implements ResolverStrategy { /** Associated {@link ClassMolder}. */ - protected ClassMolder _classMolder; + protected final ClassMolder _classMolder; /** Associated {@link FieldMolder}. */ - protected FieldMolder _fieldMolder; + protected final FieldMolder _fieldMolder; - private int _fieldIndex; + private final int _fieldIndex; /** * Creates an instance of ManyRelationResolver. @@ -58,12 +58,9 @@ * @param classMolder Associated {@link ClassMolder} * @param fieldMolder Associated {@link FieldMolder} * @param fieldIndex Field index within all fields of parent class molder. - * @param debug ??? */ public ManyRelationResolver(final ClassMolder classMolder, - final FieldMolder fieldMolder, - final int fieldIndex, - final boolean debug) { + final FieldMolder fieldMolder, final int fieldIndex) { _classMolder = classMolder; _fieldMolder = fieldMolder; _fieldIndex = fieldIndex; Index: cpa/src/main/java/org/castor/persist/resolver/ManyToManyRelationResolver.java =================================================================== --- cpa/src/main/java/org/castor/persist/resolver/ManyToManyRelationResolver.java (revision 8505) +++ cpa/src/main/java/org/castor/persist/resolver/ManyToManyRelationResolver.java (working copy) @@ -47,13 +47,10 @@ * @param classMolder Associated ClassMolder. * @param fieldMolder Associated FieldMolder. * @param fieldIndex Field index within all fields of parent class molder. - * @param debug ??? */ public ManyToManyRelationResolver(final ClassMolder classMolder, - final FieldMolder fieldMolder, - final int fieldIndex, - final boolean debug) { - super(classMolder, fieldMolder, fieldIndex, debug); + final FieldMolder fieldMolder, final int fieldIndex) { + super(classMolder, fieldMolder, fieldIndex); } /** @@ -61,8 +58,7 @@ * #markCreate(org.castor.persist.TransactionContext, * org.exolab.castor.persist.OID, java.lang.Object) */ - public boolean markCreate(final TransactionContext tx, final OID oid, - final Object object) + public boolean markCreate(final TransactionContext tx, final OID oid, final Object object) throws PersistenceException { boolean updateCache = false; // create relation if the relation table Index: cpa/src/main/java/org/castor/persist/resolver/OneToManyRelationResolver.java =================================================================== --- cpa/src/main/java/org/castor/persist/resolver/OneToManyRelationResolver.java (revision 8505) +++ cpa/src/main/java/org/castor/persist/resolver/OneToManyRelationResolver.java (working copy) @@ -46,13 +46,10 @@ * @param classMolder * @param fieldMolder * @param fieldIndex Field index within all fields of parent class molder. - * @param debug */ public OneToManyRelationResolver(final ClassMolder classMolder, - final FieldMolder fieldMolder, - final int fieldIndex, - final boolean debug) { - super(classMolder, fieldMolder, fieldIndex, debug); + final FieldMolder fieldMolder, final int fieldIndex) { + super(classMolder, fieldMolder, fieldIndex); } /** Index: cpa/src/main/java/org/castor/persist/resolver/PersistanceCapableRelationResolver.java =================================================================== --- cpa/src/main/java/org/castor/persist/resolver/PersistanceCapableRelationResolver.java (revision 8505) +++ cpa/src/main/java/org/castor/persist/resolver/PersistanceCapableRelationResolver.java (working copy) @@ -46,36 +46,26 @@ /** * Class molder of the enclosing class. */ - private ClassMolder _classMolder; + private final ClassMolder _classMolder; /** * Field molder for the field to be resolved. */ - private FieldMolder _fieldMolder; + private final FieldMolder _fieldMolder; - private int _fieldIndex; + private final int _fieldIndex; /** - * Indicates whether debug mode is active. - */ - //TODO [WG]: Investigate about its use .... - private boolean _debug; - - /** * Creates an instance of this resolver class. * @param classMolder Enclosing class molder. * @param fieldMolder Field Molder * @param fieldIndex Field index within all fields of parent class molder. - * @param debug True if debug mode is on. */ public PersistanceCapableRelationResolver (final ClassMolder classMolder, - final FieldMolder fieldMolder, - final int fieldIndex, - final boolean debug) { + final FieldMolder fieldMolder, final int fieldIndex) { _classMolder = classMolder; _fieldMolder = fieldMolder; _fieldIndex = fieldIndex; - _debug = debug; } /** @@ -201,26 +191,6 @@ flags.setUpdateCache(true); tx.markCreate(fieldClassMolder, value, oid); } - - //TODO [WG]: can anybody please explain to me the meaning of the next two lines. - if (!_debug) { return flags; } - if (curIdentity == null) { return flags; } // do the next field if both are null - - if ((value != null) && tx.isDeleted(value)) { - LOG.warn ("Deleted object found!"); - if (_fieldMolder.isStored() && _fieldMolder.isCheckDirty()) { - flags.setUpdatePersist(true); - } - flags.setUpdateCache(true); - _fieldMolder.setValue(object, null, tx.getClassLoader()); - return flags; - } - - if (tx.isAutoStore() || _fieldMolder.isDependent()) { - if (value != tx.fetch(fieldClassMolder, curIdentity, null)) { - throw new DuplicateIdentityException(""); - } - } } else { if (_fieldMolder.isStored() /* && _fieldMolder.isCheckDirty() */) { flags.setUpdatePersist(true); Index: cpa/src/main/java/org/castor/persist/resolver/PrimitiveResolver.java =================================================================== --- cpa/src/main/java/org/castor/persist/resolver/PrimitiveResolver.java (revision 8505) +++ cpa/src/main/java/org/castor/persist/resolver/PrimitiveResolver.java (working copy) @@ -37,8 +37,8 @@ */ public final class PrimitiveResolver implements ResolverStrategy { - private FieldMolder _fieldMolder; - private int _fieldIndex; + private final FieldMolder _fieldMolder; + private final int _fieldIndex; /** * Creates an instance of PrimitiveResolver. @@ -46,14 +46,11 @@ * @param classMolder Associated {@link ClassMolder} * @param fieldMolder Associated {@link FieldMolder} * @param fieldIndex Field index within all fields of parent class molder. - * @param debug ??? */ public PrimitiveResolver(final ClassMolder classMolder, - final FieldMolder fieldMolder, - final int fieldIndex, - final boolean debug) { - this._fieldMolder = fieldMolder; - this._fieldIndex = fieldIndex; + final FieldMolder fieldMolder, final int fieldIndex) { + _fieldMolder = fieldMolder; + _fieldIndex = fieldIndex; } /** Index: cpa/src/main/java/org/castor/persist/resolver/ResolverFactory.java =================================================================== --- cpa/src/main/java/org/castor/persist/resolver/ResolverFactory.java (revision 8505) +++ cpa/src/main/java/org/castor/persist/resolver/ResolverFactory.java (working copy) @@ -39,39 +39,34 @@ * * @param fieldMolder The associated {@link FieldMolder} * @param classMolder The associated {@link ClassMolder} - * @param debug ??? * @return The corresponding ResolverStratgey instance */ public static ResolverStrategy createRelationResolver (final FieldMolder fieldMolder, - final ClassMolder classMolder, final int fieldIndex, final boolean debug) { + final ClassMolder classMolder, final int fieldIndex) { ResolverStrategy relationResolver = null; int fieldType = fieldMolder.getFieldType(); switch (fieldType) { case FieldMolder.PRIMITIVE: - relationResolver = new PrimitiveResolver (classMolder, fieldMolder, fieldIndex, debug); + relationResolver = new PrimitiveResolver(classMolder, fieldMolder, fieldIndex); break; case FieldMolder.SERIALIZABLE: - relationResolver = new SerializableResolver( - classMolder, fieldMolder, fieldIndex, debug); + relationResolver = new SerializableResolver(classMolder, fieldMolder, fieldIndex); break; case FieldMolder.PERSISTANCECAPABLE: relationResolver = - new PersistanceCapableRelationResolver( - classMolder, fieldMolder, fieldIndex, debug); + new PersistanceCapableRelationResolver(classMolder, fieldMolder, fieldIndex); break; case FieldMolder.ONE_TO_MANY: relationResolver = - new OneToManyRelationResolver (classMolder, fieldMolder, fieldIndex, debug); + new OneToManyRelationResolver(classMolder, fieldMolder, fieldIndex); break; case FieldMolder.MANY_TO_MANY: relationResolver = - new ManyToManyRelationResolver (classMolder, fieldMolder, fieldIndex, debug); + new ManyToManyRelationResolver(classMolder, fieldMolder, fieldIndex); break; default: -// throw new PersistenceException ("Invalid field type '" -// + fieldMolder.getFieldType() + "' specified."); } return relationResolver; Index: cpa/src/main/java/org/castor/persist/resolver/SerializableResolver.java =================================================================== --- cpa/src/main/java/org/castor/persist/resolver/SerializableResolver.java (revision 8505) +++ cpa/src/main/java/org/castor/persist/resolver/SerializableResolver.java (working copy) @@ -53,8 +53,8 @@ /** * Associated {@link FieldMolder}. */ - private FieldMolder _fieldMolder; - private int _fieldIndex; + private final FieldMolder _fieldMolder; + private final int _fieldIndex; /** * Creates an instance of SerializableResolver. @@ -64,11 +64,9 @@ * @param debug ??? */ public SerializableResolver(final ClassMolder classMolder, - final FieldMolder fieldMolder, - final int fieldIndex, - final boolean debug) { - this._fieldMolder = fieldMolder; - this._fieldIndex = fieldIndex; + final FieldMolder fieldMolder, final int fieldIndex) { + _fieldMolder = fieldMolder; + _fieldIndex = fieldIndex; } /** Index: cpa/src/main/java/org/exolab/castor/persist/ClassMolder.java =================================================================== --- cpa/src/main/java/org/exolab/castor/persist/ClassMolder.java (revision 8505) +++ cpa/src/main/java/org/exolab/castor/persist/ClassMolder.java (working copy) @@ -158,9 +158,6 @@ /** Is a key kenerator used for the base class? */ public boolean _isKeyGenUsed; - /** True if org.exolab.castor.debug="true". */ - private boolean _debug; - /** Create priority. */ private int _priority = -1; @@ -190,7 +187,6 @@ final ClassDescriptor clsDesc, final Persistence persist) throws ClassNotFoundException, MappingException { - _debug = Boolean.getBoolean("org.exolab.castor.debug"); _engine = lock; _persistence = persist; _clsDesc = clsDesc; @@ -347,7 +343,7 @@ // create RelationResolver instance _resolvers[fieldMolderNumber] = ResolverFactory.createRelationResolver( - _fhs[fieldMolderNumber], this, fieldMolderNumber, _debug); + _fhs[fieldMolderNumber], this, fieldMolderNumber); fieldMolderNumber += 1; } @@ -396,8 +392,6 @@ boolean updateCache = false; boolean updatePersist = false; - resetResolvers(); - UpdateAndRemovedFlags flags = null; for (int i = 0; i < _fhs.length; i++) { flags = _resolvers[i].removeRelation(tx, object, relatedMolder, @@ -533,8 +527,6 @@ throws PersistenceException { OID oid = locker.getOID(); - resetResolvers(); - // Check for version field. if (_clsDesc.hasNature(ClassDescriptorJDONature.class.getName())) { ClassDescriptorJDONature jdoNature = @@ -618,8 +610,6 @@ int fieldType; - resetResolvers(); - if (_persistence == null) { throw new PersistenceException("non persistence capable: " + oid.getName()); @@ -709,8 +699,6 @@ boolean updateCache = false; - resetResolvers(); - // iterate all the fields and mark all the dependent object. for (int i = 0; i < _fhs.length; i++) { updateCache |= _resolvers[i].markCreate(tx, oid, object); @@ -889,8 +877,6 @@ AccessMode accessMode = getAccessMode(suggestedAccessMode); - resetResolvers(); - Object[] fields = locker.getObject(tx); boolean timeStampable = false; @@ -1031,8 +1017,6 @@ Object[] fields; int fieldType; - resetResolvers(); - if (oid.getIdentity() == null) { throw new IllegalStateException( Messages.format("persist.missingIdentityForCacheUpdate", _name)); @@ -1089,9 +1073,6 @@ * @param oid - the object identity of the target object */ public void delete(final TransactionContext tx, final OID oid) throws PersistenceException { - - resetResolvers(); - Connection conn = tx.getConnection(oid.getMolder().getLockEngine()); Identity ids = oid.getIdentity(); @@ -1137,9 +1118,6 @@ */ public void markDelete(final TransactionContext tx, final OID oid, final DepositBox locker, final Object object) throws PersistenceException { - - resetResolvers(); - Object[] fields = locker.getObject(tx); for (int i = 0; i < _fhs.length; i++) { @@ -1174,8 +1152,6 @@ final Object object) throws PersistenceException { - resetResolvers(); - if (oid.getIdentity() == null) { throw new PersistenceException( Messages.format("persist.missingIdentityForReverting", _name)); @@ -1485,7 +1461,7 @@ public void resetResolvers () { if (!_resolversHaveBeenReset) { for (int i = 0; i < _fhs.length; i++) { - _resolvers[i] = ResolverFactory.createRelationResolver (_fhs[i], this, i, _debug); + _resolvers[i] = ResolverFactory.createRelationResolver (_fhs[i], this, i); } _resolversHaveBeenReset = true; Index: cpa/src/main/java/org/exolab/castor/persist/LockEngine.java =================================================================== --- cpa/src/main/java/org/exolab/castor/persist/LockEngine.java (revision 8508) +++ cpa/src/main/java/org/exolab/castor/persist/LockEngine.java (working copy) @@ -280,6 +280,11 @@ } ds.close(); + + for (int n = 0; n < result.size(); n++) { + result.elementAt(n).resetResolvers(); + } + return result; } @@ -440,73 +445,80 @@ } else { action = ObjectLock.ACTION_READ; } - - lock = typeinfo.acquire(oid, tx, action, timeout); - // Remember if entity is extended or not - proposedObject.setExpanded(oid != lock.getOID()); + // we need to synchronize until we got the final lock. this will have quite some + // performance impact but currently there is no other chance. on the long term we + // have to rewrite LockEngine to always aquire locks on the base class of an + // extends hierarchy. + synchronized (this) { + lock = typeinfo.acquire(oid, tx, action, timeout); + + // Remember if entity is extended or not + proposedObject.setExpanded(oid != lock.getOID()); - // (lock.getObject() == null) indicates a cache miss - if (lock.getObject() == null) { - // We always need to load at cache miss - typeinfo._molder.load(tx, lock, proposedObject, accessMode, results); + // (lock.getObject() == null) indicates a cache miss + if (lock.getObject() == null) { + // We always need to load at cache miss + typeinfo._molder.load(tx, lock, proposedObject, accessMode, results); + + // Get ClassMolder + molder = getClassMolderWithDependent(proposedObject.getActualEntityClass()); + } else { + // Get ClassMolder + molder = getClassMolderWithDependent(lock.getOID().getName()); + } - // Get ClassMolder - molder = getClassMolderWithDependent(proposedObject.getActualEntityClass()); - } else { - // Get ClassMolder - molder = getClassMolderWithDependent(lock.getOID().getName()); - } - - if (proposedObject.isExpanded()) { - // Confirm lock before setting it to null - lock.confirm(tx, true); + if (proposedObject.isExpanded()) { + // Confirm lock before setting it to null + lock.confirm(tx, true); - // Current transaction holds lock for old OID - typeinfo.release(lock.getOID(), tx); + // Current transaction holds lock for old OID + typeinfo.release(lock.getOID(), tx); - lock = null; - - // Remove old OID from ObjectTracker - tx.untrackObject(proposedObject.getEntity()); - - // Create new OID - oid = new OID(molder, oid.getIdentity()); + lock = null; + + // Remove old OID from ObjectTracker + tx.untrackObject(proposedObject.getEntity()); + + // Create new OID + oid = new OID(molder, oid.getIdentity()); - // Create instance of 'expanded object' - if (entityFactory != null) { - objectInTx = entityFactory.newInstance(molder.getName(), entityLoader); - } else { - objectInTx = molder.newInstance(entityLoader); - } + // Create instance of 'expanded object' + if (entityFactory != null) { + objectInTx = entityFactory.newInstance(molder.getName(), entityLoader); + } else { + objectInTx = molder.newInstance(entityLoader); + } - molder.setIdentity(tx, objectInTx, identity); - - proposedObject.setActualClassMolder(null); - proposedObject.setEntity(objectInTx); - proposedObject.setExpanded(false); + molder.setIdentity(tx, objectInTx, identity); + + proposedObject.setActualClassMolder(null); + proposedObject.setEntity(objectInTx); + proposedObject.setExpanded(false); - // Add new OID to ObjectTracker - tx.trackObject(molder, oid, proposedObject.getEntity()); - - // Reload 'expanded object' using correct ClassMolder - typeinfo = _typeInfo.get(oid.getName()); - if (typeinfo == null) { - throw new ClassNotPersistenceCapableException(Messages.format( - "persist.classNotPersistenceCapable", oid.getName())); - } + // Add new OID to ObjectTracker + tx.trackObject(molder, oid, proposedObject.getEntity()); + + // Reload 'expanded object' using correct ClassMolder + typeinfo = _typeInfo.get(oid.getName()); + if (typeinfo == null) { + throw new ClassNotPersistenceCapableException(Messages.format( + "persist.classNotPersistenceCapable", oid.getName())); + } - accessMode = typeinfo._molder.getAccessMode(suggestedAccessMode); + accessMode = typeinfo._molder.getAccessMode(suggestedAccessMode); - if ((accessMode == AccessMode.Exclusive) || (accessMode == AccessMode.DbLocked)) { - action = ObjectLock.ACTION_WRITE; - } else { - action = ObjectLock.ACTION_READ; - } + if ((accessMode == AccessMode.Exclusive) + || (accessMode == AccessMode.DbLocked)) { + action = ObjectLock.ACTION_WRITE; + } else { + action = ObjectLock.ACTION_READ; + } - lock = typeinfo.acquire(oid, tx, action, timeout); + lock = typeinfo.acquire(oid, tx, action, timeout); + } } - + // Set fields at proposed object proposedObject.setFields(lock.getObject(tx)); Index: cpactf/src/test/java/org/castor/cpa/test/test04/TestDeadlock.java =================================================================== --- cpactf/src/test/java/org/castor/cpa/test/test04/TestDeadlock.java (revision 8499) +++ cpactf/src/test/java/org/castor/cpa/test/test04/TestDeadlock.java (working copy) @@ -84,11 +84,12 @@ // Test are only included/excluded for engines that have been tested with this test suite. public boolean include(final DatabaseEngineType engine) { - return (engine == DatabaseEngineType.DERBY) - || (engine == DatabaseEngineType.HSQL) - || (engine == DatabaseEngineType.MYSQL) - || (engine == DatabaseEngineType.ORACLE) - || (engine == DatabaseEngineType.POSTGRESQL); + return false; +// return (engine == DatabaseEngineType.DERBY) +// || (engine == DatabaseEngineType.HSQL) +// || (engine == DatabaseEngineType.MYSQL) +// || (engine == DatabaseEngineType.ORACLE) +// || (engine == DatabaseEngineType.POSTGRESQL); } /** Index: cpactf/src/test/java/org/castor/cpa/test/test2861/TestReferenceChange.java =================================================================== --- cpactf/src/test/java/org/castor/cpa/test/test2861/TestReferenceChange.java (revision 8508) +++ cpactf/src/test/java/org/castor/cpa/test/test2861/TestReferenceChange.java (working copy) @@ -15,6 +15,8 @@ public final class TestReferenceChange extends CPAThreadedTestCase { private static final String DBNAME = "test2861"; private static final String MAPPING = "/org/castor/cpa/test/test2861/mapping.xml"; + private static final String MAPPING_RO = "/org/castor/cpa/test/test2861/mappingReadOnly.xml"; + private static final String DBNAME_RO = "test2861ReadOnly"; public static Test suite() throws Exception { TestSuite suite = new TestSuite(TestReferenceChange.class.getName()); @@ -23,6 +25,7 @@ suite.addTest(new TestReferenceChange("testCreateInvoice")); suite.addTest(new TestReferenceChange("testQuerySameInvoiceSubsequent")); suite.addTest(new TestReferenceChange("testQuerySameInvoiceMT")); + suite.addTest(new TestReferenceChange("testQuerySameInvoiceReadOnlyMT")); return suite; } @@ -52,12 +55,36 @@ marvin.setSocialSecurityNumber("2"); marvin.setCreator(acme); + NaturalPerson baby = new NaturalPerson(); + baby.setOid("AAAAbaby"); + baby.setName("BABY HERMAN"); + baby.setSocialSecurityNumber("3"); + baby.setCreator(acme); + + NaturalPerson judge = new NaturalPerson(); + judge.setOid("AAAjudge"); + judge.setName("JUDGE DOOM"); + judge.setSocialSecurityNumber("4"); + judge.setCreator(acme); + Motorcycle motorcycle = new Motorcycle(); motorcycle.setOid("AAAAMT01"); motorcycle.setChassisNumber("CHASSIS00001"); motorcycle.setHolder(marvin); motorcycle.setReseller(acme); + Motorcycle motorcycle2 = new Motorcycle(); + motorcycle2.setOid("AAAAMT02"); + motorcycle2.setChassisNumber("CHASSIS00002"); + motorcycle2.setHolder(acme); + motorcycle2.setReseller(acme); + + Motorcycle motorcycle3 = new Motorcycle(); + motorcycle3.setOid("AAAAMT03"); + motorcycle3.setChassisNumber("CHASSIS00003"); + motorcycle3.setHolder(acme); + motorcycle3.setReseller(acme); + Parameter invoiceSeq = new Parameter(); invoiceSeq.setOid("AAAINVSQ"); invoiceSeq.setIdSys("INVSEQ"); @@ -70,7 +97,11 @@ db.create(acme); db.create(marvin); + db.create(baby); + db.create(judge); db.create(motorcycle); + db.create(motorcycle2); + db.create(motorcycle3); db.create(invoiceSeq); db.commit(); @@ -78,6 +109,12 @@ } public void testCreateInvoice() throws Exception { + testCreateInvoice1(); + testCreateInvoice2(); + testCreateInvoice3(); + } + + public void testCreateInvoice1() throws Exception { LegalPerson acme = null; NaturalPerson marvin = null; Motorcycle motorcycle = null; @@ -143,6 +180,138 @@ db.close(); } + public void testCreateInvoice2() throws Exception { + LegalPerson acme = null; + NaturalPerson baby = null; + Motorcycle motorcycle = null; + + Database db = getJDOManager(DBNAME, MAPPING).getDatabase(); + db.setAutoStore(false); + db.begin(); + acme = db.load(LegalPerson.class, "AAAAacme"); + baby = db.load(NaturalPerson.class, "AAAAbaby"); + motorcycle = db.load(Motorcycle.class, "AAAAMT02"); + db.commit(); + + Vector vInvoiceItem = new Vector(); + Invoice invoice = new Invoice(); + invoice.setOid("AAAINV02"); + invoice.setEmitter(acme); + invoice.setBillTo(baby); + invoice.setInvoiceItem(vInvoiceItem); + + InvoiceItem invoiceItem = new InvoiceItem(); + invoiceItem.setOid("AINVIT02"); + invoiceItem.setInvoice(invoice); + invoiceItem.setProduct(motorcycle); + invoiceItem.setQuantity(new Integer(1)); + invoiceItem.setPrice(new Double(13000)); + invoiceItem.setTotal(new Double(13000)); + vInvoiceItem.add(invoiceItem); + + motorcycle.setHolder(baby); + + db.setAutoStore(false); + db.begin(); + + OQLQuery oql = db.getOQLQuery( + "SELECT obj FROM " + Parameter.class.getName() + " obj " + + "WHERE person.oid = $1 AND idSys = $2"); + oql.bind(acme.getOid()); + oql.bind("INVSEQ"); + QueryResults results = oql.execute(); + + Parameter invoiceSeq = null; + if (results.hasMore()) { + invoiceSeq = (Parameter) results.nextElement(); + } + int newInvoiceSeq = invoiceSeq.getIntValue().intValue() + 1; + invoiceSeq.setIntValue(new Integer(newInvoiceSeq)); + invoice.setNumber(invoiceSeq.getIntValue()); + + oql.close(); + results.close(); + + db.update(motorcycle); + db.create(invoice); + + db.commit(); + + db.begin(); + motorcycle = db.load(Motorcycle.class, "AAAAMT02"); + db.commit(); + + assertEquals(motorcycle.getHolder().getName(), baby.getName()); + + db.close(); + } + + public void testCreateInvoice3() throws Exception { + LegalPerson acme = null; + NaturalPerson judge = null; + Motorcycle motorcycle = null; + + Database db = getJDOManager(DBNAME, MAPPING).getDatabase(); + db.setAutoStore(false); + db.begin(); + acme = db.load(LegalPerson.class, "AAAAacme"); + judge = db.load(NaturalPerson.class, "AAAjudge"); + motorcycle = db.load(Motorcycle.class, "AAAAMT03"); + db.commit(); + + Vector vInvoiceItem = new Vector(); + Invoice invoice = new Invoice(); + invoice.setOid("AAAINV03"); + invoice.setEmitter(acme); + invoice.setBillTo(judge); + invoice.setInvoiceItem(vInvoiceItem); + + InvoiceItem invoiceItem = new InvoiceItem(); + invoiceItem.setOid("AINVIT03"); + invoiceItem.setInvoice(invoice); + invoiceItem.setProduct(motorcycle); + invoiceItem.setQuantity(new Integer(1)); + invoiceItem.setPrice(new Double(23000)); + invoiceItem.setTotal(new Double(23000)); + vInvoiceItem.add(invoiceItem); + + motorcycle.setHolder(judge); + + db.setAutoStore(false); + db.begin(); + + OQLQuery oql = db.getOQLQuery( + "SELECT obj FROM " + Parameter.class.getName() + " obj " + + "WHERE person.oid = $1 AND idSys = $2"); + oql.bind(acme.getOid()); + oql.bind("INVSEQ"); + QueryResults results = oql.execute(); + + Parameter invoiceSeq = null; + if (results.hasMore()) { + invoiceSeq = (Parameter) results.nextElement(); + } + int newInvoiceSeq = invoiceSeq.getIntValue().intValue() + 1; + invoiceSeq.setIntValue(new Integer(newInvoiceSeq)); + invoice.setNumber(invoiceSeq.getIntValue()); + + oql.close(); + results.close(); + + db.update(motorcycle); + db.create(invoice); + + db.commit(); + + db.begin(); + motorcycle = db.load(Motorcycle.class, "AAAAMT03"); + db.commit(); + + assertEquals(motorcycle.getHolder().getName(), judge.getName()); + + db.close(); + } + public void testQuerySameInvoiceSubsequent() throws Exception { // first try executeQuery(); @@ -155,9 +324,12 @@ } public void testQuerySameInvoiceMT() throws Exception { + // initialize JDOManager once before starting threads. + getJDOManager(DBNAME, MAPPING).getDatabase(); + CPAThreadedTestRunnable[] tcr = new CPAThreadedTestRunnable[10]; for (int i = 0; i < tcr.length; i++) { - tcr[i] = new TestReferenceRunnable(this); + tcr[i] = new TestReferenceRunnable(this, false); } runTestRunnables(tcr); } @@ -180,7 +352,45 @@ db.commit(); db.close(); + + assertTrue(invoice != null); + } + + public void testQuerySameInvoiceReadOnlyMT() throws Exception { + // initialize JDOManager once before starting threads. + getJDOManager(DBNAME_RO, MAPPING_RO).getDatabase(); + + CPAThreadedTestRunnable[] tcr = new CPAThreadedTestRunnable[10]; + for (int i = 0; i < tcr.length; i++) { + tcr[i] = new TestReferenceRunnable(this, true); + } + runTestRunnables(tcr); + } + + protected void executeQueryReadOnly() throws Exception { + Invoice invoice = null; + Database db = getJDOManager(DBNAME_RO, MAPPING_RO).getDatabase(); + db.begin(); + + String oql = "SELECT obj FROM " + Invoice.class.getName() + " obj " + + "WHERE emitter = $1 and invoiceItem.quantity > $2"; + OQLQuery query = db.getOQLQuery(oql); + query.bind("AAAAacme"); + query.bind(new Integer(0)); + QueryResults results = query.execute(Database.READONLY); + int invoiceCount = 0; + while (results.hasMore()) { + invoice = (Invoice) results.nextElement(); + invoiceCount++; + } + results.close(); + query.close(); + + db.commit(); + db.close(); + assertTrue(invoice != null); + assertTrue(invoiceCount == 2); } } Index: cpactf/src/test/java/org/castor/cpa/test/test2861/TestReferenceRunnable.java =================================================================== --- cpactf/src/test/java/org/castor/cpa/test/test2861/TestReferenceRunnable.java (revision 8508) +++ cpactf/src/test/java/org/castor/cpa/test/test2861/TestReferenceRunnable.java (working copy) @@ -9,13 +9,19 @@ @Ignore public class TestReferenceRunnable extends CPAThreadedTestRunnable { private final TestReferenceChange _test; + private boolean _readOnly; - public TestReferenceRunnable(final TestReferenceChange test) { + public TestReferenceRunnable(final TestReferenceChange test, boolean readOnly) { _test = test; + _readOnly = readOnly; } @Override public void runTest() throws Throwable { - _test.executeQuery(); + if (_readOnly) { + _test.executeQueryReadOnly(); + } else { + _test.executeQuery(); + } } } Index: cpactf/src/test/resources/org/castor/cpa/test/test2861/mappingReadOnly.xml =================================================================== --- cpactf/src/test/resources/org/castor/cpa/test/test2861/mappingReadOnly.xml (revision 0) +++ cpactf/src/test/resources/org/castor/cpa/test/test2861/mappingReadOnly.xml (revision 0) @@ -0,0 +1,145 @@ + + + + + + Person + + + + + + + + + + + + + + + NaturalPerson + + + + + + + + + + + + + LegalPerson + + + + + + + + + + + + Product + + + + + + + + + + + + Motorcycle + + + + + + + + + + + + + + + + + + Invoice + + + + + + + + + + + + + + + + + + + + + org.castor.cpa.test.test2861.InvoiceItem + + + + + + + + + + + + + + + + + + + + + + + + Invoice + + + + + + + + + + + + + + + + + + \ No newline at end of file