select max native query hibernate - mysql

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;
}

Related

Pagination with native query using alias in spring boot

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)

Custom #Query in JPA

I am trying to do a #Query in JPA that does not return a Entity from my Model.
In my Model I have BudgetPlan, BudgetGeschaftsfeld, BudgetMarke, BudgetKampagne, BudgetBucket, Kosten Entities which have a #OneToMany relationship.
So Budgetplan has many BudgetGeschaftsfeld which has many BudgetMarke and so on.
I want to return a CustomKosten with all couple of the standard Kosten attributes, and the ID to each "Parent ID". A Pojo of the Class would look like this.
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class CustomKosten {
private long id;
private String name;
private double betrag;
private String status;
private Date local_date;
private long idBucket;
private String bucket;
private long idKampagne;
private String kampagne;
private long idMarke;
private String marke;
private long idGeschaeftsfeld;
private String geschaeftsfeld;
private long idPlan;
private String plan;
}
I have accomplished the SQL query in MySQL and it gets the result I want. Which looks like this.
public interface CustomKostenRepository extends JpaRepository<CustomKosten, Long> {
#Query(nativeQuery = true, value="select id, name, betrag, status, local_date, idbucket, bucket, idkampagne, kampagne, idmarke, marke, idgeschaeftsfeld, geschaeftsfeld, idplan, plan from \r\n"
+ "(select * from (select * from (select * from (select * from (select pid as pidgeschaeftsfeld, id as idgeschaeftsfeld, name as geschaeftsfeld from budman_db.budget where dtype=\"Budgetgeschaftsfeld\") as tgeschaeftsfeld \r\n"
+ "left join (select id as idplan, name as plan from budman_db.budget where dtype=\"Budgetplan\" ) as tplan on tgeschaeftsfeld.pidgeschaeftsfeld= tplan.idplan) as t1\r\n"
+ "left join (select pid as pidmarke, id as idmarke, name as marke from budman_db.budget where dtype=\"Budgetmarke\") as tmarke on t1.idgeschaeftsfeld= tmarke.pidmarke) as t2\r\n"
+ "Left join (select pid as pidkampagne, id as idkampagne, name as kampagne from budman_db.budget where dtype=\"Budgetkampagne\") as tkampagne on t2.idmarke= tkampagne.pidkampagne ) as t3\r\n"
+ "left join (select pid as pidbucket, id as idbucket, name as bucket from budman_db.budget where dtype=\"Budgetbucket\") as tbucket on t3.idkampagne= tbucket.pidbucket) as t4\r\n"
+ "left join (select * from budman_db.kosten ) as tkosten on t4.idbucket= tkosten.pid")
public List<CustomKosten> getCustomKostenKomplex();
}
is the result I'm trying to get even possible?
I am getting this Error when running my code.
Any advice is much appreciated
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.bm.ent.kosten.CustomKosten
Edit:
After following the Example Eugene adviced im getting another error.
Caused by: java.lang.IllegalArgumentException: Could not locate appropriate
constructor on class : com.bm.ent.kosten.CustomKosten
I'm guessing it has something to do with the Type of each field. And I think I don't have to put a type on those which have a primitive class.
note: I purposely left out the date to test. Any advice?
#Entity(name= "Kosten")
#Table(name= "kosten")
#NamedNativeQuery(name = "CustomKostenAll",
query= "select id, name, betrag, status, idbucket, bucket, idkampagne, kampagne, idmarke, marke, idgeschaeftsfeld, geschaeftsfeld, idplan, plan from \r\n"
+ "(select * from (select * from (select * from (select * from (select pid as pidgeschaeftsfeld, id as idgeschaeftsfeld, name as geschaeftsfeld from budman_db.budget where dtype=\"Budgetgeschaftsfeld\") as tgeschaeftsfeld \r\n"
+ "left join (select id as idplan, name as plan from budman_db.budget where dtype=\"Budgetplan\" ) as tplan on tgeschaeftsfeld.pidgeschaeftsfeld= tplan.idplan) as t1\r\n"
+ "left join (select pid as pidmarke, id as idmarke, name as marke from budman_db.budget where dtype=\"Budgetmarke\") as tmarke on t1.idgeschaeftsfeld= tmarke.pidmarke) as t2\r\n"
+ "left join (select pid as pidkampagne, id as idkampagne, name as kampagne from budman_db.budget where dtype=\"Budgetkampagne\") as tkampagne on t2.idmarke= tkampagne.pidkampagne ) as t3\r\n"
+ "left join (select pid as pidbucket, id as idbucket, name as bucket from budman_db.budget where dtype=\"Budgetbucket\") as tbucket on t3.idkampagne= tbucket.pidbucket) as t4\r\n"
+ "left join (select * from budman_db.kosten ) as tkosten on t4.idbucket= tkosten.pid",
resultSetMapping= "CustomKostenMapping")
#SqlResultSetMapping(name = "CustomKostenMapping",
classes = {
#ConstructorResult(targetClass = CustomKosten.class,
columns = {
#ColumnResult(name = "id"),
#ColumnResult(name = "name", type = String.class),
#ColumnResult(name = "betrag"),
#ColumnResult(name = "status", type = Status.class),
// #ColumnResult(name = "local_date"),
#ColumnResult(name = "idbucket"),
#ColumnResult(name = "bucket", type = String.class),
#ColumnResult(name = "idkampagne"),
#ColumnResult(name = "kampagne", type = String.class),
#ColumnResult(name = "idmarke"),
#ColumnResult(name = "marke", type = String.class),
#ColumnResult(name = "idgeschaeftsfeld"),
#ColumnResult(name = "geschaeftsfeld", type = String.class),
#ColumnResult(name = "idplan"),
#ColumnResult(name = "plan", type = String.class),
}
)
})
#Getter
#Setter
#NoArgsConstructor
public class Kosten {...}
First, if you want to use the CustomKosten class as return type of your repository, you need to place annotate it with #Entity so that it becomes a managed type for JPA / Hibernate.
This however would only work if you have a table with a structure corresponding to your entity. In your case, you query several columns from different tables. Therefore, you need to specify how Hibernate should map all the returned columns to your Java class. I recommend this tutorial on SQL result set mapping by one of the Hibernate maintainers Thorben Janssen. If you already use Hibernate 6, you could also use a TupleTransformer as shown here.

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();

#SqlResultSetMapping is not working with date field

I have following very straightforward code.
#SqlResultSetMapping(name="getTestTable", classes = {
#ConstructorResult(targetClass = someEntity.class,
columns = {
#ColumnResult(name="some_date", type=Date.class)
})
})
Query:
getEntityManager().createNativeQuery("SELECT distinct some_date from test " );
List<someList > list=query. getResultList();
Entity:
someEntity
#Column(name = "some_date")
#JsonFormat(pattern="dd/MM/yyyy")
private Date someDate;
public someEntity(java.util.Date someDate) {
super();
this.someDate= someDate;
}
There is data in 'someDate' column and query is working fine on Sql-Editor, but when running above code "some_date" is always null.
What is wrong in this code? How should I get value for date?
I even converted the date format and declared field String in code but nothing worked.
1) As you are using java.util.Date you should mark your date field with temporal type:
#Column(name = "some_date")
#Temporal(TemporalType.DATE)
#JsonFormat(pattern="dd/MM/yyyy")
private Date someDate;
2) Define your native query as NamedNativeQuery:
#NamedNativeQuery(name = "testTableNativeQuery",
query = "SELECT distinct some_date from test",
resultSetMapping = "getTestTable")
3) Update your query execution:
getEntityManager().createNamedQuery("testTableNativeQuery", someEntity.class).getResultList();

Convert MySql query in JPA Spring Boot query

I have this query, working, but I should use it in spring boot,
SELECT username
FROM user
WHERE enabled = 1
AND id IN (SELECT makerprofile_user_id
FROM makerprofile_has_job
WHERE job_id = (SELECT id
FROM job
WHERE job = "photographer"))
This is the part of the Job class
#JoinTable(name = "makerprofile_has_job", joinColumns = {
#JoinColumn(name = "job_id", referencedColumnName = "id")}, inverseJoinColumns = {
#JoinColumn(name = "makerprofile_user_id", referencedColumnName = "user_id")})
#ManyToMany
#JsonIgnore
private List<Makerprofile> makerprofileList;
and I write this, but it does not work
#Query("SELECT a.username\n"
+ "FROM ApplicationUser a \n"
+ "WHERE a.enabled = 1 \n"
+ "AND a.id IN(SELECT p.id\n"
+ " FROM Job m\n"
+ " JOIN m.makerprofileList p"
+ " WHERE p.id = (SELECT j.id\n"
+ " FROM Job j\n"
+ " WHERE j.job = :job))")
List<String> findUsernameByJob(#Param("job") String job);
who can help me? (excuse me, but this is my first question on Stack)
You can use one of the following ways:
Use EntityManager.createNativeQuery() API
Use #NamedNativeQuery annotation. Or #Query(value = "sql here", nativeQuery = true)