JPA @ManyToOne example in Spring Boot
Tags: webdev, java, todayilearned, springboot
Published: 2023-08-07 01:26:10

In this tutorial, I will show you how to implement Spring Data JPA Many-to-One example in Spring Boot for One-To-Many mapping using <code>@ManyToOne</code> annotation. You'll know:
<ul>
    	<li>How to configure Spring Data, JPA, Hibernate to work with Database</li>
    	<li>How to define Data Models and Repository interfaces for JPA One-To-Many relationship using <code>@ManyToOne</code></li>
   	<li>Way to use Spring JPA to interact with Database for Many-to-One association</li>
	<li>Way to create Spring Rest Controller to process HTTP requests</li>
</ul>

Related Posts:
- <a href="https://www.bezkoder.com/jpa-one-to-one/">JPA One To One example with Hibernate in Spring Boot</a>
- <a href="https://www.bezkoder.com/jpa-many-to-many/">JPA Many to Many example with Hibernate in Spring Boot</a>
- <a href="https://www.bezkoder.com/spring-boot-validate-request-body/">Validate Request Body in Spring Boot</a>
- <a href="https://www.bezkoder.com/spring-boot-jwt-authentication/">Spring Boot Token based Authentication with Spring Security & JWT</a>
- <a href="https://www.bezkoder.com/spring-boot-jpa-h2-example/">Spring JPA + H2 example</a>
- <a href="https://www.bezkoder.com/spring-boot-jpa-crud-rest-api/">Spring JPA + MySQL example</a>
- <a href="https://www.bezkoder.com/spring-boot-postgresql-example/">Spring JPA + PostgreSQL example</a>
- <a href="https://www.bezkoder.com/spring-boot-hibernate-oracle/">Spring JPA + Oracle example</a>
- <a href="https://www.bezkoder.com/spring-boot-sql-server/">Spring JPA + SQL Server example</a>
- Documentation: <a href="https://www.bezkoder.com/spring-boot-swagger-3/">Spring Boot Swagger 3 example</a>
- Caching: <a href="https://www.bezkoder.com/spring-boot-redis-cache-example/">Spring Boot Redis Cache example</a>

<h2><span id="Way">JPA @ManyToOne is appropriate way for One To Many mapping in Spring</span></h2>
In a relational database, a One-to-Many relationship between table A and table B indicates that one row in table A links to many rows in table B, but one row in table B links to only one row in table A.

For example, you need to design data model for a Tutorial Blog in which <strong>One</strong> Tutorial has <strong>Many</strong> Comments. So this is a One-to-Many association.

You can map the child entities as a collection (List of <code>Comment</code>s) in the parent object (<code>Tutorial</code>), and JPA/Hibernate provides the <a href="https://docs.oracle.com/javaee/7/api/javax/persistence/OneToMany.html">@OneToMany</a> annotation for that case: only the parent-side defines the relationship. We call it unidirectional <code>@OneToMany</code> association.

For tutorial, please visit: <a href="https://www.bezkoder.com/jpa-one-to-many-unidirectional/">JPA One To Many Unidirectional example</a>.

Similarly, when only the child-side manage the relationship, we have unidirectional Many-to-One association with <a href="https://docs.oracle.com/javaee/7/api/javax/persistence/ManyToOne.html">@ManyToOne</a> annotation where the child (<code>Comment</code>) has an entity object reference to its parent entity (<code>Tutorial</code>) by mapping the Foreign Key column (<code>tutorial_id</code>).

The most appropriate way to implement JPA/Hibernate One To Many mapping is unidirectional Many-to-One association with <a href="https://docs.oracle.com/javaee/7/api/javax/persistence/ManyToOne.html">@ManyToOne</a> because:
- With <code>@OneToMany</code>, we need to declare a collection (Comments) inside parent class (Tutorial), we cannot limit the size of that collection, for example, in case of pagination.
- With <code>@ManyToOne</code>, you can modify the Repository:
<ul>
	<li>to work with Pagination</li>
	<li>or to sort/order by multiple fields</li>
</ul>


<h2><span id="JPA_Many_to_One">JPA ManyToOne example</span></h2>
We're gonna create a Spring project from scratch, then we implement JPA/Hibernate Many to One Mapping with <code>tutorials</code> and <code>comments</code> table as following:

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-many-to-one-diagram.png" alt="jpa-many-to-one-diagram" width="460" height="180" class="alignnone size-full wp-image-15813" />

We also write Rest Apis to perform CRUD operations on the Comment entities.

These are APIs that we need to provide:
<div class="table-responsive">
<table class="table table-hover  table table-hover" width="100%">
<thead>
<tr>
<th>Methods</th>
<th>Urls</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>POST</td>
<td>/api/tutorials/:id/comments</td>
<td>create new Comment for a Tutorial</td>
</tr>
<tr>
<td>GET</td>
<td>/api/tutorials/:id/comments</td>
<td>retrieve all Comments of a Tutorial</td>
</tr>
<tr>
<td>GET</td>
<td>/api/comments/:id</td>
<td>retrieve a Comment by <code>:id</code></td>
</tr>
<tr>
<td>PUT</td>
<td>/api/comments/:id</td>
<td>update a Comment by <code>:id</code></td>
</tr>
<tr>
<td>DELETE</td>
<td>/api/comments/:id</td>
<td>delete a Comment by <code>:id</code></td>
</tr>
<tr>
<td>DELETE</td>
<td>/api/tutorials/:id</td>
<td>delete a Tutorial (and its Comments) by <code>:id</code></td>
</tr>
<tr>
<td>DELETE</td>
<td>/api/tutorials/:id/comments</td>
<td>delete all Comments of a Tutorial</td>
</tr>
</tbody>
</table>
</div>

Assume that we've had <strong>tutorials</strong> table like this:

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-manytoone-parent-table.png" alt="jpa-manytoone-parent-table" width="300" height="72" class="alignnone size-full wp-image-15814" />

Here are the example requests:

- Create new Comments: POST <code>/api/tutorials/[:id]/comments</code>

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-manytoone-example-create-child-entity.png" alt="jpa-manytoone-example-create-child-entity" width="590" height="660" class="alignnone size-full wp-image-15815" />

<strong>comments</strong> table after that:

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-manytoone-child-table.png" alt="jpa-manytoone-child-table" width="210" height="157" class="alignnone size-full wp-image-15816" />

- Retrieve all Comments of specific Tutorial: GET <code>/api/tutorials/[:id]/comments</code>

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-manytoone-example-spring-crud-retrieve.png" alt="jpa-manytoone-example-spring-crud-retrieve" width="500" height="440" class="alignnone size-full wp-image-15817" />

- Delete all Comments of specific Tutorial: DELETE <code>/api/tutorials/[:id]/comments</code>

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-manytoone-example-spring-crud-delete.png" alt="jpa-manytoone-example-spring-crud-delete" width="500" height="210" class="alignnone size-full wp-image-15818" />

Check the <strong>comment</strong> table, all Comments of Tutorial with id=2 were deleted:

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-manytoone-example-spring-crud-table-delete.png" alt="jpa-manytoone-example-spring-crud-table-delete" width="200" height="108" class="alignnone size-full wp-image-15819" />

- Delete a Tutorial: DELETE <code>/api/tutorials/[:id]</code>

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-manytoone-example-spring-crud-delete-cascade.png" alt="jpa-manytoone-example-spring-crud-delete-cascade" width="500" height="200" class="alignnone size-full wp-image-15820" />

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-manytoone-example-spring-crud-delete-parent-table.png" alt="jpa-manytoone-example-spring-crud-delete-parent-table" width="300" height="69" class="alignnone size-full wp-image-15821" />

All Comments of the Tutorial with id=3 were <strong>CASCADE</strong> deleted automatically.

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-manytoone-example-spring-crud-delete-cascade-table.png" alt="jpa-manytoone-example-spring-crud-delete-cascade-table" width="200" height="90" class="alignnone size-full wp-image-15822" />

Let's build our Spring Boot @ManyToOne CRUD example.

<h2><span id="Spring_Boot_Many_to_One">Spring Boot ManyToOne example</span></h2>
<h3><span id="Technology">Technology</span></h3>
<ul>
    	<li>Java 17 / 11 / 8</li>
    	<li>Spring Boot 3 / 2 (with Spring Web MVC, Spring Data JPA)</li>
    	<li>H2/PostgreSQL/MySQL</li>
    	<li>Maven</li>
</ul>

<h3><span id="Project_Structure">Project Structure</span></h3>

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-manytoone-example-hibernate-spring-boot-project-structure.png" alt="jpa-manytoone-example-hibernate-spring-boot-project-structure" width="300" height="500" class="alignnone size-full wp-image-15823" />

Let me explain it briefly.

– <code>Tutorial</code>, <code>Comment</code> data model class correspond to entity and table <em>tutorials</em>, <em>comments</em>.
– <code>TutorialRepository</code>, <code>CommentRepository</code> are interfaces that extends <a href="https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html">JpaRepository</a> for CRUD methods and custom finder methods. It will be autowired in <code>TutorialController</code>, <code>CommentController</code>.
– <code>TutorialController</code>, <code>CommentController</code> are <a href="https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RestController.html">RestController</a>s which has request mapping methods for RESTful CRUD API requests.
– Configuration for Spring Datasource, JPA & Hibernate in <strong>application.properties</strong>.
– <strong>pom.xml</strong> contains dependencies for Spring Boot and MySQL/PostgreSQL/H2 database.

– About <strong>exception</strong> package, to keep this post straightforward, I won't explain it. For more details, you can read following tutorial:
<a href="https://www.bezkoder.com/spring-boot-restcontrolleradvice/">@RestControllerAdvice example in Spring Boot</a>

<h3><span id="Setup">Create &#038; Setup Spring Boot project</span></h3>
Use <a href="http://start.spring.io/">Spring web tool</a> or your development tool (<a href="https://spring.io/tools">Spring Tool Suite</a>, Eclipse, <a href="https://www.jetbrains.com/idea/download/">Intellij</a>) to create a Spring Boot project.

Then open <strong>pom.xml</strong> and add these dependencies:
```html
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
```

We also need to add one more dependency.
- If you want to use <strong>MySQL</strong>:
```html
<dependency>
	<groupId>com.mysql</groupId>
	<artifactId>mysql-connector-j</artifactId>
	<scope>runtime</scope>
</dependency>
```

- or <strong>PostgreSQL</strong>:
```html
<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
	<scope>runtime</scope>
</dependency>
```

- or <strong>H2</strong> (embedded database):
```html
<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
	<scope>runtime</scope>
</dependency>
```

<h3><span id="Configuration">Configure Spring Datasource, JPA, Hibernate</span></h3>
Under src/main/resources folder, open application.properties and write these lines.

- For MySQL:
```properties
spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useSSL=false
spring.datasource.username=root
spring.datasource.password=123456

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto=update
```

- For PostgreSQL:
```properties
spring.datasource.url=jdbc:postgresql://localhost:5432/testdb
spring.datasource.username=postgres
spring.datasource.password=123

spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto=update
```

<ul>
	<li><code>spring.datasource.username</code> & <code>spring.datasource.password</code> properties are the same as your database installation.</li>
	<li>Spring Boot uses Hibernate for JPA implementation, we configure <code>MySQLDialect</code> for MySQL or <code>PostgreSQLDialect</code> for PostgreSQL</li>
	<li><code>spring.jpa.hibernate.ddl-auto</code> is used for database initialization. We set the value to <code>update</code> value so that a table will be created in the database automatically corresponding to defined data model. Any change to the model will also trigger an update to the table. For production, this property should be <code>validate</code>.</li>
</ul>

- For H2 database:
```properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
 
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update

spring.h2.console.enabled=true
# default path: h2-console
spring.h2.console.path=/h2-ui
```

<ul>
	<li><code>spring.datasource.url</code>: <code>jdbc:h2:mem:[database-name]</code> for In-memory database and <code>jdbc:h2:file:[path/database-name]</code> for disk-based database.</li>
	<li>We configure <code>H2Dialect</code> for H2 Database</li>
	<li><code>spring.h2.console.enabled=true</code> tells the Spring to start H2 Database administration tool and you can access this tool on the browser: <code>http://localhost:8080/h2-console</code>.</li>
	<li><code>spring.h2.console.path=/h2-ui</code> is for H2 console's url, so the default url <code>http://localhost:8080/h2-console</code> will change to <code>http://localhost:8080/h2-ui</code>.</li>
</ul>

<h3><span id="Model">Define Data Model for JPA ManyToOne mapping</span></h3>
In <strong>model</strong> package, we define <code>Tutorial</code> and <code>Comment</code> class.

Tutorial has four fields: id, title, description, published.

<strong>model</strong>/<em>Tutorial.java</em>
```java
package com.bezkoder.spring.hibernate.onetomany.model;

import jakarta.persistence.*;

@Entity
@Table(name = "tutorials")
public class Tutorial {

  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tutorial_generator")
  private long id;

  @Column(name = "title")
  private String title;

  @Column(name = "description")
  private String description;

  @Column(name = "published")
  private boolean published;

  public Tutorial() {

  }

  public Tutorial(String title, String description, boolean published) {
    this.title = title;
    this.description = description;
    this.published = published;
  }

  // getters and setters
}
```

- <code>@Entity</code> annotation indicates that the class is a persistent Java class.
- <code>@Table</code> annotation provides the table that maps this entity.

- <code>@Id</code> annotation is for the primary key.
- <code>@GeneratedValue</code> annotation is used to define generation strategy for the primary key. <code>GenerationType.SEQUENCE</code> means using database sequence to generate unique values.
We also indicate the name of the primary key <code>generator</code>. If you don't give it the name, id value will be generated with <strong>hibernate_sequence</strong> table (supplied by persistence provider, for all entities) by default.

- <code>@Column</code> annotation is used to define the column in database that maps annotated field.

The <code>Comment</code> class has the <code>@ManyToOne</code> annotation for many-to-one relationship with the <code>Tutorial</code> entity. <code>optional</code> element is set to <code>false</code> for non-null relationship.

<strong>model</strong>/<em>Comment.java</em>
```java
package com.bezkoder.spring.hibernate.onetomany.model;

import jakarta.persistence.*;

import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

import com.fasterxml.jackson.annotation.JsonIgnore;

@Entity
@Table(name = "comments")
public class Comment {
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "comment_generator")
  private Long id;

  @Lob
  private String content;

  @ManyToOne(fetch = FetchType.LAZY, optional = false)
  @JoinColumn(name = "tutorial_id", nullable = false)
  @OnDelete(action = OnDeleteAction.CASCADE)
  @JsonIgnore
  private Tutorial tutorial;

  // getters and setters
}
```

We also use the <a href="https://docs.oracle.com/javaee/7/api/javax/persistence/JoinColumn.html">@JoinColumn</a> annotation to specify the foreign key column (<code>tutorial_id</code>). If you don't provide the <code>JoinColumn</code> name, the name will be set automatically.

<code>@JsonIgnore</code> is used to ignore the logical property used in serialization and deserialization.

We also implement cascade delete capabilities of the foreign-key with <code>@OnDelete(action = OnDeleteAction.CASCADE)</code>.

We set the <code>@ManyToOne</code> with <code>FetchType.LAZY</code> for <code>fetch</code> type:

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-manytoone-lazy-fetch.png" alt="jpa-manytoone-lazy-fetch" width="500" height="250" class="alignnone size-full wp-image-15824" />

By default, the <code>@ManyToOne</code> association uses <code>FetchType.EAGER</code> for <code>fetch</code> type but it is bad for performance:

```java
public class Comment {
  ...

  @ManyToOne(fetch = FetchType.EAGER, optional = false)
  @JoinColumn(name = "tutorial_id", nullable = false)
  @OnDelete(action = OnDeleteAction.CASCADE)
  private Tutorial tutorial;

  ...
}
```

<img src="https://www.bezkoder.com/wp-content/uploads/jpa-manytoone-eager-fetch.png" alt="jpa-manytoone-eager-fetch" width="500" height="360" class="alignnone size-full wp-image-15825" />

<h3><span id="Repository">Create Repository Interfaces for ManyToOne mapping</span></h3>
Let's create a repository to interact with database.
In <strong>repository</strong> package, create <code>TutorialRepository</code> and <code>CommentRepository</code> interfaces that extend <code>JpaRepository</code>.

<strong>repository</strong>/<em>TutorialRepository.java</em>
```java
package com.bezkoder.spring.hibernate.onetomany.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.bezkoder.spring.hibernate.onetomany.model.Tutorial;

public interface TutorialRepository extends JpaRepository<Tutorial, Long> {
  List<Tutorial> findByPublished(boolean published);

  List<Tutorial> findByTitleContaining(String title);
}
```

<strong>repository</strong>/<em>CommentRepository.java</em>
```java
package com.bezkoder.spring.hibernate.onetomany.repository;

import java.util.List;

import jakarta.transaction.Transactional;

import org.springframework.data.jpa.repository.JpaRepository;

import com.bezkoder.spring.hibernate.onetomany.model.Comment;

public interface CommentRepository extends JpaRepository<Comment, Long> {
  List<Comment> findByTutorialId(Long postId);
  
  @Transactional
  void deleteByTutorialId(long tutorialId);
}
```

Now we can use JpaRepository's methods: <code>save()</code>, <code>findOne()</code>, <code>findById()</code>, <code>findAll()</code>, <code>count()</code>, <code>delete()</code>, <code>deleteById()</code>... without implementing these methods.

We also define custom finder methods:
<ul>
	<li><code>findByPublished()</code>: returns all Tutorials with <code>published</code> having value as input <code>published</code>.</li>
	<li><code>findByTitleContaining()</code>: returns all Tutorials which title contains input <code>title</code>.</li>
	<li><code>findByTutorialId()</code>: returns all Comments of a Tutorial specified by <code>tutorialId</code>.</li>
	<li><code>deleteByTutorialId()</code>: deletes all Comments of a Tutorial specified by <code>tutorialId</code>.</li>
</ul>

The implementation is plugged in by <a href="https://docs.spring.io/spring-data/jpa/docs/current/reference/html/">Spring Data JPA</a> automatically.

Now we can see the pros of <code>@ManyToOne</code> annotation.
- With <code>@OneToMany</code>, we need to declare a collection inside parent class, we cannot limit the size of that collection, for example, in case of pagination.
- With <code>@ManyToOne</code>, you can modify Repository:
<ul>
	<li>to work with Pagination, the instruction can be found at:
<a href="https://bezkoder.com/spring-boot-pagination-filter-jpa-pageable/">Spring Boot Pagination & Filter example | Spring JPA, Pageable</a></li>
	<li>or to sort/order by multiple fields:
<a href="https://bezkoder.com/spring-data-sort-multiple-columns/">Spring Data JPA Sort/Order by multiple Columns | Spring Boot</a></li>
</ul>
Please notice that above tutorials are for <code>TutorialRepository</code>, you need to apply the same way for <code>CommentRepository</code> .

More Derived queries at:
<a href="https://www.bezkoder.com/jpa-repository-query/">JPA Repository query example in Spring Boot</a>

Custom query with <code>@Query</code> annotation:
<a href="https://www.bezkoder.com/spring-jpa-query/">Spring JPA @Query example: Custom query in Spring Boot</a>

You also find way to write Unit Test for this JPA Repository at:
<a href="https://bezkoder.com/spring-boot-unit-test-jpa-repo-datajpatest/">Spring Boot Unit Test for JPA Repository with @DataJpaTest</a>

<h2><span id="Controller">Create Spring Rest APIs Controller</span></h2>
Finally, we create controller that provides APIs for CRUD operations: creating, retrieving, updating, deleting and finding Tutorials and Comments.

<em>controller/TutorialController.java</em>
```java
package com.bezkoder.spring.hibernate.onetomany.controller;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.bezkoder.spring.hibernate.onetomany.exception.ResourceNotFoundException;
import com.bezkoder.spring.hibernate.onetomany.model.Tutorial;
import com.bezkoder.spring.hibernate.onetomany.repository.TutorialRepository;

@CrossOrigin(origins = "http://localhost:8081")
@RestController
@RequestMapping("/api")
public class TutorialController {

  @Autowired
  TutorialRepository tutorialRepository;

  @GetMapping("/tutorials")
  public ResponseEntity<List<Tutorial>> getAllTutorials(@RequestParam(required = false) String title) {
    List<Tutorial> tutorials = new ArrayList<Tutorial>();

    if (title == null)
      tutorialRepository.findAll().forEach(tutorials::add);
    else
      tutorialRepository.findByTitleContaining(title).forEach(tutorials::add);

    if (tutorials.isEmpty()) {
      return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    return new ResponseEntity<>(tutorials, HttpStatus.OK);
  }

  @GetMapping("/tutorials/{id}")
  public ResponseEntity<Tutorial> getTutorialById(@PathVariable("id") long id) {
    Tutorial tutorial = tutorialRepository.findById(id)
        .orElseThrow(() -> new ResourceNotFoundException("Not found Tutorial with id = " + id));

    return new ResponseEntity<>(tutorial, HttpStatus.OK);
  }

  @PostMapping("/tutorials")
  public ResponseEntity<Tutorial> createTutorial(@RequestBody Tutorial tutorial) {
    Tutorial _tutorial = tutorialRepository.save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), true));
    return new ResponseEntity<>(_tutorial, HttpStatus.CREATED);
  }

  @PutMapping("/tutorials/{id}")
  public ResponseEntity<Tutorial> updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) {
    Tutorial _tutorial = tutorialRepository.findById(id)
        .orElseThrow(() -> new ResourceNotFoundException("Not found Tutorial with id = " + id));

    _tutorial.setTitle(tutorial.getTitle());
    _tutorial.setDescription(tutorial.getDescription());
    _tutorial.setPublished(tutorial.isPublished());
    
    return new ResponseEntity<>(tutorialRepository.save(_tutorial), HttpStatus.OK);
  }

  @DeleteMapping("/tutorials/{id}")
  public ResponseEntity<HttpStatus> deleteTutorial(@PathVariable("id") long id) {
    tutorialRepository.deleteById(id);
    
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
  }

  @DeleteMapping("/tutorials")
  public ResponseEntity<HttpStatus> deleteAllTutorials() {
    tutorialRepository.deleteAll();
    
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
  }

  @GetMapping("/tutorials/published")
  public ResponseEntity<List<Tutorial>> findByPublished() {
    List<Tutorial> tutorials = tutorialRepository.findByPublished(true);

    if (tutorials.isEmpty()) {
      return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
    
    return new ResponseEntity<>(tutorials, HttpStatus.OK);
  }
}
```

<em>controller/CommentController.java</em>
```java
package com.bezkoder.spring.hibernate.onetomany.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.bezkoder.spring.hibernate.onetomany.exception.ResourceNotFoundException;
import com.bezkoder.spring.hibernate.onetomany.model.Comment;
import com.bezkoder.spring.hibernate.onetomany.repository.CommentRepository;
import com.bezkoder.spring.hibernate.onetomany.repository.TutorialRepository;

@CrossOrigin(origins = "http://localhost:8081")
@RestController
@RequestMapping("/api")
public class CommentController {

  @Autowired
  private TutorialRepository tutorialRepository;

  @Autowired
  private CommentRepository commentRepository;

  @GetMapping("/tutorials/{tutorialId}/comments")
  public ResponseEntity<List<Comment>> getAllCommentsByTutorialId(@PathVariable(value = "tutorialId") Long tutorialId) {
    if (!tutorialRepository.existsById(tutorialId)) {
      throw new ResourceNotFoundException("Not found Tutorial with id = " + tutorialId);
    }

    List<Comment> comments = commentRepository.findByTutorialId(tutorialId);
    return new ResponseEntity<>(comments, HttpStatus.OK);
  }

  @GetMapping("/comments/{id}")
  public ResponseEntity<Comment> getCommentsByTutorialId(@PathVariable(value = "id") Long id) {
    Comment comment = commentRepository.findById(id)
        .orElseThrow(() -> new ResourceNotFoundException("Not found Comment with id = " + id));

    return new ResponseEntity<>(comment, HttpStatus.OK);
  }

  @PostMapping("/tutorials/{tutorialId}/comments")
  public ResponseEntity<Comment> createComment(@PathVariable(value = "tutorialId") Long tutorialId,
      @RequestBody Comment commentRequest) {
    Comment comment = tutorialRepository.findById(tutorialId).map(tutorial -> {
      commentRequest.setTutorial(tutorial);
      return commentRepository.save(commentRequest);
    }).orElseThrow(() -> new ResourceNotFoundException("Not found Tutorial with id = " + tutorialId));

    return new ResponseEntity<>(comment, HttpStatus.CREATED);
  }

  @PutMapping("/comments/{id}")
  public ResponseEntity<Comment> updateComment(@PathVariable("id") long id, @RequestBody Comment commentRequest) {
    Comment comment = commentRepository.findById(id)
        .orElseThrow(() -> new ResourceNotFoundException("CommentId " + id + "not found"));

    comment.setContent(commentRequest.getContent());

    return new ResponseEntity<>(commentRepository.save(comment), HttpStatus.OK);
  }

  @DeleteMapping("/comments/{id}")
  public ResponseEntity<HttpStatus> deleteComment(@PathVariable("id") long id) {
    commentRepository.deleteById(id);

    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
  }
  
  @DeleteMapping("/tutorials/{tutorialId}/comments")
  public ResponseEntity<List<Comment>> deleteAllCommentsOfTutorial(@PathVariable(value = "tutorialId") Long tutorialId) {
    if (!tutorialRepository.existsById(tutorialId)) {
      throw new ResourceNotFoundException("Not found Tutorial with id = " + tutorialId);
    }

    commentRepository.deleteByTutorialId(tutorialId);
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
  }
}
```

<h2><span id="Conclusion">Conclusion</span></h2>
Today we've built a Spring Boot CRUD example using Spring Data JPA, Hibernate Many To One relationship with MySQL/PostgreSQL/embedded database (H2).

We also see that <code>@ManyToOne</code> annotation is the most appropriate way for implementing JPA One To Many Mapping, and <code>JpaRepository</code> supports a great way to make CRUD operations, custom finder methods without need of boilerplate code.

Custom query with <code>@Query</code> annotation:
<a href="https://www.bezkoder.com/spring-jpa-query/">Spring JPA @Query example: Custom query in Spring Boot</a>

If you want to add Pagination to this Spring project, you can find the instruction at:
<a href="https://www.bezkoder.com/spring-boot-pagination-filter-jpa-pageable/">Spring Boot Pagination & Filter example | Spring JPA, Pageable</a>

To sort/order by multiple fields:
<a href="https://www.bezkoder.com/spring-data-sort-multiple-columns/">Spring Data JPA Sort/Order by multiple Columns | Spring Boot</a>

Handle Exception for this Rest APIs is necessary:
- <a href="https://www.bezkoder.com/spring-boot-controlleradvice-exceptionhandler/">Spring Boot @ControllerAdvice & @ExceptionHandler example</a>
- <a href="https://www.bezkoder.com/spring-boot-restcontrolleradvice/">@RestControllerAdvice example in Spring Boot</a>

Or way to write Unit Test for the JPA Repository:
<a href="https://www.bezkoder.com/spring-boot-unit-test-jpa-repo-datajpatest/">Spring Boot Unit Test for JPA Repository with @DataJpaTest</a>

You can also know:
- <a href="https://www.bezkoder.com/spring-boot-validate-request-body/">Validate Request Body in Spring Boot</a>
- how to deploy this Spring Boot App on AWS (for free) with <a href="https://www.bezkoder.com/deploy-spring-boot-aws-eb/">this tutorial</a>.
- dockerize with <a href="https://www.bezkoder.com/docker-compose-spring-boot-mysql/">Docker Compose: Spring Boot and MySQL example</a>
- way to upload an Excel file and store the data in MySQL database with <a href="https://www.bezkoder.com/spring-boot-upload-excel-file-database/">this post</a>
- upload CSV file and store the data in MySQL with <a href="https://www.bezkoder.com/spring-boot-upload-csv-file/">this post</a>.

Happy learning! See you again.

<h2><span id="Further_Reading">Further Reading</span></h2>
<ul>
	<li><a href="https://www.bezkoder.com/spring-boot-jwt-authentication/">Secure Spring Boot App with Spring Security & JWT Authentication</a></li>
	<li><a href="https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#reference">Spring Data JPA Reference Documentation</a></li>
	<li><a href="https://www.bezkoder.com/spring-boot-pagination-sorting-example/">Spring Boot Pagination and Sorting example</a></li>
</ul>

Fullstack CRUD App:
- <a href="https://www.bezkoder.com/spring-boot-vue-js-crud-example/">Vue + Spring Boot example</a>
- <a href="https://www.bezkoder.com/angular-spring-boot-crud/">Angular 8 + Spring Boot example</a>
- <a href="https://www.bezkoder.com/angular-10-spring-boot-crud/">Angular 10 + Spring Boot example</a>
- <a href="https://www.bezkoder.com/angular-11-spring-boot-crud/">Angular 11 + Spring Boot example</a>
- <a href="https://www.bezkoder.com/angular-12-spring-boot-crud/">Angular 12 + Spring Boot example</a>
- <a href="https://www.bezkoder.com/spring-boot-angular-13-crud/">Angular 13 + Spring Boot example</a>
- <a href="https://www.bezkoder.com/spring-boot-angular-14-crud/">Angular 14 + Spring Boot example</a>
- <a href="https://www.bezkoder.com/spring-boot-angular-15-crud/">Angular 15 + Spring Boot example</a>
- <a href="https://www.bezkoder.com/spring-boot-angular-16-crud/">Angular 16 + Spring Boot example</a>
- <a href="https://www.bezkoder.com/react-spring-boot-crud/">React + Spring Boot example</a>

<h2><span id="Source_Code">Source Code</span></h2>
You can find the complete source code for this tutorial on <a href="https://github.com/bezkoder/spring-boot-one-to-many">Github</a>.

Using <code>@OneToMany</code> instead: <a href="https://www.bezkoder.com/jpa-one-to-many-unidirectional/">JPA One To Many Unidirectional example</a>
Many-to-Many: <a href="https://www.bezkoder.com/jpa-many-to-many/">JPA Many to Many example with Hibernate in Spring Boot</a>
One-to-One: <a href="https://www.bezkoder.com/jpa-one-to-one/">JPA One To One example with Hibernate in Spring Boot</a>

You can apply this implementation in following tutorials:
- <a href="https://www.bezkoder.com/spring-boot-jpa-h2-example/">Spring JPA + H2 example</a>
- <a href="https://www.bezkoder.com/spring-boot-jpa-crud-rest-api/">Spring JPA + MySQL example</a>
- <a href="https://www.bezkoder.com/spring-boot-postgresql-example/">Spring JPA + PostgreSQL example</a>
- <a href="https://www.bezkoder.com/spring-boot-hibernate-oracle/">Spring JPA + Oracle example</a>
- <a href="https://www.bezkoder.com/spring-boot-sql-server/">Spring JPA + SQL Server example</a>

More Derived queries at:
<a href="https://www.bezkoder.com/jpa-repository-query/">JPA Repository query example in Spring Boot</a>

Documentation: <a href="https://www.bezkoder.com/spring-boot-swagger-3/">Spring Boot + Swagger 3 example (with OpenAPI 3)</a>
Caching: <a href="https://www.bezkoder.com/spring-boot-redis-cache-example/">Spring Boot Redis Cache example</a>