8. Transactions


Probably you have already wondered how db4o handles concurrent access to a single database. Just as any other DBMS, db4o provides a transaction mechanism. Before we take a look at multiple, perhaps even remote, clients accessing a db4o instance in parallel, we will introduce db4o transaction concepts in isolation.


    8.1. Commit and rollback


    You may not have noticed it, but we have already been working with transactions from the first chapter on. By definition, you are always working inside a transaction when interacting with db4o. A transaction is implicitly started when you open a container, and the current transaction is implicitly committed when you close it again. So the following code snippet to store a car is semantically identical to the ones we have seen before; it just makes the commit explicit.

    // storeCarCommit
    Pilot pilot = new Pilot("Rubens Barrichello", 99);
    Car car = new Car("BMW");
    car.Pilot = pilot;
    db.Store(car);
    db.Commit();


    // listAllCars
    IObjectSet result = db.QueryByExample(typeof(Car));
    ListResult(result);


    However, we can also rollback the current transaction, resetting the state of our database to the last commit point.

    // storeCarRollback
    Pilot pilot = new Pilot("Michael Schumacher", 100);
    Car car = new Car("Ferrari");
    car.Pilot = pilot;
    db.Store(car);
    db.Rollback();


    // listAllCars
    IObjectSet result = db.QueryByExample(typeof(Car));
    ListResult(result);



    8.2. Refresh live objects


    There's one problem, though: We can roll back our database, but this cannot automagically trigger a rollback for our live objects.

    // carSnapshotRollback
    IObjectSet result = db.QueryByExample(new Car("BMW"));
    Car car = (Car)result.Next();
    car.Snapshot();
    db.Store(car);
    db.Rollback();
    Console.WriteLine(car);


    We will have to explicitly refresh our live objects when we suspect they may have participated in a rollback transaction.

    // carSnapshotRollbackRefresh
    IObjectSet result=db.QueryByExample(new Car("BMW"));
    Car car=(Car)result.Next();
    car.Snapshot();
    db.Store(car);
    db.Rollback();
    db.Ext().Refresh(car, int.MaxValue);
    Console.WriteLine(car);


    What is this IExtObjectContainer construct good for? Well, it provides some functionality that is in itself stable, but the API may still be subject to change. As soon as we are confident that no more changes will occur, ext functionality will be transferred to the common IObjectContainer API.

    Finally, we clean up again.

    // deleteAll
    IObjectSet result = db.QueryByExample(typeof(Object));
    foreach (object item in result)
    {
        db.Delete(item);
    }



    8.3. Conclusion


    We have seen how transactions work for a single client. In the Client/Server chapter  we will see how the transaction concept extends to multiple clients, whether they are located within the same runtime or on a remote machine.

    Let't first revisit Activation again in the next chapter and take a look at how db4o can take care of our Object lifecycle automatically.


    8.4. Full source


    using System;
    using System.IO;
    using Db4objects.Db4o;
    namespace Db4objects.Db4o.Tutorial.F1.Chapter5
    {
        public class TransactionExample : Util
        {
            public static void Main(string[] args)
            {
                File.Delete(Util.YapFileName);
                IObjectContainer db=Db4oFactory.OpenFile(Util.YapFileName);
                try
                {
                    StoreCarCommit(db);
                    db.Close();
                    db = Db4oFactory.OpenFile(Util.YapFileName);
                    ListAllCars(db);
                    StoreCarRollback(db);
                    db.Close();
                    db = Db4oFactory.OpenFile(Util.YapFileName);
                    ListAllCars(db);
                    CarSnapshotRollback(db);
                    CarSnapshotRollbackRefresh(db);
                }
                finally
                {
                    db.Close();
                }
            }
            
            public static void StoreCarCommit(IObjectContainer db)
            {
                Pilot pilot = new Pilot("Rubens Barrichello", 99);
                Car car = new Car("BMW");
                car.Pilot = pilot;
                db.Store(car);
                db.Commit();
            }
        
            public static void ListAllCars(IObjectContainer db)
            {
                IObjectSet result = db.QueryByExample(typeof(Car));
                ListResult(result);
            }
            
            public static void StoreCarRollback(IObjectContainer db)
            {
                Pilot pilot = new Pilot("Michael Schumacher", 100);
                Car car = new Car("Ferrari");
                car.Pilot = pilot;
                db.Store(car);
                db.Rollback();
            }
        
            public static void CarSnapshotRollback(IObjectContainer db)
            {
                IObjectSet result = db.QueryByExample(new Car("BMW"));
                Car car = (Car)result.Next();
                car.Snapshot();
                db.Store(car);
                db.Rollback();
                Console.WriteLine(car);
            }
        
            public static void CarSnapshotRollbackRefresh(IObjectContainer db)
            {
                IObjectSet result=db.QueryByExample(new Car("BMW"));
                Car car=(Car)result.Next();
                car.Snapshot();
                db.Store(car);
                db.Rollback();
                db.Ext().Refresh(car, int.MaxValue);
                Console.WriteLine(car);
            }
        }
    }




    www.db4o.com