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
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?
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.
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.
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 }
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?
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.
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.
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?
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.
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>
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.
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 longserialVersionUID
= −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 longserialVersionUID
= −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 longserialVersionUID
= −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.
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?
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.
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 longserialVersionUID
= −3534434932962734600L;@Column (name="ID")
@Id
@GeneratedValue (strategy=GenerationType.
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;AUTO
)@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 longserialVersionUID
= −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 }
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?
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.
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 longserialVersionUID
= −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 longserialVersionUID
= −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 longserialVersionUID
= −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 }
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.
18.220.202.209