Pagination with native query using alias in spring boot - mysql

I have a sql table named reporting_general and I want to use a complex native sql query in which I used SQL Aliases , for that purpose I implemented JPA Projection to map the columns, so I made an interface for that purpose, but I am getting this error.
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'REPORTING_GENERAL WHERE REPORTING_GENERAL.ID > 0 AND CHANNEL in ('A', 'C') AND ' at line 1
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120) ~[mysql-connector-j-8.0.31.jar:8.0.31]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-j-8.0.31.jar:8.0.31]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:916) ~[mysql-connector-j-8.0.31.jar:8.0.31]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeQuery(ClientPreparedStatement.java:972) ~[mysql-connector-j-8.0.31.jar:8.0.31]
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52) ~[HikariCP-4.0.3.jar:na]
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java) ~[HikariCP-4.0.3.jar:na]
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
Here is My Postman
SQL Table
Entity class
#Entity
#Table(name = "reporting_general")
#Data
public class ReportingGeneral implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int id;
#Column(name = "transaction_name")
public String transactionName;
public String username;
#Column(name = "contact_number")
public String contactNumber;
public String segment;
#Column(name = "user_type")
public String userType;
#Column(name = "primary_key")
public String primaryKey;
public String channel;
#Column(name = "response_code")
public String ResponseCode;
#Column(name = "request_time")
public Date requestTime;
#Column(name = "response_time")
public Date responseTime;
}
Interface used for JPA Projection (the atributes of this class are the aliases I've used in the query)
public interface ActiveAccountReport {
String getUserName();
String getContactNumber();
String getPrimaryKey();
String getMinRequestTime();
String getMaxRequestTime();
String getSuccess();
String getFailed();
String getTotalHits();
String getChannel();
}
Repository class
public interface ReportingGenRepo extends JpaRepository<ReportingGeneral, Integer> {
#Query(value = "SELECT REPORTING_GENERAL.USERNAME AS userName, " +
"ANY_VALUE (REPORTING_GENERAL.CONTACT_NUMBER ) AS contactNumber,ANY_VALUE (REPORTING_GENERAL.PRIMARY_KEY) AS primaryKey," +
"MIN( REQUEST_TIME ) AS minRequestTime ,MAX( REQUEST_TIME ) AS maxRequestTime, " +
"COUNT(IF ( RESPONSE_CODE = '1', 1, NULL )) AS success,COUNT(IF " +
"( RESPONSE_CODE != '1', 1, NULL )) AS failed,COUNT(*) AS totalHits,CHANNEL as channel" +
" FROM REPORTING_GENERAL WHERE " +
" REPORTING_GENERAL.ID > 0 AND CHANNEL in ?3 AND (REPORTING_GENERAL.REQUEST_TIME BETWEEN ?1 AND ?2)" +
"GROUP BY channel, username", nativeQuery = true)
public Page<ActiveAccountReport> getActiveAccountReportFilters(
LocalDateTime startDate,
LocalDateTime endDate,
List<Character> channel,
Pageable pagable);
}
Service Class
#Service
public class ReportingGenService {
#Autowired
private ReportingGenRepo reportingGenRepo;
public Page<ActiveAccountReport> paginatedActiveAccountReports(ActiveAccountRequest activeAccountRequest,
Integer page,Integer size) {
Pageable pageable = PageRequest.of(page,size);
Page<ActiveAccountReport> activeAccountReports = reportingGenRepo.getActiveAccountReportFilters(activeAccountRequest.getStartDate(),
activeAccountRequest.getEndDate(),activeAccountRequest.getChannel(),pageable);
return activeAccountReports;
}}
Controller Class
#RestController
#RequestMapping("/repo")
public class ReportingGenController {
#Autowired
private ReportingGenService reportingGenService;
#GetMapping("/get")
public Page<ActiveAccountReport> findAll(#RequestBody ActiveAccountRequest activeAccountRequest,
#RequestParam("page") Integer page, #RequestParam("size") Integer size){
return reportingGenService.paginatedActiveAccountReports(activeAccountRequest,page,size);
}

If you want to use Paging and native queries you must provide a countQuery.
IN your case:
#Query(value = "SELECT REPORTING_GENERAL.USERNAME AS userName, " +
"ANY_VALUE (REPORTING_GENERAL.CONTACT_NUMBER ) AS contactNumber,ANY_VALUE (REPORTING_GENERAL.PRIMARY_KEY) AS primaryKey," +
"MIN( REQUEST_TIME ) AS minRequestTime ,MAX( REQUEST_TIME ) AS maxRequestTime, " +
"COUNT(IF ( RESPONSE_CODE = '1', 1, NULL )) AS success,COUNT(IF " +
"( RESPONSE_CODE != '1', 1, NULL )) AS failed,COUNT(*) AS totalHits,CHANNEL as channel" +
" FROM REPORTING_GENERAL WHERE " +
" REPORTING_GENERAL.ID > 0 AND CHANNEL in ?3 AND (REPORTING_GENERAL.REQUEST_TIME BETWEEN ?1 AND ?2)" +
"GROUP BY channel, username",
countQuery = "SELECT count(*) FROM REPORTING_GENERAL WHERE " +
" REPORTING_GENERAL.ID > 0 AND CHANNEL in ?3 AND (REPORTING_GENERAL.REQUEST_TIME BETWEEN ?1 AND ?2)" +
"GROUP BY channel, username"
nativeQuery = true)

Related

one to many and many to one mapping inserts null values in database

I have 3 model classes as UserBean , PolicyBean and RequestedPoliciesForUserBean.
userId is the primary key for userBean class.
policyNo is the primary key for PolicyBean class.
transactionId is the primary key for RequestedPoliciesForUserBean class.
The three model classes are as below with getter and setters (not showing getters and setters for better readability)
#Table(name="users")
public class UserBean {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY,generator = "native")
#GenericGenerator(name = "native",
strategy = "com.example.policymanagementsystem.idgenerator.UserIdGenerator",
parameters = {
#Parameter(name = UserIdGenerator.INCREMENT_PARAM, value = "50"),
#Parameter(name = UserIdGenerator.VALUE_PREFIX_PARAMETER,value="USER_"),
#Parameter(name = UserIdGenerator.NUMBER_FORMAT_PARAMETER, value ="%05d"),
})
#Column(name="userId")
private String userId;
#Size(min = 2 , max = 30, message = "must have atleast 2 characters and maximum 30")
#Column(name="name")
private String name;
#Column(name="age")
private String age;
#Column(name="city")
private String city;
#JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd")
#DateTimeFormat(pattern = "dd-MM-yyyy")
#Column(name="dateOfBirth")
private Date dateOfBirth;
#Size(max = 10 , message = "phone number must have 10 numbers")
#Column(name="phone")
private String phone;
#Email
#Column(name="email")
private String email;
#Pattern(regexp="^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!##&()–[{}]:;',?/*~$^+=<>]).{8,60}$", message = "password must have atleast 1 digit,1 upper case & 1 lower case letter,"
+ "1 special character,"
+ "no whitespace & minimum 8 & maximum 20 characters")
#NotNull(message="password cannot be null")
#Column(name="encodedPassword")
private String password;
#OneToMany(mappedBy = "users" , cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<RequestedPoliciesForUserBean> requestedPoliciesForUserBean;
#NotNull(message = "role cannot be null")
#Column(name = "role")
#Enumerated(EnumType.STRING)
private Role role;
#Entity
#Table(name="policy")
public class PolicyBean {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY,generator = "native")
#GenericGenerator(name = "native",
strategy = "com.example.policymanagementsystem.idgenerator.PolicyIdGenerator",
parameters = {
#Parameter(name = PolicyIdGenerator.INCREMENT_PARAM, value = "50"),
#Parameter(name = PolicyIdGenerator.VALUE_PREFIX_PARAMETER,value="POLICY_"),
#Parameter(name = PolicyIdGenerator.NUMBER_FORMAT_PARAMETER, value ="%05d"),
})
#Column(name="policyNo")
private String policyNo;
#Size(min = 2 , max = 30, message = "must have atleast 2 characters and maximum 30")
#Column(name="planName")
private String planName;
#Min(value = 1, message = "tenure cannot be zero")
#Column(name="tenure")
private String tenure;
#OneToMany(mappedBy = "policy" , cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<RequestedPoliciesForUserBean> requestedPoliciesForUserBean;
#Entity
#Table(name="requestedpoliciesforuser")
public class RequestedPoliciesForUserBean {
#GeneratedValue(strategy = GenerationType.IDENTITY,generator = "native")
#GenericGenerator(name = "native",
strategy = "com.example.policymanagementsystem.idgenerator.TransactionIdGenerator",
parameters = {
#Parameter(name = UserIdGenerator.INCREMENT_PARAM, value = "50"),
#Parameter(name = UserIdGenerator.VALUE_PREFIX_PARAMETER,value="TRANS_"),
#Parameter(name = UserIdGenerator.NUMBER_FORMAT_PARAMETER, value ="%05d"),
})
#Column(name="transactionId")
#Id
private String transactionId;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "userId", nullable = false)
private UserBean users;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "policyNo", nullable = false)
private PolicyBean policy;
#Column(name = "planName")
private String planName;
#Column(name = "tenure")
private String tenure;
#Enumerated(EnumType.STRING)
private PolicyStatus policyStatus;
I have three database tables as users, policy, requestedpoliciesforusers.
userId(primary key in users table) and policyNo(primary key in policy table) are the foreign keys references for requestedpoliciesforusers table.
One user can request for multiple policies and when user will request policy the data will get inserted in requestedpoliciesforusers table along with userId and policNo.Somewhat the scenario will be as below.
| User | Policy |
|------|--------|
| 1 | A |
| 1 | B |
| 2 | A |
| 3 | B |
| 1 | C |
But hibernate is inserting null values for userId and policyNo but all other fields are getting inserted properly. Below is the screenshot for the same.
My Postman request is as below:
{
"userId":"USER_02902",
"policyNo":"POLICY_00001",
"planName":"Recurring Deposit Plan",
"tenure":"20"
}
Please suggest why null values are getting inserted in database. Any changes required in one to many or many to one mapping. Thanks in Advance!
RequestedPoliciesForUserController
#RestController
#RequestMapping("/user")
public class RequestedPoliciesForUserController {
#Autowired
private RequestedPoliciesForUserService requestedPoliciesForUserService;
#PutMapping(path="/policy/request",consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public PolicyResponses requestPolicy(#RequestBody RequestedPoliciesForUserBean requestedPoliciesForUser) {
PolicyResponses policyResponses = new PolicyResponses();
boolean isPolicyRequested = requestedPoliciesForUserService.requestPolicy(requestedPoliciesForUser);
if(isPolicyRequested) {
policyResponses.setStatusCode(200);
policyResponses.setMessage("request sent");
policyResponses.setDescription("Policy request sent successfully");
}
return policyResponses;
}
}
RequestedPoliciesForUserServiceImpl
#Service
public class RequestedPoliciesForUserServiceImpl implements RequestedPoliciesForUserService {
#Autowired
RequestedPoliciesForUserDAO requestedPoliciesForUserDAO;
#Override
public boolean requestPolicy(RequestedPoliciesForUserBean requestedPoliciesForUserBean) {
// TODO Auto-generated method stub
return requestedPoliciesForUserDAO.requestPolicy(requestedPoliciesForUserBean);
}
}
RequestedPoliciesForUserDAOImpl
#Repository
public class RequestedPoliciesForUserDAOImpl implements RequestedPoliciesForUserDAO {
#PersistenceUnit
private EntityManagerFactory entityManagerFactory;
#Override
public boolean requestPolicy(RequestedPoliciesForUserBean requestedPoliciesForUserBean) {
EntityManager entityManager = null;
EntityTransaction entityTransaction = null;
boolean isPolicyRequested = false;
try {
entityManager=entityManagerFactory.createEntityManager();
entityTransaction=entityManager.getTransaction();
entityTransaction.begin();
System.out.println(requestedPoliciesForUserBean);
requestedPoliciesForUserBean.setPolicyStatus(PolicyStatus.PENDING);
entityManager.persist(requestedPoliciesForUserBean);
entityTransaction.commit();
entityManager.close();
isPolicyRequested =true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
} // end of requestPolicy()
}
Postman Response
{
"statusCode": 0,
"message": null,
"description": null,
"policyList": null
}
Create PK argument constructor in both the PolicyBean and UserBean classes. Also, the request in postman is not matching the RequestedPoliciesForUserBean. You pass userId and the class has users, policyNo and the class have policy.
UserBean constructor:
public UserBean(String userId) {
this.userId = userId;
}
PolicyBean constructor:
public PolicyBean(String policyNo) {
this.policyNo = policyNo;
}
Change your request JSON to:
{
"users": "USER_02902",
"policy": "POLICY_00001",
"planName": "Recurring Deposit Plan",
"tenure": "20"
}
It will save data in all columns of your requestedpoliciesforuser table. Also instead of using entitymanager directly use JPA. Spring Data JPA

case insensitive issue while fetching records from child table using HQL

I have a parent table and a child table where I am only getting 1 record from child table but not getting case insensitive matched record which is a mixed string. I am expecting it should return 2 records.
Below is the code for the same.
//parent Table
#Entity
#Table(name = "employee")
public class Employee implements Serializable {
#Id
#Column(name = "employeeID")
private String employeeID;
#Column(name = "name_first")
private String nameFirst;
#Column(name = "name_last")
private String nameLast;
}
//Child Table
#Entity
#Table(name = "employee_salary")
public class EmployeeSalary implements Serializable {
#EmbeddedId
private EmployeeSalaryPK employeeSalaryPKCompositeKey;
#Column(name = "salaryBracket")
private String salaryBracket;
}
#Embeddable
public class EmployeeSalaryPK implements Serializable {
#Column(name = "employeeID")
private String employeeID;
#Column(name = "salary")
private String salary;
}
In employee_salary table I have two records (as shown below) but while fetching it using HQL only one record is coming with an actual match but case insensitive record is not coming.
Employee Record:- ABC John Kramer
employee_salary table record:-
ABC 100900
aBc 76770
I am using simple HQL query (see below code) but getting only first record whenever I want to get both record as employeeID is abc.
String hqlQuery = " FROM " + Employee.class.getName() + " E WHERE E.employeeID= :EMPLOYEEID";
Session session = entityManager.unwrap(Session.class);
List<?> responseList = session.createQuery(hqlQuery).setParameter("EMPLOYEEID", "ABC").list();
To get all entities by case insensetive String id you have to convert id to same case (lowercase or uppercase) on both sides of the WHERE clause equality operator
String hqlQuery = " FROM " + Employee.class.getName() + " E WHERE lower(E.employeeID) = :EMPLOYEEID";
Session session = entityManager.unwrap(Session.class);
List<?> responseList = session.createQuery(hqlQuery).setParameter("EMPLOYEEID", "ABC".toLowerCase()).list();

Spring Boot Data JPA - how to get data for the certain id

I have two tables Ticket and Flight. One flight could have many tickets.
I want to show fields departure_date, destination_date from the table Flight and name, surname from the table Ticket. And show data only for the certain flight_id. I use findBy method.
Entity Flight
#Entity
#Table(name = "flight")
public class Flight {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer flight_id;
#Column(name = "departureDate")
private Date departureDate;
#Column(name = "destinationDate")
private Date destinationDate;
#OneToMany(mappedBy = "flight")
#JsonManagedReference("flight")
private List<Ticket> tickets;
Entity Ticket
#Entity
#Table(name = "ticket")
public class Ticket {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int ticket_id;
#Column(name = "name")
private String name;
#Column(name = "surname")
private String surname;
#ManyToOne(targetEntity = Flight.class)
#JoinColumn(name = "flight_id")
#JsonBackReference("flight")
#Fetch(FetchMode.JOIN)
private Flight flight;
I've created file FlightsTicketDto with certain fields:
public class FlightTicketDto {
private Integer flight_id;
private Date departureDate;
private Date destinationDate;
private String name;
private String surname;
public FlightTicketDto() {
}
public FlightTicketDto(Integer flight_id, Date departureDate, Date destinationDate, String name, String surname) {
this.flight_id = flight_id;
this.departureDate = departureDate;
this.destinationDate = destinationDate;
this.name = name;
this.surname = surname;
}
FlightTicketRepository with my Query
public interface FlightTicketRepository extends JpaRepository<Ticket, Integer> {
#Query("SELECT new pl.edu.wat.dto.FlightTicketDto(f.flight_id, f.departureDate, f.destinationDate, t.name, t.surname) "
+ "FROM Flight f INNER JOIN f.tickets t")
List<FlightTicketDto> findByFlightId(Integer flight_id);
}
FlightTicketController
#CrossOrigin(origins = "http://localhost:4200")
#RestController
#RequestMapping("/api")
public class FlightTicketController {
#Autowired
FlightTicketRepository flightTicketRepository;
#GetMapping("/mytickets/{flight_id}")
public List fetchEmpDeptDataInnerJoin(#PathVariable Integer flight_id) {
return flightTicketRepository.findByFlightId(flight_id);
}
Actually whatever flight_id (even not flight_id, but just another number) I write, I've got all my flights
For example I want to get result only for flight_id = 431, result you can see on the picture. What's wrong?
replace
#Query("SELECT new pl.edu.wat.dto.FlightTicketDto(f.flight_id, f.departureDate, f.destinationDate, t.name, t.surname) "
+ "FROM Flight f INNER JOIN f.tickets t")
List<FlightTicketDto> findByFlightId(Integer flight_id);
with
#Query("SELECT new pl.edu.wat.dto.FlightTicketDto(f.flight_id, f.departureDate, f.destinationDate, t.name, t.surname) "
+ "FROM Flight f INNER JOIN f.tickets t where f.flight_id = :flight_id")
List<FlightTicketDto> findByFlightId(#Param("flight_id") Integer flight_id);

select max native query hibernate

I am trying to get the max value of a column in a table using a native query with the #Query annotation
I tried to derive it from the examples here: https://www.baeldung.com/spring-data-jpa-query
#Query(value = "SELECT max(i.sequence) " +
"FROM invoices as i " +
"WHERE i.fleet_id = ?1", nativeQuery = true)
Long findMaxSequence(String fleetId);
i ve also tried:
#Query(value = "SELECT max(i.sequence) " +
"FROM invoices as i " +
"WHERE i.fleet_id = :fleetId", nativeQuery = true)
Long findMaxSequence(#Param("fleetId") String fleetId);
When i call my method as :
long maxSeq = invoiceRepository.findMaxSequenceForFleetId(invoice.getFleetId());
I get a NullPointerException. Any ideas why?
Invoice entity looks like this :
#Entity
#Table(name = "invoices"}
public class Invoice implements Serializable {
#Id
private String id;
#Column
private long sequence;
#Column(length = 12)
private String fleetId;
// ...
}
The issue was due to the fact that the database was empty so the query was returning null
and basic types such as long cannot be assigned to null values. Weirdly the compiler did not complain..
I modified my code as below:
Long maxSeq = invoiceRepository.findMaxSequenceForFleetId(invoice.getFleetId());
if(maxSeq == null){
maxSeq = 0L;
}

How to rewrite given query in jpql custom query?

I have a mysql query which look like this:
SELECT * FROM `HOST` WHERE `DATE_OF_SCAN` between
(SELECT subtime((Select max(`DATE_OF_SCAN`) from `HOST` ), '0 0:5:0'))
and
(Select max(`DATE_OF_SCAN`) from `HOST` );
which basically is returning hosts from range of max date in table and (max date - 5 min).
I'm trying to rewrite it in the jpql:
#Query("SELECT h FROM Host h WHERE h.date between "
+ "(SELECT subtime((Select max(ho.date) from Host ho ), 0 0:5:0)) "
+ "and (Select max(hos.date) from Host hos)")
public List<Host> findHosts();
but it is constantly throwing exception:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: 0 near line 1, column 126 [SELECT h FROM com.iie.model.Host h WHERE h.date between (SELECT subtime((Select max(ho.date) from com.iie.model.Host ho ), 0 0:5:0)) and (Select max(hos.date) from com.iie.model.Host hos)]
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:54)
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:47)
at org.hibernate.hql.internal.ast.ErrorCounter.throwQueryException(ErrorCounter.java:79)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:276)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:180)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:136)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:101)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:80)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:119)
at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:214)
at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:192)
at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1537)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:285)
finally. I've tried to pass the '0 0:5:0' as a parameter, but it throws an exception that this is not a proper Joda DateTime format.
The Host:
#Entity
#Table(name = "HOST")
public class Host {
#Id
#GeneratedValue
#Column(name = "HOST_ID")
private Long id;
#Column(name = "HOSTNAME")
private String hostname;
#Column(name = "IP")
private String ip;
#Column(name = "DATE_OF_SCAN")
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
private DateTime date;
public Host(Long id, String hostname, String ip, DateTime date) {
this.id = id;
this.hostname = hostname;
this.ip = ip;
this.date = date;
}
public Host(String hostname, String ip, DateTime date) {
this.hostname = hostname;
this.ip = ip;
this.date = date;
}
public Host() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getHostname() {
return hostname;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public DateTime getDate() {
return date;
}
public void setDate(DateTime date) {
this.date = date;
}
this part is not valid SQL
SELECT h FROM Host h
I suggest changing to explicit column names
SELECT h.mycolumn FROM Host h