Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Master the New Persistence Paradigm with JPA : Page 3

In this article you will learn how to persist objects using the Java Persistence API (JPA), customize their mapping with annotations, and create a one-to-one relationship. Using the entity lifecycle you will control persistency and use the query language (JPQL) to query your objects.


advertisement
One-to-One Relationship
Now that I have mapped my Customer class the way I want and used callback annotations to validate and calculate data, I need to add an address. One customer has one, and only one, address so that Watermelon can send the customer a birthday present. I represent it as a separate class Address with an id, a street, a city, a zip code, and a country (see Table 6).

Table 6: A Customer Has an Address

Customer class Address class

@Entity
@Table(name = "t_customer")
public class Customer {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;
   @Column(nullable = false)
   private String firstname;
   @Column(nullable = false, length = 30)
   private String lastname;
   @Column(length = 15)
   private String telephone;
   @Column(name = "e_mail")
   private String email;
   private Integer age;

   private Address homeAddress;

   // constuctors, getters, setters
}



@Entity
@Table(name = "t_address")

public class Address {

   @Id
   @GeneratedValue

   private Long id;
   private String street;
   @Column(length = 100)
   private String city;
   @Column(name = "zip_code",    length = 10)
   private String zipcode;
   @Column(length = 50)
   private String country;
   // constuctors, getters, setters
}


As you can see in Table 6, the class Address uses the @Entity annotation to notify JPA that this is a persistent class and @Column to customize the mapping. Creating the relationship between Customer and Address is simple: I simply add an Address attribute on the Customer class. To persist the customer's address information use the code below:

public void createCustomerWithAddress() { // Instantiates a Customer and an Address objecy Customer customer = new Customer("John", "Lennon", "+441909", "john@lenon.com", dateOfBirth); Address homeAddress = new Address("Abbey Road", "London", "SW14", "UK"); customer.setHomeAddress(homeAddress); // Persists the customer with its address trans.begin(); em.persist(homeAddress); em.persist(customer); trans.commit(); // Deletes the customer and the address trans.begin(); em.remove(customer); em.remove(homeAddress); trans.commit(); }

As this code shows, you have to first instantiate a customer and an address object. To link the two I used a setter method (setHomeAddress) and then persisted each object in the same transaction. Because it would not make sense to have an address in the system that is not linked to a customer, when I remove the customer I also remove the address.

But persisting and removing both objects seems like more work than it needs to be. Wouldn't it be better if I could persist or remove just the root object (the Customer) and allow the dependencies to be automatically persisted or removed? Furthermore, coding-by-exception makes associations optional and, based on my requirements, I want a Customer to have exactly one Address, meaning that a null value is not allowed. I can accomplish all of that using the @OneToOne annotation together with @JoinColumn.

@Entity @Table(name = "t_customer") public class Customer { @Id @GeneratedValue private Long id; (...) @Transient private Integer age; @OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.REMOVE}) @JoinColumn(name = "address_fk", nullable = false) private Address homeAddress; // constuctors, getters, setters }

@OneToOne is used to annotate a one-to-one relationship. It has several attributes including cascade used for cascading any type of action. In this example I want to cascade the persist and remove action. This way, when I remove—or persist—a customer object it will automatically carry out this action for the address. The fetch attribute tells JPA which policy to use when loading a relation. Either it can lazy load (LAZY) an association or eagerly load it (EAGER). In this example, I am using EAGER because I want to load the homeAddress as soon as the Customer object is loaded.

The @JoinColumn annotation has almost the same attributes as the @Column one (see Table 2) except it's used for association attributes. In this example, I rename the foreign key into address_fk and I don't allow any null value (nullable = false).

JPA will then create the following DDLs with an integrity constraint for the relationship between tables t_customer and t_address (see Table 7).

Table 7: DDLs with integrity constraint

t_customer DDL t_address DDL

mysql> desc t_customer
+---------------+--------------+------+
| Field | Type | Null |
+---------------+--------------+------+
| ID | bigint(20) | NO |
| FIRSTNAME | varchar(255) | NO |
| LASTNAME | varchar(30) | NO |
| TELEPHONE | varchar(15) | YES |
| e_mail | varchar(255) | YES |
| date_of_birth | date | YES |
| address_fk | bigint(20) | NO |
+---------------+--------------+------+

mysql> desc t_address
+----------+--------------+------+
| Field | Type | Null |
+----------+--------------+------+
| ID | bigint(20) | NO |
| CITY | varchar(100) | YES |
| zip_code | varchar(10) | YES |
| STREET | varchar(255) | YES |
| COUNTRY | varchar(50) | YES |
+----------+--------------+------+

ALTER TABLE t_customer ADD CONSTRAINT FK_customer_address FOREIGN KEY (address_fk) REFERENCES t_address (ID)

 

Querying Objects
Until now I've been using JPA to map my objects to a relational database and using the entity manager to do some CRUD operations. But JPA also allows you to query objects. It uses the Java Persistent Query Language (JPQL), which is similar to SQL and is also independent of the database. It is a rich language that allows you to query any complex object's model (associations, inheritance, abstract classes…).

Queries use the SELECT, FROM, and WHERE keywords, plus a set of operators to filter data (IN, NOT IN, EXIST, LIKE, IS NULL, IS NOT NULL) or to control collections (IS EMPTY, IS NOT EMPTY, MEMBER OF). There are also functions that deal with Strings (LOWER, UPPER, TRIM, CONCAT, LENGTH, SUBSTRING), numbers (ABS, SQRT, MOD), or collections (COUNT, MIN, MAX, SUM). Like SQL, you can also sort the results (ORDER BY) or group them (GROUP BY).

To query objects I need the EntityManager to create a Query object. Then I get the result of the query by calling the getResultList or getSingleResult when there is a single object returned. In the example below I want to find all the customers who have the first name 'John'. I can do this in two ways: Either the string 'John' is fixed and it can be part of the JPQL query or it's a parameter and I need to use the setParameter method.

// Finds the customers who are called John Query query = em.createQuery("SELECT c FROM Customer c WHERE c.firstname='John'"); List<Customer> customers = query.getResultList(); // Same query but using a parameter Query query = em.createQuery("SELECT c FROM Customer c WHERE c.firstname=:param"); query.setParameter(":param", "John"); List<Customer> customers = query.getResultList();

As you can see in this query, JPQL uses the object notation. You don't query a table but an object. The character c (the name is irrelevant) is the alias for a customer object and c.firstname is the first name attribute of the customer object. If you wanted to find all the customers living in the U.S., you could use this notation to get to the country attribute of the address object: SELECT c FROM Customer c WHERE c.homeAddress.country='US'.

Here is a set of queries that we can do with JPQL:

Table 8: JPQL queries

Query Comments
SELECT c FROM Customer c Returns all the customers
SELECT c FROM Customer c WHERE c.firstname='John' Returns the customers which first name is 'John'
SELECT c FROM Customer c ORDER BY c.lastname Returns all the customers ordered by last name
SELECT c FROM Customer c WHERE c.homeAddress.country='US' Customers who live in the US

Ready to Learn More?
This article introduced the basis of JPA: How to declare a persistent object, how to map it into a table, how to CRUD your object, query it and query its associations. In a future article (coming soon) I will show you how JPA can map inheritance and other types of relationships (one-to-many, many-to-many). I will also show how JPQL can deal with abstract classes and navigate through complex associations.



Antonio Goncalves is a senior architect specialized in Java/J2EE. Former BEA consultant he now helps insurance, finance and telecommunication clients set up their architectures. He also teaches J2EE at CNAM University in Paris.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap