Saturday, March 19, 2011

Spring - Hibernate: Many-To-One Association

Introduction

In this tutorial we'll explore how to implement a many-to-one association using Hibernate Annotations and Spring MVC 3. We will use @ManyToOne annotation to specify the relationship between our objects. We will not specify Cascade types or Fetch strategies, but instead rely on @ManyToOne's default settings.

If you want to see how to implement this project using @OneToMany annotation instead, see Spring - Hibernate: One-To-Many Association.

Spring MVC 3 and Hibernate Tutorials Series
Spring - Hibernate: Many-To-One Association - Explicitly Specify Join Table, Cascade, and Fetch
Spring - Hibernate: One-To-Many Association - Explicitly Specify Join Table, Cascade, and Fetch
Spring - Hibernate: Many-To-One Association
Spring - Hibernate: One-To-Many Association
Spring MVC 3, Hibernate Annotations, MySQL Integration Tutorial
Spring MVC 3, Hibernate Annotations, HSQLDB Integration Tutorial

What is Many-To-One Association?
A many-to-one relationship is where one entity (typically a column or set of columns) contains values that refer to another entity (a column or set of columns) that has unique values.

Source: http://publib.boulder.ibm.com/infocenter/ablxhelp/v8r4m0/index.jsp?topic=/com.ibm.db2.abx.cub.doc/abx-c-cube-many21relationships.html

Specification

Our application is simple CRUD system for managing a list of records. Each record corresponds to a single person containing personal information and credit card details. Each person can own zero or more credit cards. Moreover, a person's person information is edited separately from his credit card details.

Below are sample screenshots from the application:




Domain Objects

Based on the specs, we have two domain objects: Person and Credit Card.

A Person object has the following properties:
  • id
  • first name
  • last name
  • money

Whereas a Credit Card object has the following properties:
  • id
  • type
  • number
  • person

Notice each Credit Card has a reference to a Person hence, the many-to-one relationship. We can of course look at this relationship the other way around: one-to-many relationship. See Spring - Hibernate: One-To-Many Association

Development

We'll split our development in three layers: Domain, Service, and Controller. Then we'll declare the configuration files. Let's start with the Domain layer.

Domain Layer

As discussed earlier, we have two domain objects: Person and Credit Card. Consequently, we'll declare two POJOs to represent our domain. Each POJO will be annotated with @Entity so that we can persist them to a database.

Person.java
package org.krams.tutorial.domain;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * Represents a person entity
 * 
 * @author Krams at {@link http://krams915@blogspot.com
 */
@Entity
@Table(name = "PERSON")
public class Person implements Serializable {

 private static final long serialVersionUID = -5527566248002296042L;
 
 @Id
 @Column(name = "ID")
 @GeneratedValue
 private Integer id;
 
 @Column(name = "FIRST_NAME")
 private String firstName;
 
 @Column(name = "LAST_NAME")
 private String lastName;
 
 @Column(name = "MONEY")
 private Double money;
 
 public Integer getId() {
  return id;
 }

 public void setId(Integer 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 Double getMoney() {
  return money;
 }

 public void setMoney(Double money) {
  this.money = money;
 }
}

Person object has been mapped to the PERSON table. The table that gets auto-generated looks as follows:


CreditCard.java
package org.krams.tutorial.domain;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

/**
 * Represents a credit card entity
 * 
 * @author Krams at {@link http://krams915@blogspot.com
 */
@Entity
@Table(name = "CREDIT_CARD")
public class CreditCard implements Serializable {

 private static final long serialVersionUID = 5924361831551833717L;

 @Id
 @Column(name = "ID")
 @GeneratedValue
 private Integer id;
 
 @Column(name = "TYPE")
 private String type;
 
 @Column(name = "NUMBER")
 private String number;

 @ManyToOne
 private Person person;
 
 public Integer getId() {
  return id;
 }

 public void setId(Integer id) {
  this.id = id;
 }

 public String getType() {
  return type;
 }

 public void setType(String type) {
  this.type = type;
 }

 public String getNumber() {
  return number;
 }

 public void setNumber(String number) {
  this.number = number;
 }

 public Person getPerson() {
  return person;
 }

 public void setPerson(Person person) {
  this.person = person;
 }
 
 
}

CreditCard object has been mapped to the CREDIT_CARD table. The table that gets auto-generated looks as follows:


The CREDIT_CARD table has a person_ID field that references a person on the Person table via its id. Notice the @ManyToOne annotation for the field person. We did not specify any Cascade type or Fetch strategy for this field. Instead, we rely on the annotation's default settings.

@Entity
@Table(name = "CREDIT_CARD")
public class CreditCard implements Serializable {

 ...
 @ManyToOne
 private Person person;
 ...
}

Using phpymyadmin's database designer, the Hibernate auto-generated relationship between Person and CreditCard looks as follows:


Again using phpymyadmin, the auto-generated tables looks as follows:


We declared two entities: a Person entity and a CreditCard entity. We expect two tables in the database, and we did. However, if we've implemented this using the default settings of @OneToMany annotation, we get three tables instead. See Spring - Hibernate: One-To-Many Association

Service Layer

After declaring the Domain objects, we'll now proceed with the Service layer which contains two services: a PersonService and a CreditCardService.

PersonService
PersonService is responsible for handling CRUD operations for Person entities. Each method is eventually delegated to a Hibernate Session object.

PersonService.java
package org.krams.tutorial.service;

import java.util.List;
import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.krams.tutorial.domain.Person;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * Service for processing Persons
 * 
 * @author Krams at {@link http://krams915@blogspot.com
 */
@Service("personService")
@Transactional
public class PersonService {

 protected static Logger logger = Logger.getLogger("service");
 
 @Resource(name="sessionFactory")
 private SessionFactory sessionFactory;
 
 /**
  * Retrieves all persons
  * 
  * @return a list of persons
  */
 public List<Person> getAll() {
  logger.debug("Retrieving all persons");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Create a Hibernate query (HQL)
  Query query = session.createQuery("FROM  Person");
  
  // Retrieve all
  return  query.list();
 }
 
 /**
  * Retrieves a single person
  */
 public Person get( Integer id ) {
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Retrieve existing person
  Person person = (Person) session.get(Person.class, id);
  
  return person;
 }
 
 /**
  * Adds a new person
  */
 public void add(Person person) {
  logger.debug("Adding new person");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Persists to db
  session.save(person);
 }
 
 /**
  * Deletes an existing person
  * @param id the id of the existing person
  */
 public void delete(Integer id) {
  logger.debug("Deleting existing person");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Retrieve existing person
  Person person = (Person) session.get(Person.class, id);
  
  // Delete 
  session.delete(person);
 }
 
 /**
  * Edits an existing person
  */
 public void edit(Person person) {
  logger.debug("Editing existing person");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Retrieve existing person via id
  Person existingPerson = (Person) session.get(Person.class, person.getId());
  
  // Assign updated values to this person
  existingPerson.setFirstName(person.getFirstName());
  existingPerson.setLastName(person.getLastName());
  existingPerson.setMoney(person.getMoney());

  // Save updates
  session.save(existingPerson);
 }
}

CreditCardService
CreditCardService is responsible for handling CRUD operations for CreditCard entities. Each method is eventually delegated to a Hibernate Session object.

CreditCardService.java
package org.krams.tutorial.service;

import java.util.List;
import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.krams.tutorial.domain.CreditCard;
import org.krams.tutorial.domain.Person;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * Service for processing Credit Cards
 * 
 * @author Krams at {@link http://krams915@blogspot.com
 */
@Service("creditCardService")
@Transactional
public class CreditCardService {

 protected static Logger logger = Logger.getLogger("service");
 
 @Resource(name="sessionFactory")
 private SessionFactory sessionFactory;
 
 /**
  * Retrieves all credit cards
  */
 public List<CreditCard> getAll(Integer personId) {
  logger.debug("Retrieving all credit cards");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Create a Hibernate query (HQL)
  Query query = session.createQuery("FROM  CreditCard WHERE person.id=" +personId);
  
  // Retrieve all
  return  query.list();
 }
 
 /**
  * Retrieves all credit cards
  */
 public List<CreditCard> getAll() {
  logger.debug("Retrieving all credit cards");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Create a Hibernate query (HQL)
  Query query = session.createQuery("FROM  CreditCard");
  
  // Retrieve all
  return  query.list();
 }
 
 /**
  * Retrieves a single credit card
  */
 public CreditCard get( Integer id ) {
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Retrieve existing credit card
  CreditCard creditCard = (CreditCard) session.get(CreditCard.class, id);
  
  // Persists to db
  return creditCard;
 }
 
 /**
  * Adds a new credit card
  */
 public void add(Integer personId, CreditCard creditCard) {
  logger.debug("Adding new credit card");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Retrieve existing person via id
  Person existingPerson = (Person) session.get(Person.class, personId);
  
  // Add person to credit card
  creditCard.setPerson(existingPerson);
  
  // Persists to db
  session.save(creditCard);
 }
 
 /**
  * Deletes an existing credit card
  */
 public void delete(Integer id) {
  logger.debug("Deleting existing credit card");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Retrieve existing credit card
  CreditCard creditCard = (CreditCard) session.get(CreditCard.class, id);
  
  // Delete 
  session.delete(creditCard);
 }
 
 /**
  * Deletes all credit cards based on the person's id
  */
 public void deleteAll(Integer personId) {
  logger.debug("Deleting existing credit cards based on person's id");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Create a Hibernate query (HQL)
  Query query = session.createQuery("DELETE FROM CreditCard WHERE person.id=" +personId);
  
  // Delete all
  query.executeUpdate();
 }
 
 /**
  * Edits an existing credit card
  */
 public void edit(CreditCard creditCard) {
  logger.debug("Editing existing creditCard");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Retrieve existing credit card via id
  CreditCard existingCreditCard = (CreditCard) session.get(CreditCard.class, creditCard.getId());
  
  // Assign updated values to this credit card
  existingCreditCard.setNumber(creditCard.getNumber());
  existingCreditCard.setType(creditCard.getType());

  // Save updates
  session.save(existingCreditCard);
 }
}

Controller Layer

After declaring the Domain and Service objects, we'll now proceed with the Controller layer.
We'll declare two controllers: MainController and CreditCardController.

MainController
MainController is responsible for handling request for Person records. Each CRUD request is ultimately delegated to the PersonService and then returns an appropriate JSP page.

MainController.java
package org.krams.tutorial.controller;

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

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.krams.tutorial.domain.Person;
import org.krams.tutorial.dto.PersonDTO;
import org.krams.tutorial.service.CreditCardService;
import org.krams.tutorial.service.PersonService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;


/**
 * Handles and retrieves person request
 * 
 * @author Krams at {@link http://krams915@blogspot.com
 */
@Controller
@RequestMapping("/main/record")
public class MainController {

 protected static Logger logger = Logger.getLogger("controller");
 
 @Resource(name="personService")
 private PersonService personService;
 
 @Resource(name="creditCardService")
 private CreditCardService creditCardService;
 
 /**
  * Retrieves the "Records" page
  */
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public String getRecords(Model model) {
     logger.debug("Received request to show records page");
     
     // Retrieve all persons
     List<Person> persons = personService.getAll();
     
     // Prepare model object
     List<PersonDTO> personsDTO = new ArrayList<PersonDTO>();
     
     for (Person person: persons) {
      // Create new data transfer object
      PersonDTO dto = new PersonDTO();
      
   dto.setId(person.getId());
   dto.setFirstName(person.getFirstName());
   dto.setLastName(person.getLastName());
   dto.setMoney(person.getMoney());
   dto.setCreditCards(creditCardService.getAll(person.getId()));
   
   // Add to model list
   personsDTO.add(dto);
     }
     
     // Add to model
     model.addAttribute("persons", personsDTO);

     // This will resolve to /WEB-INF/jsp/records.jsp
  return "records";
 }
    
    /**
     *  Retrieves the "Add New Record" page
     */
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public String getAdd(Model model) {
     logger.debug("Received request to show add page");
    
     // Create new Person and add to model
     model.addAttribute("personAttribute", new Person());

     // This will resolve to /WEB-INF/jsp/add-record.jsp
     return "add-record";
 }
 
    /**
     * Adds a new record
     */
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String postAdd(@ModelAttribute("personAttribute") Person person) {
  logger.debug("Received request to add new record");

  // Delegate to service
  personService.add(person);

  // Redirect to url
  return "redirect:/krams/main/record/list";
 }
    
    /**
     * Deletes a record including all the associated credit cards
     */
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public String getDelete(@RequestParam("id") Integer personId) {
     logger.debug("Received request to delete record");
    
     // Delete all associated credit cards first
     creditCardService.deleteAll(personId);
     
     // Delete person
  personService.delete(personId);

  // Redirect to url
  return "redirect:/krams/main/record/list";
 }
    
    /**
     * Retrieves the "Edit Existing Record" page
     */
    @RequestMapping(value = "/edit", method = RequestMethod.GET)
    public String getEdit(@RequestParam("id") Integer personId, Model model) {
     logger.debug("Received request to show edit page");
     
     // Retrieve person by id
     Person existingPerson = personService.get(personId);

     // Add to model
     model.addAttribute("personAttribute", existingPerson);

     // This will resolve to /WEB-INF/jsp/edit-record.jsp
     return "edit-record";
 }
 
    /**
     * Edits an existing record
     */
    @RequestMapping(value = "/edit", method = RequestMethod.POST)
    public String postEdit(@RequestParam("id") Integer personId, 
              @ModelAttribute("personAttribute") Person person) {
  logger.debug("Received request to add new person");
  
  // Assign id
  person.setId(personId);
  
  // Delegate to service
  personService.edit(person);

  // Redirect to url
  return "redirect:/krams/main/record/list";
 }
    
}

Pay attention to the delete() method. In order to delete a Person record, we have to delete first all the associated Credit Cards. Then we delete the Person record.

/**
     * Deletes a record including all the associated credit cards
     */
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public String getDelete(@RequestParam("id") Integer personId) {
     logger.debug("Received request to delete record");
    
     // Delete all associated credit cards first
     creditCardService.deleteAll(personId);
     
     // Delete person
  personService.delete(personId);

  // Redirect to url
  return "redirect:/krams/main/record/list";
 }


Notice the getRecords() method maps the Person and CreditCard records to a data transfer object: PersonDTO

/**
  * Retrieves the "Records" page
  */
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public String getRecords(Model model) {
     logger.debug("Received request to show records page");
     
     // Retrieve all persons
     List<Person> persons = personService.getAll();
     
     // Prepare model object
     List<PersonDTO> personsDTO = new ArrayList<PersonDTO>();
     
     for (Person person: persons) {
      // Create new data transfer object
      PersonDTO dto = new PersonDTO();
      
   dto.setId(person.getId());
   dto.setFirstName(person.getFirstName());
   dto.setLastName(person.getLastName());
   dto.setMoney(person.getMoney());
   dto.setCreditCards(creditCardService.getAll(person.getId()));
   
   // Add to model list
   personsDTO.add(dto);
     }
     
     // Add to model
     model.addAttribute("persons", personsDTO);

     // This will resolve to /WEB-INF/jsp/records.jsp
  return "records";
 }

PersonDTO acts as a data model for the records.jsp view.

PersonDTO.java
package org.krams.tutorial.dto;

import java.util.List;

import org.krams.tutorial.domain.CreditCard;

/**
 * Data Transfer Object for displaying purposes
 */
public class PersonDTO {

 private Integer id;
 private String firstName;
 private String lastName;
 private Double money;
 private List<CreditCard> creditCards;
 
 public Integer getId() {
  return id;
 }
 public void setId(Integer 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 Double getMoney() {
  return money;
 }
 public void setMoney(Double money) {
  this.money = money;
 }
 public List<CreditCard> getCreditCards() {
  return creditCards;
 }
 public void setCreditCards(List<CreditCard> creditCards) {
  this.creditCards = creditCards;
 }
}

CreditCardController
CreditCardController is responsible for handling request for credit card records. We won't be using all methods available in this controller. They had been added for the sake of completeness.

CreditCardController.java
package org.krams.tutorial.controller;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.krams.tutorial.domain.CreditCard;
import org.krams.tutorial.service.CreditCardService;
import org.krams.tutorial.service.PersonService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;


/**
 * Handles and retrieves person request
 * 
 * @author Krams at {@link http://krams915@blogspot.com
 */
@Controller
@RequestMapping("/main/creditcard")
public class CreditCardController {

 protected static Logger logger = Logger.getLogger("controller");
 
 @Resource(name="personService")
 private PersonService personService;
 
 @Resource(name="creditCardService")
 private CreditCardService creditCardService;
 
    /**
     * Retrieves the "Add New Credit Card" page
     */
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public String getAdd(@RequestParam("id") Integer personId, Model model) {
     logger.debug("Received request to show add page");
    
     // Prepare model object
     CreditCard creditCard = new CreditCard();
     creditCard.setPerson(personService.get(personId));
     
     // Add to model
     model.addAttribute("creditCardAttribute", creditCard);

     // This will resolve to /WEB-INF/jsp/add-credit-card.jsp
     return "add-credit-card";
 }
 
    /**
     * Adds a new credit card
     */
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String postAdd(@RequestParam("id") Integer personId, 
              @ModelAttribute("creditCardAttribute") CreditCard creditCard) {
  logger.debug("Received request to add new credit card");
  
  // Delegate to service
  creditCardService.add(personId, creditCard);

  // Redirect to url
  return "redirect:/krams/main/record/list";
 }
    
    
    /**
     * Deletes a credit card
     */
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public String getDelete(@RequestParam("id") Integer creditCardId) {
     logger.debug("Received request to delete credit card");
    
     // Delegate to service
  creditCardService.delete(creditCardId);

  // Redirect to url
  return "redirect:/krams/main/record/list";
 }
   
    /**
     * Retrieves the "Edit Existing Credit Card" page
     */
    @RequestMapping(value = "/edit", method = RequestMethod.GET)
    public String getEdit(@RequestParam("id") Integer creditCardId, Model model) {
     logger.debug("Received request to show edit page");
     
     // Retrieve credit card by id
     CreditCard existingCreditCard = creditCardService.get(creditCardId);

     // Add to model
     model.addAttribute("creditCardAttribute", existingCreditCard);

     // This will resolve to /WEB-INF/jsp/edit-credit-card.jsp
     return "edit-credit-card";
 }
 
    /**
     * Edits an existing credit card
     */
    @RequestMapping(value = "/edit", method = RequestMethod.POST)
    public String postEdit(@RequestParam("id") Integer creditCardId, 
              @ModelAttribute("creditCardAttribute") CreditCard creditCard) {
  logger.debug("Received request to add new credit card");
  
  // Assign id
  creditCard.setId(creditCardId);
  
  // Delegate to service
  creditCardService.edit(creditCard);

  // Redirect to url
  return "redirect:/krams/main/record/list";
 }
}

View Layer

After discussing the Domain, Service, and Controller layers, we'll now turn our focus to the View layer. Our views are primarily composed of JSP pages. Here are the JSPs:

Show all records


records.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Records</h1>

<c:url var="editImgUrl" value="/resources/img/edit.png" />
<c:url var="deleteImgUrl" value="/resources/img/delete.png" />
<c:url var="addUrl" value="/krams/main/record/add" />
<table style="border: 1px solid; width: 100%; text-align:center">
 <thead style="background:#d3dce3">
  <tr>
   <th>Id</th>
   <th>First Name</th>
   <th>Last Name</th>
   <th>Money</th>
   <th colspan="2"></th>
   <th>CC Type</th>
   <th>CC Number</th>
   <th colspan="3"></th>
  </tr>
 </thead>
 <tbody style="background:#ccc">
 <c:forEach items="${persons}" var="person">
  <c:url var="editUrl" value="/krams/main/record/edit?id=${person.id}" />
  <c:url var="deleteUrl" value="/krams/main/record/delete?id=${person.id}" />
  
  <c:if test="${!empty person.creditCards}">
   <c:forEach items="${person.creditCards}" var="creditCard">
   <tr>
    <td><c:out value="${person.id}" /></td>
    <td><c:out value="${person.firstName}" /></td>
    <td><c:out value="${person.lastName}" /></td>
    <td><c:out value="${person.money}" /></td>
    <td><a href="${editUrl}"><img src="${editImgUrl}"></img></a></td>
    <td><a href="${deleteUrl}"><img src="${deleteImgUrl}"></img></a></td>
    
    <td><c:out value="${creditCard.type}" /></td>
    <td><c:out value="${creditCard.number}" /></td>
    <c:url var="addCcUrl" value="/krams/main/creditcard/add?id=${person.id}" />
    <c:url var="editCcUrl" value="/krams/main/creditcard/edit?id=${creditCard.id}" />
    <c:url var="deleteCcUrl" value="/krams/main/creditcard/delete?id=${creditCard.id}" />
    <td><a href="${addCcUrl}">+</a></td>
    <td><a href="${editCcUrl}"><img src="${editImgUrl}"></img></a></td>
    <td><a href="${deleteCcUrl}"><img src="${deleteImgUrl}"></img></a></td>
   </tr>
   </c:forEach>
  </c:if>
  
  <c:if test="${empty person.creditCards}">
   <tr>
    <td><c:out value="${person.id}" /></td>
    <td><c:out value="${person.firstName}" /></td>
    <td><c:out value="${person.lastName}" /></td>
    <td><c:out value="${person.money}" /></td>
    <td><a href="${editUrl}"><img src="${editImgUrl}"></img></a></td>
    <td><a href="${deleteUrl}"><img src="${deleteImgUrl}"></img></a></td>
    
    <td>N/A</td>
    <td>N/A</td>
    <c:url var="addCcUrl" value="/krams/main/creditcard/add?id=${person.id}" />
    <td><a href="${addCcUrl}">+</a></td>
    <td></td>
    <td></td>
   </tr>
  </c:if>
  
 </c:forEach>
 </tbody>
</table>

<c:if test="${empty persons}">
 No records found. 
</c:if>

<p><a href="${addUrl}">Create new record</a></p>

</body>
</html>

Add new record


add-record.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Create New Record</h1>

<c:url var="saveUrl" value="/krams/main/record/add" />
<form:form modelAttribute="personAttribute" method="POST" action="${saveUrl}">
 <table>
  <tr>
   <td><form:label path="firstName">First Name:</form:label></td>
   <td><form:input path="firstName"/></td>
  </tr>

  <tr>
   <td><form:label path="lastName">Last Name</form:label></td>
   <td><form:input path="lastName"/></td>
  </tr>
  
  <tr>
   <td><form:label path="money">Money</form:label></td>
   <td><form:input path="money"/></td>
  </tr>
 </table>
 
 <input type="submit" value="Save" />
</form:form>

</body>
</html>

Edit existing record


edit-record.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Edit Existing Record</h1>

<c:url var="saveUrl" value="/krams/main/record/edit?id=${personAttribute.id}" />
<form:form modelAttribute="personAttribute" method="POST" action="${saveUrl}">
 <table>
  <tr>
   <td><form:label path="id">Id:</form:label></td>
   <td><form:input path="id" disabled="true"/></td>
  </tr>
 
  <tr>
   <td><form:label path="firstName">First Name:</form:label></td>
   <td><form:input path="firstName"/></td>
  </tr>

  <tr>
   <td><form:label path="lastName">Last Name</form:label></td>
   <td><form:input path="lastName"/></td>
  </tr>
  
  <tr>
   <td><form:label path="money">Money</form:label></td>
   <td><form:input path="money"/></td>
  </tr>
 </table>
 
 <input type="submit" value="Save" />
</form:form>

</body>
</html>

Add new credit card


add-credit-card.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Add New Credit Card</h1>

<c:url var="saveUrl" value="/krams/main/creditcard/add?id=${personId}" />
<form:form modelAttribute="creditCardAttribute" method="POST" action="${saveUrl}">
 <table>
 
  <tr>
   <td>Person Id:</td>
   <td><input type="text" value="${personId}" disabled="true"/>
  </tr>
  
  <tr>
   <td><form:label path="type">Type:</form:label></td>
   <td><form:input path="type"/></td>
  </tr>

  <tr>
   <td><form:label path="number">Number:</form:label></td>
   <td><form:input path="number"/></td>
  </tr>
 </table>
 
 <input type="submit" value="Save" />
</form:form>

</body>
</html>

Edit existing credit card


edit-credit-card.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>Edit Existing Credit Card</h1>

<c:url var="saveUrl" value="/krams/main/creditcard/edit?id=${creditCardAttribute.id}" />
<form:form modelAttribute="creditCardAttribute" method="POST" action="${saveUrl}">
 <table>
 
  <tr>
   <td>Person Id:</td>
   <td><input type="text" value="${personId}" disabled="true"/>
  </tr>
  
  <tr>
   <td><form:label path="type">Type:</form:label></td>
   <td><form:input path="type"/></td>
  </tr>

  <tr>
   <td><form:label path="number">Number:</form:label></td>
   <td><form:input path="number"/></td>
  </tr>
 </table>
 
 <input type="submit" value="Save" />
</form:form>

</body>
</html>

Configuration

We have completed the necessary Java classes. Our next step is to declare the necessary configuration files:

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 
 <servlet>
  <servlet-name>spring</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 
 <servlet-mapping>
  <servlet-name>spring</servlet-name>
  <url-pattern>/krams/*</url-pattern>
 </servlet-mapping>

 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
 
</web-app>

spring-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:p="http://www.springframework.org/schema/p" 
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
 <!-- Declare a view resolver -->
 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
      p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />

</beans>

applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.0.xsd
   http://www.springframework.org/schema/mvc 
   http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
 
 <!-- Activates various annotations to be detected in bean classes -->
 <context:annotation-config />
 
 <!-- Scans the classpath for annotated components that will be auto-registered as Spring beans.
  For example @Controller and @Service. Make sure to set the correct base-package-->
 <context:component-scan base-package="org.krams.tutorial" />
 
 <!-- Configures the annotation-driven Spring MVC Controller programming model.
 Note that, with Spring 3.0, this tag works in Servlet MVC only!  -->
 <mvc:annotation-driven /> 
 
 <!-- Load Hibernate related configuration -->
 <import resource="hibernate-context.xml" />
 
</beans>

hibernate-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans  xmlns="http://www.springframework.org/schema/beans" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:p="http://www.springframework.org/schema/p" 
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
   http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/tx 
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-3.0.xsd
      ">

 <context:property-placeholder location="/WEB-INF/spring.properties" />
    
    <!-- Enable annotation style of managing transactions -->
 <tx:annotation-driven transaction-manager="transactionManager" /> 
   
    <!-- Declare the Hibernate SessionFactory for retrieving Hibernate sessions -->
    <!-- See http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/hibernate3/annotation/AnnotationSessionFactoryBean.html -->        
 <!-- See http://docs.jboss.org/hibernate/stable/core/api/index.html?org/hibernate/SessionFactory.html -->
 <!-- See http://docs.jboss.org/hibernate/stable/core/api/index.html?org/hibernate/Session.html -->
 <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
     p:dataSource-ref="dataSource"
     p:configLocation="${hibernate.config}"
     p:packagesToScan="org.krams.tutorial"/>
 
 <!-- Declare a datasource that has pooling capabilities-->  
 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    destroy-method="close"
    p:driverClass="${app.jdbc.driverClassName}"
    p:jdbcUrl="${app.jdbc.url}"
    p:user="${app.jdbc.username}"
    p:password="${app.jdbc.password}"
    p:acquireIncrement="5"
    p:idleConnectionTestPeriod="60"
    p:maxPoolSize="100"
    p:maxStatements="50"
    p:minPoolSize="10" />

 <!-- Declare a transaction manager-->
 <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" 
          p:sessionFactory-ref="sessionFactory" />
  
</beans>

hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
  "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
  
<hibernate-configuration>
  <session-factory>
   <!-- We're using MySQL database so the dialect needs to MySQL as well-->
    <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
    <!-- Enable this to see the SQL statements in the logs-->
    <property name="show_sql">true</property>
    <!-- This will drop our existing database and re-create a new one.
      Existing data will be deleted! -->
    <property name="hbm2ddl.auto">create</property>
  </session-factory>
</hibernate-configuration>

spring.properties
# database properties
app.jdbc.driverClassName=com.mysql.jdbc.Driver
app.jdbc.url=jdbc:mysql://localhost/mydatabase
app.jdbc.username=root
app.jdbc.password=

#hibernate properties
hibernate.config=/WEB-INF/hibernate.cfg.xml

Run the Application


Setup the database

Our application uses MySQL as its database. To run the application, make sure to setup the database first.

To create the database, follow these steps.
1. Open phpmyadmin (or any tool you prefer with)
2. Create a new database named mydatabase
3. Run the application to automatically create the database schema.

To populate the database with sample data, import the mydatabase.sql SQL script which is located under the WEB-INF folder of the application:


Access the main application

To access the main application, use the following URL:
http://localhost:8080/spring-hibernate-many-to-one-default/krams/main/record/list

You should see the following application:


Conclusion

That's it. We've successfully created a Spring MVC application and implemented a one-to-many association in Hibernate using annotations. We've also discussed issues and solutions when using default settings of @ManyToOne annotation.

Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring-mvc-hibernate-annotations-integration-tutorial/

You can download the project as a Maven build. Look for the spring-hibernate-many-to-one-default.zip in the Download sections.

You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run

If you want to learn more about Spring MVC and integration with other technologies, feel free to read my other tutorials in the Tutorials section.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring - Hibernate: Many-To-One Association ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

26 comments:

  1. Hi,
    Very nice tutorial. Appreciate it much. Can you please tell, how do you inject the Service (Person/CreditCard Service) in the controller through the annotation? Because whenever I try to inject it - I get an error starting the server that "org.springframework.beans.factory.NoSuchBeanDefinitionException"

    ReplyDelete
  2. You need to declare a mvc:annotation-driven element in your Spring configuration, and a context:component-scan base-package="org.krams.tutorial" element

    ReplyDelete
  3. Thanks for your tutorials.

    In the CreditCard Table, there is person_ID field that needs to mapped with ID field of Person Table.

    On your domain classes,
    you had created an object of Person Class and defined @ManyToOne Annotation.

    My First Doubt is that you did not specified Column name (person_ID) above person object.

    My Second Doubt is how application recognized that person object is mapped to which variable of Person class?

    ReplyDelete
  4. @admin, the person_ID is automatically generated by Hibernate and mapped to the ID property of the Person class. There's a way to specify that explicitly. If you examine the Hibernate docs, here's what you'll see from section 2.2.5.2

    "The @JoinColumn attribute is optional, the default value(s) is like in one to one, the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column in the owned side. In this example company_id because the property name is company and the column id of Company is id." - http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/

    If you want to verify this, try running the application and check the generated tables.

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. hi,
    this tutorial is helpfull. but i don't understand in this part:

    person.creditCards

    can you explain that part?
    thanks

    ReplyDelete
  7. "add new credit card" is not taking "person id" as it shown.it is showing nothing.

    ReplyDelete
  8. gives this

    java.lang.IllegalArgumentException: id to load is required for loading
    at org.hibernate.event.LoadEvent.(LoadEvent.java:74)
    at org.hibernate.event.LoadEvent.(LoadEvent.java:56)
    at org.hibernate.impl.SessionImpl.get(SessionImpl.java:840)
    at org.hibernate.impl.SessionImpl.get(SessionImpl.java:836)
    at org.krams.tutorial.service.CreditCardService.add(CreditCardService.java:85)
    at org.krams.tutorial.service.CreditCardService$$FastClassByCGLIB$$4e58c09b.invoke()
    at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
    at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:688)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.procee

    ReplyDelete
  9. Hi - I downloaded, cleaned etc what is needed your project, deployed the war file, but it shows 404 error in my browser? what could be the problem?
    Thanks in advance. :)

    ReplyDelete
  10. Just wanted to ask you one thing I am curious about.

    I can see applicationContext.xml is not being called anywhere, so how does it work with this application?

    A reply would be much appreciated.

    Thanks
    Ali

    ReplyDelete
    Replies
    1. This is an old tutorial, but your question is still applicable. Spring will automatically look for one under the name applicationContext.xml

      Delete
  11. Hi

    Can you please help me in
    http://stackoverflow.com/questions/18895585/hibernate-version-annotation-and-object-references-an-unsaved-transient-instanc

    ReplyDelete
  12. Hello Krams,

    the tutorial is very good.

    I'm currently working with Netbeans. I have to do with a ManyToOne relationship.
    I am trying to do as you did.
    But I'm lost in the mapping.
    I downloaded the project to better understand it.

    Could you explain this to me:

    @ RequestMapping ("/ main / record")

    Who's main?
    Who's record?

    @ RequestMapping (value = "/ add", method = RequestMethod.GET)

    Who is add?

    I do not see it in the project.

    thank you

    ReplyDelete
  13. Hi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me.. I am a regular follower of your blog. Really very informative post you shared here. Kindly keep blogging.

    angularjs Training in chennai
    angularjs Training in chennai

    angularjs-Training in tambaram

    angularjs-Training in sholinganallur

    angularjs-Training in velachery

    ReplyDelete
  14. Woah this blog is wonderful i like studying your posts. Keep up the great work! You understand, lots of persons are hunting around for this info, you could help them greatly.
    online Python certification course | python training in OMR | Python training course in Chennai

    ReplyDelete
  15. Thanks for the good words! Really appreciated. Great post. I’ve been commenting a lot on a few blogs recently, but I hadn’t thought about my approach until you brought it up. 
    advanced excel training in bangalore

    ReplyDelete
  16. After seeing your article I want to say that the presentation is very good and also a well-written article with some very good information which is very useful for the readers....thanks for sharing it and do share more posts like this.
    Java training in Marathahalli | Java training in Btm layout

    Java training in Marathahalli | Java training in Btm layout

    ReplyDelete
  17. I appreciate your efforts because it conveys the message of what you are trying to say. It's a great skill to make even the person who doesn't know about the subject could able to understand the subject . Your blogs are understandable and also elaborately described. I hope to read more and more interesting articles from your blog. All the best.

    Data Science Training in Chennai | Data Science course in anna nagar

    Data Science course in chennai | Data science course in Bangalore

    Data Science course in marathahalli | Data Science course in btm layout

    ReplyDelete
  18. Effective blog with a lot of information. I just Shared you the link below for Courses .They really provide good level of training and Placement,I just Had Hibernate Classes in this institute , Just Check This Link You can get it more information about the Hibernate course.


    Java training in chennai | Java training in annanagar | Java training in omr | Java training in porur | Java training in tambaram | Java training in velachery

    ReplyDelete
  19. Thank you for your informative post!!!
    Village Talkies a top-quality professional corporate video production company in Bangalore and also best explainer video company in Bangalore & animation video makers in Bangalore, Chennai, India & Maryland, Baltimore, USA provides Corporate & Brand films, Promotional, Marketing videos & Training videos, Product demo videos, Employee videos, Product video explainers, eLearning videos, 2d Animation, 3d Animation, Motion Graphics, Whiteboard Explainer videos Client Testimonial Videos, Video Presentation and more for all start-ups, industries, and corporate companies. From scripting to corporate video production services, explainer & 3d, 2d animation video production , our solutions are customized to your budget, timeline, and to meet the company goals and objectives.
    As a best video production company in Bangalore, we produce quality and creative videos to our clients.

    ReplyDelete
  20. Nice article. Thank you so much for sharing this post. Know about Bitwissend, website design company in cochin.

    ReplyDelete
  21. Thank you so much for sharing this article. Know about Mystic Mayapott, Best Resorts In Thekkady.

    ReplyDelete
  22. You have recently made some very outstanding contributions. Your breadth of knowledge, astute analysis, and lucidity of expression are admirable. Your knowledge enriches our conversations and offers priceless direction. Ruby On Rails Course

    ReplyDelete
  23. You have recently made some very outstanding contributions. Your breadth of knowledge, astute analysis, and lucidity of expression are admirable. Your knowledge enriches our conversations and offers priceless direction. Ruby On Rails Training

    ReplyDelete
  24. Your blog post on this topic is both informative and thought-provoking. It's evident that you have a deep understanding of related concept, and your ability to explain complex ideas in a clear and concise manner is commendable.
    I'd love to hear your thoughts on this or if you have any additional insights.

    Know more about our Web Design Company in Abu Dhabi. We are also known for best Digital Marketing Company in Abu Dhabi.

    ReplyDelete