I have dealt with this situation before in another project.  In that project
we addressed this by creating an Archive class which is instantiated by the
caller of the save operation rather than by the class doing the saving.  In
this manner each object which needs to save itself does so to the archive
and handles its own transactioning thru methods on the archive itself.
Because the Archive class supports nested transactioning it is very easy to
wrap a transaction across multiple object saves.
Another class that we used was an ArchiveTrx which when constructed creates
a transaction on the its Archive argument.  This removes the hassle of
checking the return values of each save() call.
The code looks like the following

class Archive
{
        Archive()
        {
                //  obtain new JDBC connection from connection pool
        }
        //  other methods here for nested transactions
}

public class ArchiveTrx
{
        public ArchiveTrx( Archive _ar )
        {
                m_ar = _ar;
                m_ar.beginTrx();
                m_fRolledback = false;
        }

        public boolean execute( boolean _flag )
        {
                if ( !flag )
                        m_fRolledback = true;
        }

        public void rollback()
        {
                m_fRolledback = true;
        }

        public void close()
        {
                if ( !m_fRolledback )
                        m_ar.commit();
                else
                        m_ar.rollbackTrx();

        private boolean m_fRolledback;
}

//
//  create a new archive which in its constructor
//  retrieves a database connection from the connection pool
//
try
{
        //  the code below assumes a boolean return value.  if exceptions are
        //  thrown then the code can be moved into the catch handler to deal with
this.
        //
        Archive ar = new Archive();
        //  if exceptions are thrown then set rollback
        ArchiveTrx trx = new ArchiveTrx(ar);
        trx.execute(obj1.save(ar));
        trx.execute(obj2.save(ar));
        trx.execute(obj3.save(ar));
}
catch( SQLException _e )
{
        trx.rollback();
}
finally
{
        trx.close();
}

This code has the side benefit of allowing the application to better control
the type of archive to which the objects are being saved.  Additionally, it
gives each object complete control over its save() and its transactioning
but allows the caller to embed transactions in larger contexts and so allows
you to build application level transactioning into the system while keeping
the lower levels completely unaware of it.  The Archive class also allows
you to keep the connection obtaining logic inn one place and makes it easy
to replace database saves with file saves or network saves because the
client allocating the Archive has little or no knowledge about what it is
(e.g. because no parameters are passed).  I have complete (though with
little testing) code for these classes available if needed.

c.
-----Original Message-----
From: [EMAIL PROTECTED]
[mailto:[EMAIL PROTECTED]]On Behalf Of Nissim
Sent: Tuesday, June 27, 2000 4:56 AM
To: Turbine
Subject: Transactions (was Re: [PATCH] Postgresql (long))


I looked at that old message...some comments:  First of all we need
transaction functionality on select as well as insert, so I guess we'll
add methods for doTransactionSelect, doTransactionInsert,
doTransactionUpdate and doTransactionDelete.  While this will be fine to
fix the postgres prob.  It's not really useful for many types of
transactions, for example if you want to update something, then delete
something, and then update something else, all in one transaction, it
would be impossible.  I guess we can start with this implementation, and
then later create a class which contains an ordered list of database
operations which should happen within a transaction.

What do you think?

        -Nissim


John McNally wrote:
>
> As a core developer of BasePeer, but someone who is having trouble finding
> the time to follow these discussions carefully, I am going to make a
> suggestion.  Please tell me nicely why the following will not work:
>
> Add transaction functionality to BasePeer as I specified a while back.
This
> should be quite generally useful code.  I assume I will have some need for
> it in the future and will add it when I need it, but if others are needing
> it now, why not submit the code.
>
> Add the postgres specific code regarding the object_data column in the
peer
> where it is being updated.  I have no objection to this update being
> generically a transaction if it is handled gracefully in the db's that
don't
> support transactions (maybe there is a metadata property on transaction
> support).  Otherwise add specific code for postgres here.
>
> John McNally
>
> ----- Original Message -----
> From: Nissim <[EMAIL PROTECTED]>
> To: <[EMAIL PROTECTED]>
> Sent: Monday, June 26, 2000 4:04 PM
> Subject: [PATCH] Postgresql (long)
>
> > Hi all,
> >
> > This is about how to get the large object stuff working in turbine.
> > there's a bunch of patches, and some of the stuff isn't all that clean,
> > but I'd need some suggestions because cleaning it up might require
> > changes to public interfaces...
> >
> > First of all, to run turbine with pgsql, you have to get the newest
> > pgsql JDBC driver, and then apply a patch to it.  Find the patch here:
> >
http://www.mail-archive.com/turbine%40list.working-dogs.com/msg02249.html
> >
> > Note: if you use OID for something other than large object references
> > and you need it to look like an int in the JDBC metadata this patch will
> > break that.  If you have no idea what that means, you're probably safe
> > using it.
> >
> > Next I have patches for turbine, because to read or write object data
> > from postgres, you must be within a transaction.  The following code
> > adds some stuff to the DB interface, and the BasePeer to check if the
> > database you're using requires this functionality, and then to create
> > the transaction if you do.
> >
> > <Questions for turbine coders>
> > 1) In the BasePeer.doUpdate, we can check whether the table(s) getting
> > updated contain an object column, and only execute the transaction then,
> > but in the executeQuery method we don't have access to the table names,
> > so we have to make the transaction every time if it's postgres, should
> > we try to fix that?  We'd have to get the connection from the pool in
> > the doSelect method, and then pass it to the executeQuery, but that
> > would change the interface...should we add another executeQuery method
> > that takes a Connection param?
> >
> > 2) To test whether a column is objectdata, we don't have the SQL type,
> > so I just check if it's not instanceof String, Date or Number then it's
> > objectdata...should we add the SQL type to the columnMap...it's not
> > easy, because of all the public helper methods have the same interface
> > as the new methods would have...because length is an int and
> > java.sql.Types.* are ints...
> > </Questions for turbine coders>
> >
> > Thanks
> >
> > -Nissim
> >
> > Patches:
> >
> > Index: docs/schemas/Postgres_users_roles_permissions.sql
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/docs/schemas/Postgres_users_roles_permissions.
> sql,v
> > retrieving revision 1.1
> > diff -r1.1 Postgres_users_roles_permissions.sql
> > 83c83
> > <     OBJECTDATA varchar (255), -- oder doch bytea???
> > ---
> > >     OBJECTDATA oid,
> > Index: src/java/org/apache/turbine/om/peer/BasePeer.java
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/src/java/org/apache/turbine/om/peer/BasePeer.j
> ava,v
> > retrieving revision 1.25
> > diff -r1.25 BasePeer.java
> > 867a868,879
> > >
> > >             Statement stmt = null;
> > >             boolean doTransaction = DBBroker.getInstance()
> > >                                             .getDB( dbName )
> > >                                             .objectDataNeedsTrans();
> > >             if (doTransaction)
> > >             {
> > >                 stmt = connection.createStatement();
> > >                 stmt.executeUpdate("BEGIN TRANSACTION");
> > >                 stmt.close();
> > >             }
> > >
> > 870a883,890
> > >
> > >             if (doTransaction)
> > >             {
> > >                 stmt = connection.createStatement();
> > >                 stmt.executeUpdate("COMMIT TRANSACTION");
> > >                 stmt.close();
> > >             }
> > >
> > 1062a1083,1100
> > >
> > >             Statement stmt = null;
> > >             boolean doTransaction = false;
> > >             if ( db.objectDataNeedsTrans() )
> > >             {
> > >                 for (int i = 0; i<tables.size(); i++)
> > >                 {
> > >                     doTransaction = doTransaction ||
> > >                                     containsObjectColumn(dbMap,
> tables.get(i));
> > >                 }
> > >             }
> > >
> > >             if ( doTransaction )
> > >             {
> > >                 stmt = dbCon.createStatement();
> > >                 stmt.executeUpdate("BEGIN TRANSACTION");
> > >                 stmt.close();
> > >             }
> > 1122a1161,1168
> > >
> > >             if ( doTransaction )
> > >             {
> > >                 stmt = dbCon.createStatement();
> > >                 stmt.executeUpdate("COMMIT TRANSACTION");
> > >                 stmt.close();
> > >             }
> > >
> > 1230a1277,1289
> > >     }
> > >
> > >     /**
> > >      * Determines whether any of the columns in the table are object
> > >      * (binary) data.
> > >      *
> > >      * @return true if the table contains a column which will be a SQL
> > >      *    large object.
> > >      */
> > >     public static boolean containsObjectColumn(DatabaseMap dbMap,
String
> tableName) throws Exception
> > >     {
> > >         TableMap tmap = dbMap.getTable(tableName);
> > >         return tmap.containsObjectColumn();
> > Index: src/java/org/apache/turbine/util/db/map/TableMap.java
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/src/java/org/apache/turbine/util/db/map/TableM
> ap.java,v
> > retrieving revision 1.6
> > diff -r1.6 TableMap.java
> > 181a182,199
> > >      * Returns true if this tableMap contains a column with object
data.
> > >      */
> > >     public boolean containsObjectColumn()
> > >     {
> > >         Enumeration e = columns.elements();
> > >         while (e.hasMoreElements())
> > >         {
> > >             Object theType = ((ColumnMap)e.nextElement()).getType();
> > >             if (! ( theType instanceof String ||
> > >                     theType instanceof Number ||
> > >                     theType instanceof java.util.Date ) )
> > >             {
> > >                 return true;
> > >             }
> > >         }
> > >         return false;
> > >     }
> > >     /**
> > Index: src/java/org/apache/turbine/util/db/pool/DB.java
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/src/java/org/apache/turbine/util/db/pool/DB.ja
> va,v
> > retrieving revision 1.8
> > diff -r1.8 DB.java
> > 123a124,126
> > >     /** The method is used to chek whether
> > >         writing large objects to the DB requires a transaction. */
> > >     public abstract boolean objectDataNeedsTrans();
> > Index: src/java/org/apache/turbine/util/db/pool/DBDB2App.java
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/src/java/org/apache/turbine/util/db/pool/DBDB2
> App.java,v
> > retrieving revision 1.4
> > diff -r1.4 DBDB2App.java
> > 145a146,153
> > >
> > >     /**
> > >      * Returns false because transactions are only necessary in
> postgres.
> > >      */
> > >     public boolean objectDataNeedsTrans()
> > >     {
> > >         return false;
> > >     }
> > Index: src/java/org/apache/turbine/util/db/pool/DBHypersonicSQL.java
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/src/java/org/apache/turbine/util/db/pool/DBHyp
> ersonicSQL.java,v
> > retrieving revision 1.3
> > diff -r1.3 DBHypersonicSQL.java
> > 131a132,139
> > >
> > >     /**
> > >      * Returns false because transactions are only necessary in
> postgres.
> > >      */
> > >     public boolean objectDataNeedsTrans()
> > >     {
> > >         return false;
> > >     }
> > Index: src/java/org/apache/turbine/util/db/pool/DBInstantDB.java
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/src/java/org/apache/turbine/util/db/pool/DBIns
> tantDB.java,v
> > retrieving revision 1.5
> > diff -r1.5 DBInstantDB.java
> > 128a129,135
> > >     /**
> > >      * Returns false because transactions are only necessary in
> postgres.
> > >      */
> > >     public boolean objectDataNeedsTrans()
> > >     {
> > >         return false;
> > >     }
> > Index: src/java/org/apache/turbine/util/db/pool/DBInterbase.java
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/src/java/org/apache/turbine/util/db/pool/DBInt
> erbase.java,v
> > retrieving revision 1.1
> > diff -r1.1 DBInterbase.java
> > 136a137,143
> > >     /**
> > >      * Returns false because transactions are only necessary in
> postgres.
> > >      */
> > >     public boolean objectDataNeedsTrans()
> > >     {
> > >         return false;
> > >     }
> > Index: src/java/org/apache/turbine/util/db/pool/DBMM.java
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/src/java/org/apache/turbine/util/db/pool/DBMM.
> java,v
> > retrieving revision 1.5
> > diff -r1.5 DBMM.java
> > 134a135,142
> > >
> > >     /**
> > >      * Returns false because transactions are only necessary in
> postgres.
> > >      */
> > >     public boolean objectDataNeedsTrans()
> > >     {
> > >         return false;
> > >     }
> > Index: src/java/org/apache/turbine/util/db/pool/DBOracle.java
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/src/java/org/apache/turbine/util/db/pool/DBOra
> cle.java,v
> > retrieving revision 1.5
> > diff -r1.5 DBOracle.java
> > 133a134,141
> > >
> > >     /**
> > >      * Returns false because transactions are only necessary in
> postgres.
> > >      */
> > >     public boolean objectDataNeedsTrans()
> > >     {
> > >         return false;
> > >     }
> > Index: src/java/org/apache/turbine/util/db/pool/DBPostgres.java
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/src/java/org/apache/turbine/util/db/pool/DBPos
> tgres.java,v
> > retrieving revision 1.4
> > diff -r1.4 DBPostgres.java
> > 146a147,153
> > >     /**
> > >      * Returns true because transactions are necessary in postgres.
> > >      */
> > >     public boolean objectDataNeedsTrans()
> > >     {
> > >         return true;
> > >     }
> > Index: src/java/org/apache/turbine/util/db/pool/DBSybase.java
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/src/java/org/apache/turbine/util/db/pool/DBSyb
> ase.java,v
> > retrieving revision 1.3
> > diff -r1.3 DBSybase.java
> > 132a133,140
> > >
> > >     /**
> > >      * Returns false because transactions are only necessary in
> postgres.
> > >      */
> > >     public boolean objectDataNeedsTrans()
> > >     {
> > >         return false;
> > >     }
> > Index: src/java/org/apache/turbine/util/db/pool/DBWeblogic.java
> > ===================================================================
> > RCS file:
> >
>
/products/cvs/turbine/turbine/src/java/org/apache/turbine/util/db/pool/DBWeb
> logic.java,v
> > retrieving revision 1.4
> > diff -r1.4 DBWeblogic.java
> > 126a127,133
> > >     /**
> > >      * Returns false because transactions are only necessary in
> postgres.
> > >      */
> > >     public boolean objectDataNeedsTrans()
> > >     {
> > >         return false;
> > >     }
> >
> >
> > ------------------------------------------------------------
> > To subscribe:        [EMAIL PROTECTED]
> > To unsubscribe:      [EMAIL PROTECTED]
> > Search: <http://www.mail-archive.com/turbine%40list.working-dogs.com/>
> > Problems?:           [EMAIL PROTECTED]
> >
>
> ------------------------------------------------------------
> To subscribe:        [EMAIL PROTECTED]
> To unsubscribe:      [EMAIL PROTECTED]
> Search: <http://www.mail-archive.com/turbine%40list.working-dogs.com/>
> Problems?:           [EMAIL PROTECTED]


------------------------------------------------------------
To subscribe:        [EMAIL PROTECTED]
To unsubscribe:      [EMAIL PROTECTED]
Search: <http://www.mail-archive.com/turbine%40list.working-dogs.com/>
Problems?:           [EMAIL PROTECTED]



------------------------------------------------------------
To subscribe:        [EMAIL PROTECTED]
To unsubscribe:      [EMAIL PROTECTED]
Search: <http://www.mail-archive.com/turbine%40list.working-dogs.com/>
Problems?:           [EMAIL PROTECTED]

Reply via email to