persistent classes
4.2 Mapping entities with identity
It’s vital to understand the difference between object identity and object equality before we discuss terms like database identity and the way Hibernate manages identity. Next, we explore how object identity and equality relate to database (pri-mary key) identity.
4.2.1 Understanding Java identity and equality
Java developers understand the difference between Java object identity and equal-ity. Object identity, ==, is a notion defined by the Java virtual machine. Two object references are identical if they point to the same memory location.
On the other hand, object equality is a notion defined by classes that imple-ment the equals() method, sometimes also referred to as equivalence. Equiva-lence means that two different (nonidentical) objects have the same value. Two different instances of String are equal if they represent the same sequence of characters, even though they each have their own location in the memory space of the virtual machine. (If you’re a Java guru, we acknowledge that String is a spe-cial case. Assume we used a different class to make the same point.)
Persistence complicates this picture. With object/relational persistence, a per-sistent object is an in-memory representation of a particular row of a database table. Along with Java identity (memory location) and object equality, you pick up database identity (which is the location in the persistent data store). You now have three methods for identifying objects:
■ Objects are identical if they occupy the same memory location in the JVM. This can be checked by using the == operator. This concept is known as object identity.
■ Objects are equal if they have the same value, as defined by the equals(Object o) method. Classes that don’t explicitly override this method inherit the implementation defined by java.lang.Object, which compares object identity. This concept is known as equality.
■ Objects stored in a relational database are identical if they represent the same row or, equivalently, if they share the same table and primary key value. This concept is known as database identity.
We now need to look at how database identity relates to object identity in Hiber-nate, and how database identity is expressed in the mapping metadata.
4.2.2 Handling database identity
Hibernate exposes database identity to the application in two ways:
■ The value of the identifier property of a persistent instance
■ The value returned by Session.getIdentifier(Object entity)
Adding an identifier property to entities
The identifier property is special—its value is the primary key value of the data-base row represented by the persistent instance. We don’t usually show the identi-fier property in the domain model diagrams. In the examples, the identiidenti-fier property is always named id. If myCategory is an instance of Category, calling myCategory.getId() returns the primary key value of the row represented by myCategory in the database.
Let’s implement an identifier property for the Category class:
public class Category { private Long id;
...
public Long getId() { return this.id;
}
private void setId(Long id) { this.id = id;
} ...
}
Should you make the accessor methods for the identifier property private scope or public? Well, database identifiers are often used by the application as a convenient handle to a particular instance, even outside the persistence layer. For example, it’s common for web applications to display the results of a search screen to the user as a list of summary information. When the user selects a particular element, the application may need to retrieve the selected object, and it’s common to use a lookup by identifier for this purpose—you’ve probably already used identifiers this way, even in applications that rely on JDBC. It’s usually appropriate to fully expose the database identity with a public identifier property accessor.
On the other hand, you usually declare the setId() method private and let Hibernate generate and set the identifier value. Or, you map it with direct field access and implement only a getter method. (The exception to this rule is classes with natural keys, where the value of the identifier is assigned by the application before the object is made persistent instead of being generated by Hibernate. We discuss natural keys in chapter 8.) Hibernate doesn’t allow you to change the identifier value of a persistent instance after it’s first assigned. A pri-mary key value never changes—otherwise the attribute wouldn’t be a suitable primary key candidate!
The Java type of the identifier property, java.lang.Long in the previous exam-ple, depends on the primary key type of the CATEGORY table and how it’s mapped in Hibernate metadata.
Mapping the identifier property
A regular (noncomposite) identifier property is mapped in Hibernate XML files with the <id> element:
<class name="Category" table="CATEGORY">
<id name="id" column="CATEGORY_ID" type="long">
<generator class="native"/>
</id>
...
</class>
The identifier property is mapped to the primary key column CATEGORY_ID of the table CATEGORY. The Hibernate type for this property is long, which maps to a BIGINT column type in most databases and which has also been chosen to match the type of the identity value produced by the native identifier generator. (We discuss identifier generation strategies in the next section.)
For a JPA entity class, you use annotations in the Java source code to map the identifier property:
@Entity
@Table(name="CATEGORY") public class Category { private Long id;
...
@Id
@GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "CATEGORY_ID")
public Long getId() { return this.id;
}
private void setId(Long id) { this.id = id;
} ...
}
The @Id annotation on the getter method marks it as the identifier property, and
@GeneratedValue with the GenerationType.AUTO option translates into a native identifier generation strategy, like the native option in XML Hibernate map-pings. Note that if you don’t define a strategy, the default is also
Generation-Type.AUTO, so you could have omitted this attribute altogether. You also specify a database column—otherwise Hibernate would use the property name. The map-ping type is implied by the Java property type, java.lang.Long.
Of course, you can also use direct field access for all properties, including the database identifier:
@Entity
@Table(name="CATEGORY") public class Category { @Id @GeneratedValue
@Column(name = "CATEGORY_ID") private Long id;
...
public Long getId() { return this.id;
} ...
}
Mapping annotations are placed on the field declaration when direct field access is enabled, as defined by the standard.
Whether field or property access is enabled for an entity depends on the posi-tion of the mandatory @Id annotation. In the preceding example, it’s present on a field, so all attributes of the class are accessed by Hibernate through fields. The example before that, annotated on the getId() method, enables access to all attributes through getter and setter methods.
Alternatively, you can use JPA XML descriptors to create your identifier mapping:
<entity class="auction.model.Category" access="FIELD">
<table name="CATEGORY"/>
<attributes>
<id name="id">
<generated-value strategy="AUTO"/>
</id>
...
</attributes>
</entity>
In addition to operations for testing Java object identity, (a == b), and object equality, ( a.equals(b) ), you may now use a.getId().equals( b.getId() ) to test database identity. What do these notions have in common? In what situa-tions do they all return true? The time when all are true is called the scope of