Having worked with C-JDBC (currently Sequoia) I have pretty high expectations of a DBMS. From the feature list db4o seems to meet all of them (distributed replication and fallover with no loss of data). However the proof will be in the pudding.
To determine how well db4o interacts with Scala I started rewriting the Java examples in Scala. The next series of posts will highlight this.
We will begin with a Util trait which implements the listResult method. Since we can inherit multiple traits we can avoid having to do a mixin unlike the Java code. I personally prefer this from my Eiffel days.
package db4osc.chapter1;
import java.util.List;
trait Util {
def listResult(result : List) : Unit = {
Console.println(result.size());
var x = 0;
while(x < result.size()) {
Console.println(result.get(x));
x = x + 1;
}
}
}
Next we will define the one class which we will store instances of in the database. This is the Pilot class.
package db4osc.chapter1;
class Pilot(namec : String, pointsc : Int) {
var name : String = namec
var points : Int = pointsc
def getPoints() : Int = return points;
def addPoints(apoints : Int) : Int = {
points = points + apoints;
return points;
}
def getName() : String = return name;
override def toString() : String = return name+"/"+points;
}
The first example shows some basic creation and updating objects.
package db4osc.chapter1;
import com.db4o.Db4o;
import com.db4o.ObjectContainer;
object FirstStepsExample extends Application with Util {
val db = Db4o.openFile("chapter1.db");
Console.println("Start Scala db4o example");
storeFirstPilot(db);
storeSecondPilot(db);
retrieveAllPilotQBE(db);
retrieveAllPilots(db);
retrievePilotByName(db);
retrievePilotByExactPoints(db);
updatePilot(db);
deleteFirstPilotByName(db);
deleteSecondPilotByName(db);
db.close();
def storeFirstPilot(db : ObjectContainer) = {
val pilot = new Pilot("Michael Schumacher", 100);
db.set(pilot);
Console.println("Stored " + pilot);
}
def storeSecondPilot(db : ObjectContainer) = {
val pilot = new Pilot("Rubens Barrichello", 99);
db.set(pilot);
Console.println("Stored " + pilot);
}
def retrieveAllPilotQBE(db : ObjectContainer) = {
val proto = new Pilot(null, 0);
val result = db.get(proto);
listResult(result);
}
def retrieveAllPilots(db : ObjectContainer) = {
val result = db.get(classOf[Pilot]);
listResult(result);
}
def retrievePilotByName(db : ObjectContainer) = {
val proto = new Pilot("Michael Schumacher", 0);
val result = db.get(proto);
listResult(result);
}
def retrievePilotByExactPoints(db : ObjectContainer) = {
val proto = new Pilot(null,100);
val result = db.get(proto);
listResult(result);
}
def updatePilot(db : ObjectContainer) = {
val result = db.get(new Pilot("Michael Schumacher",0));
val found : Pilot = (result.next()).asInstanceOf[Pilot];
found.addPoints(11);
db.set(found);
Console.println("Added 11 points for " + found);
retrieveAllPilots(db);
}
def deleteFirstPilotByName(db : ObjectContainer) = {
val result = db.get(new Pilot("Michael Schumacher",0));
val found = result.next();
db.delete(found);
Console.println("Deleted " + found);
retrieveAllPilots(db);
}
def deleteSecondPilotByName(db : ObjectContainer) = {
val result = db.get(new Pilot("Rubens Barrichello",0));
val found = result.next();
db.delete(found);
Console.println("Deleted " + found);
retrieveAllPilots(db);
}
}
Next comes the native query example.
package db4osc.chapter1;
import com.db4o._;
import com.db4o.query._;
object NQExample extends Application with Util {
val db = Db4o.openFile("chapter1.db");
storePilots(db);
retrieveComplexSODA(db);
retrieveComplexNQ(db);
retrieveArbitraryCodeNQ(db);
clearDatabase(db);
db.close();
def storePilots(db : ObjectContainer) = {
db.set(new Pilot("Michael Schumacher",100));
db.set(new Pilot("Rubens Barrichello",99));
}
def retrieveComplexSODA(db : ObjectContainer) = {
val query : Query = db.query();
query.constrain(classOf[Pilot]);
val pointQuery : Query = query.descend("points");
query.descend("name").constrain("Rubens Barrichello")
.or(pointQuery.constrain(new Integer(99)).greater()
.and(pointQuery.constrain(new Integer(199)).smaller()));
val result = query.execute();
listResult(result);
}
def retrieveComplexNQ(db : ObjectContainer) = {
val result = db.query(new Predicate() {
def `match`(point : All) : boolean = {
val p = point.asInstanceOf[Pilot];
return (p.getPoints > 99) &&
(p.getPoints < 199) &&
(p.getName.equals("Rubens Barrichello"))
}
});
listResult(result);
}
def retrieveArbitraryCodeNQ(db : ObjectContainer) = {
val points = Array(1,100);
val result = db.query(new Predicate() {
def `match`(pilot : All) : boolean = {
val p = pilot.asInstanceOf[Pilot]
return (p.getPoints == 1) || (p.getPoints == 100)
}
});
listResult(result);
}
def clearDatabase(db : ObjectContainer) = {
val result : ObjectSet = db.get(classOf[Pilot]);
while(result.hasNext()) {
db.delete(result.next());
}
}
}
And lastly the query example.
package db4osc.chapter1;
import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.query.Constraint;
import com.db4o.query.Query;
object QueryExample extends Application with Util {
val db = Db4o.openFile("chapter1.db");
storeFirstPilot(db);
storeSecondPilot(db);
retrieveAllPilots(db);
retrievePilotByName(db);
retrievePilotByExactPoints(db);
retrieveByNegation(db);
retrieveByConjunction(db);
retrieveByDisjunction(db);
retrieveByComparison(db);
retrieveByDefaultFieldValue(db);
retrieveSorted(db);
clearDatabase(db);
db.close();
def storeFirstPilot(db : ObjectContainer) = {
val pilot1 = new Pilot("Michael Schumacher",100);
db.set(pilot1);
Console.println("Stored " + pilot1);
}
def storeSecondPilot(db : ObjectContainer) = {
val pilot2 = new Pilot("Rubens Barrichello",99);
db.set(pilot2);
Console.println("Stored " + pilot2);
}
def retrieveAllPilots(db : ObjectContainer) = {
val query : Query = db.query();
query.constrain(classOf[Pilot]);
val result = query.execute();
listResult(result);
}
def retrievePilotByName(db : ObjectContainer) = {
val query : Query = db.query();
query.constrain(classOf[Pilot]);
query.descend("name").constrain("Michael Schumacher");
val result = query.execute();
listResult(result);
}
def retrievePilotByExactPoints(db : ObjectContainer) = {
val query : Query = db.query();
query.constrain(classOf[Pilot]);
query.descend("points").constrain(new Integer(100));
val result = query.execute();
listResult(result);
}
def retrieveByNegation(db : ObjectContainer) = {
val query : Query = db.query();
query.constrain(classOf[Pilot]);
query.descend("name").constrain("Michael Schumacher").not();
val result = query.execute();
listResult(result);
}
def retrieveByConjunction(db : ObjectContainer) = {
val query : Query = db.query();
query.constrain(classOf[Pilot]);
val constr : Constraint =query.descend("name")
.constrain("Michael Schumacher");
query.descend("points")
.constrain(new Integer(99)).and(constr);
val result = query.execute();
listResult(result);
}
def retrieveByDisjunction(db : ObjectContainer) = {
val query : Query = db.query();
query.constrain(classOf[Pilot]);
val constr : Constraint = query.descend("name")
.constrain("Michael Schumacher");
query.descend("points")
.constrain(new Integer(99)).or(constr);
val result = query.execute();
listResult(result);
}
def retrieveByComparison(db : ObjectContainer) = {
val query : Query = db.query();
query.constrain(classOf[Pilot]);
query.descend("points")
.constrain(new Integer(99)).greater();
val result = query.execute();
listResult(result);
}
def retrieveByDefaultFieldValue(db : ObjectContainer) = {
val somebody = new Pilot("Somebody else",0);
db.set(somebody);
val query : Query = db.query();
query.constrain(classOf[Pilot]);
query.descend("points").constrain(new Integer(0));
val result : ObjectSet = query.execute();
listResult(result);
db.delete(somebody);
}
def retrieveSorted(db : ObjectContainer) = {
val query : Query = db.query();
query.constrain(classOf[Pilot]);
query.descend("name").orderAscending();
var result : ObjectSet = query.execute();
listResult(result);
query.descend("name").orderDescending();
result = query.execute();
listResult(result);
}
def clearDatabase(db : ObjectContainer) = {
val result : ObjectSet = db.get(classOf[Pilot]);
while(result.hasNext()) {
db.delete(result.next());
}
}
}
4 comments:
Good stuff! just added a Scala DokuWiki entry that shows how you can make your program even shorter, using @BeanProperty.
Looks interesting. Just a comment on your Scala code (I'm learning myself). I think the Util trait would be better defined as:
trait Util {
def listResult(result: List) = result.foreach(println)
}
That works with Scala 2.5.0 (and possibly earlier). Also, for your Pilot class, you could define it as:
class Pilot(val name: String, var points: Int) { ... }
and you'd automatically get the variables, plus name would be read-only (val vs var).
Derek
I've been poking around Scala for a while, and when I came across your blog, you lit a fire in me to try it with db4o. I am still learning about both of these technologies, and with that I've had some growing pains. Still, I've been able to work through each of my problems. That is, until I tried native queries.
I have spent hours on my own code while trying to get this to work. Using db4o-6.3-java5.jar yields a java.lang.IllegalArgumentException due to an invalid predicate. I eventually figured this was due to Scala not supporting Java's generics and db4o's use of reflection, so I tried the same release created for Java 1.2 through 1.4. I no longer get an exception, but now the Predicate class always creates the same result. Interestingly, in my test code, this always yields a true boolean value while the copy and paste of your code always yields false for the NQ tests.
For example, I copied the Util trait, Pilot class and NQExample application into a Scala project in Eclipse. The build path includes:
- Scala compiler 2.6.9RC312812,
- JRE 1.6.0.03
- db4o-6.3-java1.2.jar
When the NQExample is executed, the following output is generated:
2
Michael Schumacher/100
Rubens Barrichello/99
0
0
Based on a reading of the code, each of these queries should have returned 1 hit. Sadly, this is not the case. I even tried hard coding a true return value in the Predicate to no effect. To remove one variable from the equation, I then reproduced the native query in Java using the same Scala objects and database file as created from the unsuccessful query test. The Java query worked as expected.
Unfortunately, you did not post any results from your program execution, so I cannot conclude that this is unique to my environment. Did you verify the results were correct? If so, what versions of dependencies were you using.
Thanks.
James
I've figured out the problem with native queries. I don't exactly know what changed to break the implementation you've got here, but I suspect it is related to the evolution of the Scala language.
First, I want to correct some statements in my previous post. The expected results should not have 1 hit for each of the native query examples. The retrieveComplexNQ(...) example should have zero results while retrieveArbitraryCodeNQ(...) should have one. Still, the problems I described are accurate.
I've documented my analysis on my own blog at http://matlik.net/blog/2007/11/28/scala-and-db4o-native-queries/ in detail. The short answer is that db4o does not support a match(java.lang.Object) filter method, which is essentially what the Scala compiler requires (scala.Any is more or less an alias for java.lang.Object) when compiling against the Java 5 versions of db4o. To work around this, you can implement a stub 'match(Any):boolean', that will never be used, and the real match method with the desired type.
Alternatively, you can use the version of db4o that doesn't support generics by defining match methods with the desired type instead of scala.All or scala.Any... the way db4o native queries are intended to be used.
Post a Comment