Thursday, March 25, 2010

Hibernate One-to-Many With Annotations

Let's say you have ONE customer with MANY purchased items. How do you do this with Hibernate's annotations? I am not sure if this is the best way, but it works. I am using Hibernate 3.

The classes assume that the database provides an auto-generated id for the primary keys. Also, ignore the version annotation if you do not know what that does.

Customer.java

package daniel.domain;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Version;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;

@Entity
public class Customer {

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;

private String firstName;
private String lastName;

@OneToMany(mappedBy="customerId")
@Cascade(value=CascadeType.ALL)
@OrderBy(value="name")
private List purchasedItems;

@Version
private Long version;

public Customer() {
}

public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public List getPurchasedItems() {
return purchasedItems;
}
public void setOrders(List purchasedItems) {
this.purchasedItems = purchasedItems;
}

public Long getVersion() {
return version;
}

public void setVersion(Long version) {
this.version = version;
}

public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("Customer [id = ").append(id).append(",").append(lastName).append(", ").append(firstName).append("]");
if(purchasedItems != null) {
buf.append("\n");
for(PurchasedItem item : purchasedItems) {
buf.append("\t").append(item).append("\n");
}
}
return buf.toString();
}
}

PurchasedItem.java

package daniel.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Version;

@Entity
public class PurchasedItem {

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private Long customerId;
private String name;

@Version
private Long version;

public PurchasedItem() {}

public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

public Long getVersion() {
return version;
}

public void setVersion(Long version) {
this.version = version;
}

public String toString() {
return "PurchasedItem [id=" + ", customerId=" + customerId + ", name=" + name + "]";
}
}

When you save a customer with it's related purchased items, an entry will be put into the customer table and zero or more entries into the purchaseditem table. Since the class and field names have not been marked with annotations specifying table and column names, Hibernate assumes the class name is mapped to a table with the same name, and the fields are mapped to that table's column names.

To run an example of this use Main.java:
package daniel.hibernate;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

import daniel.domain.Customer;
import daniel.domain.PurchasedItem;

public class Main {

@SuppressWarnings(value = { "unchecked" })
public static void main(String[] args) throws Exception {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();

// Customer
Customer c = new Customer();
c.setFirstName("Firstname");
c.setLastName("Lastname");
session.save(c);

// Purchase 1
PurchasedItem item = new PurchasedItem();
item.setCustomerId(c.getId());
item.setName("Ipod " + new Date());

// Purchase 2
PurchasedItem item2 = new PurchasedItem();
item2.setCustomerId(c.getId());
item2.setName("MP3 Song " + new Date());

// Put purchase into list
List items = new ArrayList();
items.add(item);
items.add(item2);
c.setOrders(items);

// Verify customer and purchased items info was saved
Query query = session.createQuery("from Customer");
List customers = query.list();
System.out.println("CUSTOMERS---------------> ");
for(Customer found : customers) {
System.out.println(found);
}

tx.commit();
session.close();
}
}

Running Main will insert ONE row into the customer table and TWO rows into the purchaseditems table.

I am wondering if there is a way to avoid having to manually set the customer id into the purchased item instance.

No comments:

Post a Comment