SQLGrammarException with native Query in spring boot - sql

I tried to run a GettMapping with Postman. But it's not working and I am getting the error:
Status 500 error. SQLGrammarException: could not extract ResultSet
#GetMapping("/clients/month/{month}")
public Meter getAllMeterByMonth(#PathVariable (value = "month") String month) {
return meterRepository.findByMonth(month);
}
Repository:
public interface MeterRepository extends JpaRepository<Meter, Long> {
Meter findByClientId(Long clientId);
#Query(value = "select * from meter where month = :month", nativeQuery = true)
Meter findByMonth(#Param("month")String month);
}
Client Entity:
#Entity
#Table(name = "clients")
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(max = 100)
private String name;
Meter entity:
#Entity
#Table(name = "meters")
public class Meter{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Column(name="year")
private int year;
#NotNull
#Column(name="month")
private String month;
#NotNull
private int value;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "client_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#JsonIdentityReference(alwaysAsId=true)
#JsonProperty("client_id")
private Client client;
Do you have any ideas about my issue ?

You are facing this error just because of a simple typo. Replace meter with meters in the query mentioned in MeterRepository.java.
Something like this:
package com.stackoverflow;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface MeterRepository extends JpaRepository<Meter, Long> {
#Query(value = "select * from meters where month = :month", nativeQuery = true)
Meter findByMonth(#Param("month")String month);
}

Related

Query Many-to-many jpa

I need to implement SELECT with a many-to-many relationship in #QUERY. Perhaps I am misinterpreting the information written in the documentation.
My query looks like this:
#Query("select massages.id from massages join string_massage on massages.id = string_massage.massage_id where string_massage.string_id = ?1")
List<MasageEntity> findMassagesIdByStringId(#Param("strings_id") long strings_id);
In my example, I use table names. The names are underlined as an error (without compilation). Maybe I should use Entities. Then how do I do it with many-to-many relationship?
I will show a part of my Entities.
I have two Entities. MasageEntity and RstringEntity.
//MasageEntity
#Entity
#Table(name = "massages")
public class MasageEntity {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "string_text")
private String string_text;
#Column(name = "string_speed")
private Long string_speed;
#Column(name = "string_color_type")
private Long string_color_type;
#Column(name = "string_color")
private String string_color;
#Column(name = "string_timing_type")
private String string_timing_type;
#Column(name = "string_timing")
private String string_timing;
#Column(name = "showed")
private Long showed;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "string_massage",
joinColumns = { #JoinColumn(name = "massage_id") },
inverseJoinColumns = { #JoinColumn(name = "string_id") })
//RstringEntity
#Entity
#Table(name = "string")
public class RstringsEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name="code")
private String code;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}, mappedBy = "strings")
#JsonIgnore
private Set<MasageEntity> masagess = new HashSet<>();
public RstringsEntity() {}
There are multiple ways how to perform queries in Spring Boot: native SQL and JPQL.
In case of native queries we are using pure SQL language, defining query on DB level.
In case of Java Persistence Query Language (JPQL) we define query via entity objects.
Solution 1, native queries
You created the native query in repository, but to use it we need mark it like SQL nativeQuery = true. It required for framework to understang what query laguage do you use. #Query annotation use JPQL by defult, so that's the reason of your errors.
#Repository
public interface MessageRepository extends JpaRepository<MassageEntity, Long> {
//find MessageEntities by String ID via native query
#Query(value = "select massages.* from massages join string_massage on massages.id = string_massage.massage_id where string_massage.string_id = ?1", nativeQuery = true)
List<MassageEntity> findMassagesByStringIdNativeSQL(#Param("strings_id") long strings_id);
//find Message IDs by String ID via native query
#Query(value = "select massages.id from massages join string_massage on massages.id = string_massage.massage_id where string_massage.string_id = ?1", nativeQuery = true)
List<Long> findMassagesIdByStringIdNativeSQL(#Param("strings_id") long strings_id);
}
Solution 2, JPQL queries
Example how to define JPQL queries for your case. JPQL will be translated to SQL during execution.
#Repository
public interface MessageRepository extends JpaRepository<MassageEntity, Long> {
//find MessageEntities by String ID via JPQL
#Query("select message from MassageEntity message join message.strings string where string.id = :strings_id")
List<MassageEntity> findMassagesByStringIdJPQL(#Param("strings_id") long strings_id);
//find Message IDs by String ID via JPQL
#Query("select message.id from MassageEntity message join message.strings string where string.id = :strings_id")
List<Long> findMassagesIDByStringIdJPQL(#Param("strings_id") long strings_id);
}
Native query generated by Hibernate:
select
massageent0_.id as id1_3_,
massageent0_.string_text as string_t2_3_
from
massages massageent0_
inner join
string_massage strings1_
on massageent0_.id=strings1_.massage_id
inner join
string rstringsen2_
on strings1_.string_id=rstringsen2_.id
where
rstringsen2_.id=?
Solution 3, Spring auto-generated queries
Spring can auto-generate queries by repository method definition.
Example for your case:
#Repository
public interface MessageRepository extends JpaRepository<MassageEntity, Long> {
//find MessageEntities by String ID
List<MassageEntity> findByStrings_Id(#Param("id") long strings_id);
}
Entries which I used fro sulutions:
#Entity
#Table(name = "massages")
public class MassageEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "string_text")
private String string_text;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "string_massage",
joinColumns = { #JoinColumn(name = "massage_id") },
inverseJoinColumns = { #JoinColumn(name = "string_id") })
private Set<RstringsEntity> strings = new HashSet<>();
}
#Entity
#Table(name = "string")
public class RstringsEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "code")
private String code;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}, mappedBy = "strings")
#JsonIgnore
private Set<MassageEntity> massages = new HashSet<>();
}

could not execute query; SQL nested exception is org.hibernate.exception.SQLGrammarException: could not execute query

I have a native SQL query like this, But when I run my program and I got an Error message like my topic. But In normal SQL (without Hibernate) My query works fine..I am a new student for spring and hibernate. I really need your help.
#Query(value = "select b.bookingID,b.rentDate,b.returnDate,b.custNICNumber,b.bookingStatus,rd.lossDamage,rd.driverNICNumber,rd.vehicleRegID " +
"from booking b,bookingdetails rd where (b.bookingID=rd.bookingID) and b.bookingStatus='Rejected' && rd.vehicleRegID=?", nativeQuery = true)
Booking getReturn(String vehicleRegID);
My Booking Entity -
#AllArgsConstructor
#NoArgsConstructor
#Data
#Entity
public class Booking {
#Id
private String bookingID;
private Date rentDate;
private Date returnDate;
private String bookingStatus; //For Admin Approvel
private String rentStatus;
#ManyToOne
#JoinColumn(name = "custNICNumber", referencedColumnName = "custNICNumber")
private Customer custNICNumber;
#OneToMany(mappedBy = "bookingID")
private List<BookingDetails> bookingDetails = new ArrayList<>();
My BookignDetails Entity -
#AllArgsConstructor
#NoArgsConstructor
#Data
#Entity
public class BookingDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int bookingDetailsID;
private double lossDamage;
private String lossDamageImage;
#ManyToOne
#JsonIgnore
#JoinColumn(name = "bookingID", referencedColumnName = "bookingID")
private Booking bookingID;
#ManyToOne
#JsonIgnore
#JoinColumn(name = "vehicleRegID", referencedColumnName = "vehicleRegID")
private Vehicle vehicleRegID;
#ManyToOne
#JoinColumn(name = "driverNICNumber", referencedColumnName = "driverNICNumber",nullable = true)
private Drivers driverNICNumber;
Thank You Very Much..!
Your query uses the '&&' operator, it should be AND
b.bookingStatus='Rejected' && rd.vehicleRegID=?

EntityManager.getResultList() does not return the expected result

select a.AssignAccOwner,a.AssignBillRate,a.AssignPerdiem from orion_db.assignment as a where a.AssignmentId=25
I am able to execute the above query successfully . But when I try to execute the same query programatically using entityManager.createnativeQuery I am getting the resultList as 0
Below is the code
buff.append("select a.AssignAccOwner,a.AssignBillRate,a.AssignPerdiem from orion_db.assignment as a where a.AssignmentId=25");
Query q2 = entityManager.createNativeQuery(buff.toString());
resultList = q2.getResultList();
System.out.println("Geting the resultList in dao layer " + q2.getResultList().size());
System.out.println("Geting the result in dao layer " + resultList.size());
This is the result which I get in the log
Geting the resultList in dao layer 0
Geting the result in dao layer 0
Below is the assignment.java entity class
/**
* Assignment generated by hbm2java
*/
#Entity
#Table(name = "assignment")
public class Assignment implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Integer assignmentId;
private String assignAccOwner;
private BigDecimal assignBillRate;
private String assignPerdiem;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "AssignmentId", unique = true, nullable = false)
public Integer getAssignmentId() {
return this.assignmentId;
}
public void setAssignmentId(Integer assignmentId) {
this.assignmentId = assignmentId;
}
#Column(name = "AssignBillRate", precision = 10)
public BigDecimal getAssignBillRate() {
return this.assignBillRate;
}
public void setAssignBillRate(BigDecimal assignBillRate) {
this.assignBillRate = assignBillRate;
}
#Column(name = "AssignPerdiem", length = 30)
public String getAssignPerdiem() {
return this.assignPerdiem;
}
public void setAssignPerdiem(String assignPerdiem) {
this.assignPerdiem = assignPerdiem;
}
#Column(name = "AssignAccOwner", length = 100)
public String getAssignAccOwner() {
return this.assignAccOwner;
}
public void setAssignAccOwner(String assignAccOwner) {
this.assignAccOwner = assignAccOwner;
}
}
It was not the code issue , I was not connected to the local database and hence I was not getting the expected result..

JPQL query for left outer join over parent vs inherited object

Entities/Model:
#Inheritance(strategy=InheritanceType.JOINED)
public class UserAccount implements CommonUserAccount {
#Id
private Long id;
private String email;
#Embedded
private PersonalInfo personalInfo = new PersonalInfo(); // name/surname - regular stuff
#ElementCollection
#CollectionTable(name = "UserAccountTags", joinColumns = #JoinColumn(name = "accountId", nullable = false))
#Column(name = "tag")
//#Transient
private Set<String> tags = new HashSet<String>();
#ElementCollection
#CollectionTable(name = "UserAccountRoles", joinColumns = #JoinColumn(name = "accountId", nullable = false))
#Enumerated(EnumType.STRING)
#Column(name = "userRole")
private Set<UserAccountRole> userRoles = new HashSet<UserAccountRole>();
// regular getters/setters
}
#Entity
#Table
#PrimaryKeyJoinColumn(name = "useraccountid")
public class DemoUserAccount extends UserAccount implements CommonUserAccount {
#Column
private String passwordHash;
#Column
private Long failedLogins;
#Column
#Temporal(TemporalType.TIMESTAMP)
Date lockedAt;
// regular getters/setters
}
Question:
Is it possible to build query using JPQL (for JPA2.0) that would return DemoUserAccounts joined on parent table - UserAccounts? Doing this would assume I can filter on tags/user_roles as well. In general some records will not have DemoUserAccount specific fields filled in.
When you do a SELECT from DemoUserAccount, you already have the UserAccount fields available to do a query using them.
So, if you want to filter by email and failedLogins:
SELECT d FROM DemoUserAccount d WHERE d.email = 'you#you.com' AND d.failedLogins > 3

Best way to get aggregate function result inside the entity object

Many time I have to get the SQL aggregate query result inside the Entity Object itself. As of now I could able to achive the same by the following code
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> q = cb.createTupleQuery();
Root<Test> c = q.from(Test.class);
q.multiselect(c, cb.count(c));
q.groupBy(c.get("type"));
TypedQuery<Tuple> t = em.createQuery(q);
List<Tuple> resultList = t.getResultList();
List<Test> list = new ArrayList<>();
for(Tuple tuple : resultList){
Test te = (Test) tuple.get(0);
te.setQuantity((long)tuple.get(1));
list.add(te);
}
But I want to know what could be the best way. My Test Entity is as
#Entity
#Table(name = "test")
public class Test {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "type")
private Integer type = 0;
#Transient
private long quantity;
}
If you cannot use #Formula then I'd suggest create a database view basic on your select and mapping an additional entity to that. You can then map this to your existing entity using either as a #OneToOne or by using #SecondaryTable.
This has the added advantage of being JPA compliant (i.e. not using Hibernate's propreitary #Formula) and would look something like:
#Entity
#Table(name = "test")
public class Test {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "type")
private Integer type = 0;
#OneToOne//or via secondary table
private TestSummaryInfo summaryInfo;
public long getQuantity(){
return summaryInfo.getQuantity();
}
}
Summary mapped to a view:
#Entity
#Table(name = "vw_test_summary")
public class TestSummaryInfo {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "quantity")
private Long quantity;
}