Chapter 5. Many-to-One and One-to-One Mapping

In general, entities are related or associated to each other. For example, a Customer is associated with an Address. A Customer can have one or more than one Address (for example, Billing and Shipping). These relationships or associations are represented differently in a relational model and the domain/object model.

In a relational model, a foreign key column represents the dependency of one table on another. The Customer table has an AddressId as the foreign key column, which is the primary key of the Address table.

From the Customer perspective, because a Customer can have multiple addresses, so it's a many-to-one relationship. On the other hand, from the Address perspective, one address can belong to only one customer, so it's a one-to-one relationship.

The same relationship is represented in terms of classes and attributes in an object model. The Customer class has an attribute to hold multiple addresses belonging to that Customer (in Java, this is represented as a collection—List, Set, and so on). The Address class has an attribute of Customer type.

In the case of Customer and Address example, you consider the association from the view of both the Customer entity and the Address entity, so it's a bidirectional association. If you use the association from only one entity, it's a unidirectional association. In an object/relational mapping (ORM) framework, these relationships are represented using metadata in the configuration file as well using the entities/objects.

This chapter shows you how these associations are represented in Hibernate and also discusses the following features:

  • Various ways to use the many-to-one and one-to-one mappings

  • How to make unidirectional associations into bidirectional associations

  • How to use features like lazy initialization and cascade

  • How to map an association or a join table

Using Many-To-One Associations

Problem

Coming back to the online bookshop example, a publisher can publish many books. This relationship between the books and the publisher is a many-to-one relationship. How do you map objects in this case using the many-to-one association? How do you use the cascade option to persist the class hierarchy?

Solution

As already mentioned, the association from books to publisher is a many-to-one association. Because this association is navigable from book to publisher only, it's also a unidirectional association. You use the <many-to-one> element to establish this association.

How It Works

Your application has a Publisher class that hasn't been mapped to the database. Following the best practice of object identifiers discussed in Chapter 4, you can add an autogenerated id property on the class. Figure 5-1 shows the entity relationship drawing for a many-to-one unidirectional association.

Entity-relationship drawing showing a many-to-one unidirectional association

Figure 5-1. Entity-relationship drawing showing a many-to-one unidirectional association

public class Publisher implements Serializable{

                private Long publisher_id;
                private String code;
                private String name;
                private String address;
                // getters and setters
}

<hibernate-mapping package="com.hibernaterecipes.chapter5">
        <class name="Publisher" table="Publisher" schema="BOOK5">
                <id name="Publisher_id" type="long" column="PUBLISHER_ID" >
                        <generator class="native">
                        </generator>
                </id>
                <property name="code" type="string">
                        <column name="CODE" length="4" not-null="true" unique="true" />
                </property>
                <property name="name" type="string">
                        <column name="PUBLISHER_NAME" length="100" not-null="true" />
                </property>
                <property name="address" type="string">
<column name="ADDRESS" length="200" />
                </property>
        </class>
</hibernate-mapping>

Because you add a new persistent object to your application, you need to specify it in the Hibernate configuration file:

<mapping resource="com/hibernaterecipes/chapter5/Publisher.xml" />

For the Book class, you already have a Publisher property of type Publisher, which isn't used in the previous examples:

public class Book_5_1 implements Serializable{

       private Long book_id;
       private String isbn;
       private String name;
       private Publisher publisher;
       private Date publishDate;
       private Integer price;
       // getters and setters
}

<hibernate-mapping package="com.hibernaterecipes.chapter5">
        <class name="Book_5_1" table="Book" schema="BOOK5">
                <id name="book_id" type="long" column="BOOK_ID" >
                        <generator class="native">
                        </generator>
                </id>
                <property name="isbn" type="string">
                <column name="ISBN" length="50" not-null="true" unique="true" />
                </property>
                <property name="name" type="string">
                <column name="BOOK_NAME" length="100" not-null="true" />
                </property>
                <property name="publishDate" type="date" column="PUBLISH_DATE" />
                <property name="price" type="int" column="PRICE" />
                <many-to-one name="publisher" class="Publisher" column="PUBLISHER_ID"/>

        </class>
</hibernate-mapping>

After you created a new book object together with a new publisher object, you want to save them into the database. Will Hibernate save the publisher object when you save the book object? Unfortunately, an exception occurs if you save only the book object. That means you must save them one by one:

Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
        session.save(publisher);
        session.save(book);
        tx.commit();
} catch (HibernateException e) {
        if (tx != null) tx.rollback();
        throw e;
} finally {
        session.close();
}

It's a lot of trouble to save all the objects individually, especially for a large object graph. So, Hibernate provides a way to save them in one shot. Let's add a cascade="save-update" attribute to the <many-to-one> mapping, to make Hibernate cascade the save/update operations to the associated object:

<hibernate-mapping package="com.hibernaterecipes.chapter5">
        <class name="Book_5_1" table="Book" schema="BOOK5">
        .
.
.
.
.
.
              <many-to-one name="publisher" class="Publisher" column="PUBLISHER_ID" cascade="save-update"/>

        </class>
</hibernate-mapping>

The save/update cascade is very useful when you persist a graph of objects, some of which are newly created and some of which have been updated. You can use the saveOrUpdate() method and let Hibernate decide which objects should be created and which should be updated:

session.saveOrUpdate(book);

In addition to the save/update cascade, you can also cascade the delete operation:

<many-to-one name="publisher" class="Publisher" column="PUBLISHER_ID" cascade="save-update,delete" />

In JPA, the book and publisher classes are annotated as follows:

xyp@Entity
@Table (name="PUBLISHER", schema="BOOK5")
public class Publisher implements Serializable{

                @Id
                @Column (name="PUBLISHER_ID")
                @GeneratedValue (strategy=GenerationType.AUTO)
                private Long publisher_id;
@Column (name="CODE")
                private String code;
                @Column (name="PUBLISHER_NAME")
                private String name;
                @Column (name="ADDRESS")
                private String address;
                // getters and setters
}

@Entity
@Table (name="BOOK", schema="BOOK5")
public class Book_5_1 implements Serializable{
                @Id
                @Column (name="BOOK_ID")
                @GeneratedValue (strategy=GenerationType.AUTO)
                private Long book_id;
                @Column (name="isbn")
                private String isbn;
                @Column (name="BOOK_NAME")
                private String name;
                @ManyToOne
                @Cascade (value=CascadeType.SAVE_UPDATE)
                @JoinColumn (name="PUBLISHER_ID")
                private Publisher publisher;

                @Column (name="price")
                private Integer price;
                // getters and setters
}

Using a Many-to-One Association with a Join Table

Problem

Let's say a set of books has been identified that will be published, but publishers haven't yet been allocated for those books. In the same way, you may have publishers that are ready to publish books, but the books haven't yet been written. In this case, if you use the same Book and Publisher entities, there will be rows in which the Book_Id is null in the PUBLISHER table and the Publisher_Id is null in the BOOK table. This isn't an efficient way to store data in a relational database and doesn't provide a normalized schema.

In this case, you use an additional table called a join table that contains the foreign key's Book_id and Publisher_Id as columns. This table includes all rows where a Book has a Publisher and a Publisher has published a Book.

How do you represent this data structure in Hibernate? And what are the additional features that it provides to manage this kind of association?

Solution

Hibernate provides the <join> mapping element to represent a join table.

How It Works

Use the following SQL queries to generate the tables:

CREATE TABLE "BOOK5"."BOOK_PUBLISHER"
(
   BOOK_ID bigint PRIMARY KEY NOT NULL,
   PUBLISHER_ID bigint NOT NULL
);
;
CREATE UNIQUE INDEX SQL091217230605770 ON BOOK_PUBLISHER(BOOK_ID)
;CREATE TABLE "BOOK5"."BOOK5_2"
(
   BOOK_ID bigint PRIMARY KEY NOT NULL,
   ISBN varchar(50) NOT NULL,
   BOOK_NAME varchar(100) NOT NULL,
   PUBLISH_DATE date,
   PRICE int
);

CREATE UNIQUE INDEX SQL091217225248451 ON BOOK5_2(BOOK_ID);

CREATE UNIQUE INDEX SQL091217225248450 ON BOOK5_2(ISBN);


CREATE TABLE "BOOK5"."PUBLISHER5_2"
(
   PUBLISHER_ID bigint PRIMARY KEY NOT NULL,
   CODE varchar(4) NOT NULL,
   PUBLISHER_NAME varchar(100) NOT NULL,
   ADDRESS varchar(200)
);

CREATE UNIQUE INDEX SQL091217225248980 ON PUBLISHER5_2(CODE);

CREATE UNIQUE INDEX SQL091217225248981 ON PUBLISHER5_2(PUBLISHER_ID);

The book mapping file is as follows:

<hibernate-mapping package="com.hibernaterecipes.chapter5">
        <class name="Book5_2" table="Book5_2" schema="BOOK5">
                <id name="book_id" type="long" column="BOOK_ID" >
                        <generator class="native">
                        </generator>
                </id>
                <property name="isbn" type="string">
<column name="ISBN" length="50" not-null="true" unique="true" />
                </property>
                <property name="name" type="string">
                <column name="BOOK_NAME" length="100" not-null="true" />
                </property>
                <property name="publishDate" type="date" column="PUBLISH_DATE" />
                <property name="price" type="int" column="PRICE" />
                <join table="BOOK_PUBLISHER" optional="true" schema="BOOK5">
                        <key column="BOOK_ID" unique="true" />
                        <many-to-one name="publisher" class="Publisher5_2" column="PUBLISHER_ID" not-null="true"
                        cascade="save-update" lazy="false"/>
                </join>

        </class>
</hibernate-mapping>

In the <join> mapping element, the table attribute holds the name of the join table—here it's BOOK_PUBLISHER. By setting optional=true, you tell Hibernate that it should insert a row into the join table only if the properties grouped by the mapping are non-null. Book_Id is the primary key in the join table and is implicitly unique. The <many-to-one> attribute specifies that this join table has a many-to-one relationship with the Publisher entity and the key column is Publisher_id. Figure 5-2 shows the entity relationship drawing for a many-to-one association with a join table.

Entity-relationship drawing showing a many-to-one association with a join table

Figure 5-2. Entity-relationship drawing showing a many-to-one association with a join table

The JPA mapping for the book class is as follows. You use the JoinTable annotation to map the join table BOOK_PUBLISHER:

@Entity
@Table (name="BOOK5_2", schema="BOOK5")
public class Book_5_2 implements Serializable{
                @Id
                @Column (name="BOOK_ID")
                @GeneratedValue (strategy=GenerationType.AUTO)
                private Long book_id;
@Column (name="isbn")
                private String isbn;
                @Column (name="BOOK_NAME")
                private String name;
                @ManyToOne
                @Cascade (value=CascadeType.SAVE_UPDATE)
                @JoinTable (schema="BOOK5",name="BOOK_PUBLISHER",
                                                joinColumns=@JoinColumn(name="BOOK_ID"),
                                                inverseJoinColumns=@JoinColumn(name="PUBLISHER_ID"))
                private Publisher5_2 publisher;

                @Column (name="price")
                private Integer price;
// getters and setters
}

The join table BOOK_PUBLISHER has a foreign key column named PUBLISHER_ID that points to the PUBLISHER table materialized by inverseJoinColumns, and a foreign key column named BOOK_ID pointing to the BOOK table materialized by the joinColumns attribute.

Using Lazy Initialization on Many-to-One Associations

Problem

In an association between two entities in an ORM framework, when the data of one entity is loaded from the database, the data of the dependent entity may be fetched along with it or loaded on demand.

For example, in the case of Book and Publisher, the BOOK table has the foreign key column Publisher_Id. If Book details are fetched from the database, should the Publisher details be loaded along with them, or should the Publisher details be retrieved only when you explicitly fetch them? When the dependent object's details are loaded only on demand, they're said to be lazily loaded. By lazily loading data, you can improve performance, because unnecessary queries aren't executed.

How is lazy initialization achieved in Hibernate? How effect does it have when you're retrieving an object graph? And what are the ways to deal with it?

Solution

Enabling lazy initialization defines what objects are available on the detached object. It's defined with the keyword lazy, which is different from the keyword fetch. The keyword fetch is used to define fetching strategies for tuning performance.

Hibernate provides lazy initialization of collections, associations, and even properties or value types. You learn about lazy initialization of collections in the next chapter. The value types of an entity can also be lazy initialized, but it's usually not required; it can be used with databases that have hundreds of columns.

How It Works

Suppose you have a method for retrieving a Book object given its ID. Because you added the <many-to-one> mapping, the Publisher object related to the Book should be retrieved at the same time:

Session session = factory.openSession();
try {
Book book = (Book) session.get(Book.class, id);
return book;
} finally {
session.close();
}

But when you access the Publisher object through book.getPublisher() outside this method, an exception occurs:

System.out.println(book.getName());
System.out.println(book.getPublisher().getName());

If you put the access code inside the try block of the method body, everything is OK. What's the reason for this exception? It happens because Hibernate doesn't load your Publisher object from the database until you first access the Publisher before the session is closed. This lazy initialization can avoid unnecessary database queries and thus enhance performance. Because the Publisher is first accessed outside the session (which has been closed), an exception is thrown.

If you want the Publisher object to be accessed from outside the session, there are two possible solutions. One is to initialize the Publisher explicitly by calling the method Hibernate.initialize(). Doing so forces the Publisher object to be loaded from the database:

Session session = factory.openSession();
try {
Book book = (Book) session.get(Book.class, id);
Hibernate.initialize(book.getPublisher());
return book;
} finally {
session.close();
}

Another solution is to turn off lazy initialization for this association. Doing so may decrease performance because the Publisher object is loaded together with the Book object every time:

<hibernate-mapping package="com.hibernaterecipes.chapter5">
        <class name="Book_5_1" table="Book" schema="BOOK5">
        .
.
.
.
.
              <many-to-one name="publisher" class="Publisher" column="PUBLISHER_ID" cascade="save-update" lazy="false" />
</class>
</hibernate-mapping>

Sharing Primary Key Associations

Problem

In what scenarios do you need to share the primary key, and how do you map it?

Solution

When rows in two tables are associated with the primary key value of one row, they're said to be sharing the primary key. You can use this kind of implementation when there is a requirement for tight coupling between two objects.

How It Works

Let's use the Customer and Address classes to create a one-to-one mapping with a shared primary key:

public class Customer5_1 implements Serializable {

       private static final long serialVersionUID = −3534434932962734600L;
       private Long id;
       private String countryCode;
       private String idCardNo;
       private String firstName;
       private String lastName;
       private Address5_1 address;
       private String email;
       //getters and setters
}

public class Address5_1 implements Serializable{

       private static final long serialVersionUID = −605474766287314591L;
       private Long id;
       private String city;
       private String street;
       private String doorplate;
       private Customer5_1 customer;
       //getters and setters
}

The Customer class is mapped like a regular class. It also contains the one-to-one mapping element that maps Customer to Address. You add the cascade option to save-update because you want to save the Address when you save the Customer object:

<hibernate-mapping package="com.hibernaterecipes.chapter5">
        <class name="Customer5_1" table="CUSTOMER" schema="BOOK5">
                <id name="id" type="long" column="ID">
                        <generator class="native"></generator>
                </id>
                <property name="firstName" type="string" column="FIRST_NAME" />
                <property name="lastName" type="string" column="LAST_NAME" />
                <property name="idCardNo" type="string" column="ID_CARD_NO" />
                <property name="countryCode" type="string" column="COUNTRY_CODE" />
                <property name="email" type="string" column="EMAIL" />
                <one-to-one name="address"
        class="com.hibernaterecipes.chapter5.Address5_1" cascade="save-update"></one-to-one>
        </class>
</hibernate-mapping>

By itself, this mapping establishes a unidirectional one-to-one mapping, provided the Address class is mapped as a separate entity. To achieve the sharing of the primary key, you need to map the Address class as shown here:

<hibernate-mapping package="com.hibernaterecipes.chapter5">
        <class name="Address5_1" table="ADDRESS" schema="BOOK5">
                <id name="id" type="long" column="ID" >
                        <generator class="foreign">
                                <param name="property">customer</param>
                        </generator>
                </id>
                <property name="city" type="string" column="CITY" />
                <property name="street" type="string" column="STREET" />
                <property name="doorplate" type="string" column="DOOR_PLATE" />
                <one-to-one name="customer" class="Customer5_1" constrained="true"></one-to-one>

        </class>
</hibernate-mapping>

In the Address mapping file, you establish bidirectionality with the one-to-one element. Also note that you make constrained true, which adds a foreign key constraint. The constraint links the primary key of the ADDRESS table to the primary key of the CUSTOMER table. The generator of the primary key of the Address class is set to foreign with a property mapping to the Customer instance; this causes the primary key to be shared between the Customer and Address classes. In addition, you can't delete the Customer class without also deleting the Address class.

The JPA specification doesn't have a way to deal with a shared primary key. It has serious issues with establishing bidirectionality and a foreign key. In JPA annotations, the Customer class's Address property must be mapped with the annotations OneToOne and PrimaryKeyJoinColumn:

@Entity
@Table (name="CUSTOMER",schema="BOOK5")
public class Customer5_1 implements Serializable {

       private static final long serialVersionUID = −3534434932962734600L;
       @Column (name="ID")
@Id
       @GeneratedValue (strategy=GenerationType.AUTO)
       private Long id;

       @Column (name="COUNTRY_CODE")
       private String countryCode;

       @Column (name="ID_CARD_NO")
       private String idCardNo;

       @Column (name="FIRST_NAME")
       private String firstName;

       @Column (name="LAST_NAME")
       private String lastName;

       @OneToOne
       @PrimaryKeyJoinColumn (name="ID")
       private Address5_1 address;
       // getters and setters
}

The Address class's ID property is annotated without any identity-generation strategy. Also note that the Address class doesn't have customer as a property:

@Entity
@Table (name="ADDRESS",schema="BOOK5")
public class Address5_1 implements Serializable
{

       private static final long serialVersionUID = −605474766287314591L;
       @Id
       @Column (name="ADDRESS_ID")
       private Long id;

       @Column(name="CITY")
       private String city;

       @Column(name="STREET")
       private String street;

       @Column(name="DOOR_PLATE")
       private String doorplate;
       // getters and setters
}

To persist the Address object, you have to manually set the associated Customer ID as the primary key of the Address object.

Creating a One-to-One Association Using a Foreign Key

Problem

Let's say that one Customer has only one Address and one Address belongs to only one Customer. This means there is a one-to-one relationship between Customer and an Address. How do you represent a one-to-one association in Hibernate?

Solution

The simplest way to map a one-to-one association is to treat it as a many-to-one association but add a unique constraint on it. You can declare the lazy, fetch, and cascade attributes in the same way.

How It Works

You can use the Customer and Address classes to create a one-to-one mapping with a foreign key association. (The classes Customer and Address remain the same—they're renamed for the sake of demonstration.) Customer's Hibernate mapping maps the Address property as many-to-one association. The unique constraint is set to true to change this association from many-to-one to one-to-one:

<hibernate-mapping package="com.hibernaterecipes.chapter5">
        <class name="Customer5_2" table="CUSTOMER" schema="BOOK5">
                <id name="id" type="long" column="ID">
                        <generator class="native"></generator>
                </id>
                <property name="firstName" type="string" column="FIRST_NAME" />
                <property name="lastName" type="string" column="LAST_NAME" />
                <property name="idCardNo" type="string" column="ID_CARD_NO" />
                <property name="countryCode" type="string" column="COUNTRY_CODE" />
                <property name="email" type="string" column="EMAIL" />
                <many-to-one name="address"
                                        class="com.hibernaterecipes.chapter5.Address5_2"
                                        column="ADDRESS_ID"
                                        cascade="save-update"
                                        unique="true">
               </many-to-one>
        </class>
</hibernate-mapping>

To obtain bidirectionality, you add a one-to-one mapping to the customer property in the Address class. You use the property_ref attribute to point Hibernate to the property to which the association is one-to-one; this is called an inverse property reference:

<hibernate-mapping package="com.hibernaterecipes.chapter5">
        <class name="Address5_2" table="ADDRESS" schema="BOOK5">
                <id name="id" type="long" column="ADDRESS_ID" >
                        <generator class="native">
</generator>
                </id>
                <property name="city" type="string" column="CITY" />
                <property name="street" type="string" column="STREET" />
                <property name="doorplate" type="string" column="DOOR_PLATE" />
                <one-to-one name="customer" class="Customer5_2" property-ref="address"></one-to-one>

        </class>
</hibernate-mapping>

In JPA annotations, the Address property of the Customer class is mapped with the annotations OneToOne and JoinColumn. Also note that you enable the cascade feature, which saves the address associated with the customer:

@Entity
@Table (name="CUSTOMER",schema="BOOK5")
public class Customer5_2 implements Serializable {

       private static final long serialVersionUID = −3534434932962734600L;
       @Column (name="ID")
       @Id
       @GeneratedValue (strategy=GenerationType.AUTO)
       private Long id;

       @Column (name="COUNTRY_CODE")
       private String countryCode;

       @Column (name="ID_CARD_NO")
       private String idCardNo;

       @Column (name="FIRST_NAME")
       private String firstName;

       @Column (name="LAST_NAME")
       private String lastName;

       @OneToOne (cascade=CascadeType.ALL)
       @JoinColumn (name="ADDRESS_ID")
       private Address5_2 address;

       @Column (name="EMAIL")
       private String email;
       // getters and setters
}

The Address class is as shown here. The customer is mapped with another one-to-one annotation, which makes it a bidirectional association. The inverse property reference is achieved with the mappedBy attribute:

@Entity
@Table (name="ADDRESS",schema="BOOK5")
public class Address5_2 implements Serializable
{

       private static final long serialVersionUID = −605474766287314591L;
       @Id
       @Column (name="ADDRESS_ID")
       @GeneratedValue (strategy=GenerationType.AUTO)
       private Long id;

       @Column(name="CITY")
       private String city;

       @Column(name="STREET")
       private String street;

       @Column(name="DOOR_PLATE")
       private String doorplate;

       @OneToOne (mappedBy="address")
       private Customer5_2 customer;
       // getters and setters
}

Creating a One-to-One Association Using a Join Table

Problem

Another strategy to represent a one-to-one association uses a join table. As explained in recipe 5.2, in some scenarios the foreign key in a table is null. To avoid storing the rows with null foreign keys, you can create a join table that holds the IDs of both the dependent entities. How do you use a join table to establish a one-to-one association in Hibernate?

Solution

The last method of mapping a one-to-one association uses a join-table element with a many-to-one association. Nothing is different unless both the <key> and <many-to-one> mappings need a unique constraint. This method is seldom used.

How It Works

You need to create the tables with scripts rather than use the hbm2ddl.auto option set to Create. You create the ADDRESS, CUSTOMER, and join (CUSTOMERADDRESS) tables as follows:

create table "BOOK5"."ADDRESS" ( address_Id bigint not null primary key,
CITY VARCHAR(50)
                        STREET VARCHAR(50),
                        DOOR_PLATE VARCHAR(50));


create table "BOOK5"."CUSTOMER" ( Id bigint not null primary key,
                        FIRST_NAME VARCHAR(50),
                        LAST_NAME VARCHAR(50),
                        ID_CARD_NO VARCHAR(50),
                        COUNTRY_CODE VARCHAR(50),
                        EMAIL VARCHAR(50));



create table "BOOK5"."CUSTOMERADDRESS" ( Id bigint not null primary key,
                                address_Id bigint not null unique );

The Address and Customer classes for the unidirectional association are as follows:

public class Address5_3 implements Serializable{

       private static final long serialVersionUID = −605474766287314591L;
       private Long addressId;
       private String city;
       private String street;
       private String doorplate;
       // getters and setters
}

public class Customer5_3 implements Serializable {

       private static final long serialVersionUID = −3534434932962734600L;
       private Long id;
       private String countryCode;
       private String idCardNo;
       private String firstName;
       private String lastName;
       private Address5_3 address;
       private String email;
       // getters and setters
}

The Hibernate mapping file for the Address class is like any simple entity mapping file, because this is a unidirectional mapping:

<hibernate-mapping package="com.hibernaterecipes.chapter5">
        <class name="Address5_3" table="Address" schema="BOOK5">
                <id name="addressId" type="long" column="ADDRESS_ID" >
                        <generator class="native">
                        </generator>
                </id>
<property name="city" type="string" column="CITY" />
                 <property name="street" type="string" column="STREET" />
                 <property name="doorplate" type="string" column="DOOR_PLATE" />


         </class>
</hibernate-mapping>

The customer's Hibernate mapping file has the join element. The join element is mapped to the CUSTOMERADDRESS join table; it's mapped to have the customer ID as a primary key (see the create query). The join table contains the many-to-one element that maps to the ADDRESS table. The unique attribute is set to true in the many-to-one mapping element, which along with the key column establishes a one-to-one mapping. Also note that because you're dealing with multiple schemas, you use the schema attribute in the join element:

<hibernate-mapping package="com.hibernaterecipes.chapter5">
         <class name="Customer5_3" table="Customer" schema="BOOK5">
                 <id name="id" type="long" column="ID">
                         <generator class="native"></generator>
                 </id>
                 <property name="firstName" type="string" column="FIRST_NAME" />
                 <property name="lastName" type="string" column="LAST_NAME" />
                 <property name="idCardNo" type="string" column="ID_CARD_NO" />
                 <property name="countryCode" type="string" column="COUNTRY_CODE" />
                 <property name="email" type="string" column="EMAIL" />
                 <join table="CustomerAddress" optional="true" schema="BOOK5" >
                         <key column="ID" unique="true">
                         </key>
                         <many-to-one name="address"
                                          column="ADDRESS_ID"
                                          not-null="true"
                                          cascade="save-update"
                                          unique="true">
                         </many-to-one>
                 </join>

        </class>
</hibernate-mapping>

Now, coming to bidirectionality, you have to add customer as a property in the address object:

public class Address5_3 implements Serializable{

       private static final long serialVersionUID = −605474766287314591L;
       private Long addressId;
       private String city;
       private String street;
       private String doorplate;
       private Customer5_3 customer;
       // getters and setters
}

And the address mapping file must having a join element similar to the one in the CUSTOMER table, to make it bidirectional one-to-one:

<hibernate-mapping package="com.hibernaterecipes.chapter5">
        <class name="Address5_3" table="Address" schema="BOOK5">
                <id name="addressId" type="long" column="ADDRESS_ID" >
                        <generator class="native">
                        </generator>
                </id>
                <property name="city" type="string" column="CITY" />
                <property name="street" type="string" column="STREET" />
                <property name="doorplate" type="string" column="DOOR_PLATE" />
                <join table="CustomerAddress" optional="true" inverse="true">
                         <key column="ADDRESS_ID" unique="true" />
                         <many-to-one name="customer"
                                          class="com.hibernaterecipes.chapter5.Customer5_3"
                                          column="ID"
                                          unique="true"
                                          not-null="true">
                         </many-to-one>
                </join>


        </class>
</hibernate-mapping>

Using JPA annotations, the Address class is mapped to its customer property with the OneToOne annotation:

@Entity
@Table (name="ADDRESS",schema="BOOK5")
public class Address5_3 implements Serializable
{
       private static final long serialVersionUID = −605474766287314591L;
       @Id
       @Column (name="ADDRESS_ID")
       @GeneratedValue (strategy=GenerationType.AUTO)
       private Long id;

       @Column(name="CITY")
       private String city;
       @Column(name="STREET")
       private String street;
       @Column(name="DOOR_PLATE")
       private String doorplate;

       @OneToOne (mappedBy="address")
       private Customer5_2 customer;
       // getters and setters
}

The Customer table uses the JoinTable annotation to map the address object:

@Entity
@Table (name="CUSTOMER",schema="BOOK5")
public class Customer5_3 implements Serializable {

       private static final long serialVersionUID = −3534434932962734600L;
       @Column (name="ID")
       @Id
       @GeneratedValue (strategy=GenerationType.AUTO)
       private Long id;

       @Column (name="COUNTRY_CODE")
       private String countryCode;
       @Column (name="ID_CARD_NO")
       private String idCardNo;
       @Column (name="FIRST_NAME")
       private String firstName;

       @Column (name="LAST_NAME")
       private String lastName;

       @Column (name="EMAIL")
       private String email;

       @OneToOne (cascade=CascadeType.ALL)
       @JoinTable (name="CustomerAddress", schema="BOOK5",
                joinColumns=@JoinColumn(name="ID"),
                inverseJoinColumns=@JoinColumn(name="ADDRESS_ID"))
       private Address5_3 address;

       // getters and setters
}

Summary

In this chapter, you've seen how to represent and work with many-to-one and one-to-one associations using Hibernate. For a many-to-one association, you define the <many-to-one> mapping element in the class that has the many side of the relationship. In the case where there can be null foreign key values, you use a join table to map between the two dependent objects; the join table contains IDs of both the dependent objects.

You learned that you can use lazy initialization to increase performance: the data of the dependent object is retrieved from the database only on demand. You also learned that shared primary keys are used when rows in two tables are related by a primary key association.

You saw how to map a one-to-one association with a <many-to-one> element and then have a unique constraint on the one relationship. A one-to-one association can also be mapped using a join table.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.74.25