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.
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:
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());
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 = ?
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.
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?
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).
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.
August 7th, 2007 at 1:02 am
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
August 7th, 2007 at 12:05 pm
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()));
}
// …
August 7th, 2007 at 11:16 pm
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.
August 8th, 2007 at 1:12 pm
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 = ?
August 8th, 2007 at 5:03 pm
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.
August 8th, 2007 at 5:10 pm
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?
August 8th, 2007 at 11:50 pm
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
August 8th, 2007 at 11:55 pm
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.