Re: 3.0M4 : Fetching from relationship returns TRANSIENT objects.

From: Laurent Marchal (contac..enux.fr)
Date: Fri Sep 19 2008 - 05:25:52 EDT

  • Next message: Steave: "Re: errors with derby and hsqldb (i need to switch from mysql to an embedded db)"

    Hi Andrus,

    I post on the mailing list with a new mail adress, because my provider
    sucks....
    I will try to detail more what i do, so you can reproduce this in your
    test case. Maybe i'ts because you don't have prefetching or "to dep PK".

    There is a relationship between MasterJob--->MasterJobAux where i
    checked "To Dep PK" since no ARTIST_AUX rows can exists without
    corresponding MasterJob.

    When i fetch a MasterJob i have added a prefetch for MasterJobAux :

    query.addPrefetch(MasterJob.RELATED_MASTER_JOB_AUX_PROPERTY);

    Code I use to get/create/delete MasterJobAux

                    protected MasterJobAux createAux(ILookupFieldCodes fieldCode, Short seqNo, String value) {
                            MasterJobAux aux = MasterJob.this.getObjectContext().newObject(MasterJobAux.class);
                            aux.setScheduleId(MasterJob.this.getScheduleId());
                            aux.setJobName(MasterJob.this.getJobName());
                            
                            aux.setJaFieldCode((int) fieldCode.getID());
                            aux.setJaSequenceNumber(seqNo);
                            aux.setJavalue(value);
                            aux.setRelatedMasterJob(MasterJob.this);

                            return aux;
                    }

                    protected List<MasterJobAux> fetchAuxList() throws OpconException {
                            return MasterJob.this.getRelatedMasterJobAux();
                    }

                    protected void deleteAux(MasterJobAux toDelete) throws OpconException {
                            MasterJob.this.removeFromRelatedMasterJobAux(toDelete);
                            MasterJob.this.getObjectContext().deleteObject(toDelete);
                    }

    Cayenne XML :

            <obj-entity name="MasterJob" className="com.sma.core.api.master.MasterJob" dbEntityName="JMASTER" superClassName="com.sma.core.api.DataAccessObject">
                    <obj-attribute name="accessCodeId" type="java.lang.Short" db-attribute-path="ACCESSCDID"/>
                    <obj-attribute name="altenateMachine1Id" type="java.lang.Short" db-attribute-path="ALTMACH1ID"/>
                    <obj-attribute name="altenateMachine2Id" type="java.lang.Short" db-attribute-path="ALTMACH2ID"/>
                    <obj-attribute name="altenateMachine3Id" type="java.lang.Short" db-attribute-path="ALTMACH3ID"/>
                    <obj-attribute name="deptartmentId" type="java.lang.Short" db-attribute-path="DEPTID"/>
                    <obj-attribute name="estimatedRuntime" type="java.lang.Integer" db-attribute-path="ESTRUNTIME"/>
                    <obj-attribute name="jobGroupId" type="java.lang.Short" db-attribute-path="JOBGROUPID"/>
                    <obj-attribute name="jobName" type="java.lang.String" db-attribute-path="JOBNAME"/>
                    <obj-attribute name="jobTypeId" type="java.lang.Short" db-attribute-path="JOBTYPE"/>
                    <obj-attribute name="machineGroupId" type="java.lang.Short" db-attribute-path="MACHGRPID"/>
                    <obj-attribute name="primaryMachineId" type="java.lang.Short" db-attribute-path="PRIMMACHID"/>
                    <obj-attribute name="scheduleId" type="java.lang.Integer" db-attribute-path="SKDID"/>
                    <obj-attribute name="shortName" type="java.lang.String" db-attribute-path="SHORTNAME"/>
            </obj-entity>
            <obj-entity name="MasterJobAux" className="com.sma.core.api.auxs.MasterJobAux" dbEntityName="JMASTER_AUX" superClassName="com.sma.core.api.DataAccessObject">
                    <obj-attribute name="jaFieldCode" type="java.lang.Integer" db-attribute-path="JAFC"/>
                    <obj-attribute name="jaSequenceNumber" type="java.lang.Short" db-attribute-path="JASEQNO"/>
                    <obj-attribute name="javalue" type="java.lang.String" db-attribute-path="JAVALUE"/>
                    <obj-attribute name="jobName" type="java.lang.String" db-attribute-path="JOBNAME"/>
                    <obj-attribute name="scheduleId" type="java.lang.Integer" db-attribute-path="SKDID"/>
            </obj-entity>

            <db-relationship name="toMasterJobAux" source="JMASTER" target="JMASTER_AUX" toDependentPK="true" toMany="true">
                    <db-attribute-pair source="SKDID" target="SKDID"/>
                    <db-attribute-pair source="JOBNAME" target="JOBNAME"/>
            </db-relationship>

            <obj-relationship name="relatedMasterJobAux" source="MasterJob" target="MasterJobAux" db-relationship-path="toMasterJobAux"/>

    To reproduce the problem :
    1) Fetch MasterJobAux using the relationships

            MasterJob.getRelatedMasterJobAux();

    2) delete/create some MasterJobAux

            MasterJob.removeFromRelatedMasterJobAux(toDelete);
            MasterJob.getObjectContext().deleteObject(toDelete);

    3) Rollback

    4) re-Fetch MasterJobAux using the relationships

            MasterJob.getRelatedMasterJobAux();

    I get TRANSIENT objects, you can look a the SQL log attached where there
    is some others details.

    Thanks.

    Laurent marchal.

    > Hi,
    >
    > Sorry, you posted this earlier and looks like nobody replied yet.
    >
    > So my question is : is this normal to get *TRANSIENT* objects from a
    > relationship after a rollback ?
    >
    > No, it is not normal. I just created a test case to reproduce it and
    > things seem to be working ok:
    >
    > PaintingInfo info = ctxt.newObject(PaintingInfo.class);
    > info.setTextReview("XXX"); p1.setToPaintingInfo(info);
    >
    > assertSame(info, p1.getToPaintingInfo()); ctxt.rollbackChanges();
    > assertNull(p1.getToPaintingInfo());
    >
    > Does it look like the scenario that you have, or am I missing something?
    >
    > Thanks, Andrus
    >
    > On Sep 15, 2008, at 12:22 PM, Laurent Marchal wrote:
    >
    > Hello again !
    >
    > I have a strange thing happening in Cayenne 3.0M4 : I have two tables
    > ARTIST and ARTIST_AUX where extended artist's properties are stored.
    >
    > There is a relationship between ARTIST--->ARTIST_AUX where i checked
    > "To Dep PK" since no ARTIST_AUX rows can exists without corresponding
    > ARTIST.
    >
    > I have also a addPrefetch(ARTIST_AUX) when i retrieve ARTISTs.
    >
    > In my "artist editor" I create ARTIST_AUX rows at need and when an
    > error occurs i do a rollback(). The strange thing is that when i
    > refetch ARTISTS_AUX with the relationship, instead of getting only
    > existing ARTISTS_AUX, i get also the ARTISTS_AUX created before the
    > rollback() with the *TRANSIENT* state and no ObjectContext associated.
    >
    > So my question is : is this normal to get *TRANSIENT* objects from a
    > relationship after a rollback ?
    >
    > Thanks Laurent Marchal.
    >


    ### DISPLAY A MASTERJOB ###

    --- will run 2 queries.
    --- transaction started.
    SELECT t0.SHORTNAME, t0.ALTMACH3ID, t0.ESTRUNTIME, t0.SKDID, t0.ALTMACH2ID, t0.PRIMMACHID, t0.JOBTYPE, t0.MACHGRPID, t0.DEPTID, t0.JOBGROUPID, t0.ALTMACH1ID, t0.JOBNAME, t0.ACCESSCDID FROM dbo.JMASTER t0 WHERE (t0.SKDID = ?) AND (t0.JOBNAME = ?) [bind: 1->SKDID:5, 2->JOBNAME:'Backup de la base']
    === returned 1 row. - took 3 ms.
    SELECT t0.SKDID, t0.JOBNAME, t0.JAVALUE, t0.JAFC, t0.JASEQNO FROM dbo.JMASTER_AUX t0 JOIN dbo.JMASTER t1 ON (t0.SKDID = t1.SKDID AND t0.JOBNAME = t1.JOBNAME) WHERE (t1.SKDID = ?) AND (t1.JOBNAME = ?) [bind: 1->SKDID:5, 2->JOBNAME:'Backup de la base']
    === returned 12 rows. - took 3 ms.
    +++ transaction committed.

    ### SAVE A MASTER JOB ON A READ ONLY DB TO FORCE ERROR ###

    --- will run 1 query.
    --- transaction started.
    INSERT INTO dbo.JMASTER_AUX (JAFC, JASEQNO, JAVALUE, JOBNAME, SKDID) VALUES (?, ?, ?, ?, ?)
    [batch bind: 1->JAFC:3006, 2->JASEQNO:1, 3->JAVALUE:'AUX1', 4->JOBNAME:'Backup de la base', 5->SKDID:5]
    [batch bind: 1->JAFC:3007, 2->JASEQNO:1, 3->JAVALUE:'AUX2', 4->JOBNAME:'Backup de la base', 5->SKDID:5]
    *** error.
    !STACK 0
    java.sql.BatchUpdateException: Échec de la mise à jour de la base de données "OPCONXPS4_PROD" car celle-ci est en lecture seule.
            at net.sourceforge.jtds.jdbc.JtdsStatement.executeBatch(JtdsStatement.java:944)
            at org.apache.cayenne.access.jdbc.BatchAction.runAsBatch(BatchAction.java:133)
            at org.apache.cayenne.access.jdbc.BatchAction.performAction(BatchAction.java:80)

    ### RESAVE THE MASTER JOB ###

    ERROR Transient OBJECT : {<no id>; transient; [relatedMasterJob=>{<ObjectId:MasterJob, JOBNAME=Backup de la base, SKDID=5>}; javalue=>AUX1; jaFieldCode=>3006; jaSequenceNumber=>1; scheduleId=>5; jobName=>Backup de la base]}
    ERROR Transient OBJECT : {<no id>; transient; [relatedMasterJob=>{<ObjectId:MasterJob, JOBNAME=Backup de la base, SKDID=5>}; javalue=>AUX2; jaFieldCode=>3007; jaSequenceNumber=>1; scheduleId=>5; jobName=>Backup de la base]}

    --- will run 1 query.
    --- transaction started.
    INSERT INTO dbo.JMASTER_AUX (JAFC, JASEQNO, JAVALUE, JOBNAME, SKDID) VALUES (?, ?, ?, ?, ?)
    [batch bind: 1->JAFC:3006, 2->JASEQNO:1, 3->JAVALUE:'AUX1', 4->JOBNAME:'Backup de la base', 5->SKDID:5]
    [batch bind: 1->JAFC:3007, 2->JASEQNO:1, 3->JAVALUE:'AUX2', 4->JOBNAME:'Backup de la base', 5->SKDID:5]
    *** error.
    !STACK 0
    java.sql.BatchUpdateException: Échec de la mise à jour de la base de données "OPCONXPS4_PROD" car celle-ci est en lecture seule.
            at net.sourceforge.jtds.jdbc.JtdsStatement.executeBatch(JtdsStatement.java:944)
            at org.apache.cayenne.access.jdbc.BatchAction.runAsBatch(BatchAction.java:133)
            at org.apache.cayenne.access.jdbc.BatchAction.performAction(BatchAction.java:80)



    This archive was generated by hypermail 2.0.0 : Fri Sep 19 2008 - 05:27:02 EDT