I am writing an online store using Spring Boot (MVC) and Hiberbate. The problem is that when I get a list of drinks, JSON gives me unnecessary information from the Page interface. I don't know how you can create an DTO for the interfaces to get rid of these fields. What should I do in this situation. Can someone have a ready-made solution?
public Page<DrinkDTO> getAllDrinks(int page, int pageSize) {
PageRequest pageRequest = PageRequest.of(page, pageSize, Sort.by("id"));
final Page<Drink> drinks = drinkRepository.findAll(pageRequest);
return drinkMapper.drinksToDrinksDTO(drinks);
}
#Data
#AllArgsConstructor
public class CustomPage {
Long totalElements;
int totalPages;
int number;
int size;
}
#Data
public class PageDTO<T> {
List<T> content;
CustomPage customPage;
public PageDTO(Page<T> page) {
this.content = page.getContent();
this.customPage = new CustomPage(page.getTotalElements(),
page.getTotalPages(), page.getNumber(), page.getSize());
}
Service for example:
public PageDTO<DrinkDTO> getAllDrinks(int page, int pageSize) {
PageRequest pageRequest = PageRequest.of(page, pageSize, Sort.by("id"));
final Page<Drink> drinks = drinkRepository.findAll(pageRequest);
return new PageDTO<DrinkDTO>(drinkMapper.drinksToDrinksDTO(drinks));
}
I use native query and then I i do dto projections most of the time.
Here is an example of DTO projection
public interface OvertimeRequestView {
public Long getId();
public String getEmployeeFirstName();
public String getEmployeeLastName();
public Long getOvertimeHours();
public Date getOvertimeDate();
public String getDescription();
public String getStatus();
public String getApproverName();
public default String getEmployeeFullName() {
String lastName = this.getEmployeeLastName();
String firstName = this.getEmployeeFirstName();
if (null != firstName) {
return firstName.concat(" ").concat(lastName);
}
return lastName;
}
}
and here is the repository with a native query. notice that since the query returns 'id' column I have a getId() method in the dto above,and since it has employeeFirstName i have getEmployeeFirstName() in the dto and so on. Notice also that I include a count query, without a count query, the queries sometime fail especially if the queries are complex and contain joins
#Query(value = "select ovr.id,\n" +
" u.first_name as employeeFirstName,\n" +
" u.last_name as employeeLastName,\n" +
" ovr.overtime_date as overtimeDate,\n" +
" ovr.description as description,\n" +
" ovr.overtime_hours as overtimeHours\n" +
"from overtime_requests ovr\n" +
" left join employees e on ovr.employee_id = e.id\n" +
" left join users u on e.user_id = u.id",
nativeQuery = true,
countQuery = "select count(ovr.id)\n" +
"from overtime_requests ovr\n" +
" left join employees e on ovr.employee_id = e.id\n" +
" left join users u on e.user_id = u.id")
public Page<OvertimeRequestView> getAllActive(Pageable pageable);
For more you can check from spring data documentation
Related
I have a result of type User as follows:
List<User> result = (getting values from db)
the properties of class User that I need are:
private Integer id,
private String name,
private List<Books> books = new ArrayList<>();
etc.
And Class Books has a field Id to.
Given that I have the user Id, and the books Id. I would like to access the other results inside the array book, for that book id.
So I need to -> give the result variable a specific userId, and access books property for that user, then give a bookId, and access book properties for that book id, in that user. I tried using java streams:
List<BooksDetails> userBook = result.stream()
.filter(user -> user.getId() == 1)
.collect(Collectors.toList())
.get(0)
.getBooks()
.stream()
.filter(book -> book.getId() == 2)
.collect(Collectors.toList())
.get(0)
.getBooksDetails();
But sometimes I get index out of bound error when accessing get(0) element. And it seems there might be a cleaner solution to this. What approaches can I use to accessing nested properties by giving the associated id's in a list?
OR would I have to convert it to a nested map instead?
It sounds like you want to do the following:
filter users for userId == 1
then flatMap the Books list
filter those books for bookid == 2
and then get the book details and store in a list
record BooksDetails(){};
record Books(int getId, List<BooksDetails> getBooksDetails){}
record User(int getId, List<Books> getBooks){}
List<User> result = new ArrayList<>();
Optional<List<BooksDetails>> bd = result.stream()
.filter(user -> user.getId() == 1)
.flatMap(b->b.getBooks().stream())
.filter(book -> book.getId() == 2)
.findFirst()
.map(Books::getBooksDetails);
Update
I have updated my answer to include records which shows my understanding of your classes. Since the ultimate result is based on a book Id, I am presuming that 1) it is unique. and 2) there are no duplicate books in the list. So this will return an Optional<List<BooksDetails>> which contains the list of book details for that bookId for the userId. If not book is found, it returns an empty optional.
Also,I replaced the flatMap with a Lambda and it worked.
List result = (getting values from db)
Might be not the answer you expect, but this predicate-filter (for user.id == 1 and book.id == 2) & find-first probably could be implemented in a layer before the result, as (database/SQL) query.
For example:
SELECT u.*
FROM users u
JOIN books b ON b.user_id = u.id
WHERE u.id = 1 AND b.id = 2
You can use stream to filter list based on id. Then based on filteredList is empty or not, you will get your data.
import java.util.*;
import java.util.stream.*;
public class MyClass {
public static void main(String args[]) {
final List<Book> books = Arrays.asList(new Book(1, 1, "details1"), new Book(2, 1, "details2"));
final List<User> users = Arrays.asList(new User(1, "name1", books.stream().filter(b -> b.userId == 1).collect(Collectors.toList())),
new User(2, "name2", books.stream().filter(b -> b.userId == 2).collect(Collectors.toList())), new User(3, "name3", null));
final int userId = 1, bookId = 2;
final Book filteredBook = getFilteredBook(userId, bookId, users);
if (null != filteredBook) {
// put your logic here
System.out.println(filteredBook.getDetails());
}
}
private static Book getFilteredBook(final int userId, final int bookId, final List<User> users) {
final User filteredUser = users.stream().filter(u -> u.id == userId).findFirst().orElse(null);
if (null == filteredUser) {
// log your appropriate error message here
System.out.println("No user with id: " + userId);
return null;
}
final List<Book> books = filteredUser.books;
if (null == books || books.isEmpty()) {
// log your appropriate error message here
System.out.println("No books associated with userId: " + userId);
return null;
}
final Book filteredBook = books.stream().filter(b -> b.id == bookId).findFirst().orElse(null);
if (null == filteredBook) {
// log your appropriate error message here
System.out.println("No book with id: " + bookId + " associated with userId: " + userId);
return null;
}
return filteredBook;
}
}
class User {
public final int id;
public final String name;
public final List<Book> books;
public User(int id, String name, List<Book> books) {
this.id = id;
this.name = name;
this.books = books;
}
#Override
public String toString() {
return "[id: " + id + ", name: " + name + "]";
}
}
class Book {
public final int id;
public final int userId;
public final String details;
public Book(int id, int userId, String details) {
this.id = id;
this.userId = userId;
this.details = details;
}
public String getDetails() {
return this.details;
}
#Override
public String toString() {
return "[id: " + id + ", userId: " + userId + ", details: " + details + "]";
}
}
I have some custom query ie:
#Query("SELECT cells.res, event.eventDate, count(*) FROM ABC abc " +
"JOIN abc.data as cells " +
"JOIN cells.samples as samples " +
"JOIN samples.ev as event " +
"WHERE event.id IN :ids "+
"GROUP BY event.id, samples.id")
Set<XYZProjection> findXYZBySubjectId(List<Long> ids);
( some data changed as I dont want ot paste real query ;) )
and Ive created XYZProjection class, with 3 fields inside.
public class XYZProjection implements java.io.Serializable {
private Long positive;
private LocalDate eventDate;
private Long count;
public SubjectSamplesProjection(Long res, LocalDate date, Long count) {
this.res = res;
this.date = eventDate;
this.count = count;
}
public Long getCount() {
return count;
}
public LocalDate getDate() {
return date;
}
public Long getRes() {
return res;
}
}
But then I get ConverterNotFoundException how can I fix that ?
( Idea is to create a class, dont want trigger creating a table, and this class will return from this query )
thanks!
How findXYZBySubjectId() will work where there is no field called "subjectId" is available in POJO.
And also your query is not clear.
please refer the link for more information:---
enter link description here
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 am using both JPA and hibernate in my project.
I have created a query in which i made join operation on many tables. So I created a native one. The results that i get are in a list of object[] but i would like the results to be converted automatically to a java POJO class.
You can check both the query syntax and POJO java class below.
JPA Query
#Query(value = "SELECT obsp.Identifier, obs.phenomenontimestart, nv.value " +
"From Series s " +
"INNER JOIN Featureofinterest fi on s.featureofinterestid = fi.featureofinterestid " +
"INNER JOIN ObservableProperty obsp on s.observablepropertyid = obsp.ObservablePropertyId " +
"INNER JOIN Observation obs on s.seriesid = obs.seriesid " +
"INNER JOIN NumericValue nv on nv.observationid = obs.observationid " +
"where fi.identifier = ?1 and obs.phenomenontimestart >= ?2 AND obs.phenomenontimestart <= ?3 " +
"order by obs.phenomenontimestart",
nativeQuery = true)
List<CurrentMeasure> findCurrentMeasure(String ident, Timestamp t1, Timestamp t2);
POJO class
public class CurrentMeasure {
private String identifier;
private Timestamp dateTime;
private BigDecimal bigDecimal;
public CurrentMeasure() {
}
public CurrentMeasure(String identifier, Timestamp dateTime, BigDecimal bigDecimal) {
this.identifier = identifier;
this.dateTime = dateTime;
this.bigDecimal = bigDecimal;
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public Timestamp getDateTime() {
return dateTime;
}
public void setDateTime(Timestamp dateTime) {
this.dateTime = dateTime;
}
public BigDecimal getBigDecimal() {
return bigDecimal;
}
public void setBigDecimal(BigDecimal bigDecimal) {
this.bigDecimal = bigDecimal;
}
}
With JPA you can call the constructor of your class CurrentMeasure directly inside your HQL query.
Example :
SELECT NEW <package>.CurrentMeasure(obsp.Identifier, obs.phenomenontimestart, nv.value) FROM ...
The NEW syntax is explained in Jboss documentation at chapter 11.5.
Another solution would be using HQL Transformers to achieve the same result without resorting to a constructor.
I use Neo4J database with Spring Data. I am unable to query (with custom query) a relationship directly to my Relation entity which looks like that:
#RelationshipEntity(type = "OCCURS_WITH")
public class Relation {
#GraphId
private Long id;
#StartNode
#Fetch
private Hashtag from;
#EndNode
#Fetch
private Hashtag to;
#GraphProperty(propertyType = long.class)
private Long[] timestamps = new Long[0];
private boolean active;
// getters, setters
}
I have also a repository interface as follow:
public interface RelationRepository extends CRUDRepository<Relation> {
#Query(value = " MATCH (h1)-[rel]->(h2) " +
" WHERE h1.name = {0} AND h2.name = {1}" +
" RETURN rel")
Relation find(String from, String to);
}
But when I query the repository I get an empty Relation object.
Everything works well when I am quering to dummy object in that way:
#Query(value = " MATCH (h1)-[r]->(h2) " +
" WHERE h1.name = {0} AND h2.name = {1} " +
" RETURN id(r) AS id, h1.name AS from, h2.name AS to, length(r.timestamps) AS size")
RelationshipData findData(String from, String to);
#QueryResult
public interface RelationshipData {
#ResultColumn("id")
String getId();
#ResultColumn("from")
String getFrom();
#ResultColumn("to")
String getTo();
#ResultColumn("size")
int getSize();
}
Is it possible to query directly to my entity?