mirror of
https://github.com/Example-uPagge/spring-boot-one-to-many.git
synced 2024-06-14 11:22:31 +03:00
Большой рефакторинг
This commit is contained in:
parent
ebf825b448
commit
7453c53be6
21
pom.xml
21
pom.xml
@ -5,16 +5,22 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.6.2</version>
|
||||
<version>2.7.4</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.yapract</groupId>
|
||||
|
||||
<groupId>dev.struchkov.example</groupId>
|
||||
<artifactId>spring-boot-one-to-many</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>spring-boot-one-to-many</name>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -29,14 +35,13 @@
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.4.0</version>
|
||||
<version>42.5.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
package com.yprac.spring.hibernate.onetomany;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SpringBootOneToManyApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringBootOneToManyApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
package com.yprac.spring.hibernate.onetomany.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.yprac.spring.hibernate.onetomany.exception.ResourceNotFoundException;
|
||||
import com.yprac.spring.hibernate.onetomany.repository.CommentRepository;
|
||||
import com.yprac.spring.hibernate.onetomany.repository.TutorialRepository;
|
||||
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.yprac.spring.hibernate.onetomany.model.Comment;
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
package com.yprac.spring.hibernate.onetomany.controller;
|
||||
import com.yprac.spring.hibernate.onetomany.exception.ResourceNotFoundException;
|
||||
import com.yprac.spring.hibernate.onetomany.model.Tag;
|
||||
import com.yprac.spring.hibernate.onetomany.model.Tutorial;
|
||||
import com.yprac.spring.hibernate.onetomany.repository.TagRepository;
|
||||
import com.yprac.spring.hibernate.onetomany.repository.TutorialRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@CrossOrigin(origins = "http://localhost:8081")
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class TagController {
|
||||
@Autowired
|
||||
private TutorialRepository tutorialRepository;
|
||||
@Autowired
|
||||
private TagRepository tagRepository;
|
||||
|
||||
@GetMapping("/tags")
|
||||
public ResponseEntity<List<Tag>> getAllTags() {
|
||||
List<Tag> tags = new ArrayList<Tag>();
|
||||
tagRepository.findAll().forEach(tags::add);
|
||||
if (tags.isEmpty()) {
|
||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||
}
|
||||
return new ResponseEntity<>(tags, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@GetMapping("/tutorials/{tutorialId}/tags")
|
||||
public ResponseEntity<List<Tag>> getAllTagsByTutorialId(@PathVariable(value = "tutorialId") Long tutorialId) {
|
||||
if (!tutorialRepository.existsById(tutorialId)) {
|
||||
throw new ResourceNotFoundException("Not found Tutorial with id = " + tutorialId);
|
||||
}
|
||||
List<Tag> tags = tagRepository.findTagsByTutorialsId(tutorialId);
|
||||
return new ResponseEntity<>(tags, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@GetMapping("/tags/{id}")
|
||||
public ResponseEntity<Tag> getTagsById(@PathVariable(value = "id") Long id) {
|
||||
Tag tag = tagRepository.findById(id)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Not found Tag with id = " + id));
|
||||
return new ResponseEntity<>(tag, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@GetMapping("/tags/{tagId}/tutorials")
|
||||
public ResponseEntity<List<Tutorial>> getAllTutorialsByTagId(@PathVariable(value = "tagId") Long tagId) {
|
||||
if (!tagRepository.existsById(tagId)) {
|
||||
throw new ResourceNotFoundException("Not found Tag with id = " + tagId);
|
||||
}
|
||||
List<Tutorial> tutorials = tutorialRepository.findTutorialsByTagsId(tagId);
|
||||
return new ResponseEntity<>(tutorials, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@PostMapping("/tutorials/{tutorialId}/tags")
|
||||
public ResponseEntity<Tag> addTag(@PathVariable(value = "tutorialId") Long tutorialId, @RequestBody Tag tagRequest) {
|
||||
Tag tag = tutorialRepository.findById(tutorialId).map(tutorial -> {
|
||||
long tagId = tagRequest.getId();
|
||||
|
||||
// tag is existed
|
||||
if (tagId != 0L) {
|
||||
Tag _tag = tagRepository.findById(tagId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Not found Tag with id = " + tagId));
|
||||
tutorial.addTag(_tag);
|
||||
tutorialRepository.save(tutorial);
|
||||
return _tag;
|
||||
}
|
||||
|
||||
// add and create new Tag
|
||||
tutorial.addTag(tagRequest);
|
||||
return tagRepository.save(tagRequest);
|
||||
}).orElseThrow(() -> new ResourceNotFoundException("Not found Tutorial with id = " + tutorialId));
|
||||
return new ResponseEntity<>(tag, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
@PutMapping("/tags/{id}")
|
||||
public ResponseEntity<Tag> updateTag(@PathVariable("id") long id, @RequestBody Tag tagRequest) {
|
||||
Tag tag = tagRepository.findById(id)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("TagId " + id + "not found"));
|
||||
tag.setName(tagRequest.getName());
|
||||
return new ResponseEntity<>(tagRepository.save(tag), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@DeleteMapping("/tutorials/{tutorialId}/tags/{tagId}")
|
||||
public ResponseEntity<HttpStatus> deleteTagFromTutorial(@PathVariable(value = "tutorialId") Long tutorialId, @PathVariable(value = "tagId") Long tagId) {
|
||||
Tutorial tutorial = tutorialRepository.findById(tutorialId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Not found Tutorial with id = " + tutorialId));
|
||||
|
||||
tutorial.removeTag(tagId);
|
||||
tutorialRepository.save(tutorial);
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||
}
|
||||
|
||||
@DeleteMapping("/tags/{id}")
|
||||
public ResponseEntity<HttpStatus> deleteTag(@PathVariable("id") long id) {
|
||||
tagRepository.deleteById(id);
|
||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||
}
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
package com.yprac.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.yprac.spring.hibernate.onetomany.exception.ResourceNotFoundException;
|
||||
import com.yprac.spring.hibernate.onetomany.model.Tutorial;
|
||||
import com.yprac.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);
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package com.yprac.spring.hibernate.onetomany.exception;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class ControllerExceptionHandler {
|
||||
|
||||
@ExceptionHandler(ResourceNotFoundException.class)
|
||||
@ResponseStatus(value = HttpStatus.NOT_FOUND)
|
||||
public ErrorMessage resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
|
||||
ErrorMessage message = new ErrorMessage(
|
||||
HttpStatus.NOT_FOUND.value(),
|
||||
new Date(),
|
||||
ex.getMessage(),
|
||||
request.getDescription(false));
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ErrorMessage globalExceptionHandler(Exception ex, WebRequest request) {
|
||||
ErrorMessage message = new ErrorMessage(
|
||||
HttpStatus.INTERNAL_SERVER_ERROR.value(),
|
||||
new Date(),
|
||||
ex.getMessage(),
|
||||
request.getDescription(false));
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package com.yprac.spring.hibernate.onetomany.exception;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class ErrorMessage {
|
||||
private int statusCode;
|
||||
private Date timestamp;
|
||||
private String message;
|
||||
private String description;
|
||||
|
||||
public ErrorMessage(int statusCode, Date timestamp, String message, String description) {
|
||||
this.statusCode = statusCode;
|
||||
this.timestamp = timestamp;
|
||||
this.message = message;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public Date getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package com.yprac.spring.hibernate.onetomany.exception;
|
||||
|
||||
public class ResourceNotFoundException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ResourceNotFoundException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package com.yprac.spring.hibernate.onetomany.model;
|
||||
|
||||
import javax.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;
|
||||
|
||||
@Column(name = "content")
|
||||
private String content;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "tutorial_id", nullable = false)
|
||||
// @OnDelete(action = OnDeleteAction.CASCADE)
|
||||
@JsonIgnore
|
||||
private Tutorial tutorial;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public Tutorial getTutorial() {
|
||||
return tutorial;
|
||||
}
|
||||
|
||||
public void setTutorial(Tutorial tutorial) {
|
||||
this.tutorial = tutorial;
|
||||
}
|
||||
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package com.yprac.spring.hibernate.onetomany.model;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.*;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
@Entity
|
||||
@Table(name = "tags")
|
||||
public class Tag {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "tags")
|
||||
@JsonIgnore
|
||||
private Set<Tutorial> tutorials = new HashSet<>();
|
||||
public Tag() {
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public Set<Tutorial> getTutorials() {
|
||||
return tutorials;
|
||||
}
|
||||
public void setTutorials(Set<Tutorial> tutorials) {
|
||||
this.tutorials = tutorials;
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
package com.yprac.spring.hibernate.onetomany.model;
|
||||
|
||||
import org.hibernate.annotations.OnDelete;
|
||||
import org.hibernate.annotations.OnDeleteAction;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@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;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
||||
@JoinColumn(name = "tutorial_id")
|
||||
private Set<Comment> comments = new HashSet<>();
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
|
||||
@JoinTable(name = "tutorial_tags",
|
||||
joinColumns = { @JoinColumn(name = "tutorial_id") },
|
||||
inverseJoinColumns = { @JoinColumn(name = "tag_id") })
|
||||
private Set<Tag> tags = new HashSet<>();
|
||||
|
||||
public Tutorial() {
|
||||
|
||||
}
|
||||
|
||||
public Tutorial(String title, String description, boolean published) {
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.published = published;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public boolean isPublished() {
|
||||
return published;
|
||||
}
|
||||
|
||||
public void setPublished(boolean isPublished) {
|
||||
this.published = isPublished;
|
||||
}
|
||||
|
||||
public Set<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(Set<Tag> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public Set<Comment> getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
public void setComments(Set<Comment> comments) {
|
||||
this.comments = comments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
|
||||
}
|
||||
|
||||
public void addTag(Tag tag) {
|
||||
this.tags.add(tag);
|
||||
tag.getTutorials().add(this);
|
||||
}
|
||||
|
||||
public void removeTag(Long tagId) {
|
||||
Tag tag = this.tags.stream().filter(t -> t.getId() == tagId).findFirst().orElse(null);
|
||||
if (tag != null) {
|
||||
this.tags.remove(tag);
|
||||
tag.getTutorials().remove(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package com.yprac.spring.hibernate.onetomany.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import com.yprac.spring.hibernate.onetomany.model.Comment;
|
||||
|
||||
public interface CommentRepository extends JpaRepository<Comment, Long> {
|
||||
List<Comment> findByTutorialId(Long postId);
|
||||
|
||||
@Transactional
|
||||
void deleteByTutorialId(long tutorialId);
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package com.yprac.spring.hibernate.onetomany.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import com.yprac.spring.hibernate.onetomany.model.Tutorial;
|
||||
|
||||
public interface TutorialRepository extends JpaRepository<Tutorial, Long> {
|
||||
List<Tutorial> findByPublished(boolean published);
|
||||
|
||||
List<Tutorial> findByTitleContaining(String title);
|
||||
|
||||
List<Tutorial> findTutorialsByTagsId(Long tagId);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package dev.struchkov.example.portal;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SpringBootApp {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringBootApp.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package dev.struchkov.example.portal.controller;
|
||||
|
||||
import dev.struchkov.example.portal.manager.TutorialManager;
|
||||
import dev.struchkov.example.portal.model.Comment;
|
||||
import dev.struchkov.example.portal.service.CommentService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/api")
|
||||
public class CommentController {
|
||||
|
||||
private final TutorialManager tutorialManager;
|
||||
|
||||
private final CommentService commentService;
|
||||
|
||||
@GetMapping("/tutorials/{tutorialId}/comments")
|
||||
public ResponseEntity<List<Comment>> getAllCommentsByTutorialId(
|
||||
@PathVariable(value = "tutorialId") Long tutorialId
|
||||
) {
|
||||
return ResponseEntity.ok(commentService.getByTutorialId(tutorialId));
|
||||
}
|
||||
|
||||
@GetMapping("/comments/{commentId}")
|
||||
public ResponseEntity<Comment> getCommentsByTutorialId(
|
||||
@PathVariable(value = "commentId") Long commentId
|
||||
) {
|
||||
return ResponseEntity.ok(commentService.getByIdOrThrow(commentId));
|
||||
}
|
||||
|
||||
@PostMapping("/tutorials/{tutorialId}/comments")
|
||||
public ResponseEntity<Comment> createComment(
|
||||
@PathVariable(value = "tutorialId") Long tutorialId,
|
||||
@RequestBody Comment newComment
|
||||
) {
|
||||
return ResponseEntity.ok(tutorialManager.addNewComment(tutorialId, newComment));
|
||||
}
|
||||
|
||||
@PutMapping("/comments/{commentId}")
|
||||
public ResponseEntity<Comment> updateComment(
|
||||
@PathVariable("commentId") Long id,
|
||||
@RequestBody Comment updateComment
|
||||
) {
|
||||
return ResponseEntity.ok(commentService.update(id, updateComment));
|
||||
}
|
||||
|
||||
@DeleteMapping("/comments/{commentId}")
|
||||
public ResponseEntity<HttpStatus> deleteComment(
|
||||
@PathVariable("commentId") Long commentId
|
||||
) {
|
||||
commentService.deleteById(commentId);
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/tutorials/{tutorialId}/comments")
|
||||
public ResponseEntity<List<Comment>> deleteAllCommentsOfTutorial(
|
||||
@PathVariable(value = "tutorialId") Long tutorialId
|
||||
) {
|
||||
commentService.deleteAllByTutorialId(tutorialId);
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package dev.struchkov.example.portal.controller;
|
||||
|
||||
import dev.struchkov.example.portal.manager.TutorialManager;
|
||||
import dev.struchkov.example.portal.model.Tag;
|
||||
import dev.struchkov.example.portal.model.Tutorial;
|
||||
import dev.struchkov.example.portal.service.TagService;
|
||||
import dev.struchkov.example.portal.service.TutorialService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
@RequiredArgsConstructor
|
||||
public class TagController {
|
||||
|
||||
private final TutorialManager tutorialManager;
|
||||
|
||||
private final TutorialService tutorialService;
|
||||
private final TagService tagService;
|
||||
|
||||
@GetMapping("/tags")
|
||||
public ResponseEntity<List<Tag>> getAllTags() {
|
||||
final List<Tag> tags = tagService.getAll();
|
||||
if (tags.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
|
||||
}
|
||||
return ResponseEntity.ok(tags);
|
||||
}
|
||||
|
||||
@GetMapping("/tutorials/{tutorialId}/tags")
|
||||
public ResponseEntity<List<Tag>> getAllTagsByTutorialId(@PathVariable(value = "tutorialId") Long tutorialId) {
|
||||
final List<Tag> tags = tagService.getAllByTutorialId(tutorialId);
|
||||
|
||||
if (tags.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(tags);
|
||||
}
|
||||
|
||||
@GetMapping("/tags/{tagId}")
|
||||
public ResponseEntity<Tag> getTagsById(@PathVariable(value = "tagId") Long tagId) {
|
||||
return ResponseEntity.ok(tagService.getByIdOrThrow(tagId));
|
||||
}
|
||||
|
||||
@GetMapping("/tags/{tagId}/tutorials")
|
||||
public ResponseEntity<List<Tutorial>> getAllTutorialsByTagId(@PathVariable(value = "tagId") Long tagId) {
|
||||
final List<Tutorial> tutorials = tutorialService.getAllByTagId(tagId);
|
||||
|
||||
if (tutorials.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(tutorials);
|
||||
}
|
||||
|
||||
@PostMapping("/tutorials/{tutorialId}/tags")
|
||||
public ResponseEntity<Tag> addTagToTutorial(@PathVariable(value = "tutorialId") Long tutorialId, @RequestBody Tag tag) {
|
||||
tutorialManager.addTagToTutorial(tutorialId, tag);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@PutMapping("/tags/{tagId}")
|
||||
public ResponseEntity<Tag> updateTag(@PathVariable("tagId") Long tagId, @RequestBody Tag newTag) {
|
||||
return ResponseEntity.ok(tagService.update(tagId, newTag));
|
||||
}
|
||||
|
||||
@DeleteMapping("/tutorials/{tutorialId}/tags/{tagId}")
|
||||
public ResponseEntity<HttpStatus> deleteTagFromTutorial(
|
||||
@PathVariable(value = "tutorialId") Long tutorialId,
|
||||
@PathVariable(value = "tagId") Long tagId
|
||||
) {
|
||||
tutorialService.deleteTagFromTutorial(tutorialId, tagId);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/tags/{tagId}")
|
||||
public ResponseEntity<HttpStatus> deleteTag(@PathVariable("tagId") Long tagId) {
|
||||
tagService.deleteById(tagId);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package dev.struchkov.example.portal.controller;
|
||||
|
||||
import dev.struchkov.example.portal.model.Tutorial;
|
||||
import dev.struchkov.example.portal.service.TutorialService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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 java.util.List;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/api")
|
||||
@CrossOrigin(origins = "http://localhost:8081")
|
||||
public class TutorialController {
|
||||
|
||||
private final TutorialService tutorialService;
|
||||
|
||||
@GetMapping("/tutorials")
|
||||
public ResponseEntity<List<Tutorial>> getAllTutorials(
|
||||
@RequestParam(required = false) String title
|
||||
) {
|
||||
final List<Tutorial> tutorials;
|
||||
if (title == null) {
|
||||
tutorials = tutorialService.getAll();
|
||||
} else {
|
||||
tutorials = tutorialService.findByTitle(title);
|
||||
}
|
||||
|
||||
if (tutorials.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(tutorials);
|
||||
}
|
||||
|
||||
@GetMapping("/tutorials/{tutorialId}")
|
||||
public ResponseEntity<Tutorial> getTutorialById(
|
||||
@PathVariable("tutorialId") Long tutorialId
|
||||
) {
|
||||
return ResponseEntity.ok(tutorialService.getByIdOrThrow(tutorialId));
|
||||
}
|
||||
|
||||
@PostMapping("/tutorials")
|
||||
public ResponseEntity<Tutorial> createTutorial(
|
||||
@RequestBody Tutorial tutorial
|
||||
) {
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.CREATED)
|
||||
.body(tutorialService.create(tutorial));
|
||||
}
|
||||
|
||||
@PutMapping("/tutorials/{tutorialId}")
|
||||
public ResponseEntity<Tutorial> updateTutorial(
|
||||
@PathVariable("tutorialId") Long tutorialId,
|
||||
@RequestBody Tutorial newTutorial
|
||||
) {
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.OK)
|
||||
.body(tutorialService.update(tutorialId, newTutorial));
|
||||
}
|
||||
|
||||
@DeleteMapping("/tutorials/{tutorialId}")
|
||||
public ResponseEntity<HttpStatus> deleteTutorial(
|
||||
@PathVariable("tutorialId") Long tutorialId
|
||||
) {
|
||||
tutorialService.deleteById(tutorialId);
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/tutorials")
|
||||
public ResponseEntity<HttpStatus> deleteAllTutorials() {
|
||||
tutorialService.deleteAll();
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
@GetMapping("/tutorials/published")
|
||||
public ResponseEntity<List<Tutorial>> findByPublished() {
|
||||
final List<Tutorial> tutorials = tutorialService.findByPublished(true);
|
||||
|
||||
if (tutorials.isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(tutorials);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package dev.struchkov.example.portal.exception;
|
||||
|
||||
import dev.struchkov.example.portal.model.ErrorMessage;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class ControllerExceptionHandler {
|
||||
|
||||
@ExceptionHandler(NotFoundException.class)
|
||||
public ResponseEntity<ErrorMessage> resourceNotFoundException(NotFoundException ex, WebRequest request) {
|
||||
final ErrorMessage message = new ErrorMessage(
|
||||
NOT_FOUND.value(),
|
||||
new Date(),
|
||||
ex.getMessage(),
|
||||
request.getDescription(false)
|
||||
);
|
||||
|
||||
return ResponseEntity.status(NOT_FOUND).body(message);
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ErrorMessage> globalExceptionHandler(Exception ex, WebRequest request) {
|
||||
ErrorMessage message = new ErrorMessage(
|
||||
INTERNAL_SERVER_ERROR.value(),
|
||||
new Date(),
|
||||
ex.getMessage(),
|
||||
request.getDescription(false)
|
||||
);
|
||||
|
||||
return ResponseEntity.status(INTERNAL_SERVER_ERROR).body(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package dev.struchkov.example.portal.exception;
|
||||
|
||||
public class NotFoundException extends RuntimeException {
|
||||
|
||||
public NotFoundException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package dev.struchkov.example.portal.manager;
|
||||
|
||||
import dev.struchkov.example.portal.model.Comment;
|
||||
import dev.struchkov.example.portal.model.Tag;
|
||||
import dev.struchkov.example.portal.model.Tutorial;
|
||||
import dev.struchkov.example.portal.service.CommentService;
|
||||
import dev.struchkov.example.portal.service.TagService;
|
||||
import dev.struchkov.example.portal.service.TutorialService;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TutorialManager {
|
||||
|
||||
private final TutorialService tutorialService;
|
||||
private final CommentService commentService;
|
||||
private final TagService tagService;
|
||||
|
||||
@Transactional
|
||||
public void addTagToTutorial(Long tutorialId, Tag tag) {
|
||||
final Tutorial tutorial = tutorialService.getByIdOrThrow(tutorialId);
|
||||
final Tag newTag = getOrCreateTag(tag);
|
||||
tutorial.addTag(newTag);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Comment addNewComment(@NonNull Long tutorialId, @NonNull Comment newComment) {
|
||||
final Tutorial tutorial = tutorialService.getByIdOrThrow(tutorialId);
|
||||
newComment.setTutorial(tutorial);
|
||||
return commentService.create(newComment);
|
||||
}
|
||||
|
||||
private Tag getOrCreateTag(Tag newTag) {
|
||||
if (newTag.getId() != null) {
|
||||
return tagService.getByIdOrThrow(newTag.getId());
|
||||
} else {
|
||||
return tagService.create(newTag);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package dev.struchkov.example.portal.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
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.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@Table(name = "comments")
|
||||
public class Comment {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "comment_generator")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "content")
|
||||
private String content;
|
||||
|
||||
@JsonIgnore
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "tutorial_id", nullable = false)
|
||||
// @OnDelete(action = OnDeleteAction.CASCADE)
|
||||
private Tutorial tutorial;
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package dev.struchkov.example.portal.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class ErrorMessage {
|
||||
|
||||
private int statusCode;
|
||||
private Date timestamp;
|
||||
private String message;
|
||||
private String description;
|
||||
|
||||
public ErrorMessage(int statusCode, Date timestamp, String message, String description) {
|
||||
this.statusCode = statusCode;
|
||||
this.timestamp = timestamp;
|
||||
this.message = message;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public Date getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
}
|
35
src/main/java/dev/struchkov/example/portal/model/Tag.java
Normal file
35
src/main/java/dev/struchkov/example/portal/model/Tag.java
Normal file
@ -0,0 +1,35 @@
|
||||
package dev.struchkov.example.portal.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
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.ManyToMany;
|
||||
import javax.persistence.Table;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@Table(name = "tags")
|
||||
public class Tag {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@JsonIgnore
|
||||
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "tags")
|
||||
private Set<Tutorial> tutorials = new HashSet<>();
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package dev.struchkov.example.portal.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
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.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
@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;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
||||
@JoinColumn(name = "tutorial_id")
|
||||
private Set<Comment> comments = new HashSet<>();
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
|
||||
@JoinTable(
|
||||
name = "tutorial_tags",
|
||||
joinColumns = @JoinColumn(name = "tutorial_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "tag_id")
|
||||
)
|
||||
private Set<Tag> tags = new HashSet<>();
|
||||
|
||||
public void addTag(Tag tag) {
|
||||
this.tags.add(tag);
|
||||
tag.getTutorials().add(this);
|
||||
}
|
||||
|
||||
public void removeTag(Long tagId) {
|
||||
tags.stream()
|
||||
.filter(tag -> tag.getId().equals(tagId))
|
||||
.findFirst()
|
||||
.ifPresent(foundTag -> {
|
||||
foundTag.getTutorials().remove(this);
|
||||
tags.remove(foundTag);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package dev.struchkov.example.portal.repository;
|
||||
|
||||
import dev.struchkov.example.portal.model.Comment;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CommentRepository extends JpaRepository<Comment, Long> {
|
||||
|
||||
List<Comment> findByTutorialId(Long postId);
|
||||
|
||||
void deleteByTutorialId(Long tutorialId);
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package com.yprac.spring.hibernate.onetomany.repository;
|
||||
package dev.struchkov.example.portal.repository;
|
||||
|
||||
import com.yprac.spring.hibernate.onetomany.model.Tag;
|
||||
import dev.struchkov.example.portal.model.Tag;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
@ -8,4 +8,5 @@ import java.util.List;
|
||||
public interface TagRepository extends JpaRepository<Tag, Long> {
|
||||
|
||||
List<Tag> findTagsByTutorialsId(Long tutorialId);
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package dev.struchkov.example.portal.repository;
|
||||
|
||||
import dev.struchkov.example.portal.model.Tutorial;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TutorialRepository extends JpaRepository<Tutorial, Long> {
|
||||
|
||||
List<Tutorial> findByPublished(boolean published);
|
||||
|
||||
List<Tutorial> findByTitleContaining(String title);
|
||||
|
||||
List<Tutorial> findTutorialsByTagsId(Long tagId);
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package dev.struchkov.example.portal.service;
|
||||
|
||||
import dev.struchkov.example.portal.exception.NotFoundException;
|
||||
import dev.struchkov.example.portal.model.Comment;
|
||||
import dev.struchkov.example.portal.repository.CommentRepository;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CommentService {
|
||||
|
||||
private final CommentRepository repository;
|
||||
|
||||
public Comment create(@NonNull Comment newComment) {
|
||||
newComment.setId(null);
|
||||
return repository.save(newComment);
|
||||
}
|
||||
|
||||
public Comment update(@NonNull Long commentId, @NonNull Comment updateComment) {
|
||||
final Comment comment = repository.findById(commentId)
|
||||
.orElseThrow(() -> new NotFoundException("Not found Comment with id = " + commentId));
|
||||
|
||||
comment.setContent(updateComment.getContent());
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
public List<Comment> getByTutorialId(@NonNull Long tutorialId) {
|
||||
return repository.findByTutorialId(tutorialId);
|
||||
}
|
||||
|
||||
public Comment getByIdOrThrow(@NonNull Long commentId) {
|
||||
return repository.findById(commentId)
|
||||
.orElseThrow(() -> new NotFoundException("Not found Comment with id = " + commentId));
|
||||
}
|
||||
|
||||
public void deleteById(@NonNull Long commentId) {
|
||||
repository.deleteById(commentId);
|
||||
}
|
||||
|
||||
public void deleteAllByTutorialId(@NonNull Long tutorialId) {
|
||||
repository.deleteByTutorialId(tutorialId);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package dev.struchkov.example.portal.service;
|
||||
|
||||
import dev.struchkov.example.portal.exception.NotFoundException;
|
||||
import dev.struchkov.example.portal.model.Tag;
|
||||
import dev.struchkov.example.portal.repository.TagRepository;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TagService {
|
||||
|
||||
private final TagRepository repository;
|
||||
|
||||
public Tag create(@NonNull Tag newTag) {
|
||||
return repository.save(newTag);
|
||||
}
|
||||
|
||||
public Tag update(@NonNull Long tagId, @NonNull Tag newTag) {
|
||||
final Tag oldTag = repository.findById(tagId)
|
||||
.orElseThrow(() -> new NotFoundException("Not found Tag with id = " + tagId));
|
||||
|
||||
oldTag.setName(newTag.getName());
|
||||
|
||||
return oldTag;
|
||||
}
|
||||
|
||||
public List<Tag> getAll() {
|
||||
return repository.findAll();
|
||||
}
|
||||
|
||||
public List<Tag> getAllByTutorialId(@NonNull Long tutorialId) {
|
||||
return repository.findTagsByTutorialsId(tutorialId);
|
||||
}
|
||||
|
||||
public Tag getByIdOrThrow(@NonNull Long tagId) {
|
||||
return repository.findById(tagId)
|
||||
.orElseThrow(() -> new NotFoundException("Not found Tag with id = " + tagId));
|
||||
}
|
||||
|
||||
public void deleteById(@NonNull Long tagId) {
|
||||
repository.deleteById(tagId);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package dev.struchkov.example.portal.service;
|
||||
|
||||
import dev.struchkov.example.portal.exception.NotFoundException;
|
||||
import dev.struchkov.example.portal.model.Tutorial;
|
||||
import dev.struchkov.example.portal.repository.TutorialRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TutorialService {
|
||||
|
||||
private final TutorialRepository repository;
|
||||
|
||||
public Tutorial getByIdOrThrow(Long tutorialId) {
|
||||
return repository.findById(tutorialId)
|
||||
.orElseThrow(() -> new NotFoundException("Not found Tutorial with id = " + tutorialId));
|
||||
}
|
||||
|
||||
public List<Tutorial> getAll() {
|
||||
return repository.findAll();
|
||||
}
|
||||
|
||||
public List<Tutorial> findByTitle(String title) {
|
||||
return repository.findByTitleContaining(title);
|
||||
}
|
||||
|
||||
public Tutorial create(Tutorial tutorial) {
|
||||
tutorial.setId(null);
|
||||
tutorial.setPublished(true);
|
||||
return repository.save(tutorial);
|
||||
}
|
||||
|
||||
public Tutorial update(Long tutorialId, Tutorial newTutorial) {
|
||||
final Tutorial oldTutorial = repository.findById(tutorialId)
|
||||
.orElseThrow(() -> new NotFoundException("Not found Tutorial with id = " + tutorialId));
|
||||
|
||||
oldTutorial.setDescription(newTutorial.getDescription());
|
||||
oldTutorial.setTitle(newTutorial.getTitle());
|
||||
oldTutorial.setPublished(newTutorial.isPublished());
|
||||
|
||||
return oldTutorial;
|
||||
}
|
||||
|
||||
public void deleteById(Long tutorialId) {
|
||||
repository.deleteById(tutorialId);
|
||||
}
|
||||
|
||||
public void deleteAll() {
|
||||
repository.deleteAll();
|
||||
}
|
||||
|
||||
public List<Tutorial> findByPublished(boolean published) {
|
||||
return repository.findByPublished(published);
|
||||
}
|
||||
|
||||
public List<Tutorial> getAllByTagId(Long tagId) {
|
||||
return repository.findTutorialsByTagsId(tagId);
|
||||
}
|
||||
|
||||
public void deleteTagFromTutorial(Long tutorialId, Long tagId) {
|
||||
repository.findById(tutorialId)
|
||||
.orElseThrow(() -> new NotFoundException("Not found Tutorial with id = " + tutorialId))
|
||||
.removeTag(tagId);
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
|
||||
spring.datasource.url=
|
||||
spring.datasource.username=
|
||||
spring.datasource.password=
|
||||
|
||||
spring.jpa.show-sql=true
|
||||
#spring.jpa.properties.hibernate.format_sql=true
|
||||
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.PostgreSQL9Dialect
|
||||
#spring.jpa.generate.ddl=true
|
||||
spring.jpa.hibernate.ddl-auto=none
|
||||
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
|
16
src/main/resources/application.yml
Normal file
16
src/main/resources/application.yml
Normal file
@ -0,0 +1,16 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:postgresql://localhost/test
|
||||
username: postgres
|
||||
password: 121314Ma
|
||||
driver-class-name: org.postgresql.Driver
|
||||
jpa:
|
||||
show-sql: true
|
||||
hibernate:
|
||||
ddl-auto: update
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.PostgreSQL10Dialect
|
||||
jdbc:
|
||||
lob:
|
||||
non_contextual_creation: true
|
@ -1,13 +0,0 @@
|
||||
package com.yprac.spring.hibernate.onetomany;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class SpringBootOneToManyApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user