JAKARTA EE FOR JUNIOR DEVELOPERS - Lesson 7 Many to One Mapping

 

INTRODUCTION

In our existing application, we would like to have the ability to save more information about the employees, such as, for example, the department in which they work. Such information can be added if we practically create a new table named Departments and establish a many-to-one relationship between the Employees table and the Departments table. In this Jakarta EE For Junior Developers course, we will not only learn the "how" of creating this relationship through Jakarta Persistence but also analyze the "why" we need this logic.

WHY WE NEED A FOREIGN KEY

A foreign key in a database is a column (or a set of columns) in one table that creates a link between the data in two tables. It acts as a "reference" to a primary key in another table, establishing a relationship between them.

Foreign keys enforce referential integrity by ensuring that the value in the foreign key column must match a value in the referenced table's primary key column. If no match exists, the database will prevent the action (e.g., insertion or update).

The following diagram is the final output of the Jakarta Persistence code that we will develop on this lesson. At the end we will end up with two tables - Employees and Departments. 

 

The dept_id column in the Employees table is a foreign key that references the dept_id primary key in the Departments table. This ensures that an employee can only be associated with a valid department. In other words, every time we insert a new employee into the Employees table we need to assign to the employee a department name that already exists. A foreign key ensures that the values in one table match valid entries in another table. For example, in the Employees table, a dept_id foreign key ensures that every employee is linked to an existing department in the Departments table. This prevents invalid or "orphaned" data.

MANY-TO-ONE RELATIONSHIP DEFINITION

A many-to-one relationship is a type of relationship in a relational database where many rows in one table are associated with a single row in another table. It’s one of the most common relationships used in databases to model real-world scenarios.

In our scenario, each employee belongs to one department, but one department can have many employees. In simple words, many employees can belong to the same department. Employee is the "many" side and the source of the relationship, and Department is the "one" side and the target. 

DEFINING THE ENTITIES

Let's see now how we code a many-to-one relationship between two Jakarta Persistence entities. This is the complete code for the Employees table.

Employees.java

 

package com.mycompany.jakartaee.entities;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.io.Serializable;
import java.util.Date;

@Entity
@Table(name = "EMPLOYEES")
public class Employee implements Serializable {

    public Employee() {
    }

    private static final long serialVersionUID = 1L;
   
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long employee_id;
   
    @Column(name = "emp_firstname", nullable = false)
    private String firstName;
   
    @Column(name = "emp_lastname", nullable = false)
    private String lastName;
   
    @Column(name = "emp_email")
    private String email;
   
    @Column(name = "emp_phone")
    private String phoneNumber;
   
    @Column(name = "emp_hiredate", nullable = false)
    private Date hireDate;
   
    @Column(name = "emp_salary", precision=0, nullable = false)
    private Double monthlySalary;
   
    @ManyToOne
    @JoinColumn(name="dept_id")
    private Department department;

    public Long getEmployee_id() {
        return employee_id;
    }

    public void setEmployee_id(Long employee_id) {
        this.employee_id = employee_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 String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public Date getHireDate() {
        return hireDate;
    }

    public void setHireDate(Date hireDate) {
        this.hireDate = hireDate;
    }

    public Double getMonthlySalary() {
        return monthlySalary;
    }

    public void setMonthlySalary(Double monthlySalary) {
        this.monthlySalary = monthlySalary;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

   
}

@ManyToOne:

  • Defines a many-to-one relationship between the Employee entity and the Department entity.
  • Many employees can belong to one department.
  •  Note that the Employee entity, even if contains a variable called department that contains a reference to a single Department instance, the actual attribute is not shown in the final table, but only the column that defines the relationship.

@JoinColumn(name="dept_id"):

  • Specifies that the department field (of type Department) is mapped to the foreign key column dept_id in the EMPLOYEES table.
  •  The database term for a column that refers to a key (usually the primary key) in another table is a foreign key column. In Jakarta Persistence, they are called join columns, and the @JoinColumn annotation is the primary annotation used to configure these types of columns.
  •  One of the two tables will have the join column in its table. That side is called the owning side or the owner of the relationship. 

Please refer to https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1#unidirectional-manytoone-relationships for more information.

Let's now take a look at the code that represents the Department entity, which maps to a database table named DEPARTMENTS

Departments.Java

package com.mycompany.jakartaee.entities;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.io.Serializable;
import java.util.List;


@Entity
@Table(name = "DEPARTMENTS")
public class Department implements Serializable {

    public Department() {
    }
   
    private static final long serialVersionUID = 1L;
    @Id
    private Long dept_id;
   
    @Column(name = "dept_name")
    private String name;
   
    @OneToMany(mappedBy = "department")
    private List<Employee> employees;

    public Long getDept_id() {
        return dept_id;
    }

    public void setDept_id(Long dept_id) {
        this.dept_id = dept_id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Employee> getEmployees() {
        return employees;
    }

    public void setEmployees(List<Employee> employees) {
        this.employees = employees;
    }

}

@OneToMany(mappedBy = "department"):

  • Establishes a one-to-many relationship between Department and Employee.
  • A department can have many employees, but an employee belongs to only one department.
  • mappedBy = "department": Indicates that the employees field is mapped by the department field in the Employee class (as defined with @ManyToOne in the Employee entity).
  • The @mappedBy attribute in Jakarta Persistence is used to specify the inverse side of a bidirectional relationship between two entities. Its primary role is to tell Jakarta Persistence which field or property in the other entity is responsible for managing the relationship.

List<Employee> employees:

  • A list of all employees that belong to this department.
  • It’s effectively a collection of Employee objects linked to this Department.


LOAD DATA ON THE DEPARTMENTS TABLE

As we have mentioned earlier, in order for us to successfully insert a new employee, we need to assign a category that already exists in the Departments table. But, if we run the project, we will end up having two empty tables without any data in them. How can we load date into the Departments table during the deployment process?

All we need to do is to create an SQL query file that inserts the desired data. That file needs to be created under the META-INF folder.

 

department-data.sql

INSERT INTO "public".departments (dept_id, dept_name) VALUES  (1, 'HR');
INSERT INTO "public".departments (dept_id, dept_name) VALUES  (2, 'IT');
INSERT INTO "public".departments (dept_id, dept_name) VALUES  (3, 'Finance');
INSERT INTO "public".departments (dept_id, dept_name) VALUES  (4, 'Marketing');

Now we need to use the jakarta.persistence.sql-load-script-source property inside the persistence unit to specify the SQL script file (META-INF/department-data.sql) to be executed to load initial data into the database after the schema is generated.

 persistence.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="3.0" xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence                                          
 https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">
  <!-- Define Persistence Unit -->
  <persistence-unit name="my_persistence_unit" transaction-type="JTA">
    <jta-data-source>java:/PostgresDS</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <validation-mode>AUTO</validation-mode>
    <properties>
      <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
      <property name="jakarta.persistence.sql-load-script-source"
                      value="META-INF/department-data.sql" />
    </properties>
  </persistence-unit>
</persistence>

As a final step, we need to make some modifications to our Controller (Servlet) and the index.jsp page. Let's see the complete code for both of those files.

Controller.java

 

package com.mycompany.controllers;

import com.mycompany.jakartaee.entities.Department;
import com.mycompany.jakartaee.entities.Employee;
import jakarta.annotation.Resource;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.transaction.HeuristicMixedException;
import jakarta.transaction.HeuristicRollbackException;
import jakarta.transaction.NotSupportedException;
import jakarta.transaction.RollbackException;
import jakarta.transaction.SystemException;
import jakarta.transaction.UserTransaction;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

@WebServlet(name = "Controller", urlPatterns = {"/Controller"})
public class Controller extends HttpServlet {
   
    @PersistenceContext
    private EntityManager em;

    @Resource
    private UserTransaction userTransaction;

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {

    }

    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
            Long dept_id = null;
       
            String firstName = request.getParameter("firstName");
            String lastName = request.getParameter("lastName");
            String email = request.getParameter("email");
            String date = request.getParameter("hireDate");
            String phoneNumber = request.getParameter("phoneNumber");
            Double monthlySalary = Double.valueOf(request.getParameter("monthlySalary"));
            String dept = request.getParameter("department");
           
           
            switch (dept) {
                case "HR" -> dept_id=1L;
                case "IT" -> dept_id=2L;
                case "Finance" -> dept_id=3L;
                case "Marketing" -> dept_id=4L;
                default -> {
                }
            }

                Department department = new Department();
                Employee employee = new Employee();
           
                department.setName(dept);
                department.setDept_id(dept_id);
           
           
                employee.setFirstName(firstName);
                employee.setLastName(lastName);
                employee.setMonthlySalary(monthlySalary);
                Date dt = null;
                try {
                    dt = new SimpleDateFormat("yyyy-MM-dd").parse(date);
                } catch (ParseException ex) {
                    Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
                }
                employee.setHireDate(dt);
                employee.setEmail(email);
                employee.setPhoneNumber(phoneNumber);
                employee.setDepartment(department);
             
   
            try {
            userTransaction.begin();
            em.persist(employee);
            userTransaction.commit();
             } catch (NotSupportedException |
                SystemException | RollbackException |
                HeuristicMixedException | HeuristicRollbackException |
                SecurityException | IllegalStateException ex) {
            Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
        }

            PrintWriter out = response.getWriter();
            out.println("<!DOCTYPE html>");
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet NewServlet</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>The user " + employee.getFirstName() + " has been registered </h1>");
            out.println("</body>");
            out.println("</html>");

       

    }

}

 

index.jsp

 

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Employee Registration</title>
        <link rel="stylesheet"
              href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
              integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
              crossorigin="anonymous">
    </head>
    <body>
        <h1 style="text-align: center">Employee Registration Form</h1>
        <div class="container mt-5">
            <section>
                <form action="Controller" method="post">
                    <div class="row">
                        <div class="col-md-6 mb-3">
                            <label for="firstname" class="form-label">First Name</label>
                            <input type="text" class="form-control" id="firstname"
                                   placeholder="Enter your first name" name="firstName" >
                        </div>
                        <div class="col-md-6 mb-3">
                            <label for="lastname" class="form-label">Last Name</label>
                            <input type="text" class="form-control" id="lastname"
                                   placeholder="Enter your last name" name="lastName" >
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-md-6 mb-3">
                            <label for="email" class="form-label">Enter your email</label>
                            <input type="email" class="form-control" id="email"
                                   placeholder="Enter your email" name="email" >
                        </div>
                        <div class="col-md-6 mb-3">
                            <label for="phone" class="form-label">Enter your Phone number</label>
                            <input type="text" class="form-control" id="phone"
                                   placeholder="Enter your phone number" name="phoneNumber" >
                        </div>
                    </div>

                    <div class="row">
                        <div class="col-md-6 mb-3">
                            <label for="date" class="form-label">Enter the hire date</label>
                            <input type="date" class="form-control" id="date"
                                   placeholder="Enter the hire date" name = "hireDate"  >
                        </div>
                        <div class="col-md-6 mb-3">
                            <label for="salary" class="form-label">Monthly Salary</label>
                            <input type="number" class="form-control" id="salary"
                                   placeholder="Enter yout salary" name = "monthlySalary" >
                        </div>
                        <div class="col-md-6 mb-3">
                            <label for="department" class="form-label">Department</label>
                            <select class="custom-select" id="department" name="department">
                                <option value="HR">HR</option>
                                <option value="IT">IT</option>
                                <option value="Finance">Finance</option>
                                <option value="Marketing">Marketing</option>
                            </select>
                        </div>

                    </div>

                    <button type="submit" class="btn btn-primary">Submit</button>
                </form>
            </section>
        </div>

    </body>
</html>


We are ready to run the updated application.




 

 

full-width



full-width

Post a Comment

0 Comments