📜 ⬆️ ⬇️

Spring - Hibernate: One-to-Many Association

We continue the cycle of articles - translations in Spring and Hibernate, from krams .

Previous article:
“Spring MVC 3, Hibernate Annotations, MySQL. Integration Tutorial .

Introduction
In this tutorial, we’ll introduce you to using a one-to-many relationship, using the Hibernate and Spring MVC 3 annotations. We will use the @OneToMany annotation to indicate the relationships between our objects. We will not use cascading types or fetch strategies; instead, we will use the standard @OneToMany settings.
')
What is a one-to-many association?
A one-to-many association occurs when each entry in Table A corresponds to a set of entries in Table B, but each entry in Table B has only one corresponding entry in Table A.

The specification of our application.

The application is a simple CRUD system for managing the list of entries. Each entry corresponds to one person, it contains personal data and credit card information. Each person can own several credit cards. We will also add a system for editing individuals and credit cards.

Below are screenshots from the future application:







Domain objects

Based on the specification, we have two domain objects: a person (Person) and a credit card (Credit Card).

The person object must have the following fields:
- id
- first name
- last name (last name)
- money
- credit cards

The property has the following field credit card:
- id
- type
- number

Please note that each person corresponds to a set of credit cards, and consequently we use the one-to-many association. Of course, we can look at this situation from the other side and use the many-to-one association, but this will be the topic of another lesson.

Development
We will break our development into three layers: domain, service and controller, then we will specify configuration files.

Let's start with the domain layer.
As mentioned earlier, we have two domain objects: Person and CreditCard. Therefore, we will declare two POJO objects representing our domain layer. Each one will have an Entity annotation stored in a database.
Person.java
package org.krams.tutorial.domain; import java.io.Serializable; import java.util.Set; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; 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; @OneToMany private Set<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 Set<CreditCard> getCreditCards() { return creditCards; } public void setCreditCards(Set<CreditCard> creditCards) { this.creditCards = creditCards; } } 



The Person class is displayed in the PERSON table. And it looks like this:



Pay attention to the @OneToMany annotation for the variable creditCards; we did not specify either the cascade type or the fetch strategy, relying on the default settings. Later we will find out some of the problems associated with this.
Person.java
 @Entity @Table(name = "PERSON") public class Person implements Serializable { ... @OneToMany private Set<CreditCard> creditCards; ... } 


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.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; 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; } } 


The CreditCard class is displayed in the CREDIT_CARD table and looks like:



Using the phpmyadmin db designer, take a look at the relationship between Person and CreditCard:



Take a look at the generated tables:



We specified only two entities: CreditCard and Person, and expected to see only two tables in the database. So why are there three? As in the default setting, a third link table is created.

Quote from Hibernate Annotations Reference Guide:
Without a mapping description, the link table is used for the one-to-many relationship. The name of the table is the concatenation of the name of the first table, the symbol "_" and the name of the second table. To ensure a one-to-many relationship with the id of the first table, the UNIQUE modifier is assigned.
We will discuss other default installation issues later.

Service layer
After the declaration of domain objects, we need to create a service layer that contains two services: PersonService and CreditCardService.

PersonService is responsible for handling CRUD operations on the Person entity. Each method ultimately passes the session Hibernate object.

PersonService.java
 package org.krams.tutorial.service; import java.util.List; import java.util.Set; 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 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 // Create a Hibernate query (HQL) Query query = session.createQuery("FROM Person as p LEFT JOIN FETCH p.creditCards WHERE p.id="+id); return (Person) query.uniqueResult(); } /** * 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(); // Create a Hibernate query (HQL) Query query = session.createQuery("FROM Person as p LEFT JOIN FETCH p.creditCards WHERE p.id="+id); // Retrieve record Person person = (Person) query.uniqueResult(); Set<CreditCard> creditCards =person.getCreditCards(); // Delete person session.delete(person); // Delete associated credit cards for (CreditCard creditCard: creditCards) { session.delete(creditCard); } } /** * 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); } } 



PersonService looks deceptively simple, it solves two main problems that deserve special attention:

Problem 1. Fetch strategies
Receiving a person’s record does not load credit card records associated with it.

The following request gets the person by id.
Query query = session.createQuery ("FROM Person WHERE p.id =" + id);

The problem occurs because we did not specify the fetch-strategy when describing the @OneToMany annotation, in order to fix this - we change the query:
Query query = session.createQuery ("FROM Person as p LEFT JOIN FETCH p.creditCards WHERE p.id =" + id);

Problem 2. Cascade types.
Deleting a person does not remove the corresponding credit cards.
The following entry removes the face entry:
session.delete (person);

The problem occurs because we did not specify cascade types in the @OneToMany annotation. This means that we must implement our strategy to delete credit card records.

First, you need to create a request for obtaining credit cards, which we place for temporary storage in the collection. Then we delete the person record. After removing everything from the collection, we remove the cracks one by one.
Corrected request
 // Create a Hibernate query (HQL) Query query = session.createQuery("FROM Person as p LEFT JOIN FETCH p.creditCards WHERE p.id="+id); // Retrieve record Person person = (Person) query.uniqueResult(); Set<CreditCard> creditCards =person.getCreditCards(); // Delete person session.delete(person); // Delete associated credit cards for (CreditCard creditCard: creditCards) { session.delete(creditCard); } 



CreditCardService
CreditCardService is responsible for handling CRUD operations on the CreditCard entity. Each method ultimately passes the session Hibernate object.
CreditCardService.java
 package org.krams.tutorial.service; import java.util.ArrayList; 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 Person as p LEFT JOIN FETCH p.creditCards WHERE p.id="+personId); Person person = (Person) query.uniqueResult(); // Retrieve all return new ArrayList<CreditCard>(person.getCreditCards()); } /** * 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(); // Persists to db session.save(creditCard); // Add to person as well // Retrieve existing person via id Person existingPerson = (Person) session.get(Person.class, personId); // Assign updated values to this person existingPerson.getCreditCards().add(creditCard); // Save updates session.save(existingPerson); } /** * Deletes an existing credit card */ public void delete(Integer id) { logger.debug("Deleting existing credit card"); // Retrieve session from Hibernate Session session = sessionFactory.getCurrentSession(); // Delete reference to foreign key credit card first // We need a SQL query instead of HQL query here to access the third table Query query = session.createSQLQuery("DELETE FROM PERSON_CREDIT_CARD " + "WHERE creditCards_ID="+id); query.executeUpdate(); // Retrieve existing credit card CreditCard creditCard = (CreditCard) session.get(CreditCard.class, id); // Delete session.delete(creditCard); } /** * 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); } } 


Pay special attention to the delete () method:
delete ()
 public void delete(Integer id) { logger.debug("Deleting existing credit card"); // Retrieve session from Hibernate Session session = sessionFactory.getCurrentSession(); // Delete reference to foreign key credit card first // We need a SQL query instead of HQL query here to access the third table Query query = session.createSQLQuery("DELETE FROM PERSON_CREDIT_CARD " + "WHERE creditCards_ID="+id); query.executeUpdate(); // Retrieve existing credit card CreditCard creditCard = (CreditCard) session.get(CreditCard.class, id); // Delete session.delete(creditCard); } 



To remove a credit, you first need to remove it from the link table with the following query:
Query query = session.createSQLQuery ("DELETE FROM PERSON_CREDIT_CARD" +
"WHERE creditCards_ID =" + id);

Notice that the link table is created by Hibernate and a SQL query, not HQL.
After deleting the data from the link table, we delete the information from the CREDIT_CARD table.
session.delete (creditCard)

Controller layer
After creating the service and domain layers, you need to create a controller layer. We will create two controllers: MainController and CreditCardController.

MainController

MainController is responsible for processing requests for records of individuals. Each CRUD request is ultimately passed to the PersonService, and then returns the corresponding 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 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 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 edit existing person"); // Assign id person.setId(personId); // Delegate to service personService.edit(person); // Redirect to url return "redirect:/krams/main/record/list"; } } 



Note that the getRecords () method displays the Person and CreditCard as the data transfer object: PersonDTO.
getRecords ()
 /** * 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 records.jsp.
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 processing requests for credit cards. We will not use all the methods available in this controller, they have been added for 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.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 credit card requests * * @author Krams at {@link http://krams915@blogspot.com */ @Controller @RequestMapping("/main/creditcard") public class CreditCardController { protected static Logger logger = Logger.getLogger("controller"); @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(); // Add to model model.addAttribute("personId", personId); 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("pid") Integer personId, @RequestParam("cid") 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("personId", personId); 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 layer, create a VIEW layer. It consists mainly of JSP pages. Here they are:


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?pid=${person.id}&cid=${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 entry

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 entry:

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 a 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 created all the necessary Java classes. The next step is to create 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 



.
.
MySQL . , .

:
1. phpmyadmin ( , )
2. mydatabase
3. , .

mydatabase.sql, WEB_INF .

URL:
localhost :8080/spring-hibernate-one-to-many-default/krams/main/record/list

Conclusion
Spring MVC -- Hibernate. @OneToMany.

GIT: github.com/sa4ek/spring-hibernate-one-to-many-default

Source: https://habr.com/ru/post/249073/


All Articles