- Code Commit - http://www.codecommit.com/blog -

An Easier Java ORM Part 2

Posted By Daniel Spiewak On July 19, 2007 @ 12:57 pm In Java | 9 Comments

Well, by popular demand on the first post [1], I now bring you the second part in my series introducing the new Java ORM, ActiveObjects [2]. In the first post, I gave some very basic examples of how AO could be used to simplify database access in your application. And while trivially interesting, these examples really don’t do justice to the power of AO’s interface model and how it can best be used to serve you. So, I will be devoting this post to SaveableEntity(s) and implementations.

SaveableEntity

SaveableEntity is an interface which extends the Entity super-interface designed to overcome the inherent performance drawbacks in a simple getter/setter model ORM. For example, suppose we have the following trivial Entity interface:

public interface Person extends Entity {
    public String getFirstName();
    public void setFirstName(String firstName);
 
    public String getLastName();
    public void setLastName(String lastName);
}

We could use this entity in the following way:

private void initializePerson(EntityManager em, String name) throws SQLException {
    String[] names = name.split(' ');
 
    Person p = em.create(Person.class);
    p.setFirstName(names[0]);
    p.setLastName(names[1]);
}

This is all well and good, until we look at the actual SQL being executed by this code:

INSERT INTO person (id) VALUES (DEFAULT);
UPDATE person SET firstName = ? WHERE id = ?;
UPDATE person SET lastName = ? WHERE id = ?;

These are executed as separate PreparedStatement(s), under separate connections, totally unconnected. If we didn’t include a pooling library in the classpath, the connections wouldn’t be pooled and we would incur a significant performance hit from this sort of thing.

Enter SaveableEntity. SaveableEntity prevents the setters from immediately executing their respective UPDATE statements. Rather, the UPDATEs are held in reserve until the save() method is invoked. Here’s the above example rewritten to use SaveableEntity:

public interface Person extends SaveableEntity {
    public String getFirstName();
    public void setFirstName(String firstName);
 
    public String getLastName();
    public void setLastName(String lastName);
}
 
private void initializePerson(EntityManager em, String name) throws SQLException {
    String[] names = name.split(' ');
 
    Person p = em.create(Person.class);
    p.setFirstName(names[0]);
    p.setLastName(names[1]);
    p.save();
}

Only one more line of Java code, but it diminishes the SQL executed by 30%:

INSERT INTO person (ID) VALUES (DEFAULT);
UPDATE person SET firstName = ?,lastName = ? WHERE ID = ?;

This might not seem like such a big deal when you’re only working with single and duel fielded entities, but when you get into far more complex models – say, on the order of 30-40 fields – it makes a big difference if UPDATEs are merged where possible.

Implementations

So on a swift turn round another tack, we’re now going to look at defined implementations in ActiveObjects and how they are both a) a major pain you-know-where, and b) incredibly useful.

As was pointed out in a comment [3] on my last post, ActiveObjects as I have presented it so far is just a data wrapping layer. It just allows simple database access. No frills, and no model-specific business logic. This actually poses a bit of a problem in many senarios.

For example, let’s assume you have a basic, user-based authentication system. Each user has a username and a password. However, authentication best practice says not to store the password in clear-text in the database. In fact, it’s best to hash the password when it is stored to prevent anyone from decrypting it. The problem comes in that ActiveObjects doesn’t allow us to do something like that in its current form. You’d have to do all the hashing prior to calling User#setPassword(String), and that would be majorly painful and about as unDRY as it gets. The solution: defined implementations.

@Implementation(UserImpl.class)
public interface User extends SaveableEntity {
    public String getUsername();
    public void setUsername(String username);
 
    public String getPassword();
    public void setPassword(String password);
}
 
public class UserImpl {
    private User user;
 
    public UserImpl(User user) {
        this.user = user;
    }
 
    public void setPassword(String password) {
        user.setPassword(Utilities.md5sum(password));
    }
}
 
// ...
User u = em.create(User.class);
u.setUsername("daniel");
u.setPassword("password");
u.save();
 
System.out.println(u.getPassword());   // prints the MD5 hashed value of "password"

See what I mean about a major pain? It’s not exactly clear what’s going on here so I’ll give you a brief run-down:

  • EntityManager instantiates a dynamic interface proxy for the User interface and returns it (where it assigned to u). It sees the @Implementation annotation and looks in the class for an init() method. Not finding one, it moves on.
  • setUsername(String) is invoked on the dynamic proxy. An @Implementation annotation on the interface is detected and the class in question is instantiated, passing the User instance into the constructor. The proxy looks for a setUsername(String) method within the implementation class and doesn’t find one. The method is invoked normally, caching the String username for future UPDATE into the database.
  • setPassword(String) is invoked. The dynamic proxy once again finds the @Implementation annotation and thus uses the cached UserImpl instance, looking for a setPassword(String) method. This time, it finds one and invokes it.
  • UserImpl#setPassword(String) is invoked by the dynamic proxy. The method calls to a utility class to hash the password, and in turn calls User#setPassword(String) with the resulting value.
  • The dynamic proxy receives the setPassword(String) call, and uses stack tracing to detect that the call originated from a defined implementation. The call is handled normally, caching the result for future UPDATE into the database.

Not so bad, I guess… The trick to remember is that any defined implementation takes an instance of the interface as the only parameter to its constructor. Also, it’s important to remember to only implement the methods that you expressly want to override. By defining a method in the implementation class, you are effectively overriding the dynamic implementation of any method with the same signature in the entity interface. This can lead to weird results if you aren’t careful.

So, we’ve now got SaveableEntity and defined implementations under out collective belts. With these two features, you can unlock a great deal of power and control over the ActiveObjects ORM and the entities it controls.


Article printed from Code Commit: http://www.codecommit.com/blog

URL to article: http://www.codecommit.com/blog/java/an-easier-java-orm-part-2

URLs in this post:

[1] first post: http://blogs.dzone.com/daniel/2007/07/18/an-easier-java-orm/

[2] ActiveObjects: https://activeobjects.dev.java.net

[3] comment: http://blogs.dzone.com/daniel/2007/07/18/an-easier-java-orm/#comment-2192

All content copyright © 2010 Daniel Spiewak. Distributed under Creative Commons.
All code (unless otherwise stated) is licensed under the BSD License.