Can't update child entity spring boot with JPA - spring-boot

when i request to update my parent and child table throw put request (send data through JSON) parent table is update and in child table new extra row create with the same parent id rather than update row.
#Entity
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String title;
#OneToMany(mappedBy = "book",cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JsonManagedReference
private List<Author> author;
#OneToOne(cascade = CascadeType.ALL)
#JsonManagedReference
private BookImage bookImage;
// Getters and Setter
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
#Entity
public class Author{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String firstname;
private String lastname;
private String language;
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JsonBackReference
private Book book;
// Getters and Setter
#Entity
public class BookImage{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
private String type;
private Long size;
#Lob
private Blob image;
#OneToOne(mappedBy = "bookImage",cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JsonBackReference
private Book book;
// Getters and Setter
#PutMapping("/book/{id}")
public ResponseEntity<Book> updateBook(#RequestParam("file") MultipartFile file,
#RequestParam("book") String bookData, #PathVariable("id") int id) {
try {
Book book = this.objectMapper.readValue(bookData, Book.class);
// Create Book Image
BookImage bookImage = new BookImage();
bookImage.setId(id);
bookImage.setName(file.getOriginalFilename());
bookImage.setType(file.getContentType());
bookImage.setSize(file.getSize());
bookImage.setImage(new SerialBlob(file.getBytes()));
book.setBookImage(bookImage);
book.setId(id);
this.bookService.updateBook(book);
return ResponseEntity.of(Optional.of(book));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
#Component
public class BookService
{
#Autowired
private BookRepository bookRepository;
public List<Book> getAllBooks(){
return this.bookRepository.findAll();
}
public Book getBookById(int id) {
Book book = null;
try {
book = this.bookRepository.getBookById(id);
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return book;
}
public Book addBook(Book book) {
return this.bookRepository.save(book);
}
public void deleteBook(int id) {
this.bookRepository.deleteById(id);
}
public void updateBook(Book book) {
this.bookRepository.save(book);
}
}
Table Data After POST
now put with book id request
after put table data
Create 2 new raw rather than update raw...
Put request with book id 152 update book and book_image data but in author table create a 2 extra new row rather than update the data
i want to update the author table data

Related

Spring Controller Returns Object Incompletely

There are three classes (Course, Lesson, User).
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "usr")
#Data
public class User extends RepresentationModel<User> implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstname;
private String lastname;
private String username;
private String password;
#ElementCollection(targetClass = ERole.class, fetch = FetchType.EAGER)
#CollectionTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"))
#Enumerated(EnumType.STRING)
private Set<ERole> roles;
}
#Data
#Entity
#NoArgsConstructor
public class Lesson extends RepresentationModel<Lesson> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String startTime;
private String endTime;
private String dayOfWeek;
#ManyToOne
private User teacher;
}
#EqualsAndHashCode(callSuper = true)
#Data
#Entity
public class Course extends RepresentationModel<Course> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Date startDate;
private Date endDate;
private String name;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<User> teachers;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<User> students;
private String description;
#ManyToMany(cascade = CascadeType.ALL)
private Set<Lesson> lessons;
}
And also RestController (CoursesController). When accessing the server at /courses, I get the correct server response with all fields
.
#RestController
#RequestMapping("/courses")
public class CoursesController {
private final CourseService courseService;
private final UserService userService;
private final LessonService lessonService;
#Autowired
public CoursesController(CourseService courseService, UserService userService, LessonService lessonService) {
this.courseService = courseService;
this.userService = userService;
this.lessonService = lessonService;
}
#GetMapping
#Operation(
summary = "getAllCourses",
description = "Returns all available courses"
)
public ResponseEntity<Page<Course>> getAllCourses(#PageableDefault(sort = "id", size = 5) Pageable pageable) {
try {
Page<Course> coursePage = courseService.findAll(pageable);
for (Course course : coursePage.getContent())
course.add(linkTo(methodOn(CoursesController.class).getCourse(course.getId().toString())).withSelfRel());
return ResponseEntity.ok(courseService.findAll(pageable));
}
catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
#GetMapping("/{course-id}")
#Operation(
summary = "getCourse",
description = "Returns course by ID"
)
public ResponseEntity<Course> getCourse(#PathVariable ("course-id") String courseId) {
try {
Course course = courseService.getCourseById(courseId);
course.add(linkTo(methodOn(CoursesController.class).getCourse(courseId)).withSelfRel());
return ResponseEntity.ok(course);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}
Why, when requesting a course by ID (GET /courses/{id}), does Spring return an incomplete object (despite the fact that I manually added several teachers, students and lessons)?
I need to get all the fields of my object.
My CourseRepository below.
#Repository
#Transactional
public interface CourseRepository extends JpaRepository<Course, Long> {
}
My CourseService below.
#Service
public class CourseService {
private final CourseRepository courseRepository;
private final LessonRepository lessonRepository;
private final UserRepository userRepository;
#Autowired
public CourseService(CourseRepository courseRepository, LessonRepository lessonRepository, UserRepository userRepository) {
this.courseRepository = courseRepository;
this.lessonRepository = lessonRepository;
this.userRepository = userRepository;
}
public Page<Course> findAll(Pageable pageable) {
return courseRepository.findAll(pageable);
}
public Course createCourse(CourseDto courseDto) {
Course course = new Course(courseDto.getStartDate(), courseDto.getEndDate(), courseDto.getName(), courseDto.getDescription());
return courseRepository.saveAndFlush(course);
}
public Optional<Course> getCourseById(String id) {
return courseRepository.findById(Long.parseLong(id));
}
public Course updateCourse(CourseDto courseDto, String id) {
Course course = courseRepository.findById(Long.parseLong(id)).get();
course.setStartDate(courseDto.getStartDate());
course.setEndDate(courseDto.getEndDate());
course.setName(courseDto.getName());
course.setDescription(courseDto.getDescription());
return courseRepository.saveAndFlush(course);
}
public Page<Lesson> getLessonsByCourse(String courseId, Pageable pageable) {
Course course = courseRepository.findById(Long.parseLong(courseId)).get();
return new PageImpl<>(new ArrayList<>(course.getLessons()), pageable, course.getLessons().size());
}
public Course addLesson(String courseId, LessonDto lessonDto) {
Course course = courseRepository.findById(Long.parseLong(courseId)).get();
Lesson lesson = new Lesson();
lesson.setStartTime(lessonDto.getStartTime());
lesson.setEndTime(lessonDto.getFinishTime());
lesson.setDayOfWeek(lessonDto.getDayOfWeek());
lesson.setTeacher(userRepository.getUserById(lessonDto.getTeacherId()));
lessonRepository.saveAndFlush(lesson);
System.out.println(lesson);
course.getLessons().add(lesson);
return courseRepository.saveAndFlush(course);
}
public void deleteCourse(String id) {
courseRepository.deleteById(Long.parseLong(id));
}
}
Which I would (or might) expect as well. I would links to be generated for those additional relationshps (at least normally with Spring Data RESt handling this is what would happen). I wonder what happens if you ditch the RepresentationModel from your JPA model and just expose Course then. As stated you don't really want your JPA and HATEOAS stuff to be intertwined. You want to have a specialized projection/dto to expose. WHy does it work for your findAll. well you aren't adding links to it (although you think it does but your findAll executes twice!).
Removed RepresentationModel from User class.
Thx to #M.Deinum

Aggregate function on related entities in jpa + Spring

I want implementation of method should to find all items,
that have a rating lower than passed as an argument. Using reviews associated with each item to calculate item rating.
Following are my classes
#Entity
public class Item {
#Id
#GeneratedValue
private Long id;
#Column(length = 100)
#NotEmpty
private String title;
#Column(length = 200)
private String description;
#OneToMany(mappedBy = "item", cascade = CascadeType.ALL, orphanRemoval = true)
Set<Review> reviews = new HashSet<>();
public Item() {
}
public Item(String title, String description) {
this.title = title;
this.description = description;
}
public Long getId() {
return id;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public Set<Review> getReviews() {
return reviews;
}
}
#Entity
public class Review {
#Id
#GeneratedValue
private Long id;
#Min(0)
#Max(10)
private Integer rating;
#Length(max=200)
private String comment;
#ManyToOne(optional = false)
private Item item;
#ManyToOne(optional = false)
private User author;
}
#Repository
#Transactional
public class ItemRepository {
#PersistenceContext
EntityManager entityManager;
public List<Item> findItemsWithAverageRatingLowerThan(Integer rating) {
return new ArrayList<>();
}
I want to find all the items who has average rating of supplied parameter.
Average of rating should be calculated from set of reviews present in Item entity
You can have a look on the following url:
JPA / Hibernate One to One Mapping Example with Spring Boot

How to fix jpa one to many

Need to fetch data from one table to another.I performed jpa one to many mapping. But id cannot fetched. Where is my mistake?
I have tried mapping using one to many and many to one concepts but can't able to fetch data from one table to another
User.java
#Entity
#Table(name = "users")
public class User implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "User_ID")
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
private String id;
private String firstName;
private String lastName;
private Long phoneNumber;
#NotNull(message="Password is compulsory")
#Email(message = "Email is invalid")
private String email;
private String password;
#OneToMany(mappedBy="user", cascade = CascadeType.ALL)
Set<Data> data = new HashSet<Data>();
public Set<Data> getData() {
return data;
}
public void setData(Set<Data> data) {
this.data = data;
}
public User() {
super();
}
Data.java
#Entity
#Table(name = "data")
public class Data implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "DataID")
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
private String id;
#ManyToOne(fetch = FetchType.EAGER,cascade= CascadeType.ALL)
#JoinColumn(name = "User_ID")
private User user;
public Data() {
super();
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
DataController.java
#PostMapping("/data/{userId}")
public Data createData(
#PathVariable(value= "userId") String userId,
#Valid #RequestBody Data data) {
return userRepository.findById(userId).map(user -> {
data.setUser(user);
return dataRepository.save(data);
}).orElseThrow(() -> new ResourceNotFoundException("userId" + userId +
"not found"));
}
Results in no error but can't able to fetch user id

Spring Data JPA: How to fetch all entities of a specific type along with each entity's associated entities?

I have a Post entity
#Entity
public class Post {
#Id
private UUID id;
#NotNull
private String title;
#NotNull
private String content;
#NotNull
private String identifier;
#NotNull
private String category;
#NotNull
#Column(name = "created_at")
private Date createdAt;
#NotNull
#Column(name = "updated_at")
private Date updatedAt;
public Post (){
}
public Post (String title, String content, String category){
this.title = title;
this.content = content;
this.category = category;
}
// rest of the getters and setters
}
And this is my Comment entity:
#Entity
public class Comment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private UUID id;
#NotNull
private String name;
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer identifier;
#NotNull
private String email;
#NotNull
private String content;
#NotNull
#ManyToOne
#JoinColumn(name = "post_id")
private Post postId;
#NotNull
#Column(name = "created_at")
private Date createdAt;
public Comment() {
}
public Comment(String name, String email, String content){
this.name = name;
this.email = email;
this.content = content;
}
}
And this is my post controller:
#RestController
#RequestMapping("/posts")
public class PostController {
private String getIdentifier(String str){
return String.join("-", str.split(" "));
}
#Autowired
private PostService postService;
#RequestMapping(value = "", method = {GET, HEAD})
public List<Post> getAllPosts(){
return postService.getAllPosts();
}
#RequestMapping(value = "", method = {POST, OPTIONS})
public Post addNewPost(#RequestBody Post post){
post.setId(UUID.randomUUID());
post.setIdentifier(this.getIdentifier(post.getTitle()));
post.setCreatedAt(new Date());
post.setUpdatedAt(new Date());
return postService.savePost(post);
}
#RequestMapping(value = "/{id}", method = {GET, HEAD})
public Post getOnePost(#PathVariable UUID id){
return postService.getOne(id);
}
#RequestMapping(value = "/{id}", method = DELETE)
public void deleteOnePost(#PathVariable UUID id){
postService.deleteOnePost(id);
}
}
My question is how do I fetch all the comments for each individual post, whenever I fetch all the posts?
Sorry, I come from a NoSQL background, so this is a bit daunting at first.
What you need to do is to create a bidirectional #OneToMany association from the Post to Comments:
add a field in Post class
#OneToMany(
mappedBy = "postId",
cascade = CascadeType.ALL
)
private List<Comments> comments = new ArrayList<>();
From now on, when you get Post from the database, Comments will be fetched at the same time.

JPA repository null pointer exception for many to one mapping with composite primary key

Post class
one to many mapping
Composite primary key using id
I am getting null pointer exception when I make get request for getting comments
#Entity
#Table(name = "posts")
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Size(max = 100)
#Column(unique = true)
private String title;
#NotNull
#Size(max = 250)
private String description;
#NotNull
#Lob
private String content;
#NotNull
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "posted_at")
private Date postedAt = new Date();
#NotNull
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "last_updated_at")
private Date lastUpdatedAt = new Date();
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "post")
private Set<Comment> comments = new HashSet<>();
public Post() {
}
public Post(String title, String description, String content) {
this.title = title;
this.description = description;
this.content = content;
}
//getters and setters
}
Comment class
many to one mapping with composite primary keys using #Idclass
#Entity
#IdClass(CommentId.class)
#Table(name = "comments")
public class Comment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Lob
private String text;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "post_id", nullable = false)
private Post post;
public Comment() {
}
public Comment(String text) {
this.text = text;
}
//getters and setters
}
Id class
CommentId
public class CommentId implements Serializable {
private static final long serialVersionUID = 1L;
private Post post;
private Long id;
public CommentId(Post post, Long id) {
super();
this.post = post;
this.id = id;
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result+ ((post == null) ? 0 : post.hashCode());
result = prime * result ;
return result;
}
public boolean equals(Object object) {
if (object instanceof CommentId) {
CommentId pk = (CommentId)object;
return id.equals(pk.id) && post == pk.post;
} else {
return false;
}
}
//getters and setters
}
repositories
PostRepository
CommentRepository
#Repository
public interface PostRepository extends JpaRepository<Post, Long> {
}
#Repository
public interface CommentRepository extends JpaRepository<Comment, Long>
{
}
Controller class get request and I am using mysql database
#RestController
#RequestMapping("/demo")
public class Controller {
#Autowired
PostRepository ps;
CommentRepository cs;
#GetMapping("/post")
public List<Post> getAll(){
return ps.findAll();
}
#GetMapping("/comment")
public List<Comment> getAllcom(){
return cs.findAll();
}
}

Resources