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
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;
}
}
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,
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.
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
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);
}