Monday, December 11, 2006

Java 6 is Live!

Java 6 has been officially released. I've been using the beta for a while now and it's really solid with some awesome new features. I especially like the built in web services stack so you can create web services with simple annotations on your methods, for example:


@WebMethod
public String echo(String msg) throws EchoException
{
return "echo: " + msg;
}



And you don't need to get a big web service framework like Axis to do it! More Info...

Get it now!

Monday, November 27, 2006

db4o for Web Applications and Managing Object Identity

Time and time again, people ask about how to best manage disconnected object identity when using db4o. This is primarily a problem in web applications or web services since objects tend to live much longer than the connection to the database. db4o only "knows" about an object if it's in the same virtual machine and the connection to the database remains open. In fact, this is probably the number one problem I had when I first started to use db4o since web apps and web services was what I did. So I'm going to present you with some tips and a way to deal with this problem.

1: Keep Your Object Model Very Simple

Keep It Simple, Stupid. Words to live by. Whoever coined K.I.S.S. deserves a big pat on the back. In any case, the point here is to keep everything simple. Here are a few tips:

  • Don't build huge, complex object graphs just because you can. It doesn't make you smarter.
  • Keep business logic out of your objects and stick to POJO's (I don't know what the .NET camp calls them, PO.NO's?).
  • Don't overuse inheritance or interfaces (trust me, when it comes to web services, simple concrete classes are the easiest way to go)
  • Your object model does not have to be fully connected.

These tips apply to your data objects that will be persisted and passed around, not your application logic.

2: Consider Performance

Use common sense for this one. Ask yourself questions like: What will the performance be like if I send an object across the line that has a collection of 10,000 other objects? Do I really need all those objects at the same time or would it make more sense to query for subsets when I really need them? And what about memory consumption??

3: References to Other Objects by Identifiers Sometimes DOES Make Sense

For instance, lets say you are a large online retailer with 100 product categories and each category has 10,000 products which in turn have options like colors, sizes, reviews, ratings, etc. You may be tempted to just load up your Store object which has a Collection of Categories, which has a Collection of Products, which has a Collection of Reviews, and so on and so forth. Very bad idea.

Instead, try one way references from the lower item in the hierarchy to it's parent, for instance Product might have a Category field. Or even just a categoryId field:

class Product { int categoryId; }

Then when you want the Products in a category, you simply query for them based on the categoryId and you gain the advantage of being able to get a smaller subset of the original Collection by applying more constraints to the query which you couldn't do with a Collection:

query.constraint(Product.class);
query.descend("categoryId").constrain(categoryId);
query.descend("color").constrain("red"); // finer control

(I can already feel the flames from the OODB purists for this one).

Another prime example of why this is good is for web services. Lets say for instance there is a web service method call like this:

List getCategoryStats(Category category, Date startDate, Date endDate);

The method getCategoryStats returns a list of statistics such as category views, product views, purchases, etc for each day over the date range. Now if Category has a collection of 10,000 products that is has to send for every call to this service, it would be very inefficent! With just a simple Category object, it would be much faster. Or better yet, how about just sending the Category id:

List getCategoryStats(int categoryId, Date startDate, Date endDate);

4: Assigning ID's

I generally use a wrapper class around ObjectContainer that allows me to do some extra processing before or after an ObjectContainer call. This wrapper implements an interface that is pretty much the same as ObjectContainer:

public interface Db {
void set(Object ob);
void close();
Query query();
void delete(Object ob);
boolean isClosed();
Object refresh(Object ob);
void commit();
boolean isAttached(Object obj);
}

The wrapper class itself looks like the following:


public class DbWrapper implements Db {

private ObjectContainer db;
private static Map idMap = new HashMap();

public DbWrapper(ObjectContainer db) {
this.db = db;
}

public void set(Object ob) {
if (ob instanceof Idable) {
Idable idOb = (Idable) ob;
assignId(db, idOb);
}
db.set(ob);
}

public void close() {
db.close();
}

public void commit(){
db.commit();
}

public boolean isAttached(Object obj) {
return db.ext().isActive(obj);
}

private static synchronized void assignId(ObjectContainer db, Idable idOb) {
if (idOb.getId() != null) return; // don't set if it already has been
Integer counter = (Integer) idMap.get(idOb.getClass().getName());
if (counter == null) {
counter = getMax(db, idOb);
}
counter++;
idMap.put(idOb.getClass().getName(), counter);
idOb.setId(counter);
}

private static Integer getMax(ObjectContainer db, Idable idOb) {
Query q = db.query();
q.constrain(idOb.getClass());
q.descend("id").orderDescending();
ObjectSet result = q.execute();
while (result.hasNext()) {
Idable ob = (Idable) result.next();
Integer ret = ob.getId();
if(ret == null) ret = 0;
return ret;
}
return 0;
}

public Query query() {
return db.query();
}

public void delete(Object ob) {
db.delete(ob);
}

public boolean isClosed() {
return db.ext().isClosed();
}

public Object refresh(Object ob) {
if (ob instanceof Idable) {
Idable idable = (Idable) ob;
Object ret = null;
Query q = db.query();
q.constrain(idable.getClass());
q.descend("id").constrain(idable.getId());
ObjectSet result = q.execute();
if (result.hasNext()) {
ret = result.next();
}
return ret;
} else {
throw new RuntimeException("Object " + ob + " does not implement Idable, cannot refresh.");
}
}
public ObjectContainer getObjectContainer() {
return db;
}
}

As you can see in the set(Object) method, it will assign an ID to any object that implements Idable:

public interface Idable {
public Integer getId();
public void setId(Integer id);
}

There are some issues with this for instance it will not assign ID's down the object graph and you can only use this if connecting to the database from a single application. You could change this to traverse the graph, but what I usually do is just call set() on each object immediately after I create it so it gets an ID assigned.

Also, notice the refresh(Object o) method. This will re-get your object fresh from the db and at the same time, attach it to the database session so it will also be in db4o's reference system. If you are using the connection-per-request pattern, it may be a good idea to do a refresh at the start of each request before changes are applied.

An easy way to start using the code above is to modify db4oUtil from here and change getObjectContainer() to getDb() and have it return a Db instance instead of ObjectContainer.

This is far from the best solution, but it works for a lot of cases. There are other solutions too for instance you can use external callbacks. You can also vote for COR-161 in JIRA which is for putting ID support natively in db4o.

Monday, October 30, 2006

The db4o User I Met in the Phoenix Airport

Something funny happened to me while I was at the airport last week on the way home from Phoenix, Arizona. This guy who was at the boarding gate waiting for the same flight asked me if I worked for db4o because I was wearing my db4o t-shirt (as worn by Carl in the picture to the right). And I said "yes, do you know it?". He said he uses it!

So we got talking and he said they generally use it for development and rapid prototyping, and when it's ready, they actually outsource the work to India to convert it to TopLink (create all the mappings and database schema) for production. An interesting use of db4o that just goes to show that it sure helps with productivity.

It was really great to meet a random db4o user and to see how they are using it. I guess I'll be wearing my shirt more often. Ummm, Christof, could i get a 2 week supply of db4o schwag? ;)

Thursday, October 19, 2006

db4objects is Hiring!

db4objects is looking for great software engineers to join our team! Do you have have a passion for creating cutting edge software? Do you want to be a part of the team that brought you the most popular next generation Object Database? Do you want to work in a truly unique company with a team spread across the globe?

Your location does not matter, as long as you have a high speed connection. Please check out the openings in the Job Forum.

Sunday, September 03, 2006

Thursday, August 31, 2006

Less Than, Greater Than and Everything in Between

Some new stuff has gone into Sql4o:
  • supports all the comparison operations for the queries: <, >, <=, >=, =, <>, != .
  • supports Integers, Longs and Doubles as well as Strings.
  • and lastly, you can now build more complex where statements and they get correctly converted into SODA!
    • eg: from com.spaceprogram.db4o.Contact c where (age = 10 or age = 20) and income >= 50000.02
The latest code in SVN has it all, and of course this also means that you do do all this fun stuff in the latest ObjectManager source.

Next up... Aggregation.

Friday, August 25, 2006

Db4o on the Desktop

I've been taking the reigns on the new db4o ObjectManager 2.0 and of course I love to eat our own dog food (this ain't no kibbles and bits), so db4o is used for everything that needs persisting. This includes all the user settings, preferences, previous connections, query history, even previous window locations, and any other state I can get my hands on. I'm really trying to make it remember your previous sessions so you don't have to repeat anything the next time you start it up. And this turns out to be a LOT easier than you'd expect.

How It Works

There is a single HashMap in a Preferences object with a couple main methods; Preferences.get(String key) to retrieve any Object and Preferences.set(String key, Object value) to store anything that's changed. When set(key, object) is called, it will store and commit to a db4o database. The Preferences object is read in from the db4o database at startup and it is used to setup the application. If certain keys are not found in the Preferences HashMap, the default values are used.

Now every time the the app is restarted, the previous state can be restored with a simple db4o query asking for the Preferences object.

This is perhaps the easiest and most reliable way to store state across sessions for any desktop application. Plus you get the power of having a full-fledged object database at your disposable for storing all the other data in your application.

Why is this better than java.util.prefs ?

Because you can store objects, and it's much easier to use. Consider these comparisons:
  • Store and retrieve a database connection
    • With java.util.prefs, you'd have to store the connection url, username, and password in three different keys or as some string that you'd later have to parse, then probably put them in a DatabaseConnectionInfo object to use in your program.
    • With db4o prefs, you just store the DatabaseConnectionInfo object, then retrieve the full object again.
  • Storing frame size and location
    • With java.util.prefs, you'd store "width", "height", "top", and "left" values, then you'd have to get each of those and put them into a Dimension and Point object.
    • With db4o prefs, you store the Dimension and Point directly off the component and retrieve those directly back to set on the component.
      • eg: on componentResized(ComponentEvent e){
        Preferences.set("frameSize", e.getComponent().getSize());
      • then on startup: frame.setSize(Preferences.get("frameSize"));
So simple, yet saves so much time.

Wednesday, August 16, 2006

SQL For Objects - Sql4o

I’ve started an SQL for Objects project to enable string based querying support for db4o. This means you will be able to write queries like:

select * from com.abc.Contact c where c.name = 'leroy'

The project page is up here:

http://developer.db4o.com/ProjectSpaces/view.aspx/SQL_Support

The subversion repository with the code is up here:

http://code.google.com/p/db4o-sql/

I’ll release a jar file soon.

db4o Performance

When I tell people about db4o, I will always mention the following two things:

  1. It cuts your development time significantly (25% or more I would say). No more creating database schemas and making mapping files to map from your objects to your tables.
  2. It’s FAST! As in faster than that relational database you’re using right now.

Now a note on performance, take a look at these benchmark results. They compare a variety of common setups including Hibernate with MySQL and HSQLDB. db4o is clearly faster in most cases. And these benchmarks were done with db4o 5.2. db4o 5.5 is currently the latest and greatest and is generally substantially faster than 5.2. It will be interesting to see the Pole Position results with 5.5.

After seeing these benchmark results and knowing that db4o can save you a boatload of time (it’s also so pleasant to use), why would you not choose it?

Sunday, August 13, 2006

For All Things db4o... Unofficial

This blog will keep you abreast on all things unofficial for db4o.