Mapping Relations


You aren't done with the mapping. You've mapped the entity beans to the field level, but you haven't touched the container-managed relationship yet. In the ToDo application, tasks have a one-to-many relationship with comments that needs to be captured in the schema.

Actually, the relationship was captured. The comment bean has a field named task that contains the ID of the task that it is related to. JBoss even figured out that it needed to change the type of the foreign key to VARCHAR(32) when you changed the type of the primary key fields, but it didn't conform to the naming conventions we used. You'll fix that. You'll also see that this isn't the only option for managing the relationship. Even though it's not strictly necessary for a one-to-many relationship, you'll see how to manage the relationship with a separate middle table.

How do I do that?

Let's get some terminology out of the way. There are three types of object relationships: one-to-one, one-to-many, and many-to-many. The distinction lies in the multiplicity of both sides of a relationship.

The task-comment relationship is a one-to-many relationship because every task can have many comments, but each comment belongs to only one task. If each task could have only a single comment associated with it, it would be a one-to-one relationship. On the other hand, if the same comment could be attached to multiple tasks (not just the same comment text, but the same comment object) that would be a many-to-many relationship.

If either side of the relationship is a "one" relationship, meaning that it is related to only one object on the other side of the relationship, you can use a foreign key mapping. JBoss used a foreign key mapping to relate a comment to its associated tasks. The primary key of the task to which the comment belongs is stored with the comment.

JBoss gave us a simple mapping by default. To customize it, you'll need to go back to the jbosscmp-jdbc.xml file and add a relationships element that again mirrors the relationships element in the ejb-jar.xml file. It contains an ejb-relation for each relation you want to configure, and the ejb-relation-name matches up a relation in jbosscmp-jdbc.xml with one in ejb-jar.xml. The structure looks like this:

     <!DOCTYPE jbosscmp-jdbc PUBLIC                "-//JBoss//DTD JBOSSCMP-JDBC 4.0//EN"               "http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_4_0.dtd">     <jbosscmp-jdbc>         <defaults>             <!-- ... -->         </defaults>         <enterprise-beans>             <!-- ... -->         </enterprise-beans>         <relationships>             <ejb-relation>                 <ejb-relation-name>task-comment</ejb-relation-name>                 <!-- details on the relationship -->             </ejb-relation>         </relationships>     </jbosscmp-jdbc> 

To declare the relationship as a foreign key mapping, add a foreign-key-mapping element to ejb-relation:

     <ejb-relation>         <ejb-relation-name>task-comment</ejb-relation-name>         <foreign-key-mapping/>         <!-- details on the relationship -->     </ejb-relation> 

Each ejb-relation has two named roles, representing both sides of the relationship. The ejb-relationship-role-name links an ejb-relationship-role in jbosscmp-jdbc.xml to an ejb-relationship-role in ejb-jar.xml.


Note: If you've ever wondered why each side of a relationship needs to be named, this is why. The sides have to be named so that you can apply additional external configurations to them.
     <ejb-relation>         <ejb-relation-name>task-comment</ejb-relation-name>         <foreign-key-mapping/>         <ejb-relationship-role>             <ejb-relationship-role-name>                 comment-belongs-to-task             </ejb-relationship-role-name>             <!-- configuration for the relationship role -->         </ejb-relationship-role>         <ejb-relationship-role>             <ejb-relationship-role-name>                 task-has-comments             </ejb-relationship-role-name>             <!-- configuration for the relationship role -->         </ejb-relationship-role>     </ejb-relation> 

The final step is to add a key-fields element on the task-has-comments relationship role to specify the column name of the foreign key field:

     <ejb-relationship-role>         <ejb-relationship-role-name>            task-has-comments         </ejb-relationship-role-name>             <key-fields>                 <key-field>                     <field-name>id</field-name>                     <column-name>TASK_ID</column-name>                 </key-field>             </key-fields>         </ejb-relationship-role>     </ejb-relationship-role> 

The TASK_ID column goes on the table for the comment bean, so it may seem odd to associate it with the task side of the relationship in this way. In JBoss, key fields for relationships are always defined on the source side of the relationship, regardless of whether they are mapped as foreign keys in another entity table or in a middle table. Although it may seem slightly odd in this particular case, JBoss is very consistent.

The complete configuration looks like this:

     <!DOCTYPE jbosscmp-jdbc PUBLIC                "-//JBoss//DTD JBOSSCMP-JDBC 4.0//EN"               "http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_4_0.dtd">     <jbosscmp-jdbc>         <defaults>             <!-- ... -->         </defaults>         <enterprise-beans>             <!-- ... -->         </enterprise-beans>         <relationships>             <ejb-relation>                 <ejb-relation-name>task-comment</ejb-relation-name>                 <foreign-key-mapping/>                 <ejb-relationship-role>                     <ejb-relationship-role-name>                         comment-belongs-to-task                     </ejb-relationship-role-name>                 </ejb-relationship-role>                 <ejb-relationship-role>                     <ejb-relationship-role-name>                         task-has-comments                     </ejb-relationship-role-name>                     <key-fields>                         <key-field>                             <field-name>id</field-name>                             <column-name>TASK_ID</column-name>                         </key-field>                     </key-fields>                 </ejb-relationship-role>             </ejb-relation>         </relationships>     </jbosscmp-jdbc> 

To deploy this configuration, set optional.dd to cmr-fk and build the application:

     [todo]$ ant -Doptional.dd=cmr-fk main deploy 

When you do that, JBoss will update the table to look like this:

     CREATE TABLE TODO_COMMENT      (         COMMENT_ID VARCHAR(32) NOT NULL,          COMMENT_TEXT VARCHAR(256),          COMMENT_DATE TIMESTAMP,          TASK_ID VARCHAR(32),          CONSTRAINT PK_TODO_COMMENT PRIMARY KEY (COMMENT_ID)     ) 

This is most likely the way you will want to approach one-to-many relationships, but you might also want to use a middle table to map tasks to comments. Middle tables are generally reserved for many-to-many relationships in which neither side has a one relationship to which to attach a foreign key. However, you never know what type of relational mappings you may need in the field, and since middle tables are configured exactly the same way regardless of the multiplicity of the relationship, it will be a good example.

To do that, the foreign-key-mapping element needs to be replaced with a relation-table-mapping element that contains the name of the middle table:


Note: It really isn't far-fetched at all. You might not always be able to add relationship fields to an existing table just to satisfy the needs of your application.
     <ejb-relation>         <ejb-relation-name>task-comment</ejb-relation-name>         <relation-table-mapping>             <table-name>TODO_TASK_COMMENT</table-name>         </relation-table-mapping>         <!--relationship roles -->     </ejb-relation> 

The middle table needs to have a foreign key reference for each side of the relationship, so both sides need to declare a complete key-fields element for their respective keys. The complete ejb-relation looks like this:

     <ejb-relation>         <ejb-relation-name>task-comment</ejb-relation-name>         <relation-table-mapping>             <table-name>TODO_TASK_COMMENT</table-name>         </relation-table-mapping>         <ejb-relationship-role>             <ejb-relationship-role-name>                 comment-belongs-to-task             </ejb-relationship-role-name>             <key-fields>                 <key-field>                     <field-name>id</field-name>                     <column-name>COMMENT_ID</column-name>                 </key-field>             </key-fields>         </ejb-relationship-role>         <ejb-relationship-role>             <ejb-relationship-role-name>                 task-has-comments              </ejb-relationship-role-name>             <key-fields>                 <key-field>                     <field-name>id</field-name>                     <column-name>TASK_ID</column-name>                 </key-field>             </key-fields>         </ejb-relationship-role>     </ejb-relation> 

The complete jbosscmp-jdbc.xml file for this configuration is available in the etc/cmr-table directory and you can deploy it by setting optional.dd to cmr-table when building:

     [todo]$ ant -Doptional.dd=cmr-table main deploy 

When you deploy, the TODO_COMMENT table will no longer contain a foreign key field. In its place, JBoss creates the TODO_TASK_COMMENT table as instructed:

     CREATE TABLE TODO_TASK_COMMENT      (         COMMENT_ID VARCHAR(32) NOT NULL,          TASK_ID VARCHAR(32) NOT NULL,          CONSTRAINT PK_TODO_TASK_COMMENT PRIMARY KEY (COMMENT_ID, TASK_ID)     ) 

What just happened

You completed the object-relational mapping configuration for the beans. You saw both basic foreign key mapping as well as relational table mapping with a middle table. While the entity beans in the ToDo application demonstrate only a one-to-many relationship, the configuration is exactly the same regardless of the multiplicity of the relationships.

What about...

...using XDoclet to generate the mapping details?

XDoclet does indeed have code generation tasks that will generate the JBoss deployment descriptors, just as it generates the J2EE deployment descriptors. We've chosen not to use them here because the prime directive of code generation is to never generate things that you don't understand. While you are still learning about the JBoss deployment descriptors, it is better to code them by hand. After you have mastered them, you can come back and add the appropriate XDoclet tags to allow all of this work to be done behind the scenes.



JBoss. A Developer's Notebook
JBoss: A Developers Notebook
ISBN: 0596100078
EAN: 2147483647
Year: 2003
Pages: 106

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net