How to write Query find Book by Category? - spring-data-jpa

Suppose I have 2 tables: Book and Category.
I use Spring Data JPA
I want to get a list of books by category and view it on Thymeleaf as Category Menu
-> how should I write Query?
below is my way but it has no data
Book
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
#Column(length = 100)
private int isbn;
private String description;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "categoryid")
private Category category;
Category
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotEmpty(message="Thể loại không được trống")
private String name;
#NotEmpty(message="Mã không được trống")
private String code;
#OneToMany(mappedBy = "category")
private List<Book> books = new ArrayList<>();
BookRepo
#Query(value = "SELECT b.* FROM book b INNER JOIN category c on b.name = c.id WHERE c.id = ?1", nativeQuery = true)
List<Book> findBookByCategory(Integer categoryId);
Controller
#GetMapping("/{category}")
public String getBookByProduct(#PathVariable("category") Integer category, Model model, HttpSession session) {
List<Book> bookList = bookService.findBookByCategory(category);
if(bookList == null) {
session.setAttribute("message", new MessageResponse("Không có Sách","danger"));
}
for(Book book : bookList ) {
System.out.println(book.toString());
}
model.addAttribute("category", bookList);
return "home/category";
}
View
<h4 class="text-uppercase font-weight-bold mb-3">Thể Loại</h4>
<div class="mt-2 mb-2 pl-2 ">
<div class="custom-control" th:each="category : ${category}" th:value="${category.id}">
<h5>
<a class="btn btn-info popovers" th:href="#{'/category/' + ${category.id}}" >[[${category.name}]]</a>
</h5>
</div>
</div>

hello this is the approach I used in my case;
in your repository interface
or
List<Book> findBookByCategoryId(Integer cId);
then in your service class implement the above repo methods
List<Book> bks= new ArrayList<>();
BookRepo.findBookByCategoryName("programming books")
.forEach(bks::add);
System.out.println(bks);
BookRepo.findBookByCategoryId(1)
.forEach(bks::add);
System.out.println(bks);

Related

Calculate percent with sum jpql query

I have to calculate the percent of (product's quantity/total quantity in database)*100 using jpql query and I didn't have the result that I want.
My query that I used:
#Query(value = "select new com.food.countProduct( product,SUM(p.quantity)/(select SUM(quantity) from Order)*100)from Order p group by p.product")
public class Order
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne (cascade = CascadeType.MERGE)
#OnDelete(action = OnDeleteAction.CASCADE)
#JoinColumn(name="productId")
private Product product;
private int quantity;
#ManyToOne (cascade = CascadeType.MERGE)
#OnDelete(action = OnDeleteAction.CASCADE)
#JoinColumn(name = "reservationId")
private Reservation reservation;
}
public class countProduct
{
private Product product;
private Long quantity;
public countProduct(Product product, Long quantity)
{
this.product = product;
this.quantity = quantity;
}
}

JPA Join with childEntity.childForeignEntity

I do not know how it is called in one word, but let me explain in details.
Lets assume I have following tables/schema in my database:
And following classes accordingly:
1.Post
#Entity
#Table(name = "posts")
public class Post {
#Id
private Long id;
#Column(name = "text")
private String text;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "post")
private Set<PostComment> postComments = new HashSet<>();
}
2.Post Comments
#Entity
#Table(name = "post_comments")
public class PostComment {
#Id
private Long id;
#Column(name = "post_id")
private Long postId;
#Column(name = "user_id")
private Long userId;
#Column(name = "text")
private String text;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="post_id")
private Post post;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="user_id")
private User user;
}
3.User
#Entity
#Table(name = "users")
public class User {
#Id
private Long id;
#Column(name = "some_attributes")
private String someAttributes;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private Set<PostComment> postComments = new HashSet<>();
}
How can I join Post with User via PostComment, so in my Post entity I could get all users commented:
#Entity
#Table(name = "posts")
public class Post {
....
//# join with post_comments.user_id
private Set<User> users = new HashSet<>();
....
}
Well, just get PostComment.user where PostComment.post equals your post.
#Query("select pc.user from PostComment pc where pc.post = :post")
List<User> getUsersWithComments(#Param("post") Post post);
Seems to work for me. Gives me the following SQL:
Hibernate: select user1_.id as id1_2_, user1_.some_attributes as some_att2_2_ from post_comments postcommen0_ inner join users user1_ on postcommen0_.user_id=user1_.id where postcommen0_.post_id=?
I don't know what this is all about:
#Column(name = "post_id")
private Long postId;
#Column(name = "user_id")
private Long userId;
or this
#JoinColumn(name="user_id")
#JoinColumn(name="post_id")
and you shouldn't do this:
= new HashSet<>();
and while we're at it this is redundant.
fetch = FetchType.LAZY,

JPA Repository Query on additional table #ManytoMany

I want to do select like this in my jpa spring repository
SELECT sicknes_id, count(symptomp_id) as ilosc FROM symptomp_sicknes where symptomp_id IN (1,2) group by sicknes_id Order by ilosc DESC;
My enitity
#Entity
#Table(name = "symptomp")
public class Symptomp {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "symptomp_id")
private Long symptomp_id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#ManyToMany(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}, fetch = FetchType.LAZY)
#JoinTable(name = "symptomp_sicknes",joinColumns = #JoinColumn(name = "symptomp_id"),inverseJoinColumns = #JoinColumn(name = "sicknes_id"))
private Set<Sicknes> sicknes = new HashSet<>();
#Entity
#Table(name = "sicknes")
public class Sicknes {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sicknes_id")
private Long sicknes_id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#ManyToOne(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}, fetch = FetchType.LAZY)
#JoinColumn(name = "speciesId")
private Species species;
My Symptomp repository:
public interface SymptompRepository extends JpaRepository<Symptomp, Long> {
#Query("select p from Symptomp p where name like ?1%")
public List<Symptomp> findAllBySymptompName(String symptomp);
public Symptomp findByName(String symptomp);
public List<Symptomp> findByNameIn(List<String> list);
Integer countDistinctSymptompByName(String id);
}
How I can create this select in my JPA repository?
I try get value like in select but i got error mapping bean.
You can get query result as List<Object[]> using nativeQuery=true parameter
#Query("SELECT sicknes_id, count(symptomp_id) as ilosc FROM symptomp_sicknes where symptomp_id IN (1,2) group by sicknes_id Order by ilosc DESC", nativeQuery=true)
List<Object[]> getQueryResult();
Other option is to create dto class with appropriate constructor
public class QueryResultDto {
Long sicknesId;
Long count;
public QueryResultDto(Long sicknesId, Long count) {
this.sicknesId = sicknesId;
this.count = count;
}
}
Then using JPQL
#Query("select new yourproject.dtopath.QueryResultDto(...")
List<QueryResultDto> getQueryResult(#Param("symptompIds") List<Long> symptompIds);
If you want to avoid a native Query the best way is to create an Entity for that JoinTable. Then you can query it easily. Additional benefit if this is that if in future a requirement will pop up that you have to store additional attributes in that relation you will have the Entity already there to do that easily.

Project data from different tables to a model

I defined my model classes like below.
#Entity
#Table(name = "my_employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "emp_address_mapping", joinColumns = #JoinColumn(name = "emp_id"), inverseJoinColumns = #JoinColumn(name = "address_id"))
private List<Address> addresses = new ArrayList<Address>();
.......
.......
}
#Entity
#Table(name = "my_address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String country;
.....
.....
}
public class EmployeeDetails {
private int empId;
private String name;
private String country;
......
......
}
How can I write a query using #Query annotation to populate all the EmployeeDetails.
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
#Query("SELECT new com.sample.app.model.EmployeeDetails......")
List<EmployeeDetails> getEmployeeDetails();
}
Create the constructor in EmployeeDetails
public EmployeeDetails(int id,String name,String country){
this.id=id;
this.name=name;
this.country=country;
}
Try this query
To get all employee details:
SELECT new com.sample.app.model.EmployeeDetails(e.id,e.name,a.country) from Employee e,Address a

JPA JSF creating a new record doesn't update the page

The following p:dialog creates a new comment using:
<p:dialog id="buyDlg" widgetVar="buyDialog" modal="true" resizable="false" appendTo="#(body)"
header="#{myBundle.CreateCommentsTitle}" closeOnEscape="true" width="auto" height="auto">
<h:form id="buyCommentCreateForm">
<h:panelGroup id="buyDisplay">
<p:outputPanel id="buyCommentsPanel">
<p:row>
<p:column>
<p:editor id="commentText" value="#{commentsController.selected.commentText}" style="margin-bottom:10px"/>
</p:column>
</p:row>
</p:outputPanel>
<p:commandButton actionListener="#{commentsController.setBuyComment(pmMainController.selected.idPmMain, commentsController.selected.commentText)}" value="#{myBundle.Save}" update=":PmMainEditForm:display" oncomplete="handleSubmit(xhr,status,args,PF('buyDialog'));">
<p:confirm header="#{myBundle.ConfirmationHeader}" message="#{myBundle.ConfirmEditMessage}" icon="ui-icon-alert"/>
</p:commandButton>
<p:commandButton value="#{myBundle.Cancel}" oncomplete="PF('buyDialog').hide()" update="buyDisplay" process="#this" immediate="true" resetValues="true"/>
</h:panelGroup>
</h:form>
</p:dialog>
Where the save method is:
public void setBuyComment(long idPmMain, String commentText){
PmMain pmMain = pmMainFacadeEJB.find(idPmMain);
Comments comments = new Comments();
pmMain.setBuyComment(comments);
comments.setBuyComment(pmMain);
comments.setCommentText(commentText);
commentsFacadeEJB.edit(comments);
}
Everything works fine but I need to reload the page in order to visualize the comment id in PmMain (it gets inserted above where it says PmMain.setBuyComments(comments)). Any ideas?
EDIT
Adding information about setBuyComment:
For PmMain.setBuyComment I have:
public void setBuyComment(Comments buyComment) {
this.buyComment = buyComment;
}
And for comments.setBuyComment:
public void setBuyComment(PmMain pmMain){
pmMain.setBuyComment(this);
pmMainCollection24.add(pmMain);
}
EDIT 2
The backing Bean from PmMain looks like this:
#Entity
#Table(name = "pm_main")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "PmMain.findAll", query = "SELECT p FROM PmMain p")
, #NamedQuery(name = "PmMain.findByPropId", query = "SELECT p FROM PmMain p WHERE p.propId = :propId")
, #NamedQuery(name = "PmMain.findByPropName", query = "SELECT p FROM PmMain p WHERE p.propName = :propName")
, #NamedQuery(name = "PmMain.findByIdPmMain", query = "SELECT p FROM PmMain p WHERE p.idPmMain = :idPmMain")})
public class PmMain implements Serializable {
private static final long serialVersionUID = 1L;
#Size(max = 25)
#Column(name = "prop_id")
private String propId;
#Size(max = 125)
#Column(name = "prop_name")
private String propName;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id_pm_main")
private Long idPmMain;
#JoinColumn(name = "buy_comment", referencedColumnName = "id_comments")
#ManyToOne
private Comments buyComment;
[... Getters and Setters ...]
And the Comments bean looks like:
#Entity
#Table(name = "comments")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Comments.findAll", query = "SELECT c FROM Comments c")
, #NamedQuery(name = "Comments.findByCommentText", query = "SELECT c FROM Comments c WHERE c.commentText = :commentText")
, #NamedQuery(name = "Comments.findByIdComments", query = "SELECT c FROM Comments c WHERE c.idComments = :idComments")})
public class Comments implements Serializable {
private static final long serialVersionUID = 1L;
#Size(max = 2147483647)
#Column(name = "comment_text")
private String commentText;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id_comments")
private Long idComments;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "buyComment")
private List<PmMain> pmMainCollection24 = new ArrayList<>();
[... Getters and Setters ...]
public void setBuyComment(PmMain pmMain){
pmMain.setBuyComment(this);
pmMainCollection24.add(pmMain);
}