I have two objects, Student and Book. Student has #OneToMany relationship with Book, and Book has only id, name, and publishYear. What i want is to return from Query is this -> Select s.name, b.name, b.publishYear from Student s inner join Book b on s.id = b.studentId. How to return s.name, b.name and b.publishYear?
entityManager.createQuery("select s.name, b.name, b.publishYear from Student s join s.books b");
This will return a List<Object[]>
If you want to return a list of DTOs you can use the constructor expression:
entityManager.createQuery("select new model.BookDTO(s.name, b.name, b.publishYear) from Student s join s.books b");
This will return a List<BookDTO>
The BookDTO must look like this:
package model;
public class BookDTO() {
private final String studentName;
private final String bookName;
private final String publishYear;
public BookDTO(String studentName, String bookName, String publishYear) {
this.studentName = studentName;
this.bookName = bookName;
this.publishYear = publishYear;
}
// getters
}
Read more about projection in the Hibernate documentation:
https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#hql-select-clause
Related
SPRING BOOT , JAVA 11 , POSTGRES
I have a big SQL query:
select c.id,c.image_url,c.status,c.name, cat.id,cat.name
from client_organization o join organization c
on o.organization_id = c.id
join client w on o.client_id = w.id
join org_category cat on c.category_id = cat.id
where w.id = ?
So i need to convert the given result to List of Data Transfer Objects(DTO).
If it is important, here is my DTO(constructor, getters and setters are not included):
public class OrganizationListDto {
Long id;
String image;
Boolean status;
String name;
Long categoryId;
String categoryName;
}
I have been looking for solution for some days and found that this operation can be implemented in different ways(jdbc, hibernate etc)
So please explain me what and when I need to use them?
How to implement this operation?
Follow the below approach have used spingJpa
#Service
public class OrganizationServiceImpl
{
#Autowired
OrganizationListRepository organizationListRepository;
public OrganizationListDto fetch(usrId)
{
OrganizationListDto orgListData = new OrganizationListDto();
List<OrganizationListMapping> orgData = organizationListRepository.fetchData(usrId);
BeanUtils.copy(orgData,orgListData)
return orgData;
}
}
#Repository
public interface OrganizationListRepository extends JpaRepository{
#Query(native="true",value="select cat.id as id,cat.name as name
from client_organization o join organization c
on o.organization_id = c.id
join client w on o.client_id = w.id
join org_category cat on c.category_id = cat.id
where w.id = usrId"
OrganizationListMapping fetchData(#Param ("usrId") Integer usrId);
}
public interface OrganizationListMapping {
public Long getId();
public String getName();
}
public class OrganizationListDto {
Long id;
String name;
}
I've a custom object
#Component
public class SaleReport {
private Date date;
private String customerName;
private String phoneNo;
private String productName;
private Integer quantity;
private Double totalAmount;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getPhoneNo() {
return phoneNo;
}
public void setPhoneNo(String phoneNo) {
this.phoneNo = phoneNo;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public Double getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(Double totalAmount) {
this.totalAmount = totalAmount;
}
}
I need to return List of this object using a JpaRepository<Sale, Integer>
The SaleRepository is as follow:
#Repository
public interface SaleRepository extends JpaRepository<Sale, Integer> {
#Query(value = "SELECT B.order_date AS date, E.customer_name AS customerName, " +
"E.phone_no AS phoneNo, D.product_name AS productName, C.quantity AS quantity, " +
"C.total_price AS totalAmount " +
"FROM SALE A " +
"INNER JOIN A.quotation_id B " +
"INNER JOIN B.quotation_id C " +
"INNER JOIN C.product_id D " +
"INNER JOIN B.customer_id E " +
"WHERE (B.order_date BETWEEN :from_date AND :to_date)", nativeQuery = true)
public List<SaleReport> getSaleReportByDate(#Param("from_date")String fromDate, #Param("to_date")String toDate);
}
Here is my Controller method:
#GetMapping("api/report/sale/{from_date}/{to_date}")
public List<SaleReport> getSaleReportByDate(#PathVariable("from_date") String fromDate, #PathVariable("to_date") String toDate){
return saleRepository.getSaleReportByDate(fromDate, toDate);
}
While I run the code it shows this error:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown database 'a'
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_161]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_161]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_161]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_161]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:425) ~[mysql-connector-java-5.1.47.jar:5.1.47]
at com.mysql.jdbc.Util.getInstance(Util.java:408) ~[mysql-connector-java-5.1.47.jar:5.1.47]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:944) ~[mysql-connector-java-5.1.47.jar:5.1.47]
My DATABASE NAME is alright. It works perfectly while I request in other methods.
Please suggest me an ideal solution.
1) You need to create a constructor for the SaleReport class that accepts all the parameters that you use in the select statement and in the exact order.
2) Alter your query so that you wrap the selected columns with a new statement:
#Query(value = "SELECT new org.mypackage.SaleReport(B.order_date AS date,"
+ "E.customer_name AS customerName, "
+ "E.phone_no AS phoneNo, D.product_name AS productName,
+ "C.quantity AS quantity, " +
+ "C.total_price AS totalAmount) " +
"FROM SALE A " +
"INNER JOIN A.quotation B " +
"INNER JOIN B.quotation C " +
"INNER JOIN C.product D " +
"INNER JOIN B.customer E " +
"WHERE (B.order_date BETWEEN :from_date AND :to_date)")
3) In my opinion you don't need a native query here and you can use HQL directly (changed joins).
Spring has an annotation called #SqlResultSetMapping
This annotation can be used for above problem. Details about this annotation can be found from following link:
Annotation Type ConstructorResult
This is actually mapping a hibernate query result into a plain POJO Class. Thats what actually needed.
Example:
Query q = em.createNativeQuery(
"SELECT c.id, c.name, COUNT(o) as orderCount, AVG(o.price) AS avgOrder " +
"FROM Customer c, Orders o " +
"WHERE o.cid = c.id " +
"GROUP BY c.id, c.name",
"CustomerDetailsResult");
#SqlResultSetMapping(
name="CustomerDetailsResult",
classes={
#ConstructorResult(
targetClass=com.acme.CustomerDetails.class,
columns={
#ColumnResult(name="id"),
#ColumnResult(name="name"),
#ColumnResult(name="orderCount"),
#ColumnResult(name="avgOrder", type=Double.class)
}
)
}
)
Put #SqlResultSetMapping into top of any #Entity and make sure the datatype returning from database is same as your POJO Class datatype.
Hope this will help you. Enjoy :)
I hope this answer might help you !!
Sale is your entity class and SaleReport is pojo class.
public List<SaleReport> getSaleReportByDate(#Param("from_date")String fromDate, #Param("to_date")String toDate);
you can not return a pojo class list from database.It return entity class list.
Use public List not public List
public List<Sale> getSaleReportByDate(#Param("from_date")String fromDate, #Param("to_date")String toDate);
#Repository
public interface SaleRepository extends JpaRepository<**Sale**, Integer>
you have to map Entity Class list to POJO class list.
Mapping of Entity class list to a pojo class list:
As there are lot of ways to achieve this still
Easiest solution is loop over entity class and set entity attribute to pojo attribute:
I don't suggest to use constructor because some times you may want to set only several fields not all fields of POJO class.
Arraylist<SaleReport> sr = new Arraylist<SaleReport>();
ArrayList<Sale> saleEntityList= saleRepository.getSaleReportByDate(fromDate,toDate);
for(Sale s : saleEntityList){
saleReport = new SaleReport();
saleReport.setDate(s.getDate);
//same for all attributes
sr.add(saleReport);
}
return sr;
Here sr is your POJO list.
I have a simple spring JPA repository that has a method that needs to return result in List of DTO's.
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
#Query(value = "SELECT new com.project.jpa.entity.EmployeeDTO(e.name, r.roleName) FROM Employee e JOIN e.role r")
List<EmployeeDTO> getEmployee_1();
#Query("SELECT new EmployeeDTO(e.name, r.roleName) FROM Employee e JOIN e.role r")
List<EmployeeDTO> getEmployee_2();
#Query(value = "SELECT new EmployeeDTO(e.name, r.roleName) FROM Employee e JOIN e.role r")
List<EmployeeDTO> getEmployee_3();
}
The three methods that you see above are three variations of what I have tried to get the result. But all three fail and thrown the following error.
Caused by: java.lang.ClassCastException: org.hibernate.hql.internal.ast.tree.SqlNode cannot be cast to org.hibernate.hql.internal.ast.tree.PathNode
Am I doing something wrong here.
This is my DTO
public class EmployeeDTO {
private String name;
private String roleName;
public EmployeeDTO (String name, String roleName) {
this.name = name;
this.roleName = roleName;
}
/*
Getters and setters
*/
}
While creating repository class , need to pass same DTO object with identifier of DTO object
public interface EmployeeRepository extends JpaRepository<EmployeeDTO, Integer> {
//If you need to retrieve all employees then simply try below
#Query("FROM EmployeeDTO e")
List<EmployeeDTO> getEmployee_1();
//If you need to retrieve employees on specific condition then try below
#Query("FROM EmployeeDTO e where e.roleName =:roleName")
List<EmployeeDTO> getEmployeeByRoleName(#Param("roleName") String roleName);
}
I'm stuck, I run this query with hibernate, but do not know how to get the values that the query returns.
Query:
select idMagistrado, count(*) as numeroDemandas from demandamagistrado group by idMagistrado order by numeroDemandas asc limit 3;
DAO:
public Set<Magistrado> get3Magistrado() {
ConnectionDataBase cdb = new ConnectionDataBase();
Set<Magistrado> magistrados = null;
try {
Query createQuery = cdb.getSession().createQuery("select idMagistrado, count(*) as numeroDemandas from demandamagistrado group by idMagistrado order by numeroDemandas asc limit 3");
// Here, HELP!
if (magistrados == null) {
return null;
}
}
catch (HibernateException he) {
cdb.catchException(he);
}
finally {
cdb.closeSession();
}
return magistrados;
}
And the result in de MySQL console:
mysql> select idMagistrado, count(*) as numeroDemandas from demandamagistrado group by idMagistrado order by numeroDemandas asc limit 3;
+--------------+----------------+
| idMagistrado | numeroDemandas |
+--------------+----------------+
| 3 | 1 |
| 2 | 2 |
| 1 | 3 |
+--------------+----------------+
3 rows in set (0.00 sec)
The Query object has no method to let me know the values of idMagistrado and numeroDemandas. I do not know what to do, HELP.
I think you should know "HQL" is different from "SQL";
when you use HQL, you should query from class (not table).
so if you wanted excute the sql and get result in hibernate:
String sql = "select idMagistrado, count(*) as numeroDemandas from demandamagistrado
group by idMagistrado order by numeroDemandas asc limit 3";
you should use the method createSQLQuery (not createQuery) in this way:
List<Object[]> objList = cdb.getSession().createSQLQuery(sql).list();
for(Object[] objs : objList){
int idMagistrado = Integer.valueOf(objs[0]);
int numeroDemandas = Integer.valuesOf(objs[1]);
System.out.println(idMagistrado + ":" + numeroDemandas);
}
HQL is an object-oriented query language, similar to SQL, but instead of operating on tables and columns, HQL works with persistent objects and their properties.
Lets assume that we have a database table structure like below :
Then the Entity class for each table can be given (Hibernate mapping Java classes to database tables):
Subject
public class Subject implements java.io.Serializable {
private Integer id;
private String subName;
private String day;
private String time;
private Set subjectHasStuDetailses = new HashSet();
public Subject() {
}
//getters and setters
...
StuDetails
public class StuDetails implements java.io.Serializable {
private Integer id;
private String FName;
private String LName;
private String sex;
private String dob;
private Set subjectHasStuDetailses = new HashSet();
public StuDetails() {
}
//getters and setters
...
SubjectHasStuDetails
public class SubjectHasStuDetails implements java.io.Serializable {
private Integer id;
private StuDetails stuDetails;
private Subject subject;
public SubjectHasStuDetails() {
}
//getters and setters
...
In every entity class we can override the default constructor as we need.
Lets assume that our entity classes are in a package named com.hibernate.entities. If we need to get all the subjects doing by a selected student then our Hql query can be:
Query query = ss.createQuery("FROM com.hibernate.entities.SubjectHasStuDetails shs WHERE "
+ "shs.stuDetails.id= :s_id").
setInteger("s_id", 32);
List<com.hibernate.entities.SubjectHasStuDetails> shs = query.list();
for (SubjectHasStuDetails sh : shs) {
sh.getSubject().getSubName();
}
As we can see in the given example of Hql query we are referring to entity members of the class which is mapped to the required table.Not referring the table column names.
So in your case also you should refer the mapped entities.
SELECT d.member_variable, COUNT(*) AS d.member_variable FROM packagename.EntitityClass d GROUP BY d.member_variable ORDER BY d.member_variable ASC LIMIT 3
Update:
If you are using identified relationship:
Subject:
public class Subject implements java.io.Serializable {
private Integer id;
private String subName;
private String day;
private String time;
private Set<Student> students = new HashSet<>();
...
Student:
public class Student implements java.io.Serializable {
private Integer id;
private String FName;
private String LName;
private int age;
private Set subjects = new HashSet();
...
Query:
Query q = s.createQuery("FROM com.hibernate.entities.Subject sub JOIN sub.students s WHERE s.id= :stu_id")
.setInteger("stu_id", 1);
List<String> subj = q.list();
for (String ss : subj) {
System.out.println(ss);
}
Using Eclipselink 2.4.1, I'm trying to avoid the "distinct" keyword from appearing in batch queries. Documentation suggests that when using batch type EXISTS the distinct keyword isn't used, however in my experience it's used in all cases when retrieving a many-to-one relationship. For example, I have the following classes written against the SCOTT demo schema in oracle:
#Entity
public class Emp implements Serializable {
#Id private long empno;
private String ename;
#ManyToOne
#JoinColumn(name="DEPTNO")
private Dept dept;
public Emp() { }
public long getEmpno() { return this.empno; }
public void setEmpno(long empno) { this.empno = empno; }
public String getEname() { return this.ename; }
public void setEname(String ename) { this.ename = ename; }
public Dept getDept() { return this.dept; }
public void setDept(Dept dept) { this.dept = dept; }
}
#Entity
public class Dept implements Serializable {
#Id private long deptno;
private String dname;
public Dept() {}
public long getDeptno() { return this.deptno; }
public void setDeptno(long deptno) { this.deptno = deptno; }
public String getDname() { return this.dname; }
public void setDname(String dname) { this.dname = dname; }
}
I'm attempting to retrieve the employees and departments via batch fetch:
Query query = em.createQuery("select emp from Emp as emp ");
query.setHint(QueryHints.BATCH_TYPE, BatchFetchType.EXISTS);
query.setHint(QueryHints.BATCH, "emp.dept");
List<Emp> resultList = query.getResultList();
resultList.get(0).getDept();
The following SQL is generated:
[EL Fine]: sql: 2012-12-12 17:04:21.178--ServerSession(1034011695)--Connection(312759349)--SELECT EMPNO, ENAME, DEPTNO FROM SCOTT.EMP
[EL Fine]: sql: 2012-12-12 17:04:21.286--ServerSession(1034011695)--Connection(312759349)--SELECT distinct t0.DEPTNO, t0.DNAME FROM SCOTT.DEPT t0 WHERE EXISTS (SELECT t1.EMPNO FROM SCOTT.EMP t1 WHERE (t0.DEPTNO = t1.DEPTNO))
Is there something additional that needs to be done in addition to using EXISTS batch type to avoid the distinct keyword in batch queries?
Please log a bug. It should only be using this for JOIN batching.
You can set distinct to false on the query to avoid it. (there is no hint, you need to call dontUseDistinct() on the root ObjectLevelReadQuery).
((ObjectLevelReadQuery)((JpaQuery)query).getDatabaseQuery()).dontUseDistinct();