Skip to content
Print

An Easier Java ORM Part 2

19
Jul
2007

Well, by popular demand on the first post, I now bring you the second part in my series introducing the new Java ORM, ActiveObjects. 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 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.

Comments

  1. Thanks for another story. Things are getting more and more clear. I tried it with MySQL, working very fine so far. Unfortunately its broken for PostreSQL as I tested with 8.2 .

    I know there are still many things that must be described/cleared in docs but can you describe how relations work behind? etc. I have company – contacts relation and I access contacts from company object. When are the contacts loaded? Is there some lazy loading behind? Used approad is significant when we load many companies in one query and want access theirs contact. is the each contacts set loaded in separate query or can we say in some way that we want include contacts when loading companies using e.g. a left join?

    Thanks once again,
    David

    David Marko Friday, July 20, 2007 at 12:46 am
  2. Yeah, I figured it wasn’t working in PostgreSQL. I have the database installed in my linux VM, I should probably do some testing against it. :-) If you wouldn’t mind sending a stack trace to dev@activeobjects.dev.java.net, it would be helpful. Otherwise I’ll just trace out the issues myself. :-)

    Behind the scenes, relations are loaded eagerly as far as the proxy objects go, but there isn’t much data actually loaded. For example, AO could do something like this (when you call Company#getPeople():Person[]):

    SELECT personID FROM company WHERE id = ?

    ActiveObjects would iterate through the ResultSet and construct a Person instance for each id. This is actually a lot less expensive than it sounds, since some of the Person(s) may already be cached, and there really isn’t too much data involved in the creation. There might be a minor issue with heap-space if you tried to load 500,000 entities from a relation in this way. Perhaps I could add an option in the future to return a List from relation methods, to allow for paging.

    If you need to actually pre-load data as part of the query, rather than grabbing it individually, you can do this:

    Person[] contacts = em.find(Person.class, “person.id”, Query.select(“person.id,person.name AS name”).from(Company.class).join(Person.class, “company.personID = person.id”).where(“company.id = ?”, company.getID()));

    It’s certainly not the clearest of invocations. You could also do this (less flexible, but maybe a bit more intuitive):

    Person[] contacts = em.findWithSQL(Person.class, “p.id”, “SELECT p.id,p.name AS name FROM company c JOIN person p ON c.personID = p.id WHERE c.id = ?”, company.getID());

    This one isn’t too clear either. But what AO is actually doing here is paging through the result set and caching the contents into the entity instances it creates. Since “name” is actually a field in Person, the cached value (returned in the initial query result set) is available without query. Thus, you can invoke:

    for (Person person : contacts) System.out.println(person.getName());

    …and no further queries are made to the database, since the value is already cached.

    Daniel Spiewak Friday, July 20, 2007 at 1:00 am
  3. Thanks for clarification. The syntax is not bad. Its not ActiveRecord, but its very good for Java and one can read such code easily.

    Is there any kind of logging to see on console what query has been created?

    David
    PS: Also Derby in standalone mode support is not working for me. Embeded mode is working fine. I looked to ActiveRecord source and the problem is in this code:

    # SupportedDBProvider.java
    public enum SupportedDBProvider {
    MYSQL(“jdbc:mysql”, MySQLDatabaseProvider.class),
    DERBY(“jdbc:derby”, ClientDerbyDatabaseProvider.class),
    ORACLE_THIN(“jdbc:oracle:thin”, OracleDatabaseProvider.class),
    ORACLE_OCI(“jdbc:oracle:oci”, OracleDatabaseProvider.class),
    POSTGRESQL(“jdbc:postgresql”, PostgreSQLDatabaseProvider.class),
    MS_SQL_SERVER(“jdbc:sqlserver”, SQLServerDatabaseProvider.class);

    You have provider ClientDerbyDatabaseProvider.java but it cannot be used with any syntax.

    David Marko Friday, July 20, 2007 at 1:24 am
  4. Re: logging Yeah, I had to add that to just allow for development. :-) After creating the EntityManager instance (or triggering the EntityManager static init somehow), set the JDK 1.4 Logger for “net.java.ao” to Level.FINE. Like so:

    EntityManager em = new EntityManager(…);
    Logger.getLogger(“net.java.ao”).setLevel(Level.FINE); // will log all queries to stdout

    You can manipulate the Logger instance just like a normal Logger.

    Re: Networked Derby That’s right, there’s another JDBC URI prefix for Derby in stand-alone mode isn’t there? I’ve got a stand-alone DatabaseProvider implemented, I just didn’t tie it to SupportedDBProvider. I’ll take care of that.

    Daniel Spiewak Friday, July 20, 2007 at 1:28 am
  5. Re standalone derby: Done. :-) It should handle the config automagically like the rest of the databases in the latest trunk/ version.

    Daniel Spiewak Friday, July 20, 2007 at 1:50 am
  6. Can you also commenta bit follwing code from your example application:
    ### Generator.migrate(manager.getProvider(), Comment.class);

    How is the migration supposed to work? When running your example I noticed it creates all tables in database. How this can be used?

    David

    David Marko Friday, July 20, 2007 at 3:19 am
  7. It’s not exactly a migration in terms of porting data from one schema version to another. All it does is (as you observed) creates tables in the database. Basically, the one represented by the Comment.class (in this example), as well as any of its foreign key dependencies (in the proper order).

    Not shown in the examples is actually another method which is probably more useful. If you call EntityManager#conditionallyMigrate(Comment.class), the method will check for the existence of the schema *first*, and if it is not found it will be created. This is really useful for applications which create their own database on startup. Also, the method itself has very little overhead, so it can be run every time the application does.

    Daniel Spiewak Friday, July 20, 2007 at 10:38 am
  8. Could you SQL server? I use the jtds driver (which is better than Microsofts imo).

    jtds looks like this:

    url=jdbc:jtds:sqlserver://server;DatabaseName=dbname;User=user;Password=pw;instance=dbinstance;useLOBs=false
    driver=net.sourceforge.jtds.jdbc.Driver

    MS looks like this:
    jdbc:microsoft:sqlserver://server;DatabaseName=dbname;User=user;Password=pw
    driver=com.microsoft.jdbc.sqlserver.SQLServerDriver OR com.microsoft.sqlserver.jdbc.SQLServerDriver

    The SQL syntax is the same. I believe you would need to have a renderAutoIncrement looking like this:

    protected String renderAutoIncrement() {
    return “IDENTITY(1,1) NOT NULL “;
    }

    Dan Howard Saturday, July 21, 2007 at 6:11 pm
  9. Actually, I just this week implemented SQL server in trunk/ using the MS driver. Personally, I know nothing about the merits of one over the other. PMing you…

    Daniel Spiewak Sunday, July 22, 2007 at 12:20 am

Post a Comment

Comments are automatically formatted. Markup are either stripped or will cause large blocks of text to be eaten, depending on the phase of the moon. Code snippets should be wrapped in <pre>...</pre> tags. Indentation within pre tags will be preserved, and most instances of "<" and ">" will work without a problem.

Please note that first-time commenters are moderated, so don't panic if your comment doesn't appear immediately.

*
*