Tuesday, March 16, 2010

ActiveJDBC Features - Birds View

August 12 update: it seems that some people link directly to this post and do not see in later posts that this project has been published on Google Code: http://code.google.com/p/activejdbc/
Original post text follows:


This blog is not really a tutorial, but rather a high level overview of some important features this framework has. As I stated in a previous post, I really bent backwards when implementing it, only to make it easier for developers to access persistent data.

I will present various use cases in a list format:

How to run a simple query

//find by id:
Person p = Person.findById(0);

//find first:
Person p = Person.first("name = ?", "John");

//simple select of multiples:
List<Person> people = 
Person.where("department = ? and hire_date > ? ", "IT", hireDate);
//...iterate

How to build pageable resultsets

List<Employee> people = 
Employee.where("department = ? and hire_date > ? ", "IT", hireDate)
              .offset(21)
                 .limit(10)
                    .orderBy("hire_date asc");
...iterate

This query will ensure that the returned result set will start at the 21st record and will return only 10 records, according to the "orderBy". The ActiveJDBC has a built in facility for various database flavors and it will generate appropriate SQL statement that is specific for a DB (Oracle, MySQL, etc) and is efficient. It will not fetch all records, starting with 1.
I tried these queries on tables with millions of records on Oracle and performance is flat.
In fact, you can learn how to create queries like this if ActiveJDBC logging is enabled.

How to create new records

Person p = new Person();
p.set("name", "Marilyn");
p.set("last_name", "Monroe");
p.set("dob", "1935-12-06");
p.saveIt();

This code should be self explanatory. As you can see, ActiveJDBC does not require to have getters and setters. You can write them, if you like, but IMHO, they are nothing but code pollution.

The set(name, value) method returns reference to the same model object, which makes it possible to string method calls like this:

Person p = new Person();
p.set("name", "Marilyn").set("last_name", "Monroe").set("dob", "1935-12-06").saveIt();

There is even a third way to set values into a model:

String[] names = {"first_name", "last_name", "dob"};
Object[] values = {"John", "Doe", new Date(johnsDobTime)}

Person john = new Person();
john.set(names, values).saveIt();
...and yet another way to set values into a model is with a use of a map:
Map values = ... initialize map
Person p = new Person();
p.fromMap(values);
p.saveIt();

I hope this was entertaining. I will write more about features of ActiveJDBC in future posts. Specifically how it handles relationships.

Constructive feedback is much appreciated!

Have fun :)

8 comments:

Ron Smith said...

Hi Igor,

I really like the amount of functionality this approach provides, while not bringing in all the baggage of a Hibernate, JPA, etc. It feels like something that hits the sweet spot where it works well for 90%+ of the scenarios where you need to access a RDBMS, with the minimal amount of baggage. Don't get me wrong, I'm glad to keep Hibernate in my tool belt, but there is definitely baggage that goes along with it; whether it's conceptual (e.g. sessions, caching, mappings, criteria queries), or the other libraries you have to drag in with it. I guess that's the price of doing so much in one framework.

What about a feature to reverse generate your POJOs from existing tables?

Igor Polevoy Blog said...

Thank you, Ron!

Well, the reverse generation of the entities from tables makes sense in cases you have code in those entities (read Hibernate with all the XML and getters and setters). For ActiveJDBC, for instance for a table People, an entity will look like this:


public class Person extends Model{}


Really nothing to generate, while it comes complete with the power of the framework.
That was one reason not to generate entities, while there is another:
I just do not believe that a developer writing DB code should be far removed from it. Therefore, I think that ActiveRecord (IMHO) is a model here with it's migrations capability (although not too happy about it's syntax). Basically, you generate your DDL, then write a model/entity class to support it.

I hope this clears it (my view :))

cheers,
igor

Blacktiger said...

That looks great. What about a feature to let you access a table without even an empty object? Something like:

Table person = new Table("person");
//find by id:
Row p = person.findById(0);

//find first:
Row p = person.first("name = ?", "John");

That way you don't have to go creating objects unless you actually have business logic for them. If you have tables where all you do is load and store data in them you don't have to clutter up your project with extra files.

Blacktiger said...

A couple more thoughts...

1) Why not have a constructor for the Person class that takes a map directly instead of a fromMap method?

2) I don't know if you already considered this, but what about returning a list of records that doesn't actually query the database until you request some values?

Igor Polevoy Blog said...

blacktiger, thanks for suggestion. You write: "...without even an empty object.." and then you proceed to create an empty object type Table.

Nevertheless, your approach is interesting. However, the ActiveJDBC is a true ORM and it is expected that you have logic (at least some basic validations) in your model. In other words, ActiveJDBC philosophy is against a way of coding called "Anemic Model" and for creation of meaningful models.

However, ActiveJDBC also provides a class called Base. This class encapsulates functionality similar to what you are proposing, but uses standard Java classes: Map, List, etc.

I will write a blog entry with examples on Base.

In any case, thanks for your input.

Blacktiger said...

Table was just a name I came up with on the fly. Perhaps you could simple create an instance of model directly, or use your Base object.

Most of the tables would have business logic, but I find that occasionally you just need to store and retrieve data directly from the database without the need to do anything particularly special with the object itself. Why force yourself to write extra code (even though it isn't much)? I could also see a situation in which you need to add a table to a running app but don't want to do a full build. You could drop a jsp in to do what you need to with the data until the next build when you replace it with a real object.

Igor Polevoy said...

Blacktiger, you wrote:
"A couple more thoughts..."

Well, correct. The Person class is a client class, and a developer can write a constructor that takes in a map and calls fromMap() there.

To answer your second question, the list that is returned from all the finder methods is actually a LazyList. This class only queries the DB when you start iterating through it.

thanks,
igor

Igor Polevoy said...

Blacktiger, you wrote:
"Table was just a name I came up with on the fly.."

I thought I answered this in a previous comment. Base class could be used to do ad hoc SQL, while the Model class is for ORM.

thanks,
igor