In this blog, I will explain how to use one-to-many mapping in Spring boot Application
What you need?
- JAVA
- MySql
- Eclipse IDE ( whatever you like IDE, I'm using Eclipse for this example)
- Maven ( you can use the Gradle as well)
Initial Plan
I will create a spring boot application project using the Spring Initializer web tool and import the project as a maven project. after configuring the all necessary setting, I will code for one-to-many mapping.
Below diagram is the database model diagram which we going to install using the spring boot application.
Let's Start to Code.
You need to configure the application.properties file for database connections. add the following content to the src/main/resources/application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/learning spring.datasource.username=root spring.datasource.password=root spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect spring.jpa.hibernate.ddl-auto = updateChange the database username and password according to your MySql configurations.
1. BaseAuditModel class
This class will automatically create create_at and update_at fields while persisting the entities. Here is the Spring basic definition of the Auditing Class.
"Spring Data provides sophisticated support to transparently keep track of who created or changed an entity and the point in time this happened. To benefit from that functionality you have to equip your entity classes with auditing metadata that can be defined either using annotations or by implementing an interface."[1].
package com.hksoft.springbootonetomany.model;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(
value= {"createdAt", "updatedAt"},
allowGetters=true
)
public abstract class BaseAuditModel implements Serializable {
@Temporal(TemporalType.TIMESTAMP)
@Column(name="createdAt", nullable=false, updatable=false)
@CreatedDate
private Date createdAt;
@Temporal(TemporalType.TIMESTAMP)
@Column(name="updatedAt", nullable=false)
@LastModifiedDate
private Date updatedAt;
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
}
@JsonIgnoreProperties ignores the specified logical properties in JSON serialization and deserialization. It is annotated at the class level.[2]
Ok. now you need to enable the auditing in one of the configuration class. Open the main class and add the @EnableJpaAuditing annotation.
package com.hksoft.springbootonetomany; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication @EnableJpaAuditing public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }Post Model
package com.hksoft.springbootonetomany.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.Table; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; @Entity @Table(name = "POSTS") public class Post extends BaseAuditModel { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotNull @Size(max = 100) @Column(name = "title") private String title; @NotNull @Size(max = 200) @Column(name = "description") private String description; @NotNull @Lob private String content; // Getter and setters }Comment Model
package com.hksoft.springbootonetomany.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
@Table(name = "COMMENTS")
public class Comment extends BaseAuditModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Lob
@Column(name = "text")
private String comment;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "postId", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
@JsonIgnore
private Post post;
//Getter and Setter
}
@JsonIgnore is used to ignore the logical property used in serialization and deserialization. @JsonIgnore can be used at setter, getter or field.[2]
Repositories.
Now we'll create the repository to access the data from database.
PostRepository
package com.hksoft.springbootonetomany.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.hksoft.springbootonetomany.model.Post; @Repository public interface PostRepository extends JpaRepository<Post, Long>CommentRepository{ }
package com.hksoft.springbootonetomany.repository; import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import com.hksoft.springbootonetomany.model.Comment; public interface CommentRepository extends JpaRepository<Comment, Long>Creating the Service package{ Page findByPostId(Long id, Pageable pageable); Optional findByIdAndPostId(Long id, Long postId); }
Post Services interface
package com.hksoft.springbootonetomany.service; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import com.hksoft.springbootonetomany.model.Post; public interface PostService { Post createPost(Post post); PagePostServiceImpl ClassgetAllPost(Pageable pageable); Post updatePost(Long id, Post post); ResponseEntity deletePost(Long id); }
package com.hksoft.springbootonetomany.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import com.hksoft.springbootonetomany.exception.ResourceNotFoundException; import com.hksoft.springbootonetomany.model.Post; import com.hksoft.springbootonetomany.repository.PostRepository; @Service public class PostServiceImpl implements PostService{ @Autowired private PostRepository postRepository; @Override public Post createPost(Post post) { return postRepository.save(post); } @Override public PageComment Service interfacegetAllPost(Pageable pageable) { return postRepository.findAll(pageable); } @Override public Post updatePost(Long postId, Post postUpdate) { return postRepository.findById(postId).map(post -> { post.setTitle(postUpdate.getTitle()); post.setDescription(postUpdate.getDescription()); post.setContent(postUpdate.getContent()); return postRepository.save(post); }).orElseThrow(() -> new ResourceNotFoundException("PostId " + postId + " Not Found" )); } @Override public ResponseEntity deletePost(Long postId) { return postRepository.findById(postId).map(post -> { postRepository.delete(post); return ResponseEntity.ok().build(); }).orElseThrow(() -> new ResourceNotFoundException("PostId " + postId + " Not Found" )); } }
package com.hksoft.springbootonetomany.service; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import com.hksoft.springbootonetomany.model.Comment; public interface CommentService { PageCommentServiceImpl ClassgetAllComments(Long postId, Pageable pageable); Comment createComment(Long postId, Comment comment); Comment updateComment(Long postId, Long commentId, Comment commentUpdate); ResponseEntity deleteComment(Long postId, Long commentId); }
package com.hksoft.springbootonetomany.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import com.hksoft.springbootonetomany.exception.ResourceNotFoundException; import com.hksoft.springbootonetomany.model.Comment; import com.hksoft.springbootonetomany.repository.CommentRepository; import com.hksoft.springbootonetomany.repository.PostRepository; @Service public class CommentServiceImpl implements CommentService { @Autowired private CommentRepository commentRepository; @Autowired private PostRepository postRepository; @Override public PageLet's write REST API to perform CRUD operations.getAllComments(Long postId, Pageable pageable) { return commentRepository.findByPostId(postId, pageable); } @Override public Comment createComment(Long postId, Comment comment) { return postRepository.findById(postId).map(post -> { comment.setPost(post); return commentRepository.save(comment); }).orElseThrow(() -> new ResourceNotFoundException("PostId " + postId + " not found")); } @Override public Comment updateComment(Long postId, Long commentId, Comment commentUpdate) { if(!postRepository.existsById(postId)) { throw new ResourceNotFoundException("PostId " + postId + " not found"); } return commentRepository.findById(commentId).map(comment -> { comment.setComment(commentUpdate.getComment()); return commentRepository.save(comment); }).orElseThrow(() -> new ResourceNotFoundException("commentId " + commentId + " not found")); } @Override public ResponseEntity deleteComment(Long postId, Long commentId) { return commentRepository.findByIdAndPostId(commentId, postId).map(comment -> { commentRepository.delete(comment); return ResponseEntity.ok().build(); }).orElseThrow(() -> new ResourceNotFoundException("commentId " + commentId + " not found")); } }
PostController
package com.hksoft.springbootonetomany.controller; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; 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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import com.hksoft.springbootonetomany.model.Post; import com.hksoft.springbootonetomany.service.PostService; @RestController @RequestMapping("/rest/posts") public class PostController { @Autowired private PostService postSevice; @PostMapping @ResponseStatus(value=HttpStatus.CREATED) public Post createPost(@Valid @RequestBody Post post) { return postSevice.createPost(post); } @GetMapping public PageComment ControllergetAllPage(Pageable pageable) { return postSevice.getAllPost(pageable); } @PutMapping("/{postId}") public Post updatePost(@PathVariable Long postId, @Valid @RequestBody Post postUpdate) { return postSevice.updatePost(postId, postUpdate); } @DeleteMapping("/{postId}") public ResponseEntity deletePost(@PathVariable Long postId) { return postSevice.deletePost(postId); } }
package com.hksoft.springbootonetomany.controller; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; 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.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import com.hksoft.springbootonetomany.model.Comment; import com.hksoft.springbootonetomany.service.CommentService; @RestController @RequestMapping("/rest/posts/{postId}/comments") public class CommentController { @Autowired private CommentService commentService; @GetMapping() public PageOk. we finished the coding. Now we need to test the REST API using postman.getAllComments(@PathVariable (value="postId") Long postId, Pageable pageable) { return commentService.getAllComments(postId, pageable); } @PostMapping() @ResponseStatus( value= HttpStatus.CREATED) public Comment createComment(@PathVariable (value="postId") Long postId, @RequestBody Comment comment) { return commentService.createComment(postId, comment); } @PutMapping("/{commentId}") public Comment updateComment(@PathVariable ( value= "postId") Long postId, @PathVariable ( value="commentId") Long commentId , @Valid @RequestBody Comment commentUpdate) { return commentService.updateComment(postId, commentId, commentUpdate); } @DeleteMapping("/{commentId}") public ResponseEntity deleteComment(@PathVariable ( value= "postId") Long postId, @PathVariable ( value="commentId") Long commentId) { return commentService.deleteComment(postId, commentId); } }
You can download the code from my GitHub.
Rest API Details
[1]. https://docs.spring.io/spring-data/jpa/docs/1.7.0.DATAJPA-580-SNAPSHOT/reference/html/auditing.html
[2]. https://www.concretepage.com/jackson-api/jackson-jsonignore-jsonignoreproperties-and-jsonignoretype
[3]. https://vladmihalcea.com/how-to-inherit-properties-from-a-base-class-entity-using-mappedsuperclass-with-jpa-and-hibernate/
[4]. https://www.baeldung.com/java-optional
[5]. https://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
Comments
Post a Comment