From fb35c15f67ea8e17b13d3d96c58af125586b5f2a Mon Sep 17 00:00:00 2001 From: Christian Bauer Date: Tue, 3 May 2005 21:34:28 +0000 Subject: [PATCH] Completed Pt2 git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@6665 1b8cb986-b30d-0410-93ca-fae66ebed9b2 --- reference/en/modules/tutorial.xml | 426 ++++++++++++++++++++++++++++++ 1 file changed, 426 insertions(+) diff --git a/reference/en/modules/tutorial.xml b/reference/en/modules/tutorial.xml index 2618ddbc81..16deaf6571 100644 --- a/reference/en/modules/tutorial.xml +++ b/reference/en/modules/tutorial.xml @@ -752,4 +752,430 @@ else if (args[0].equals("list")) { + + Part 2 - Mapping associations + + + We mapped a persistent entity class to a table. Let's build on this and add some class associations. + First we'll add people to our application, and store a list of events they participate in. + + + + Mapping the Person class + + + The first cut of the Person class is simple: + + + + + + Create a new mapping file called Person.hbm.xml: + + + + + + + + + + + + + +]]> + + + Finally, add the new mapping to Hibernate's configuration: + + + + +]]> + + + We'll now create an association between these two entities. Obviously, persons + can participate in events, and events have participants. The design questions + we have to deal with are: directionality, multiplicity, and collection + behavior. + + + + + + A unidirectional Set-based association + + + We'll add a collection of events to the Person class. That way we can + easily navigate to the events for a particular person, without executing an explicit query - + jyst by calling aPerson.getEvents(). We use a Java collection, a Set, + because the collection will not contain duplicate elements and the ordering is not relevant for us. + + + + So far we designed a unidirectional, many-valued associations, implemented with a Set. + Let's write the code for this in the Java classes and then map it: + + + + + + Before we map this association, think about the other side. Clearly, we could just keep this + unidirectional. Or, we could create another collection on the Event, if we + want to be able to navigate it bi-directional, i.e. anEvent.getParticipants(). + This is a design choice left to you, but what is clear from this discussion is the multiplicity + of the association: "many" valued on both sides, we call this a many-to-many + association. Hence, we use Hibernate's many-to-many mapping: + + + + + + + + + + + + + + + +]]> + + + Hibernate supports all kinds of collection mappings, a <set> being most + common. For a many-to-many association (or n:m entity relationship), an + association table is needed. Each row in this table represents a link between a person and an event. + The table name is configured with the table attribute of the set + element. The identifier column name in the association, for the person's side, is defined with the + <key> element, the column name for the event's side with the + column attribute of the <many-to-many>. You also + have to tell Hibernate the class of the objects in your collection (correct: the class on the + other side of the collection of references). + + + + The database schema for this mapping is therefore: + + + | *EVENT_ID | | | + | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | + | TITLE | |__________________| | AGE | + |_____________| | FIRSTNAME | + | LASTNAME | + |_____________| + ]]> + + + + + Working the association + + + Let's bring some people and events together in a new method in EventManager: + + + + + + After loading a Person and an Event, simply + modify the collection using the normal collection methods. As you can see, there is no explicit call + to update() or save(), Hibernate automatically + detects the collection has been modified and needs to be saved. This is called automatic + dirty checking, and you can also try it by modifying the name or the date property of + any of your objects. As long as they are in persistent state, that is, bound + to a particular Hibernate Session (i.e. they have been just loaded or saved in + a unit of work), Hibernate monitors any changes and executes SQL in a write-behind fashion. The + process of synchronizing the memory state with the database, usually only at the end of a unit of + work, is called flushing. + + + + You might of course load person and event in different units of work. Or you modify an object + outside of a Session, when it is not in persistent state (if it was persistent + before, we call this state detached). In (not very realistic) code, this might + look as follows: + + + + + + The call to update makes a detached object persistent again, you could + say it binds it to a new unit of work, so any modifications you made to it while detached + can be saved to the database. + + + + Well, this is not much use in our current situation, but it's an important concept you can + design into your own application. For now, complete this exercise by adding a new action + to the EventManager's main method and call it from the command line. If + you need the identifiers of a person and an event - the save() method + returns it. + + + + This was an example of an association between two equally important classes, two entities. + As mentioned earlier, there are other classes and types in a typical model, usually "less + important". Some you have already seen, like an int or a String. + We call these classes value types, and their instances depend + on a particular entity. Instances of these types don't have their own identity, nor are they + shared between entities (two persons don't reference the same firstname + object, even if they have the same first name). Of course, value types can not only be found in + the JDK (in fact, in a Hibernate application all JDK classes are considered value types), but + you can also write dependent classes yourself, Address or MonetaryAmount, + for example. + + + + You can also design a collection of value types. This is conceptually very different from a + collection of references to other entities, but looks almost the same in Java. + + + + + + Collection of values + + + We add a collection of value typed objects to the Person entity. We want to + store email addresses, so the type we use is String, and the collection is + again a Set: + + + + + The mapping of this Set: + + + + + +]]> + + + The difference compared with the earlier mapping is the element part, which tells Hibernate that the collection + does not contain references to another entity, but a collection of elements of type + String (the lowercase name tells you it's a Hibernate mapping type/converter). + Once again, the table attribute of the set element determines + the table name for the collection. The key element defines the foreign-key column + name in the collection table. The column attribute in the element + element defines the column name where the String values will actually be stored. + + + + Have a look at the updated schema: + + + | *EVENT_ID | | | |___________________| + | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID | + | TITLE | |__________________| | AGE | | *EMAIL_ADDR | + |_____________| | FIRSTNAME | |___________________| + | LASTNAME | + |_____________| + ]]> + + + You can see that the primary key of the collection table is in fact a composite key, + using both columns. This also implies that there can't be duplicate email addresses + per person, which is exactly the semantics we need for a set in Java. + + + + You can now try and add elements to this collection, just like we did before by + linking persons and events. It's the same code in Java. + + + + + + Bi-directional associations + + + Next we are going to map a bi-directional association - making the association between + person and event work from both sides in Java. Of course, the database schema doesn't + change, we still have many-to-many multiplicity. A relational database is more flexible + than a network programming language, so it doesn't need anything like a navigation + direction - data can be viewed and retrieved in any possible way. + + + + First, add a collection of participants to the Event Event class: + + + + + + Now map this side of the association too, in Event.hbm.xml. + + + + + +]]> + + + As you see, these are normal set mappings in both mapping documents. + Notice that the column names in key and many-to-many are + swapped in both mapping documents. The most important addition here is the + inverse="true" attribute in the set element of the + Event's collection mapping. + + + + What this means is that Hibernate should take the other side - the Person class - + when it needs to find out information about the link between the two. This will be a lot easier to + understand once you see how the bi-directional link between our two entities is created . + + + + + + Working bi-directional links + + + First, keep in mind that Hibernate does not affect normal Java semantics. How did we create a + link between a Person and an Event in the unidirectional + example? We added an instance of Event to the collection of event references, + of an instance of Person. So, obviously, if we want to make this link working + bi-directional, we have to do the same on the other side - adding a Person + reference to the collection in an Event. This "setting the link on both sides" + is absolutely necessary and you should never forget doing it. + + + + Many developers program defensive and create a link management methods to + correctly set both sides, e.g. in Person: + + + + + + Notice that the get and set methods for the collection are now protected - this allows classes in the + same package and subclasses to still access the methods, but prevents everybody else from messing + with the collections directly (well, almost). You should probably do the same with the collection + on the other side. + + + + What about the inverse mapping attribute? For you, and for Java, a bi-directional + link is simply a matter of setting the references on both sides correctly. Hibernate however doesn't + have enough information to correctly arrange SQL INSERT and UPDATE + statements (to avoid constraint violations), and needs some help to handle bi-directional associations + properly. Making one side of the association inverse tells Hibernate to basically + ignore it, to consider it a mirror of the other side. That's all that is necessary + for Hibernate to work out all of the issues when transformation a directional navigation model to + a SQL database schema. The rules you have to remember are straightforward: All bi-directional associations + need one side as inverse. In a one-to-many association it has to be the many-side, + in many-to-many association you can pick either side, there is no difference. + + + + In the next section we integrate Hibernate with Tomcat and WebWork - the EventManager + doesn't scale anymore with our growing application. + + + + + + \ No newline at end of file