Wednesday, February 02, 2022

One-To-One Relationship In Spring Boot Between The Entities

springboot,java,hibernate,jpa,onetoone,programming,software development,technology
In Spring Boot, a One-To-One relationship is one in which both entities are associated with each other. In this tutorial, we will learn how to implement a One-To-One relationship between entities in Spring Boot using JPA and Hibernate.

1. What is a One-To-One association

Before delving into the specifics of creating relationships between entities in Hibernate, consider how we create relationships between objects in JAVA. An object association in JAVA is very simple: we use an attribute (variable) of a class to do so. The definitions of two simple classes, Factory and Product, are provided below:
// Factory class
  public class Factory {
    …
    // Factory can make product
    private Product product;
    …
  }
// Product class
  public class Product {
    …
    private String productName;
    …
  }
We've declared a Factory POJO with a Product class attribute variable (product). In JAVA, we can create a relationship (association) between the Factory and Product classes using attributes.
JAVA,Hibernate,Autowired,PostMapping,unidirectional,Spring Boot,RestController,JpaRepository,OneToOne Mapping,Object-relational mapping (ORM),RequestMapping,


A One-To-One relationship is when one JAVA object is linked to another. A motorcycle, for example, has an engine, and each engine belongs to a specific motorcycle; similarly, any location on Earth must have a geolocation, and the geolocation refers to a specific location.

Consider for a moment how we can store the objects in a relational database. Because traditional databases store data in two-dimensional table format (rows and columns), relationships between tables are maintained using the primary key and foreign key. However, objects store data in variables, and we cannot store object associations directly in database tables using our traditional JDBC APIs.

We can solve this problem with the help of an object-relational mapping (ORM) tool. Hibernate is one of the most widely used ORM tools. We can use Hibernate to automate the process of saving the association of JAVA objects to database tables, as well as accessing database table data and converting it back into JAVA objects. In Spring Boot, we use some special annotations to convert our plain JAVA class into a persistent class or entity, and then use Hibernate to access and manipulate the data. Hibernate has implemented JPA (JAVA Persistence API) and uses it for data manipulation to interact with relational databases. JAVA defines JPA as an object-relational mapping specification. Hibernate provides us with a collection of classes and methods for data persistence with this specification.

Now it is time to make our hands dirty. Let’s dive into coding.💪

2. Spring Boot project creation

We will use Spring Initializr to create a new Spring Boot project, which will generate a basic structure for our Spring Boot project. The following dependencies have been added:
  • Spring Boot DevTools - necessary development tools  
  • Spring Web - for Spring MVC and embedded Tomcat that will run the Spring Boot application  
  • Spring Data JPA - Java Persistence API
  • MySQL Driver - JDBC driver for MySQL (for other DB you have to choose that dependency for that DB)
JAVA,Hibernate,Autowired,PostMapping,unidirectional,Spring Boot,RestController,JpaRepository,OneToOne Mapping,Object-relational mapping (ORM),RequestMapping,
Then, to download the project zip file, click GENERATE. Unzip the zip archive. Import the project as a Maven project into Eclipse/STS.

3. Connect to the Database

We'll put the connection information in the application because we're using MySQL as our database. Hibernate will use this information to connect to the database as the application.properties file has a name/value pair. The connection information is described in the following snippet:
JAVA,Hibernate,Autowired,PostMapping,unidirectional,Spring Boot,RestController,JpaRepository,OneToOne Mapping,Object-relational mapping (ORM),RequestMapping,
Here, we've set datasource.url to the URL of our JDBC connection. The database credentials are mentioned in datasource.user and datasource.password.

Spring Boot can gather the necessary information about the database from the connection URL, so it is not necessary to specify datasource.driver-class-name. However, we will be safer if we specify the driver-class-name.

When the application is running, jpa.show-sql displays Hibernate SQL queries in the console, jpa.hibernate.ddl-auto is set to update, which updates the database schema every time we restart the application, and hibernate.dialect indicates which database dialect we are using.


👉 To demonstrate, we'll make two entities: the Motorcycle and the Engine. Then, between these two entities, establish a One-To-One relationship.

4. Entities

First, we'll add annotations to the Engine entity to make it persistent.
@Entity
@Table(name = "ENGINE")
public class Engine {
           
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id")
  private int Id;
           
  @Column(name = "engine_type")
  private String engineType = "";
           
  @Column(name = "capacity")
  private String capacity = "";
           
  @Column(name = "max_torque")
  private String maxTorque = "";
           
  public Engine() {
  }
           
  public Engine(String engineType, String capacity, String maxTorque) {
    this.engineType = engineType;
    this.capacity = capacity;
    this.maxTorque = maxTorque;
  }
           
  public int getId() { return Id; }
  public void setId(int id) { Id = id; }
           
  public String getEngineType() { return engineType; }
  public void setEngineType(String engineType) { this.engineType = engineType; }
           
  public String getCapacity() { return capacity; }       
  public void setCapacity(String capacity) { this.capacity = capacity; }
           
  public String getMaxTorque() { return maxTorque; }     
  public void setMaxTorque(String maxTorque) { this.maxTorque = maxTorque;}
           
  @Override
  public String toString() {
    return "Engine{" +
      "Id=" + Id +
      ", engineType='" + engineType + '\'' +
      ", capacity='" + capacity + '\'' +
      ", maxTorque='" + maxTorque + '\'' +
      '}';
  }
}
The Engine class, as you can see, is a simple JAVA class with some private variables, a constructor, some getter and setter methods, and an overridden toString() method. The main thing to notice is that we used annotations (metadata about a class) such as @Entity, @Table, @Id, and so on.  
         
We convert a simple JAVA class into a persistent class by using the @Entity annotation.
         
@Table refers to our database table (ENGINE).
         
The @Id attribute is used to specify the primary key (object identifier). The primary key is defined as an Id variable in the Engine class.
         
@Column annotation is used to define column names, size, and whether or not null is allowed for a specific column of a database table. This annotation is required if we want the column name of the database table to differ from the variable name (Id).
         
We have annotated the Id variable with @GeneratedValue. This annotation specifies the strategy for generating the value of the primary key (id). We applied GenerationType.IDENTITY to the primary key, which is equivalent to MySQL's AUTO_INCREMENT.
         
When we run the project, Hibernate will create a table named ENGINE with the primary key id (AUTO_INCREMENT) and other columns with the names we specify.

Now, the Motorcycle entity with annotations:
@Entity
@Table(name = "MOTOR_CYCLE")
public class Motorcycle {
           
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id")
  private int Id;
           
  @Column(name = "model_name")
  private String modelName = "";
           
  @Column(name = "manufacturer_name")
  private String manufacturerName = "";
           
  @Column(name = "gear_box")
  private String gearBox = "";
           
  @Column(name = "wheels")
  private String wheels = "";
           
  @Column(name = "length")
  private String length = "";
           
  @Column(name = "height")
  private String height = "";
           
  @OneToOne(cascade = CascadeType.ALL)
  @JoinColumn(name = "engine_id")
  private Engine engine;
           
  public Motorcycle() {
  }

  public Motorcycle(String modelName, String manufacturerName, String gearBox, String wheels, 
                      String length, String height) {
    this.modelName = modelName;
    this.manufacturerName = manufacturerName;
    this.gearBox = gearBox;
    this.wheels = wheels;
    this.length = length;
    this.height = height;
  }
           
  public int getId() { return Id; }
  public void setId(int id) { Id = id; }
           
  public String getModelName() { return modelName; }       
  public void setModelName(String modelName) { this.modelName = modelName; }
           
  public String getManufacturerName() { return manufacturerName; }
  public void setManufacturerName(String manufacturerName) { this.manufacturerName = manufacturerName; }
           
  public String getGearBox() { return gearBox; }
  public void setGearBox(String gearBox) { this.gearBox = gearBox; }
           
  public String getWheels() { return wheels; }
  public void setWheels(String wheels) { this.wheels = wheels; }
           
  public String getLength() { return length; }
  public void setLength(String length) { this.length = length; }
           
  public String getHeight() { return height; }
  public void setHeight(String height) { this.height = height; }
           
  public Engine getEngine() { return engine; }
  public void setEngine(Engine engine) { this.engine = engine; }
           
  @Override
  public String toString() {
    return "Motorcycle{" +
      "Id=" + Id +
      ", modelName='" + modelName + '\'' +
      ", manufacturerName='" + manufacturerName + '\'' +
      ", gearBox='" + gearBox + '\'' +
      ", wheels='" + wheels + '\'' +
      ", length='" + length + '\'' +
      ", height='" + height + '\'' +
      ", engine=" + engine +
      '}';
  }
}
We used the annotations @OneToOne and @JoinColumn in the persistent Motorcycle class.

The @OneToOne annotation is used to link one JAVA object to another. In other words, if we look at it from a database standpoint, one row of one table is exactly related to one row of another table, and vice versa.

In this example, we will establish a connection between the Motorcycle and the Engine. In this case, one entity is dependent on another entity in a relationship; in other words, there is no meaning of an Engine without the Motorcycle. This is a recursive relationship. So, by specifying CascadeType.ALL, we can simultaneously perform INSERT, PERSIST, MERGE, DELETE, and REFRESH operations on both related entities.

The @JoinColumn annotation is used to specify a foreign key by name (engine_id).

As a result, Hibernate will join the MOTOR_CYCLE table and the ENGINE table with a foreign key named engine_id as an attribute, and the engine_id column will be created in the MOTOR_CYCLE table to store the primary key value from the ENGINE table.
JAVA,Hibernate,Autowired,PostMapping,unidirectional,Spring Boot,RestController,JpaRepository,OneToOne Mapping,Object-relational mapping (ORM),RequestMapping,


5. Repository

The repository in Spring Boot is the data access layer that allows us to interact with our real database for operations like insert, update, and delete using Spring Data JPA. We have significantly reduced the number of boilerplate codes required to perform database operations as our MotorcycleRepository extends JpaRepository.  

Here is our MotorcycleRepository:
public interface MotorcycleRepository extends JpaRepository<Motorcycle, Integer> {
}

6. Controller

A controller is a class that has one or more public methods. Controllers are typically placed in the Controller directory. If a class is annotated with @Controller or @RestController in Spring Boot, it will serve as a controller, and its public methods will be exposed as HTTP endpoints if they are annotated with @PostMapping or @GetMapping.  

As a result, an HTTP GET request to http://localhost:PORT/method-name invokes the @GetMapping method of the ExampleController class.

The following code snippet is for MotorcycleController:
@RestController
@RequestMapping("/motorcycle")
public class MotorcycleController {
           
  @Autowired
  private MotorcycleRepository motorcycleRepository;
           
  @PostMapping("/saveMotorcycle")
  public Motorcycle saveMotorcycle(@RequestBody Motorcycle motorcycle) {
    System.out.println("Motorcycle save called...");
    Motorcycle outMotorcycle = motorcycleRepository.save(motorcycle);
    System.out.println("Saved Motorcycle :: " + outMotorcycle);

    return outMotorcycle;
  }
}

The Spring Boot framework is used to create RESTful web services. When we use a RESTful web service, the HTTP request (represented as a URL) is routed to a specific controller. To make MorotcycleController a controller, we annotated it with @RestController. We mapped MotorcycleController to /motorcycle using the @RequestMapping annotation. The controller class is made up of several public methods that are served as HTTP requests. As a result, the saveMotorcycle() method has a @PostMapping annotation and is mapped to /saveMotorcycle. The annotation @PostMapping is synonymous with HTTP POST.    

An HTTP POST request to http://localhost:PORT/motorcycle/saveMotorcycle invokes the MotorcycleController class's @PostMapping (saveMotorcycle()) method.

We used the @Autowired annotation to inject the MotorcycleRepository into our controller.

7. Run the Project

To run the project in Visual Studio Code, follow these steps:
  1. Open SpringbootonetoonemappingApplication.java.
  2. Click on Run to run the Java program.
JAVA,Hibernate,Autowired,PostMapping,unidirectional,Spring Boot,RestController,JpaRepository,OneToOne Mapping,Object-relational mapping (ORM),RequestMapping,


To run the project in Eclipse, follow these steps:
  1. Right-click on SpringbootmanytomanyApplication.java.
  2. Then choose Run As, then click on Spring Boot App.
JAVA,Hibernate,Autowired,PostMapping,unidirectional,Spring Boot,RestController,JpaRepository,OneToOne Mapping,Object-relational mapping (ORM),RequestMapping,

Take a look at the audit log in the console (Eclipse/VS Code): As you can see, Hibernate has created two tables - ENGINE and MOTOR_CYCLE - with specified column names and modified the MOTOR_CYCLE table to add a foreign key that references the ENGINE table.

JAVA,Hibernate,Autowired,PostMapping,unidirectional,Spring Boot,RestController,JpaRepository,OneToOne Mapping,Object-relational mapping (ORM),RequestMapping,

Because we did not specify a port in the application.properties file, our project will run on port 8080, and the default URL to access any REST API in this project is http://localhost:8080/. Because our project runs on our local machine, we used localhost. If your project is running on a remote server or in EC2, you must use the remote server's or EC2's IP address or the elastic IP address.

8. Testing of the REST APIs

Now we'll put those REST APIs we built in the MotorcycleController to the test.

👉 Save Motorcycle Details

In Postman, we call the following URL to save motorcycle details as well as engine details:
http://localhost:8080/motorcycle/saveMotorcycle
JAVA,Hibernate,Autowired,PostMapping,unidirectional,Spring Boot,RestController,JpaRepository,OneToOne Mapping,Object-relational mapping (ORM),RequestMapping,

Let’s see the audit logs in Console:
JAVA,Hibernate,Autowired,PostMapping,unidirectional,Spring Boot,RestController,JpaRepository,OneToOne Mapping,Object-relational mapping (ORM),RequestMapping,
Hibernate saves Engine data first, followed by Motorcycle data, as shown in the console. Hibernate obtains the value of the primary key column from the ENGINE table and saves it in the MOTRO_CYCLE table's foreign key column.

👉 Let's look at how we can get the Motorcycle details as well as the Engine. To accomplish this, we must modify the MotorcycleRepository and MotorcycleController classes.

Repository - Modification

The code of the modified MotorcycleRepository is as follows:
public interface MotorcycleRepository extends JpaRepository<Motorcycle, Integer> {
  @Override
  Optional<Motorcycle> findById(Long aLong);
}

Controller - Modification

The modified MotorcycleController is as follows:
@RestController
@RequestMapping("/motorcycle")
public class MotorcycleController {
           
  @Autowired
  private MotorcycleRepository motorcycleRepository;
           
  @PostMapping("/saveMotorcycle")
  public Motorcycle saveMotorcycle(@RequestBody Motorcycle motorcycle) {
    System.out.println("Motorcycle save called...");
    Motorcycle outMotorcycle = motorcycleRepository.save(motorcycle);
    System.out.println("Saved Motorcycle :: " + outMotorcycle);

    return outMotorcycle;
  }
           
  @GetMapping("/getMotorcycle/{id}")
  public Optional<Motorcycle> getMotorcycle(@PathVariable String id) {
    System.out.println("Motorcycle get() called...");
    Optional<Motorcycle> outMotorcycle = motorcycleRepository.findById(Long.valueOf(id));
    System.out.println("Motorcycle with Engine :: " + outMotorcycle);

    return outMotorcycle;
  }
}
In this case, we've simply added a new GetMapping to retrieve Motorcycle details from the database table by passing the id (primary key MOTOR_CYCLE table) value.

Testing - Again

👉 Fetch Motorcycle Details

To get the details of a specific motorbike as well as its engine, we use Postman and the following URL with an id value of 1:
http://localhost:8080/motorcycle/getMotorcycle/1

JAVA,Hibernate,Autowired,PostMapping,unidirectional,Spring Boot,RestController,JpaRepository,OneToOne Mapping,Object-relational mapping (ORM),RequestMapping,

And the audit logs in the console:
JAVA,Hibernate,Autowired,PostMapping,unidirectional,Spring Boot,RestController,JpaRepository,OneToOne Mapping,Object-relational mapping (ORM),RequestMapping,
Hibernate generates a select SQL query by joining the MOTOR_CYCLE table with the ENGINE table on the MOTOR_CYCLE table engine_id (foreign key) column and the ENGINE table id (primary key) column, as shown in the console. 

9. Conclusion

This is a simple example of using JPA and Hibernate to demonstrate a unidirectional One-To-One relationship in Spring Boot. You may need to customize and extend the implementation based on your use case.

Now what is unidirectional

Here, we link Product to Factory by declaring a Product attribute (variable) in Factory. As a result, when we retrieve the Factory details, we also obtain the Product details. However, we cannot obtain Factory information by querying the Product. This is also true for the motorcycle and engine examples. This is known as a unidirectional relationship. In the following tutorial, we'll go over bi-directional One-To-One relationships in Spring Boot.

You can download the source code.
Happy coding!!! ðŸ˜Š
in


1 comment:

Popular posts