I have two classes, user and team, which are m:n associative. There is no need for an
associative class between the two, so I'm attempting to use transparent mapping in OJB
as desribed here:
http://jakarta.apache.org/ojb/tutorial3.html#mapping%20m:n%20associations. Here are
the respective entries in repository_user.xml:
<class-descriptor
class="com.blah.User"
table="users"
>
<field-descriptor id="1"
name="id"
column="id"
jdbc-type="INTEGER"
primarykey="true"
autoincrement="true"
/>
<field-descriptor id="2"
name="username"
column="username"
jdbc-type="VARCHAR"
/>
<field-descriptor id="3"
name="passwordDigest"
column="password"
jdbc-type="VARCHAR"
/>
<field-descriptor id="4"
name="emailAddress"
column="email_address"
jdbc-type="VARCHAR"
/>
<collection-descriptor
name="teams"
element-class-ref="com.blah.Team"
auto-retrieve="true"
auto-update="true"
indirection-table="user_team"
>
<fk-pointing-to-this-class column="user_id"/>
<fk-pointing-to-element-class column="team_id"/>
</collection-descriptor>
</class-descriptor>
<class-descriptor
class="com.blah.Team"
table="teams"
>
<field-descriptor id="1"
name="id"
column="id"
jdbc-type="INTEGER"
primarykey="true"
autoincrement="true"
/>
<field-descriptor id="2"
name="name"
column="name"
jdbc-type="VARCHAR"
/>
<collection-descriptor
name="users"
element-class-ref="com.blah.User"
auto-retrieve="true"
auto-update="true"
indirection-table="user_team"
>
<fk-pointing-to-this-class column="team_id"/>
<fk-pointing-to-element-class column="user_id"/>
</collection-descriptor>
</class-descriptor>
In my junit test case I create 3 users and 2 teams and organize them like so:
team1.getUsers().add(user1);
team1.getUsers().add(user2);
team1.getUsers().add(user3);
team2.getUsers().add(user1);
I then store the teams, so team1 has 3 users, team2 has 1 user, and user1 belongs to 2
teams. When I clear the broker cache and reload the teams, the following two asserts
should pass:
assertEquals(1, reloadedTeam2.getUsers().size());
assertEquals(3, reloadedTeam1.getUsers().size());
In my case, the first one passes, the second one does not. When I looked in the
user_team association table in the database, there were only 3 rows, not 4. So I set
the debug flag on the OJB SqlGenerator to DEBUG, reran the test, and it generated the
following output:
DEBUG: SQL: INSERT INTO users (id,username,password,email_address) VALUES ( ?, ?, ?, ?
)
DEBUG: SQL: DELETE FROM user_team WHERE (user_id = 11)
DEBUG: SQL: DELETE FROM user_team WHERE (user_id = 12)
DEBUG: SQL: DELETE FROM user_team WHERE (user_id = 13)
yDefaultImpl] INFO: Already created persistence broker instances: 5
DEBUG: SQL: SELECT TABLENAME,FIELDNAME,GRAB_SIZE,MAX_KEY FROM OJB_HL_SEQ WHERE
(OJB_HL_SEQ.TABLENAME = ? ) AND (OJB_HL_SEQ.FIELDNAME = ? )
DEBUG: SQL: INSERT INTO OJB_HL_SEQ (TABLENAME,FIELDNAME,MAX_KEY,GRAB_SIZE) VALUES ( ?,
?, ?, ? )
DEBUG: SQL: SELECT name,id FROM teams WHERE (teams.id = ? )
DEBUG: SQL: INSERT INTO teams (id,name) VALUES ( ?, ? )
DEBUG: SQL: DELETE FROM user_team WHERE (team_id = 1)
DEBUG: SQL: DELETE FROM user_team WHERE (team_id = 2)
DEBUG: SQL: SELECT name,id FROM teams WHERE (teams.id = ? )
DEBUG: SQL: UPDATE teams SET name=? WHERE (teams.id = ? )
DEBUG: SQL: DELETE FROM user_team WHERE (team_id = 1)
DEBUG: SQL: UPDATE users SET username=?, password=?, email_address=? WHERE (users.id =
? )
DEBUG: SQL: DELETE FROM user_team WHERE (user_id = 11)
DEBUG: SQL: INSERT INTO user_team (team_id,user_id) VALUES ( 1,11 )
DEBUG: SQL: DELETE FROM user_team WHERE (user_id = 12)
DEBUG: SQL: INSERT INTO user_team (team_id,user_id) VALUES ( 1,12 )
DEBUG: SQL: DELETE FROM user_team WHERE (user_id = 13)
DEBUG: SQL: INSERT INTO user_team (team_id,user_id) VALUES ( 1,13 )
DEBUG: SQL: DELETE FROM user_team WHERE (team_id = 2)
DEBUG: SQL: DELETE FROM user_team WHERE (user_id = 11)
DEBUG: SQL: INSERT INTO user_team (team_id,user_id) VALUES ( 2,11 )
DEBUG: SQL: SELECT A0.name,A0.id FROM teams A0 WHERE A0.id = ?
DEBUG: SQL: SELECT A0.password,A0.id,A0.username,A0.email_address FROM users
A0,user_team WHERE (user_team.team_id = ? ) AND user_team.user_id = A0.id
DEBUG: SQL: SELECT A0.name,A0.id FROM teams A0,user_team WHERE (user_team.user_id = ?
) AND user_team.team_id = A0.id
DEBUG: SQL: SELECT A0.name,A0.id FROM teams A0,user_team WHERE (user_team.user_id = ?
) AND user_team.team_id = A0.id
Interpreting the SQL, it looks like when saving a collection the initial DELETEs that
OJB generates to clear the collection are too broad. So team1 is saved with all three
users in it, and then when team2 is saved, the "DELETE FROM user_team WHERE (user_id =
11)" wipes out user1's entry for team1. In fact, that query is completely unnecessary
in this instance.
So the question is, where does the problem lie? Is it in my repository definitions or
is this a OJB SQL generation bug? I registered for the OJB bug database, but
registering unfortunately doesn't give you access to browse. I also scoured the
archives of the mailing list and saw a number of problems that people encountered
using m:n associations, but nothing that detailed the problem I encountered. I am
using OJB 0.9.5 on Win2K with JSDK 1.4.0_01 running against Postgresql 7.2.1-2 on
Debian and using the latest JDBC driver. Any help would be greatly appreciated.
Thanks!
-Brian