Issue
Here is the Background information of my Restful API. I am trying to create Many to One Relation between Comments and Category Where Many Comments belong to One Category. If I am trying to call the Get All Categories -> I am getting the correct response. Also, If I am trying to call the Comments API where I am passing the CategoryID as a Path Variable I am able to retrive the correct response as well. However, When I am trying to consolidate the API, it is repeating the comments even though they are not belong to category one For instance.
Category Entity
package com.mshakir.restfulwebservice.dto.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;
@Entity
@Table(name = "categories")
`
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "category_generator")
@SequenceGenerator(name = "category_generator", sequenceName = "category_sequence", initialValue = 20000, allocationSize = 1)
@Column(name = "category_id")
private Long id;
@Column(name = "category_name", nullable = false, length = 32)
private String categoryName;
@Column(name = "category_desc", nullable = false, length = 64)
private String categoryDescription;
@Column(name = "is_published", nullable = false, length = 32)
private boolean isPublished = true;
@Transient
private String publishedMessage;
private String isActive;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public String getCategoryDescription() {
return categoryDescription;
}
public void setCategoryDescription(String categoryDescription) {
this.categoryDescription = categoryDescription;
}
public boolean getIsPublished() {
return isPublished;
}
public void setIsPublished(boolean isPublished) {
this.isPublished = isPublished;
}
public String getPublishedMessage() {
String message = "";
if (isPublished) {
message = "Category Id has been published to the Website";
} else {
message = "Category Id has not been published to the Website";
}
return message;
}
public String getIsActive() {
return isActive;
}
public void setIsActive(String isActive) {
this.isActive = isActive;
}
public void setPublishedMessage(String publishedMessage) {
this.publishedMessage = publishedMessage;
}
@Override
public String toString() {
return "Category [id=" + id + ", categoryName=" + categoryName + ", categoryDescription=" + categoryDescription
+ " , isActive=" + isActive + "]";
}
}`
Comment Entity
package com.mshakir.restfulwebservice.dto.entity;
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.SequenceGenerator;
import javax.persistence.Table;
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")
@SequenceGenerator(name = "comment_generator", sequenceName = "comment_sequence", initialValue = 15001, allocationSize = 1)
@Column(name = "comment_id")
private Long id;
@Lob
@Column(name = "comment_content")
private String content;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "category_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
@JsonIgnore
private Category category;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
@Override
public String toString() {
return "Comment [id=" + id + ", content=" + content + ", category=" + category + "]";
}
}`
Category Controller with Implement Service
`
@RestController
@RequestMapping("/categories")
public class CategoryController {
public static Logger log = LoggerFactory.getLogger(CategoryController.class);
@Autowired
CategoryRepository categoryRepository;
@Autowired
CommentRepository commentRepository;
private final CategoryServiceImpl categoryServiceImpl;
@Autowired
public CategoryController(CategoryServiceImpl categoryServiceImpl) {
this.categoryServiceImpl = categoryServiceImpl;
}
@PostMapping("/create-new-category")
public ResponseEntity<Category> createCategoryRecord(@RequestBody Category category) throws Exception {
log.info("Category Controller: createCategoryRecord()");
Category createNewCategory = categoryServiceImpl.createNewCategory(category);
HttpStatus statusCreated201 = HttpStatus.CREATED;
return new ResponseEntity<Category>(createNewCategory, statusCreated201);
}
}`
Implement Service Method For this Post Category
`
@Service
public class CategoryServiceImpl implements CategoryService {
private CategoryRepository categoryRepository;
@Autowired
public CategoryServiceImpl(CategoryRepository categoryRepository) {
this.categoryRepository = categoryRepository;
}
@Override
public Category createNewCategory(Category category) {
String categoryName = category.getCategoryName();
Optional<Category> findByCategoryName = categoryRepository.findByCategoryName(categoryName);
if (findByCategoryName.isPresent()) {
throw new InvalidDataException("Category already exists in the Database");
}
Category newCategory = categoryRepository.save(category);
return newCategory;
}
}`
Show All Categories
`
@GetMapping("/show-all-categories")
public ResponseEntity<List>
showAllCategoriesAll(@RequestParam(required = false) String categoryName) {
List listOfCategories = new ArrayList();
if (Utils.isEmptyString(categoryName)) {
categoryRepository.findAll().forEach(category -> {
listOfCategories.add(category);
});
}
else {
List<Category> findByCategoryNameContaining = categoryRepository.findByCategoryNameContaining(categoryName);
findByCategoryNameContaining.forEach(category -> listOfCategories.add(category));
}
if (listOfCategories.isEmpty()) {
return new ResponseEntity<List<Category>>(listOfCategories, HttpStatus.OK);
}
return new ResponseEntity<List<Category>>(listOfCategories, HttpStatus.OK);
}`
Get List of Comments Based on Category ID
`
@GetMapping("/categories/{categoryId}/comments")
public ResponseEntity<List>
getAllCommentsByTutorialId(@PathVariable(value = "categoryId") Long categoryId) {
if (!categoryRepository.existsById(categoryId)) {
throw new ResourceNotFoundException("Not found Tutorial with id = " + categoryId);
}
List<Comment> comments = commentRepository.findByCategoryId(categoryId);
List<Comment> findAll = commentRepository.findAll();
for (Comment comment: findAll) {
System.out.println(comment.getCategory().getCategoryName());
System.out.println(comment.getCategory().getCategoryDescription());
}
System.out.println(findAll);
return new ResponseEntity<>(comments, HttpStatus.OK);
}`
Consolidated API
`
@RestController
@RequestMapping("/list-of-categories")
public class ListOfCategory {
@Autowired
CategoryRepository categoryRepository;
@Autowired
CommentRepository commentRepository;
@GetMapping("/show-all")
public ResponseEntity<List<CategoryCommentResponse>> showAll() {
List<CommentInfo> listOfComments = new ArrayList<CommentInfo>();
List<CategoryCommentResponse> listofAll = new ArrayList<CategoryCommentResponse>();
List<Category> categoryList = categoryRepository.findAll();
System.out.println(categoryList);
for (Category category : categoryList) {
CategoryCommentResponse response = new CategoryCommentResponse();
Long categoryId = category.getId();
response.setCategoryId(categoryId);
response.setCategoryDescription(category.getCategoryDescription());
response.setCategoryName(category.getCategoryName());
List<Comment> listOfCommentsByCategoryId = commentRepository.findByCategoryId(categoryId);
for (Comment comment : listOfCommentsByCategoryId) {
CommentInfo commentInfo = new CommentInfo();
Long commentId = comment.getId();
commentInfo.setId(commentId);
commentInfo.setContent(comment.getContent());
System.out.println(commentInfo);
listOfComments.add(commentInfo);
response.setComments(listOfComments);
}
listofAll.add(response);
}
return new ResponseEntity<List<CategoryCommentResponse>>(listofAll, HttpStatus.OK);
}
}`
Please find attached Screenshot to review as well. Thank you
List Of Comments based on Category Id
Solution
Error is a logic flaw in your showAll method. You instantiate and build a single listOfComments List and populate it with comments as you process categories, and use this same list for all categories. As it runs, the list will contain all comments from them all.
Each Category needs its own independent list:
public ResponseEntity<List<CategoryCommentResponse>> showAll() {
..
List<Comment> listOfCommentsByCategoryId =commentRepository.findByCategoryId(categoryId);
List<CommentInfo> listOfCommentInfos = new ArrayList<CommentInfo>();
for (Comment comment : listOfCommentsByCategoryId) {
CommentInfo commentInfo = new CommentInfo();
commentInfo.setId(comment.getId());
commentInfo.setContent(comment.getContent());
System.out.println(commentInfo);
listOfCommentInfos.add(commentInfo);
response.setComments(listOfCommentInfos);
}
listofAll.add(response);
}
return new ResponseEntity<List<CategoryCommentResponse>>(listofAll, HttpStatus.OK);
}
Answered By - Chris
Answer Checked By - Katrina (JavaFixing Volunteer)