P. 1
Hibernate Association Mappings

Hibernate Association Mappings

|Views: 1,479|Likes:
Published by Naveen Kumar

More info:

Published by: Naveen Kumar on Mar 06, 2009
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as DOC, PDF, TXT or read online from Scribd
See more
See less

01/25/2013

pdf

text

original

Hibernate Association Mappings Introduction Association mappings are the often most difficult thing to get right.

In this section we'll go through the canonical cases one by one, starting with unidirectional mappings, and then considering the bidirectional cases. We'll use Person and Address in all the examples. We'll classify associations by whether or not they map to an intervening join table, and by multiplicity. Nullable foreign keys are not considered good practice in traditional data modelling, so all our examples use not null foreign keys. This is not a requirement of Hibernate, and the mappings will all work if you drop the nullability constraints. Unidirectional associations Many- to-One: A unidirectional many-to-one association is the most common kind of unidirectional association. <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class> create table Person ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key ) One- to-One: A unidirectional one-to-one association on a foreign key is almost identical. The only difference is the column unique constraint. <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" unique="true" not-null="true"/> </class>

<class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class> create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key ) A unidirectional one-to-one association on a primary key usually uses a special id generator. (Notice that we've reversed the direction of the association in this example.) <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> </class> <class name="Address"> <id name="id" column="personId"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <one-to-one name="person" constrained="true"/> </class> create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key ) One- to- Many: A unidirectional one-to-many association on a foreign key is a very unusual case, and is not really recommended. <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses"> <key column="personId" not-null="true"/> <one-to-many class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class> create table Person ( personId bigint not null primary key ) create table Address ( addressId bigint not null primary key, personId bigint not null )

We think it's better to use a join table for this kind of association. Unidirectional associations with join tables One-to-Many: A unidirectional one-to-many association on a join table is much preferred. Notice that by specifying unique="true", we have changed the multiplicity from many-to-many to one-to-many. <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses" table="PersonAddress"> <key column="personId"/> <many-to-many column="addressId" unique="true" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class> create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId not null, addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key ) Many- to-One: A unidirectional many-to-one association on a join table is quite common when the association is optional. <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>

create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key ) One- to- One: A unidirectional one-to-one association on a join table is extremely unusual, but possible. <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true" unique="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class> create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key ) Many- to- Many: Finally, we have a unidirectional many-to-many association. <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses" table="PersonAddress"> <key column="personId"/> <many-to-many column="addressId" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>

create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) ) create table Address ( addressId bigint not null primary key ) Bidirectional associations One- to- Many / Many- to- One: A bidirectional many-to-one association is the most common kind of association. (This is the standard parent/child relationship.) <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <set name="people" inverse="true"> <key column="addressId"/> <one-to-many class="Person"/> </set> </class> create table Person ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key ) If you use a List (or other indexed collection) you need to set the key column of the foreign key to not null, and let Hibernate manage the association from the collections side to maintain the index of each element (making the other side virtually inverse by setting update="false" and insert="false"): <class name="Person"> <id name="id"/> ... <many-to-one name="address" column="addressId" not-null="true" insert="false" update="false"/> </class> <class name="Address"> <id name="id"/> ... <list name="people"> <key column="addressId" not-null="true"/>

<list-index column="peopleIdx"/> <one-to-many class="Person"/> </list> </class> It is important that you define not-null="true" on the <key> element of the collection mapping if the underlying foreign key column is NOT NULL. Don't only declare not-null="true" on a possible nested <column> element, but on the <key> element. One- to- One: A bidirectional one-to-one association on a foreign key is quite common. <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" unique="true" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <one-to-one name="person" property-ref="address"/> </class> create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key ) A bidirectional one-to-one association on a primary key uses the special id generator. <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <one-to-one name="address"/> </class> <class name="Address"> <id name="id" column="personId"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <one-to-one name="person" constrained="true"/> </class> create table Person ( personId bigint not null primary key )

create table Address ( personId bigint not null primary key ) Bidirectional associations with join tables One- to- Many / Many- to- One: A bidirectional one-to-many association on a join table. Note that the inverse="true" can go on either end of the association, on the collection, or on the join. <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses" table="PersonAddress"> <key column="personId"/> <many-to-many column="addressId" unique="true" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <join table="PersonAddress" inverse="true" optional="true"> <key column="addressId"/> <many-to-one name="person" column="personId" not-null="true"/> </join> </class> create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key ) One- to-One: A bidirectional one-to-one association on a join table is extremely unusual, but possible. <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address"

column="addressId" not-null="true" unique="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true" inverse="true"> <key column="addressId" unique="true"/> <many-to-one name="person" column="personId" not-null="true" unique="true"/> </join> </class> create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key ) Many- to-Many: Finally, we have a bidirectional many-to-many association. <class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses" table="PersonAddress"> <key column="personId"/> <many-to-many column="addressId" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <set name="people" inverse="true" table="PersonAddress"> <key column="addressId"/> <many-to-many column="personId" class="Person"/> </set> </class> create table Person ( personId bigint not null primary key )

create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) ) create table Address ( addressId bigint not null primary key ) More complex association mappings: More complex association joins are extremely rare. Hibernate makes it possible to handle more complex situations using SQL fragments embedded in the mapping document. For example, if a table with historical account information data defines accountNumber, effectiveEndDate and effectiveStartDatecolumns, mapped as follows: <properties name="currentAccountKey"> <property name="accountNumber" type="string" not-null="true"/> <property name="currentAccount" type="boolean"> <formula>case when effectiveEndDate is null then 1 else 0 end</formula> </property> </properties> <property name="effectiveEndDate" type="date"/> <property name="effectiveStateDate" type="date" not-null="true"/> Then we can map an association to the current instance (the one with null effectiveEndDate) using: <many-to-one name="currentAccountInfo" property-ref="currentAccountKey" class="AccountInfo"> <column name="accountNumber"/> <formula>'1'</formula> </many-to-one> In a more complex example, imagine that the association between Employee and Organization is maintained in an Employment table full of historical employment data. Then an association to the employee's most recent employer (the one with the most recent startDate) might be mapped this way: <join> <key column="employeeId"/> <subselect> select employeeId, orgId from Employments group by orgId having startDate = max(startDate) </subselect> <many-to-one name="mostRecentEmployer" class="Organization" column="orgId"/> </join> You can get quite creative with this functionality, but it is usually more practical to handle these kinds of cases using HQL or a criteria query.

Hibernate: Understanding Associations - Associations: What are They? To represent relationships between classes, associations are used. Before going into the details of how Hibernate perceives associations, an understanding of the working of container managed associations is needed. Managed association means that, if a change is made to one end of the association, it will be reflected at the other end. For example, let's consider the Order table. If it has a one-to-many relationship with the Product table, the Order class will have a one-to-many association with the Product class. So when changes are made to the attributes participating in this association, they will be reflected at the other end automatically. The developer doesn’t have to mange the associations manually. How Hibernate implements the management of association is different from that of Container Managed Relationships/Associations or CMR generally provided by EJB CMP. In CMR the association is bidirectional, whereas Hibernate treats each association as different. The primary reason is that Hibernate builds its persistence based on Plain Old Java Object or POJO, and in Java associations are unidirectional. Thus Hibernate doesn’t implement CMR. So the associations are unidirectional. In essence it means if the on-update-cascade attribute is set in the mapping for Order and not in Product, any operation on Product would not affect Order. Keeping these points in mind, let's move on to the different types of associations supported by Hibernate. Hibernate mainly supports two types of associations: 1. One-to-Many 2. Many-to-One To work with associations, the changes would be required in both the mapping as well as in the persistence class. The details are as follows: 1. One-to-Many: In this kind of association one object of a class is in a relationship with many objects of another class. The class that is having the single instance contains a collection of instances of the other class. To specify this association the mapping file would have to be modified. The added code would be: <one-to-many name=”nameOfheVariable column="NAME_OF_THE_COLUMN" class="ClassName" not-null="true"/> The name attribute takes the name of the variable that is participating in the association. The column attribute is used to specify the table column that is participating in the association. The class attribute takes the class name to which this class is associated. In the Persistent class the following change would be there: class <className> { //other variable declarations Set <className> =new HashSet(); //constructors and getter/setter code follows : }

Then the constructor with the added parameter for Set must be given along with the getter and setter for the Set. In the third section I will be discussing a real world example to illustrate this point. 2. Many-to-One: This is the opposite of the One-to-Many association. In this case, the class that is having a Many-to-One association contains the object of the class. For example, if class A has a Many-to-One association with class B, then each instance of B would have an instance of A. And the identity of this instance can be the same for multiple objects of B. The change in the mapping would be: <many-to-one name=”nameOfheVariable column="NAME_OF_THE_COLUMN" class="ClassName" not-null="true"/> The attributes are the same as the case of One-to-Many. In the case of code it would be class <className> { <classNameofAssociatedClass> o=new <classNameofAssociatedClass> //construtors and getter/setter code follows : } The associations will be clearer when I discuss the real world usage of association in the next section. Hibernate: Understanding Associations - Associations in the Real World Till now I was using only one table. Let's make things interesting by adding one more table. This table is the Product table. Each Order can have more than one Product. Hence the relationship between Order and Product is One-to-Many. The schema of the Product table is: CREATE TABLE PRODUCT( ID VARCHAR NOT NULL PRIMARY KEY, NAME VARCHAR NOT NULL, PRICE DOUBLE NOT NULL, AMOUNT INTEGER NOT NULL, ORDER_ID VARCHAR NOT NULL) The next step is to create the persistent class for the Product table. The persistent class is as follows: package com.someorg.persist; public class Product { private String id; private String name; private double price; private int amount; private Order order;

public Product(String id, String name, double price, int amount, Order order) { this.order=order; //others not shown for brevity } public String getId() { return id; } public void setId(String string) { id = string; } // default constructor and other // getters/setters not shown for brevity // ... } The next part is Product.hbm.xml, that is the mapping file: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"> <hibernate-mapping> <class name="test.hibernate.Product" table="products"> <id name="id" type="string" unsaved-value="null"> <column name="id" sql-type="char(32)" not-null="true"/> <generator class="assigned"/> </id> <property name="name"> <column name="name" sql-type="char(255)" not-null="true"/> </property> <property name="price"> <column name="price" sql-type="double" not-null="true"/> </property> <property name="amount"> <column name="amount" sql-type="integer" not-null="true"/> </property> <property name="orderId"> <column name="ORDER_ID" sql-type="char(255)" not-null="true"/> <many-to-one name="orderId"

column="ORDER_ID" class="ORDER" not-null="true"/> </class> </hibernate-mapping> That is all that is required for the Product table. Now we need to make some changes in the Order class. package com.someorg.persist; import java.util.Date; import java.util.HashSet; import java.util.Set; public class Order { private String id; private Date date; private double priceTotal; private Set products =new HashSet(); // Automatically set the creation time of // this Order public Order() { this.date = new Date(); } public Order(String id, Date date, private double priceTotal, Set products){ this.products=products; //others are not shown for brevity } public String getId() { return id; } public void setProducts(Set products) { this.products = products; } public Set getProducts () { return products; } public void setId(String string) { id = string; } // other getters/setters not shown for // brevity // ... } The next part is changing in the Order.hbm.xml, which is: <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-mapping default-cascade="none" default-access="property" auto-import="true"> <class name=" com.someorg.persist.Order" table="orders" mutable="true" select- before-update="false" optimistic-lock="version"> <id name="id" type="string" unsaved-value="null"> <column name="id" sql-type="char(32)" not-null="true" /> <generator class="assigned" /> </id> <property name="date" not-null="false" > <column name="order_date" sql-type="datetime" not-null="true" /> </property> <property name="priceTotal" not-null="false" > <column name="price_total" sql-type="double" not-null="true" /> </property> <set name="products"> <key column="PRODUCT_ID"/> <one-to-many class="Product"/> </set> </class> </hibernate-mapping> The next step is to test it. To test it I will be using the Criteria query. In a QBC the joins are done using the setFetchMode method of the Criteria class. The mode would be JOIN. Here is how it works: import java.util.List; //other imports // use as // java test. FindOrderById name public class FindOrderById { public static void main(String[] args) throws Exception { // query to issue String query = "select order from Order " + "where order.id=:id"; // search for what? String name = args[0]; // init Configuration cfg = new Configuration() .addClass(Order.class); SessionFactory sf = cfg.buildSessionFactory(); // open session Session sess = sf.openSession(); // search and return Criteria criteria = session.createCriteria(Order.class); criteria.add( Expression.eq("id", name) ) .setFetchMode(“products”,FetchMode.JOIN); List result = criteria.list();

if (list.size() == 0) { System.out.println("No Order having id " + name); System.exit(0); } Order o = (Order) list.get(0); sess.close(); System.out.println("Found Order: " + o);//this is just an example Here the o // //object can be traversed to achieve anything } } That brings us to the end of this discussion. Though the complete picture is becoming clear, some edges are still hazy. These edges will be brought into sharper focus in the forthcoming discussions. Till next time.

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->