Aggregate function on related entities in jpa + Spring - 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

Related

Can't update child entity spring boot with JPA

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

I have a problem with "You may also like" feature in Spring boot JPA

I want to create a simple "You may also like" feature for a blog.
There are posts and each of them has one or more tags. Also a tag can contain many posts. I want to implement the feature where you open a post and the posts, which have similar tags, are recommended to you.
So i created 3 entities:
Post.java
#Entity
#Table
public class Post {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String text;
#Column
private String author;
#OneToMany(mappedBy = "post")
Set<PostTags> postTags;
public Post(){}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Set<PostTags> getPostTags() {
return postTags;
}
public void setPostTags(Set<PostTags> postTags) {
this.postTags = postTags;
}
}
Tags.java
#Entity
#Table
public class Tags {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#Column
private String name;
#OneToMany(mappedBy = "tag")
Set<PostTags> postTags;
public Set<PostTags> getPostTags() {
return postTags;
}
public void setPostTags(Set<PostTags> postTags) {
this.postTags = postTags;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
PostTags.java
#Entity
public class PostTags {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column
private Long id;
#ManyToOne
#JoinColumn(name = "post_id")
private Post post;
#ManyToOne
#JoinColumn(name = "tag_id")
private Tags tag;
public PostTags(){}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
public Tags getTag() {
return tag;
}
public void setTag(Tags tag) {
this.tag = tag;
}
}
And repositories:
#Repository
public interface PostTagsRepository extends JpaRepository<PostTags, Long> {
#Query("select p.post from PostTags p where p.tag.id IN :tagIds")
Set<Post> findPostsbyTagIds (List<Long> tagIds);
}
#Repository
public interface PostRepository extends JpaRepository<Post, Long> {
#Query("select p from Post p where p.author = :author")
Set<Post> findPostsByAuthor(String author);
}
I managed to create this feature in an amateurish way, but better than nothing. I piled up everything in one method just to test it:
#GetMapping("/posts")
public Set<Post> showRecommendedPosts(){
//Imitate post id
long postId = 1;
Post postFound = postRepository.findById(postId).get();
Set<PostTags> postTags = postFound.getPostTags();
List<Long> listTagIds = new ArrayList<>();
//extract ids of the tags from the post
for(PostTags tag : postTags){
listTagIds.add(tag.getTag().getId());
}
//find posts by Author
Set<Post> postsByAuthor = postRepository.findPostsByAuthor(postFound.getAuthor());
//find posts by Tags
Set<Post> postsByTagIds = postTagsRepository.findPostsbyTagIds(listTagIds);
//We combine both sets
Set<Post> recommendedPosts = new HashSet<>(postsByAuthor);
recommendedPosts.addAll(postsByTagIds);
recommendedPosts.remove(postFound);
return recommendedPosts;
}
But this works only if i manually add data to "post_tags" table in the db like this:
Here is my question, i don't know how to add multiple tags to a post in Spring. Because it would be something like this:
PostTags newPostTag1 = new PostTags();
newPostTag.setPost(post1);
newPostTag.setTag(tag1);
PostTags newPostTag2 = new PostTags();
newPostTag2.setPost(post1);
newPostTag2.setTag(tag2);
PostTags newPostTag3 = new PostTags();
newPostTag3.setPost(post1);
newPostTag3.setTag(tag3);
And so on...
Therefore, it's not an option. So how can i save tags correctly? Or have my entities been created incorrectly? What is my mistake? Thank you!
I am not sure to understand the idea behind Tag being an Entity.
How I see it is you use the postTags and change it to tags. This tags would be a Set of an enum if you want to restrict the user or a Set of String other way. After that, I would add an endpoint that return post based on a tag or a list of tags for your You may also like feature. This endpoint just make a request to the database (find posts where tags contains givenTag max 10). At the end, you only have one entity :
#Entity
#Table
public class Post {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String text;
#Column
private String author;
#Column
#Convert(converter = StringListConverter.class)
Set<String> tags;
// ...
}
Converter implementation here

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