Page 4 of 6
Now, how do you find a record in the database with a primary key if the entity has a composite primary key? You'll see how that works in a moment. Assume that the CUSTOMER table doesn't have a CUST_ID
field, but that FIRST_NAME
and LAST_NAME
together make up the primary key. To make this work, you need to create a separate class, generally called an identity class, with attributes the same as the IDs; then you reference the identity class in the entity class. This is shown in Listing 7.
public class CustomerId {
public String firstName;
public String lastName;
// override equal() method
//override hascode() method
..................
}
The identity class can be a separate class or an inner class. If the class is an inner class, it must be static and should be referenced in the entity class, as in Listing 8. The code for finding records with a composite primary key is exactly the same as that for finding records with a single primary key.
@Entity
@IdClass(Customer.CustomerId.class)
public class Customer implements Serializable{
@Id
@Column(name = "FIRST_NAME", nullable = false, length = 50)
private String firstName;
@Id
@Column(name = "LAST_NAME", length = 50)
private String lastName;
private String street;
@Column(name = "APPT",nullable = false)
private String appt;
................
}
JPA provides callback methods for performing actions at different stages of persistence operations. Imagine that you want to update a customer record, but, before you update, you want to remove the hyphen from the zip code if one is present. Or say that you want to populate some transient fields after a successful fetch. JPA provides listeners for these kinds of activities before and after each fetch, insert, or update operation. The callback methods can be annotated as any of the following:
@PostLoad
@PrePersist
@PostPersist
@PreUpdate
@PostUpdate
@PreRemove
@PostRemove
You can write the callback methods in the entity class itself, or you can write them in a separate class and reference them in the entity class with @EntityListeners
, as shown in Listing 9.
@EntityListeners({CustListner.class})
@Entity(name = "CUSTOMER") //Name of the entity
public class Customer implements Serializable{
...
...
}
public class CustListner {
@PreUpdate
public void preUpdate(Customer cust) {
System.out.println("In pre update");
}
@PostUpdate
public void postUpdate(Customer cust) {
System.out.println("In post update");
}
}
As you've seen so far, the Customer
entity has the address information inline in the entity itself. What if you want to apply class normalization concepts and come up with a separate Address
class and refer to that in the Customer
entity? After all, an address object could be used with Customer
, Employee
, Order
, or User
entities.
All you need is an embedded object. You move the address information into a separate class and mark that class as being embeddable, as shown in Listing 10. Refer to this newly created class from the Customer
entity with @Embedded
annotations.
@Embeddable
public class Address implements Serializable{
private String street;
@Column(name = "APPT",nullable = false)
private String appt;
private String city;
..
..
}
Embedded classes are mapped together with their owning entity as part of the state of that entity. However, they cannot be queried separately. Listing 11 illustrates a sample entity that uses an embedded object.
@Entity
public class Customer {
...............
@Column(name = "FIRST_NAME", nullable = false,length = 50)
private String firstName;
@Embedded
private Address address;
..............
}