You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-dev@db.apache.org by th...@apache.org on 2003/05/15 08:20:58 UTC

cvs commit: db-ojb/xdocs howto-use-anonymous-keys.xml

thma        2003/05/14 23:20:58

  Added:       xdocs    howto-use-anonymous-keys.xml
  Log:
  add a howto on anonymous fields
  
  Revision  Changes    Path
  1.1                  db-ojb/xdocs/howto-use-anonymous-keys.xml
  
  Index: howto-use-anonymous-keys.xml
  ===================================================================
  <?xml version="1.0"?>
  <document>
  
    <properties>
      <author email="brian@skife.org">Brian McCallister</author>
      <title>ObJectRelationalBridge - HOWTO Use Anonymous Keys</title>
    </properties>
  
  <body>
  
  <section name="HOWTO Use Anonymous Keys">
  	<subsection name="Why Do We Need Anonymous Keys?">
  		<p>
  		The core difference between referential integrity in Java and in an RDBMS lies in where the specific referential information is maintained. Java, and most modern OO languages, maintain referential integrity information in the runtime environment. Actual object relationships are maintained by the virtual machine so that the symbolic variable used in the application is dereferenced it will in fact provide access to the object instance which it is expected to provide access to. There is no need for a manual lookup or search across the heap for the correct object instance. Entity reference integrity is maintained and handled for the programmer by the environment.
  		</p>
  		<p>
  		Relational databases, on the other, purposefully place the referential integrity and lookups into the problem domain - that is the problem they are designed to solve. An RDBMS presumes you can design somehtign more efficient for your specific circumstances than the JVM does (you trust its ability to do object lookups in the heap is sufficiently efficient). As an RDBMS has a much larger heap equivalent, it is designed to not operate under that assumption (mostly). So, in an RDBMS the concept of specific foreign keys exists to maintain the referential integrity.
  		</p>
  		<p>
  		In crossing the object to relational entity barrier there is a mismatch between the referential integrity implementations. Java programers do not want to have to maintain both object referential integrity and key referential integrity analogous to
  		</p>
  		<source><![CDATA[
  {
      Foo parent = new SomeFooType();
      Foo child = new SomeOtherFooType();
      parent.addChild(child);
      child.setParentId(parent.getId());
  }
  		]]></source>
  		<p>
  		This is double the work required - you set up the object relationship, then set up the key relationship.
  		</p>
  		<p>
  		OJB can provide transparent key relationship maintenance behind the scenes via anonymous access fields. As object relationships change, the relationships will be propogated into the key values without the Java object ever being aware of a relational key being in use.
  		</p>
  	</subsection>
  </section>
  <section name="Using Anonymous Keys">
  	<subsection name="The Code">
  		<p>
  		Take the following classes designed to model a particular problem domain. They may do it reasonably well, or may not. Presume they model it perfectly well for the problem being solved.
  		</p>
  		<source><![CDATA[
  public class Desk
  {
      private Finish finish;
  
      /** Contains Thing instances */
      private Set drawers;
  
      private int numberOfLegs;
  
      public Set getDrawers()              { return this.drawers; }
  
      public int getNumberOfLegs()         { return this.numberOfLegs; }
      public void setNumberOfLegs(int num) { this.numberOfLegs = num; }
  
      public Finish getFinish              { return this.finish; }
      public void setFinish(Finish finish) { this.finish = finish }
  }
      
  public class Drawer
  {
      /** Contains anything */
      private Collection stuffInDrawer;
  
      public Collection getStuffInDrawer() { return this.stuffInDrawer; }
  }
  	
  public class Finish
  {
      private String wood;
      private String color;
  
      public String getWood()              { return this.wood; }
      public void setWood(String nom)      { this.wood = wood; }
  
      public String getColor()             { return this.color; }
      public void setColor(String color)   { this.color = color; }
  }
      
  public class Thing
  {
      private String name;
  
      public String getName()              { return this.name; }
      public String setName(String name)   { this.name = name; }
  }
  		]]></source>
  		<p>
  		A Desk will typically reference multiple drawers and one finish.
  		</p>
  	</subsection>
  	<subsection name="The Database">
  		<p>
  		When we need to store our instances in a database we use a fairly typical table per class persistance model.
  		</p>
  		<source><![CDATA[
  CREATE TABLE desk
  (
      id          INTEGER PRIMARY KEY,
      num_legs    INTEGER,
  );
      
  CREATE TABLE drawer
  (
      id          INTEGER PRIMARY KEY,
      desk_id     INTEGER FOREIGN KEY REFERENCES desk(id)
  );
      
  CREATE TABLE thing
  (
      id          INTEGER PRIMARY KEY,
      name        VARCHAR(255),
      drawer_id   INTEGER FOREIGN KEY REFERENCES drawer(id)
  );
      
  CREATE TABLE finish
  (
      id          INTEGER PRIMARY KEY,
      wood        VARCHAR(255),
      color       VARCHAR(255),
      desk_id     INTEGER FOREIGN KEY REFERENCES desk(id)
  );
  		]]></source>
          <p>
          At the database level the possible relationships need to be explicitely defined by teh foreign key constraints. These model all the possible object relationships according to the domain model (until generics enter the Java language for the collections API, this is technically untrue for the classes used here).
          </p>
  	</subsection>
  	<subsection name="The Repository Configuration">
          <p>
              When we go to map the classes to the database, it is almost a one-to-one property to field mapping. The exception here is the primary key on each entity. This is meaningless information in Java, so we would like to keep it out of the object model. Anonymous access keys allow us to do that.
          </p>
          <p>
              The repository.xml must know about the database columns used for referential integrity, but OJB can maintain the foreign key relationships behind the scenes - freeing the developer to focus on more accurate modeling of her objects to the problem, instead of the the persistance mechanism. Doing this is also very simple - in the repository.xml file mark the field descriptors with a <code>access="anonymous"</code> attribute. 
          </p>
          <source><![CDATA[
  <class-descriptor
      class="Desk"
      table="desk">
      
      <field-descriptor
          name="id"
          column="id"
          jdbc-type="INTEGER"
          primarykey="true"
          autoincrement="true"
          access="anonymous"
          />
      <field-descriptor
          name="numberOfLegs"
          column="num_legs"
          jdbc-type="INTEGER"
          />
      <collection-descriptor
          name="drawers"
          element-class-ref="Drawer"
          >
          <inverse-foreignkey field-ref="deskId"/>
      </collection-descriptor>
      
      <reference-descriptor
          name="finish"
          class-ref="Finish">
              <foreignkey field-ref="deskId""/>
      </reference-descriptor>
  </class-descriptor>
  
  <class-descriptor
      class="Finish"
      table="finish">
      
      <field-descriptor
          name="id"
          column="id"
          jdbc-type="INTEGER"
          primarykey="true"
          autoincrement="true"
          access="anonymous"
          />
      <field-descriptor
          name="wood"
          column="wood"
          jdbc-type="VARCHAR"
          size="255"
          />
     <field-descriptor
          name="color"
          column="color"
          jdbc-type="VARCHAR"
          size="255"
          />
      <field-descriptor
          name="deskId"
          column="desk_id"
          jdbc-type="INTEGER"
          />
  </class-descriptor>
  
  <class-descriptor
      class="Drawer"
      table="drawer">
      
      <field-descriptor
          name="id"
          column="id"
          jdbc-type="INTEGER"
          primarykey="true"
          autoincrement="true"
          access="anonymous"
          />
      <field-descriptor
          name="deskId"
          column="desk_id"
          jdbc-type="INTEGER"
          access="anonymous"
          />
      <collection-descriptor
          name="stuffInDrawer"
          element-class-ref="Thing"
          >
          <inverse-foreignkey field-ref="drawerId"/>
      </collection-descriptor>
  </class-descriptor>
  
  <class-descriptor
      class="Thing"
      table="thing">
      
      <field-descriptor
          name="id"
          column="id"
          jdbc-type="INTEGER"
          primarykey="true"
          autoincrement="true"
          access="anonymous"
          />
      <field-descriptor
          name="name"
          column="name"
          jdbc-type="VARCHAR"
          size="255"
          />
      <field-descriptor
          name="drawerId"
          column="drawer_id"
          jdbc-type="INTEGER"
          access="anonymous"
          />
  </class-descriptor>
          ]]></source>
          <p>
          Look first at the class descriptor for the Thing class. Notice the field-descriptor with the name attribute "drawerId". This field is labeled as anonymous access. Because it is anonymous access OJB will not attempt to assign the value here to a "drawerId" field or property on the Thing class. Normally the name attribute is used as the Java name for the attribute, in this case it is not. The name is still required because it is used as an indicated for references to this anonymous field.
          </p>
          <p>
          In the field descriptor for Drawer, look at the collection descriptor with the name "stuffInDrawer". This collection descriptor references a foreign key with the <code>field-ref="drawerId"</code>. This reference is to the anonymous field descriptor in the Thing descriptor. The field-ref matches to the name in the descriptor whether or not the name also maps to the Java attribute name. This dual use of <code>name</code> can be confusing - be careful.
          </p>
          <p>
          The same type mapping that is used for the collection descriptor in the Drawer descriptor is also used for the 1:1 reference descriptor in the Desk descriptor.
          </p>
  	</subsection>
      <subsection name="Benefits and Drawbacks">
          <p>
          There are both benefits and drawbacks to using anonymous field references for maintaining referential integrity between Java objects and database relations. The most immediate benefit is avoiding semantic code duplication. The second major benefit is avoiding cluttering class definitions with persistance mechanism artifacts. In a well layered application, the persistance mechanism will not really need to be so obvious in the object model implementation. Anonymous fields helpt o achieve this - thereby making persistence mechanisms more flexible. Moving to a different one becomes easier.
          </p>
          <p>
          There is an indirect benefit to populating the various id type keys though, particularly in web based applications where yet another layer boundary must be crossed - to an html or html-form format where object references are again lost. If unique ids are not part of a class, they will wind up being regenerated at the object level for crossing to the next layer. While this might be a better method to a layer purist, it is considerably less efficient.
          </p>
      </subsection>
  </section>
  </body>
  </document>