Thursday, October 15, 2009

Section 5.5. Reflexive Associations








5.5. Reflexive Associations


It's also possible for objects and tables to have associations back to themselves. This supports persistent
recursive data structures like trees, in which nodes link to other nodes.
Tracing through a database table storing such relationships using a SQL
query interface is a major chore. Luckily, once it's mapped to Java
objects, the process is much more readable and natural.


One way we might use a reflexive link in our music database is to
allow alternate names for artists. This is useful more often than you
might expect, because it makes it very easy to let the user find either
"The Smiths" or "Smiths, The" depending on how they're thinking of the
group, with little code, and in a language-independent way.


NOTE


I mean human language here—English versus Spanish or something
else. Put the links in the data rather than trying to write tricky code
to guess when an artist name should be permuted.




5.5.1. How do I do that?


All you need to do is add another field to the
Artist mapping in Artist.hbm.xml, establishing a link back
to Artist. Example 5-14 shows
one option.


Example 5-14. Supporting a reflexive association in the Artist class



<many-to-one name="actualArtist" class="com.oreilly.hh.data.Artist">
<meta attribute="use-in-tostring">true</meta>
</many-to-one>




This gives us an actualArtist property that we can set to the id of the "definitive"
Artist record when we're setting up an alternate
name. For example, our "The Smiths" record might have id 5, and
its actualArtist field would
be null since it is definitive. Then
we can create an "alias" Artist record with the
name "Smiths, The" at any time, and set the actualArtist field in that record to point to
record 5.



This kind of reflexive link is one instance where a column
containing a foreign key can't be named the same as the key column to
which it is a link. We are associating a row in ARTIST with another row in ARTIST, and of course the table already has
a column named ARTIST_ID.




Why is this association set up as many-to-one? There might be
many alias records that point to one particular definitive
Artist. So, each nickname needs to store the
id of the actual artist record for
which it is an alternative name. This is, in the language of data
modeling, a many-to-one relationship.


Code that looks up artists just needs to check the actualArtist property
before returning. If it's null, all
is well. Otherwise, it should return the record indicated by actualArtist. Example 5-15 shows how
we could extend the getArtist⁠⁠(⁠ ⁠) method in
CreateTest to support this new feature (additions
are in bold). Notice that the Artist constructor
gets a new argument for setting actualArtist, which
means we had to update the other places that call it in
CreateTest too, even though we aren't showing
them here.


Example 5-15. Artist lookup method supporting resolution of alternate
names



public static Artist getArtist(String name, boolean create, Session session) {
Query query = session.getNamedQuery("com.oreilly.hh.artistByName");
query.setString("name", name);
Artist found = (Artist)query.uniqueResult();
if (found == null && create) {
found = new Artist(name, new HashSet(), null);
session.save(found);
}
if (found != null && found.getActualArtist() != null) {
return found.getActualArtist();
}

return found;
}





Hopefully this chapter has given you a feel for the rich and
powerful ways you can use associations and collections in Hibernate. As
should be obvious from the way you can nest and combine these
capabilities, there are far more variations than we can hope to cover in
a book like this.



The good news is that Hibernate seems well equipped to handle
almost any kind of relationship your application might need, and it can
even do the drudge work of building the data classes and database
schema for you. This works much more effectively and deeply than I
ever expected it would when I started
creating these examples.










No comments:

Post a Comment