Eclipse in Styleby Daniel Spiewak
Beyond development, beyond usage

An Easier Java ORM: Relations

Posted by Daniel Spiewak on July 31st, 2007
Categories: Java
Tags: , , ,

Moved to: http://www.codecommit.com/blog/java/an-easier-java-orm-relations

Add to DZone

8 Responses to “An Easier Java ORM: Relations”

  1. David Marko Says:

    When using @OneToMany relations, how one can specify, what is the field with foreign key? I need this when e.g. have Person.class and Event.class and Event.class has fields for 3 persons. So in Event.class I have :

    public Person getUser1();
    public void setUser1(Person person);

    public Person getUser2();
    public void setUser2(Person person);

    public Person getUser3();
    public void setUser3(Person person);

    But not sure how to specify relationship from Person.class(@OneToMany) for each user type.

    Thanks for info.
    David

  2. Daniel Spiewak Says:

    Each of the user* fields will have a foreign key attached to it. In the person class, you would do something like this:

    @OneToMany
    public Event[] getEvents();

    However, this would pull back *all* related events, matching the userID against user1, user2 *and* user3. I’m not sure this is exactly what you want…

    If you need to do a more complex mapping, you should use the @Ignore annotation and implement the actual query inside a defined implementation. Thusly:

    @Ignore
    public Event[] getEventsForUser1();

    // PersonImpl.java
    // …
    public Event[] getEventsForUser1() throws SQLException {
    return person.getEntityManager().find(Event.class, Query.select().where(”user1ID = ?”, person.getID()));
    }
    // …

  3. Daniel Spiewak Says:

    Oh, I just committed a slightly easier syntax (in Person):

    @OneToMany(”user1̸ ;)
    public Event[] getEventsForUser1();

    @OneToMany(”user2″, “user3̸ ;)
    public Event[] getEventsForOtherUsers();

    I haven’t tested it yet, but the changes were minimal to the existing code, so it should work nicely.

  4. David Marko Says:

    AO lazyness behaves very strange when looking at log console with SQL statemes, that AO issues. Loot to following code snippet and see what I can see in log from AO. It seems that SELECT statement is ssued for every accessing each object property. Why it is or do I miss something?

    Person[] p_all=manager.find(Person.class,”id>1″);
    Person p1=p_all[0];
    Person p2=p_all[1];
    Person p3=p_all[2];
    System.out.println(p1.getFirstname());

    Resource r=manager.create(Resource.class);
    r.setName(”Server SARAH”);
    r.save();

    BaseEvent b1=manager.create(BaseEvent.class);
    b1.setResource(r);
    b1.setUser1(p1);
    b1.setUser2(p2);
    b1.setUser3(p3);
    b1.setTitle(”Kontrola běhu Lotus NOtes”);
    b1.save();

    SQL statements from AO log:

    8.8.2007 21:02:21 net.java.ao.EntityManager find
    INFO: SELECT id FROM person WHERE id>1
    8.8.2007 21:02:22 net.java.ao.EntityProxy invokeGetter
    INFO: SELECT firstname FROM person WHERE id = ?
    8.8.2007 21:02:22 net.java.ao.DatabaseProvider executeInsertReturningKeys
    INFO: INSERT INTO resource (id) VALUES (DEFAULT)
    8.8.2007 21:02:22 net.java.ao.EntityProxy invokeGetter
    INFO: SELECT name FROM resource WHERE id = ?
    8.8.2007 21:02:22 net.java.ao.EntityProxy save
    INFO: UPDATE resource SET name = ? WHERE id = ?
    8.8.2007 21:02:22 net.java.ao.DatabaseProvider executeInsertReturningKeys
    INFO: INSERT INTO baseEvent (id) VALUES (DEFAULT)
    8.8.2007 21:02:22 net.java.ao.EntityProxy invokeGetter
    INFO: SELECT resourceID FROM baseEvent WHERE id = ?
    8.8.2007 21:02:22 net.java.ao.EntityProxy invokeGetter
    INFO: SELECT user1ID FROM baseEvent WHERE id = ?
    8.8.2007 21:02:22 net.java.ao.EntityProxy invokeGetter
    INFO: SELECT user2ID FROM baseEvent WHERE id = ?
    8.8.2007 21:02:22 net.java.ao.EntityProxy invokeGetter
    INFO: SELECT user3ID FROM baseEvent WHERE id = ?
    8.8.2007 21:02:22 net.java.ao.EntityProxy invokeGetter
    INFO: SELECT title FROM baseEvent WHERE id = ?
    8.8.2007 21:02:22 net.java.ao.EntityProxy save
    INFO: UPDATE baseEvent SET resourceID = ?,user1ID = ?,user2ID = ?,user3ID = ?,title = ? WHERE id = ?

  5. Daniel Spiewak Says:

    The last batch of SELECTs seems really odd. There should be a SELECT statement for the first line, the third line (getFirstName()), an INSERT for the create(), and two UPDATEs. Is there any other (possibly concurrent) code happening here?

    As for the lazy-loading… Yeah, it will execute a SELECT for any non-cached property. By default, it does cache any values loaded on the initial find() eg:

    Person[] people = manager.find(Person.class, Query.select(”*”).where(”id > ?”, 1));
    people[0].getFirstName(); // no SELECT will be executed here since the value is already cached

    You don’t really see the advantages of the constant lazy-loading in a short running application. In fact, for anything which doesn’t hit the properties several times and/or have large result sets, lazy-loading is a bad idea (which is why I added the Query.select(”*̶ ;) mechanism). However, when you’ve got a long running application, a webapp for instance, it really starts to save on performance and memory over time. Granted, in the short run it is more query intensive (which is annoying), but it buys you a lot in that it only grabs the values it needs as it needs them, and there’s never any SELECT * FROM blah JOIN blah2.

  6. Daniel Spiewak Says:

    Possibly, there should be some mechanism to deactivate the lazy loading, or at least override it in a less clumsy fashion than the Query.select(”*”). What do you think?

  7. David Marko Says:

    At the beginning of the code I have:
    Person[] p_all=manager.find(Person.class,”id>1″);

    One would expect that this will load all person objects into memory. I think its very common scenario, that you load e.g. some articles with specific criteria and list all titles in browser. But current mechanism hits database for each article title, what is inefficent. I think, that primary fetched objects should be fetch immediately as one expects. But e.g. when article has relationship with autor, the author can be fetch when needed(lazy-loading).

    Current lazy-loading is very aggressive :-)

  8. David Marko Says:

    Also to lazy-loading, ROR uses :include clause so developer have a full controll, or Django uses select_related() when fetching objects. Moving the lazy-load decision on developer is better way as developer knows what is the est for current situation.

Leave a Reply