6. Collections and Arrays


We will slowly move towards real-time data processing now by installing sensors to our car and collecting their output.

package com.db4odoc.f1.chapter4;
import java.util.*;
public class SensorReadout {
    private double[] values;
    private Date time;
    private Car car;
    public SensorReadout(double[] values,Date time,Car car) {
        this.values=values;
        this.time=time;
        this.car=car;
    }
    public Car getCar() {
        return car;
    }
    public Date getTime() {
        return time;
    }
    public int getNumValues() {
        return values.length;
    }
    
    public double[] getValues(){
        return values;
    }
    
    public double getValue(int idx) {
        return values[idx];
    }
    public String toString() {
        StringBuffer str=new StringBuffer();
        str.append(car.toString())
            .append(" : ")
            .append(time.getTime())
            .append(" : ");
        for(int idx=0;idx<values.length;idx++) {
            if(idx>0) {
                str.append(',');
            }
            str.append(values[idx]);
        }
        return str.toString();
    }
}


A car may produce its current sensor readout when requested and keep a list of readouts collected during a race.

package com.db4odoc.f1.chapter4;
import java.util.*;
public class Car {
    private String model;
    private Pilot pilot;
    private List history;
    public Car(String model) {
        this(model,new ArrayList());
    }
    public Car(String model,List history) {
        this.model=model;
        this.pilot=null;
        this.history=history;
    }
    public Pilot getPilot() {
        return pilot;
    }
    public void setPilot(Pilot pilot) {
        this.pilot=pilot;
    }
    public String getModel() {
        return model;
    }
    public List getHistory() {
        return history;
    }
    
    public void snapshot() {
        history.add(new SensorReadout(poll(),new Date(),this));
    }
    
    protected double[] poll() {
        int factor=history.size()+1;
        return new double[]{0.1d*factor,0.2d*factor,0.3d*factor};
    }
    
    public String toString() {
        return model+"["+pilot+"]/"+history.size();
    }
}


We will constrain ourselves to rather static data at the moment and add flexibility during the next chapters.


    6.1. Storing


    This should be familiar by now.

    // storeFirstCar
    Car car1 = new Car("Ferrari");
    Pilot pilot1 = new Pilot("Michael Schumacher", 100);
    car1.setPilot(pilot1);
    db.store(car1);


    The second car will take two snapshots immediately at startup.

    // storeSecondCar
    Pilot pilot2 = new Pilot("Rubens Barrichello", 99);
    Car car2 = new Car("BMW");
    car2.setPilot(pilot2);
    car2.snapshot();
    car2.snapshot();
    db.store(car2);



    6.2. Retrieving



      6.2.1. QBE


      First let us verify that we indeed have taken snapshots.

      // retrieveAllSensorReadout
      SensorReadout proto = new SensorReadout(null, null, null);
      ObjectSet results = db.queryByExample(proto);
      listResult(results);


      As a prototype for an array, we provide an array of the same type, containing only the values we expect the result to contain.

      // retrieveSensorReadoutQBE
      SensorReadout proto = new SensorReadout(new double[] { 0.3, 0.1 },
              null, null);
      ObjectSet results = db.queryByExample(proto);
      listResult(results);


      Note that the actual position of the given elements in the prototype array is irrelevant.

      To retrieve a car by its stored sensor readouts, we install a history containing the sought-after values.

      // retrieveCarQBE
      SensorReadout protoreadout = new SensorReadout(
              new double[] { 0.6, 0.2 }, null, null);
      List protohistory = new ArrayList();
      protohistory.add(protoreadout);
      Car protocar = new Car(null, protohistory);
      ObjectSet result = db.queryByExample(protocar);
      listResult(result);


      We can also query for the collections themselves, since they are first class objects.

      // retrieveCollections
      ObjectSet result = db.queryByExample(new ArrayList());
      listResult(result);


      This doesn't work with arrays, though.

      // retrieveArrays
      ObjectSet result = db.queryByExample(new double[] { 0.6, 0.4 });
      listResult(result);



      6.2.2. Native Queries


      If we want to use Native Queries to find SensorReadouts with matching values, we simply write this as if we would check every single instance:


      // retrieveSensorReadoutNative
      List<SensorReadout> results = db.query(new Predicate<SensorReadout>() {
          public boolean match(SensorReadout candidate) {
              return Arrays.binarySearch(candidate.getValues(), 0.3) >= 0
                      && Arrays.binarySearch(candidate.getValues(), 1.0) >= 0;
          }
      });
      listResult(results);


      And here's how we find Cars with matching readout values:


      // retrieveCarNative
      List<Car> results = db.query(new Predicate<Car>() {
          public boolean match(Car candidate) {
              List history = candidate.getHistory();
              for (int i = 0; i < history.size(); i++) {
                  SensorReadout readout = (SensorReadout) history.get(i);
                  if (Arrays.binarySearch(readout.getValues(), 0.6) >= 0
                          || Arrays.binarySearch(readout.getValues(), 0.2) >= 0)
                      return true;
              }
              return false;
          }
      });
      listResult(results);



      6.2.3. Query API


      Handling of arrays and collections is analogous to the previous example. First, lets retrieve only the SensorReadouts with specific values:

      // retrieveSensorReadoutQuery
      Query query = db.query();
      query.constrain(SensorReadout.class);
      Query valuequery = query.descend("values");
      valuequery.constrain(new Double(0.3));
      valuequery.constrain(new Double(0.1));
      ObjectSet result = query.execute();
      listResult(result);


      Then let's get some Cars with matching Readout values:

      // retrieveCarQuery
      Query query = db.query();
      query.constrain(Car.class);
      Query historyquery = query.descend("history");
      historyquery.constrain(SensorReadout.class);
      Query valuequery = historyquery.descend("values");
      valuequery.constrain(new Double(0.3));
      valuequery.constrain(new Double(0.1));
      ObjectSet result = query.execute();
      listResult(result);



    6.3. Updating and deleting


    This should be familiar, we just have to remember to take care of the update depth.

    // updateCar
    EmbeddedConfiguration config = Db4oEmbedded.newConfiguration();
    config.common().objectClass(Car.class).cascadeOnUpdate(true);
    ObjectContainer db = Db4oEmbedded.openFile(config, DB4OFILENAME);
    List<Car> results = db.query(new Predicate<Car>() {
        public boolean match(Car candidate) {
            return true;
        }
    });
    if (results.size() > 0) {
        Car car = results.get(0);
        car.snapshot();
        db.store(car);
        retrieveAllSensorReadoutNative(db);
    }
    db.close();


    There's nothing special about deleting arrays and collections, too.

    Deleting an object from a collection is an update, too, of course.

    // updateCollection
    EmbeddedConfiguration config = Db4oEmbedded.newConfiguration();
    config.common().objectClass(Car.class).cascadeOnUpdate(true);
    ObjectContainer db = Db4oEmbedded.openFile(config, DB4OFILENAME);
    ObjectSet<Car> results = db.query(new Predicate<Car>() {
        public boolean match(Car candidate) {
            return true;
        }
    });
    if (results.hasNext()) {
        Car car = (Car) results.next();
        car.getHistory().remove(0);
        db.store(car.getHistory());
        results = db.query(new Predicate<Car>() {
            public boolean match(Car candidate) {
                return true;
            }
        });
        while (results.hasNext()) {
            car = results.next();
            for (int idx = 0; idx < car.getHistory().size(); idx++) {
                System.out.println(car.getHistory().get(idx));
            }
        }
    }
    db.close();


    (This example also shows that with db4o it is quite easy to access object internals we were never meant to see. Please keep this always in mind and be careful.)

    We will delete all cars from the database again to prepare for the next chapter.

    // deleteAll
    EmbeddedConfiguration config = Db4oEmbedded.newConfiguration();
    config.common().objectClass(Car.class).cascadeOnDelete(true);
    ObjectContainer db = Db4oEmbedded.openFile(config, DB4OFILENAME);
    ObjectSet<Car> cars = db.query(new Predicate<Car>() {
        public boolean match(Car candidate) {
            return true;
        }
    });
    while (cars.hasNext()) {
        db.delete(cars.next());
    }
    ObjectSet<SensorReadout> readouts = db
            .query(new Predicate<SensorReadout>() {
                public boolean match(SensorReadout candidate) {
                    return true;
                }
            });
    while (readouts.hasNext()) {
        db.delete(readouts.next());
    }
    db.close();



    6.4. Conclusion


    Ok, collections are just objects. But why did we have to specify the concrete ArrayList type all the way? Was that necessary? How does db4o handle inheritance? We will cover that in the next chapter.


    6.5. Full source


    package com.db4odoc.f1.chapter4;
    import java.io.*;
    import java.util.*;
    import com.db4o.*;
    import com.db4o.config.*;
    import com.db4o.query.*;
    import com.db4odoc.f1.*;
    public class CollectionsExample extends Util {
        final static String DB4OFILENAME = System.getProperty("user.home") + "/formula1.db4o";
        public static void main(String[] args) {
            new File(DB4OFILENAME).delete();
            ObjectContainer db = Db4oEmbedded.openFile(Db4oEmbedded
                    .newConfiguration(), DB4OFILENAME);
            try {
                storeFirstCar(db);
                storeSecondCar(db);
                retrieveAllSensorReadout(db);
                retrieveSensorReadoutQBE(db);
                retrieveCarQBE(db);
                retrieveCollections(db);
                retrieveArrays(db);
                retrieveAllSensorReadoutNative(db);
                retrieveSensorReadoutNative(db);
                retrieveCarNative(db);
                retrieveSensorReadoutQuery(db);
                retrieveCarQuery(db);
                db.close();
                updateCar();
                updateCollection();
                deleteAll();
            } finally {
                db.close();
            }
        }
        public static void storeFirstCar(ObjectContainer db) {
            Car car1 = new Car("Ferrari");
            Pilot pilot1 = new Pilot("Michael Schumacher", 100);
            car1.setPilot(pilot1);
            db.store(car1);
        }
        public static void storeSecondCar(ObjectContainer db) {
            Pilot pilot2 = new Pilot("Rubens Barrichello", 99);
            Car car2 = new Car("BMW");
            car2.setPilot(pilot2);
            car2.snapshot();
            car2.snapshot();
            db.store(car2);
        }
        public static void retrieveAllSensorReadout(ObjectContainer db) {
            SensorReadout proto = new SensorReadout(null, null, null);
            ObjectSet results = db.queryByExample(proto);
            listResult(results);
        }
        public static void retrieveAllSensorReadoutNative(ObjectContainer db) {
            List<SensorReadout> results = db.query(new Predicate<SensorReadout>() {
                public boolean match(SensorReadout candidate) {
                    return true;
                }
            });
            listResult(results);
        }
        public static void retrieveSensorReadoutQBE(ObjectContainer db) {
            SensorReadout proto = new SensorReadout(new double[] { 0.3, 0.1 },
                    null, null);
            ObjectSet results = db.queryByExample(proto);
            listResult(results);
        }
        public static void retrieveSensorReadoutNative(ObjectContainer db) {
            List<SensorReadout> results = db.query(new Predicate<SensorReadout>() {
                public boolean match(SensorReadout candidate) {
                    return Arrays.binarySearch(candidate.getValues(), 0.3) >= 0
                            && Arrays.binarySearch(candidate.getValues(), 1.0) >= 0;
                }
            });
            listResult(results);
        }
        public static void retrieveCarQBE(ObjectContainer db) {
            SensorReadout protoreadout = new SensorReadout(
                    new double[] { 0.6, 0.2 }, null, null);
            List protohistory = new ArrayList();
            protohistory.add(protoreadout);
            Car protocar = new Car(null, protohistory);
            ObjectSet result = db.queryByExample(protocar);
            listResult(result);
        }
        public static void retrieveCarNative(ObjectContainer db) {
            List<Car> results = db.query(new Predicate<Car>() {
                public boolean match(Car candidate) {
                    List history = candidate.getHistory();
                    for (int i = 0; i < history.size(); i++) {
                        SensorReadout readout = (SensorReadout) history.get(i);
                        if (Arrays.binarySearch(readout.getValues(), 0.6) >= 0
                                || Arrays.binarySearch(readout.getValues(), 0.2) >= 0)
                            return true;
                    }
                    return false;
                }
            });
            listResult(results);
        }
        public static void retrieveCollections(ObjectContainer db) {
            ObjectSet result = db.queryByExample(new ArrayList());
            listResult(result);
        }
        public static void retrieveArrays(ObjectContainer db) {
            ObjectSet result = db.queryByExample(new double[] { 0.6, 0.4 });
            listResult(result);
        }
        public static void retrieveSensorReadoutQuery(ObjectContainer db) {
            Query query = db.query();
            query.constrain(SensorReadout.class);
            Query valuequery = query.descend("values");
            valuequery.constrain(new Double(0.3));
            valuequery.constrain(new Double(0.1));
            ObjectSet result = query.execute();
            listResult(result);
        }
        public static void retrieveCarQuery(ObjectContainer db) {
            Query query = db.query();
            query.constrain(Car.class);
            Query historyquery = query.descend("history");
            historyquery.constrain(SensorReadout.class);
            Query valuequery = historyquery.descend("values");
            valuequery.constrain(new Double(0.3));
            valuequery.constrain(new Double(0.1));
            ObjectSet result = query.execute();
            listResult(result);
        }
        public static void updateCar() {
            EmbeddedConfiguration config = Db4oEmbedded.newConfiguration();
            config.common().objectClass(Car.class).cascadeOnUpdate(true);
            ObjectContainer db = Db4oEmbedded.openFile(config, DB4OFILENAME);
            List<Car> results = db.query(new Predicate<Car>() {
                public boolean match(Car candidate) {
                    return true;
                }
            });
            if (results.size() > 0) {
                Car car = results.get(0);
                car.snapshot();
                db.store(car);
                retrieveAllSensorReadoutNative(db);
            }
            db.close();
        }
        public static void updateCollection() {
            EmbeddedConfiguration config = Db4oEmbedded.newConfiguration();
            config.common().objectClass(Car.class).cascadeOnUpdate(true);
            ObjectContainer db = Db4oEmbedded.openFile(config, DB4OFILENAME);
            ObjectSet<Car> results = db.query(new Predicate<Car>() {
                public boolean match(Car candidate) {
                    return true;
                }
            });
            if (results.hasNext()) {
                Car car = (Car) results.next();
                car.getHistory().remove(0);
                db.store(car.getHistory());
                results = db.query(new Predicate<Car>() {
                    public boolean match(Car candidate) {
                        return true;
                    }
                });
                while (results.hasNext()) {
                    car = results.next();
                    for (int idx = 0; idx < car.getHistory().size(); idx++) {
                        System.out.println(car.getHistory().get(idx));
                    }
                }
            }
            db.close();
        }
        public static void deleteAll() {
            EmbeddedConfiguration config = Db4oEmbedded.newConfiguration();
            config.common().objectClass(Car.class).cascadeOnDelete(true);
            ObjectContainer db = Db4oEmbedded.openFile(config, DB4OFILENAME);
            ObjectSet<Car> cars = db.query(new Predicate<Car>() {
                public boolean match(Car candidate) {
                    return true;
                }
            });
            while (cars.hasNext()) {
                db.delete(cars.next());
            }
            ObjectSet<SensorReadout> readouts = db
                    .query(new Predicate<SensorReadout>() {
                        public boolean match(SensorReadout candidate) {
                            return true;
                        }
                    });
            while (readouts.hasNext()) {
                db.delete(readouts.next());
            }
            db.close();
        }
    }




    www.db4o.com