Is it possible to change mysql to jpaquery? - java

I tried to create the mysql code in #query, but failed to validate, so i want to make this sql code exactly the same as querydsl, can someone help me put the sum and the hypothesis in the dsl query? Do you have any documentation on this? or it is not possible to do so. Thank you for your time and your help will be invaluable! I'm really trying my best to deal with this problem.
Mysql:
SELECT id_product,date,
sum(case
when action_description = "import"
then quantity_product
else -quantity_product
END) as test
FROM testdb.warehouse_management
inner join product
on warehouse_management.product_id = product.id_product
where id_product = 3 and date <= 1200
group by id_product,date
Querydsl:
public class ManagementRepositoryImpl implements ManagementRepositoryCustom {
#PersistenceContext
private EntityManager entityManager;
#Override
public List<StockRecoveryDTO> findTotal(Long id_product, String date){
JPAQuery<StockRecoveryDTO> stockRecoveryDTOJPAQuery = new JPAQuery<>(entityManager);
QManagement management = QManagement.management;
QProduct product = QProduct.product;
return stockRecoveryDTOJPAQuery.select(Projections.bean(StockRecoveryDTO.class,
product.id_product,
management.date,
product.quantity_product,
management.action_description,
management.id_action,
management.quantity))
.from(management)
.innerJoin(product)
.on(management.product_id.eq(product)
DTO: package com.example.dto;
public class StockRecoveryDTO {
private Long id_product;
private String date;
private int quantity_product;
private String action_description;
private Long id_action;
private String quantity;
private int total;
public StockRecoveryDTO() {
}
public StockRecoveryDTO(Long id_product, String date, int quantity_product, String action_description, Long id_action, String quantity, int total) {
this.id_product = id_product;
this.date = date;
this.quantity_product = quantity_product;
this.action_description = action_description;
this.id_action = id_action;
this.quantity = quantity;
this.total = total;
}
//GETTER SETTER

Related

How to mapped my query for recieving the data?

I send a Get Postman request and i am receiving status 200. The problem is that I don't get any data about it, it gives me only : [] .
Probably my error is that I don't map the constructor for the DTO class I made.
As another option I guess I have to make a projection or something similar to this, can anyone explain to me how to map the constructor and give me the data I want? I'm so messed up and dealing with this error for 2 hours and nothing.
Repository:
#Repository
public interface ManagementRepository extends JpaRepository<Management,Long>,ManagementRepositoryCustom {
#Query(value = "SELECT wm.action_description " +
"FROM testdb.warehouse_management wm " +
"WHERE wm.action_description = :action_description ", nativeQuery = true)
List<StockRecoveryDTO> findByDog(#Param("action_description") String action_description);
}
DTO:
package com.example.dto;
public class StockRecoveryDTO {
private Long id_product;
private String date;
private int quantity_product;
private String action_description;
private Long id_action;
private String quantity;
public StockRecoveryDTO() {
}
public StockRecoveryDTO(Long id_product, String date, int quantity_product, String action_description, Long id_action, String quantity) {
this.id_product = id_product;
this.date = date;
this.quantity_product = quantity_product;
this.action_description = action_description;
this.id_action = id_action;
this.quantity = quantity;
}
//GETTER SETTER
Console:
Hibernate: SELECT wm.action_description FROM testdb.warehouse_management wm WHERE wm.action_description = ?
That native query you are return only action_description so change the return type to List<String>.

Adding an object to a List of another type

I'm trying to return the record that I got from my database. But I'm having a problem on how I can do that because the data than I retrieved from the database is in a different class from the return parameter.
public List<Record> getRecord(List<Request> requests) {
List<Record> records = new ArrayList<>();
for (Request request : requests) {
Billing billing = billingRepository
.findByBillingCycleAndStartDateAndEndDate(
request.getBillingCycle()
, request.getStartDate()
, request.getEndDate());
if (billing != null) {
// Need to add "billing" to "records" list here
}
}
return records;
}
Record.class
public class Record {
private int billingCycle;
private LocalDate startDate;
private LocalDate endDate;
private String accountName;
private String firstName;
private String lastname;
private double amount;
public Record() {
}
//Getters and setters
Billing.class
public class Billing {
private int billingId;
private int billingCycle;
private String billingMonth;
private Double amount;
private LocalDate startDate;
private LocalDate endDate;
private String lastEdited;
private Account accountId;
public Billing() {
}
//Getters and setters
What can I do? and please explain the answer so I can understand it. I really want to learn
You can use DozerMapper. It will map the object to another object having same name properties or you have to write the mapping in the dozer-mapping xml.
Lets come to your question. Here you are trying to convert your entity to another object.
For that you have to write mapping code. It will be something like this and it is very common practice to convert entity objects to another object before using them.
Record toRecord(Billing billing) {
if(billing == null) {
return null;
}
Record record = new Record();
record.setBillingCycle = billing.getBillingCycle();
...
...
// other properties
...
return record;
}

How work with immutable object in mongodb and lombook without #BsonDiscriminator

I tried to work with immutable objects in MongoDB and Lombok. I found a solution to my problem but it needs to write additional code from docs but I need to used Bson annotations and create a constructor where describes fields via annotations. But if I user #AllArgsConstructor catch exception: "Cannot find a public constructor for 'User'" because I can't use default constructor with final fields. I think i can customize CodecRegistry correctly and the example will work correctly but I couldn't find solution for it in docs and google and Stackoverflow.
Is there a way to solve this problem?
#Data
#Builder(builderClassName = "Builder")
#Value
#BsonDiscriminator
public class User {
private final ObjectId id;
private final String name;
private final String pass;
private final String login;
private final Role role;
#BsonCreator
public User(#BsonProperty("id") final ObjectId id,
#BsonProperty("name") final String name,
#BsonProperty("pass") final String pass,
#BsonProperty("login") final String login,
#BsonProperty("role") final Role role) {
this.id = id;
this.name = name;
this.pass = pass;
this.login = login;
this.role = role;
}
#AllArgsConstructor
public enum Role {
USER("USER"),
ADMIN("ADMIN"),
GUEST("GUEST");
#Getter
private String value;
}
public static class Builder {
}
}
Example for MongoDB where I create, save and then update users:
public class ExampleMongoDB {
public static void main(String[] args) {
final MongoClient mongoClient = MongoClients.create();
final MongoDatabase database = mongoClient.getDatabase("db");
database.drop();
final CodecRegistry pojoCodecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(),
fromProviders(PojoCodecProvider.builder().automatic(true).build()));
final MongoCollection<User> users = database.getCollection("users", User.class).withCodecRegistry(pojoCodecRegistry);
users.insertMany(new ExampleMongoDB().getRandomUsers());
System.out.println("Before updating:");
users.find(new Document("role", "ADMIN")).iterator().forEachRemaining(
System.out::println
);
System.out.println("After updating:");
users.updateMany(eq("role", "ADMIN"), set("role", "GUEST"));
users.find(new Document("role", "GUEST")).iterator().forEachRemaining(
System.out::println
);
}
public List<User> getRandomUsers() {
final ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 15; i++) {
users.add(
User.builder()
.login("log" + i)
.name("name" + i)
.pass("pass" + i)
.role(
(i % 2 == 0) ? User.Role.ADMIN : User.Role.USER
).build()
);
}
return users;
}
}
This should work (it worked for me):
#Builder(builderClassName = "Builder")
#Value
#AllArgsConstructor(onConstructor = #__(#BsonCreator))
#BsonDiscriminator
public class User {
#BsonId
private final ObjectId _id;
#BsonProperty("name")
private final String name;
#BsonProperty("pass")
private final String pass;
#BsonProperty("login")
private final String login;
#BsonProperty("role")
private final Role role;
}
Then in lombok.config add these (in your module/project directory):
lombok.addLombokGeneratedAnnotation=true
lombok.anyConstructor.addConstructorProperties=true
lombok.copyableAnnotations += org.bson.codecs.pojo.annotations.BsonProperty
lombok.copyableAnnotations += org.bson.codecs.pojo.annotations.BsonId
Also piece of advice, keep _id if you are going to use automatic conversion to POJOs using PojoCodec, which will save a lot of trouble.

Spring Data JPA - Custom Query with multiple aggregate functions in result

I was trying to return an average and count of a set of ratings in one query.
I managed it fairly easily in two queries following the example I found browsing. For example:
#Query("SELECT AVG(rating) from UserVideoRating where videoId=:videoId")
public double findAverageByVideoId(#Param("videoId") long videoId);
but as soon as I wanted an average and a count in the same query, the trouble started. After many hours experimenting, I found this worked, so I am sharing it here. I hope it helps.
1) I needed a new class for the results:
The I had to reference that class in the query:
#Query("SELECT new org.magnum.mobilecloud.video.model.AggregateResults(AVG(rating) as rating, COUNT(rating) as TotalRatings) from UserVideoRating where videoId=:videoId")
public AggregateResults findAvgRatingByVideoId(#Param("videoId") long videoId);
One query now returns average rating and count of ratings
Solved myself.
Custom class to receive results:
public class AggregateResults {
private final double rating;
private final int totalRatings;
public AggregateResults(double rating, long totalRatings) {
this.rating = rating;
this.totalRatings = (int) totalRatings;
}
public double getRating() {
return rating;
}
public int getTotalRatings() {
return totalRatings;
}
}
and
#Query("SELECT new org.magnum.mobilecloud.video.model.AggregateResults(
AVG(rating) as rating,
COUNT(rating) as TotalRatings)
FROM UserVideoRating
WHERE videoId=:videoId")
public AggregateResults findAvgRatingByVideoId(#Param("videoId") long videoId);
Thanks.
You should prevent NPEs and hibernate parsing tuple errors as following :
public class AggregateResults {
private final double rating;
private final int totalRatings;
public AggregateResults(Double rating, Long totalRatings) {
this.rating = rating == null ? 0 : rating;
this.totalRatings = totalRatings == null ? 0 : totalRatings.intValue();
}
public double getRating() {
return rating;
}
public int getTotalRatings() {
return totalRatings;
}}

JPA returning empty result list while DB returns row set

I am trying to run a query to fetch some statistic data from my database. And I'm using JPA. But I faced such a trouble: when I run JPQL query, the empty result set is returned. But when I run SQL, produced with JPA for that JPQL query, I got a single row of data.
Here's what I've got:
The Ticket entity
#Entity
#Table(name="tickets")
public class Ticket extends AbstractEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Embedded
private Owner owner;
#ManyToOne
#JoinColumn(name="flightId")
private Flight flight;
private String status;
public Ticket() {
this.status = "AVAILABLE";
}
The Flight entity
#Entity
#Table(name="flights")
public class Flight extends AbstractEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String departure;
private String destination;
private Date date;
private float ticketCost;
#OneToMany(mappedBy="flight", fetch=FetchType.LAZY, cascade=CascadeType.ALL)
private List<Ticket> tickets = new ArrayList<Ticket>();
The result row class
public class SoldReportRow {
private String departure;
private String destination;
private DateTime date;
private int ticketsSold;
private float totalCost;
public SoldReportRow(Date date, String departure, String destination, Long ticketsSold, Double totalCost) {
this.departure = departure;
this.destination = destination;
this.ticketsSold = ticketsSold.intValue();
this.totalCost = totalCost.floatValue();
this.date = new DateTime(date);
}
The JPQL
SELECT NEW entities.SoldReportRow(f.date, f.departure, f.destination,
COUNT(t.id), SUM(f.ticketCost))
FROM Ticket t JOIN t.flight f
WHERE t.status = 'SOLD' AND t.owner IS NOT NULL AND f.date BETWEEN ? and ?
GROUP BY f.id
The generated SQL
SELECT t0.DATE, t0.DEPARTURE, t0.DESTINATION, COUNT(t1.ID), SUM(t0.TICKETCOST)
FROM flights t0, tickets t1
WHERE ((((t1.STATUS = ?) AND NOT ((((((t1.ADDRESS IS NULL)
AND (t1.EMAIL IS NULL)) AND (t1.NAME IS NULL)) AND (t1.OWNERFROM IS NULL))
AND (t1.PHONE IS NULL)))) AND (t0.DATE BETWEEN ? AND ?))
AND (t0.ID = t1.flightId)) GROUP BY t0.ID
So here is what I got when I run JPQL:
And here is what I got when I run the generated SQL:
UPD: the TicketDAO methods
// ...
protected static EntityManagerFactory factory;
protected static EntityManager em;
static {
factory = Persistence.createEntityManagerFactory(UNIT_NAME);
}
// ...
public static List<SoldReportRow> soldReportByDate(String from, String to) {
DateTimeFormatter dfTxt = DateTimeFormat.forPattern("dd/MM/yyyy");
DateTimeFormatter dfSql = DateTimeFormat.forPattern("yyyy-MM-dd");
String startDate = dfSql.print(dfTxt.parseDateTime(from));
String endDate = dfSql.print(dfTxt.parseDateTime(to));
String query = String.format(
"SELECT NEW entities.SoldReportRow(f.date, f.departure, f.destination, COUNT(t.id), SUM(f.ticketCost)) FROM " +
"Ticket t JOIN t.flight f " +
"WHERE t.status = 'SOLD' AND t.owner IS NOT NULL AND f.date BETWEEN '%s' and '%s' " +
"GROUP BY f.id",
startDate, endDate
);
return TicketDAO.query(SoldReportRow.class, query);
}
public static <T> List<T> query(Class<T> entityClass, String query) {
EntityManager entityManager = getEntityManager();
TypedQuery<T> q = entityManager.createQuery(query, entityClass);
List<T> entities = null;
try {
entities = q.getResultList();
} finally {
entityManager.close();
}
return entities;
}
public static EntityManager getEntityManager() {
return factory.createEntityManager();
}
The question is, why does this happen and how to fix that?
Thanks!
After the research, I've found that the trouble was caused by the data at the database. By default, SQLite does not have the DATE column type. And it uses strings to describe timestamps. So for date comparison (just like SELECT ... WHERE date BETWEEN a AND b) it's better to use UTC date form, not string one (1397036688 is the better value than the 2014-03-09).

Categories