SQLNative query returning empty results - sql

I'm trying to execute a query which needs 4 tables :
#Query(value="SELECT e.* FROM erreur e, synop sy, synop_decode sd, station st WHERE e.id_synop = sd.id_synop_decode "
+ "and sd.id_synop_decode = sy.id_synop" + " and DATE(sy.date)= :date and "
+ "sy.id_station = st.id_station and st.id_station= :stationId", nativeQuery=true)
public List<Erreur> recherche(#Param("date") Date date, #Param("stationId") Long stationId);
This query works fine et native sql, i pass an existing stationId and a date like the following :
SELECT e.* FROM erreur e, synop sy, synop_decode sd, station st WHERE e.id_synop = sd.id_synop_decode and sd.id_synop_decode = sy.id_synop
and DATE(sy.date)= '2019-05-27' and sy.id_station = st.id_station and st.id_station= 60355;
This query works fine in Mysql Workbench.
Here's the actual controller i'm using for testing purpose :
#GetMapping("/station/{stationId}/erreurs/today")
public List<Erreur> getTodayErreurByStationId(#PathVariable Long stationId)
{
List<Erreur> erreurs = new ArrayList<Erreur>();
Optional<Station> stationOptional = stationRepository.findById(stationId);
if(stationOptional.isPresent())
{
return erreurRepository.recherche(new Date(), stationId);
}
return null;
}
The expected results are the actual "Ererur" objects in my array list, but RestClient just returns an empty array [], while the query works just fine in mysql like i described it above.
So my question is : How can i write this query into Hql language so that i can return the right entities. Or how can i map my sql results to my target custom calss "Erreur"?
#Entity
#Getter #Setter #NoArgsConstructor
#Table(name="erreur")
public class Erreur {
public Erreur(int section, int groupe, String info) {
this.section = section;
this.groupe = groupe;
this.info = info;
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id_erreur")
private Long id;
#ManyToOne(cascade= {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH},
fetch=FetchType.LAZY)
#JsonIgnore
#JoinColumn(name="id_synop")
private SynopDecode synopDecode;
#OneToOne
#JoinColumn(name="id_controle")
private Controle controle;
#ManyToOne(cascade= {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH},
fetch=FetchType.LAZY)
#JsonIgnore
#JoinColumn(name="id_station")
private Station station;
#Column(name="section")
private int section;
#Column(name="groupe")
private int groupe;
#Column(name="info")
private String info;
}

If you want to use jpa convention directly then you will have to make associations between different entities i.e. how two entities are linked. When we define these associations then spring jpa knows how to convert method name or custom queries into SQL.
Your code will need to be something like
public class Erreur {
...
#ManyToOne
#JoinColumns//define how Erreur and SynopeDecone are linked
private SynopDecode synopDecode;
...
public class SynopDecode {
...
#ManyToOne // or #OneToOne its not mentioned in question how these two are linked
#JoinColumns//define how SynopDecode and Synop are linked
private Synop synop;
...
Then you can write your query like
#Query("select e from Erreur e LEFT JOIN e.synopDecode sy LEFT JOIN sy.synop sy WHERE DATE(sy.date) = :date AND sy.id_station = :stationId")
List<Erreur> getByDateAndStationId(#Param("date") Date date, #Param("stationId") Long stationId)
You can't use method name based query because you want to use SQL function to match only "date" part of your date and not the whole timestamp.

You can use jpa methods by conventions.
Assuming SynopDecode has property like:
//SynopDecode class
#ManyToOne
private Synop synop;
//repository interface
List<Erreur> findByStationIdAndSynopDecodeSynopDate(Long stationId, Date date);
//or
//List<Erreur> findByStationIdAndSynopDecode_Synop_Date(Long stationId, Date date);
UPDATE
As Punit Tiwan (#punit-tiwan) note that, the above methods used for a specific datettime.
You can use methods below for just DATE.
//repository interface
List<Erreur> findByStationIdAndSynopDecodeSynopDateBetween(Long stationId, Date startOfDate, Date endOfDate);
//or
//List<Erreur> findByStationIdAndSynopDecode_Synop_DateBetween(Long stationId, Date startOfDate, Date endOfDate);

I figured a way to get the same results as my SQL Query using the #Query annotation and accessing object properties like this :
#Query("from Erreur e where e.synopDecode.synop.station.id = :stationId and "
+ "DATE(e.synopDecode.synop.date) = :date")
public List<Erreur> recherche(#Param("date") Date date, #Param("stationId") Long stationId);
I think it solves my problem, thanks for the help

Related

LocalDate as where clause in Intellij's JPA Console

Let's say I have a Person with a name and a birth date. Entity model is as follow :
#Entity
public class Person {
private int id;
private String name;
private LocalDate birthDate;
// constructors, getters and setters...
}
With the JPA Console, I would like to write a query like this :
#Query("select p from Person p where p.birthDate between :start and :stop")
List<Person> getPeopleBornBetween(LocalDate start, LocalDate stop);
If I try the query as it is and write the date between ' , parameters do not match expected type.
I cannot use Local.parse(...) in the parameters popup window neither in the console.
I also cannot use p.birthDate.format(...) in the console.
What should I do ?
There might be a problem in you query, try this one:
select p from Person p where p.birthDate between :start and :stop
^

Spring Data JPA: CriteriaQuery to get entities with max value for each unique foreign key

There's an Event class:
#Entity
public class Event {
#Id
private Integer id;
#ManyToOne(cascade = CascadeType.ALL)
private Company company;
#Column
private Long time;
...
}
I want to have an EventFilter class (implementing Specification) which will produce CriteriaQuery to select entities the same way as the following SQL query:
SELECT *
FROM events e1
WHERE e1.time = (
SELECT MAX(time)
FROM events e2
WHERE e1.company_id = c2.company_id
)
Filtered result will contain only events with unique Company and max time value per company.
This is the EventFilter class with what I ended up with:
public class EventFilter implements Specification<Event> {
#Override
public Predicate toPredicate(Root<Event> root, CriteriaQuery<?> q, CriteriaBuilder cb) {
Subquery<Long> subquery = q.subquery(Long.class);
Root<Event> subRoot = subquery.from(Event.class);
subquery.select(cb.max(root.get("time")))
.where(cb.equal(root.get("company"), subRoot.get("company")));
return cb.equal(root.get("time"), subquery);
}
}
When EventRepository#findAll(EventFilter filter) is called, results are not filtered at all. Please help me to implement this logic correctly.
After inspecting SQL statement generated by Hibernate I've found an error: root was used instead of subRoot. The correct method body is:
Subquery<Long> sub = q.subquery(Long.class);
Root<Event> subRoot = sub.from(Event.class);
sub.select(cb.max(subRoot.get("time")))
.where(cb.equal(root.get("company"), subRoot.get("company")));
return cb.equal(root.get("time"), sub);

Ignite SqlQuery for complex java objects

In my cache I have a complex java object as below -
class Person{
private Department d;
....
}
class Department {
private Department code;
....
}
I am using below SQLQuery to read it -
SqlQuery<Short, BinaryObject> query = new SqlQuery<>(Person.class, "d.code = ?");
String args="101"; // department code
QueryCursor<Cache.Entry<Short, BinaryObject>> resultSet = personCache.query(query.setArgs(args))
I am getting below error -
Caused by: class org.apache.ignite.internal.processors.query.IgniteSQLException: Failed to parse query: SELECT "PERSON_CACHE"."PERSONENTITY"._KEY, "TPERSON_CACHE"."PERSONENTITY"._VAL FROM "PERSON_CACHE"."PERSONENTITY" WHERE id.code = ?
Am I doing anything wrong here ?
You can access nested fields, but only if they were configured with QuerySqlField annotation in advance:
class Person{
private Department d;
...
}
class Department {
#QuerySqlField
private Department code;
....
}
SqlQuery<Short, BinaryObject> query = new SqlQuery<>(Person.class, "code = ?");
Destructuring is not supported by Ignite SQL and there are no solid plans to implement it.
This means you can't peek into fields that are rich objects, maps, lists, etc. You should introduce a departmentId numeric field here.
Theoretically you could also try putting #QuerySqlField annotation on Department's field code, and then access it as CODE = ?. Your mileage may vary. I for one would like to hear about the result of such experiment.
I resolved it by using predicate.
IgniteBiPredicate<Long, BinaryObject> predicate = new IgniteBiPredicate<Long, BinaryObject>() {
#Override
public boolean apply(Long e1, BinaryObject e2) {
Person p= e2.deserialize();
short s = (short) args[0];
return p.getId().getCode == s;
}
};

Join tables in eclipselink (jpa)

I'm trying to convert a simple sql statement to jpql but vainly. I have an entity "Banque" that has many "Compte" and i'm trying to get all the "Compte" for a specific "Banque" using id_Banque.
This is the sql statement :
SELECT COMPTE.* FROM COMPTE INNER JOIN BANQUE ON COMPTE.ID_BANQUE=BANQUE.ID_BANQUE;
============================Entity Banque=========================
#Entity
#Table(name="BANQUE")
public class Banque implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="banque_seq_gen")
#SequenceGenerator(name="banque_seq_gen", sequenceName="BANQUE_SEQ", allocationSize = 1, initialValue = 1)
private Long id_banque;
#OneToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY, mappedBy = "Banque")
private Set<Compte> comptes;
public Banque(){
}
}
============================Entity Compte=========================
#Entity
#Table(name="COMPTE")
public class Compte implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="compte_seq_gen")
#SequenceGenerator(name="compte_seq_gen", sequenceName="COMPTE_SEQ", allocationSize = 1, initialValue = 1)
private Long id_compte;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "ID_BANQUE")
private Banque Banque;
public Compte(){
}
}
And the following method help me to get all the comptes (Entity compte) associated to a specific Banque (from id_banque) :
public Set<Compte> getComptesFromIdBanque(Long id_banque){
EntityManager em=new JPAContainerFactory().createEntityManagerForPersistenceUnit("addressbook");
Query query = em.createQuery("SELECT c FROM Compte c INNER JOIN c.Banque b WHERE b.id_banque = :id_banque");
query.setParameter("id_banque", id_banque);
Set<Compte> comptes = new HashSet<>(query.getResultList());
return comptes;
}
This is an illustration of the two tables and what i'd like to have :
Then a display this list of comptes in a ComboBox :
compteCombo.addItems(saut.getComptesFromIdBanque(banque.getId_banque()));
compteCombo.setItemCaptionPropertyId("numero");
Unfortunately this doesn't show any item in the comboBox.
I hope i will find a pertinent answer as i searched for this in vain. It'll help me a lot.
Thanks.
Your JPA query seems fine to me (although as #Davids mentioned it could be simplified to SELECT c FROM Compte c WHERE c.banque.id_banque = :id_banque).
My guess is your ComboBox caption is not set up properly to show anything.
First, write a test to make sure your JPA function returns the right data. Then play around with the setItemCaptionMode of ComboBox to get the right setting. Assuming your Compete class has a getNumero() method, you can set up your Combobox like this:
compteCombo = new ComboBox( "Caption", saut.getComptesFromIdBanque(banque.getId_banque()) );
compteCombo.setItemCaptionMode( ItemCaptionMode.PROPERTY );
compteCombo.setItemCaptionPropertyId( "numero" );

Join query in hibernate search

I want to write a lucene query like
" from activity where metaentityByEntity.id in(select metaentityByEntity.id from userentity where user.id=1)"
My domain classes are:
public class Activity implements java.io.Serializable {
private Long id;
private Date timeStamp;
private User user;
#IndexedEmbedded
private Metaentity metaentityByEntity;
}
public class Userentitydetail implements java.io.Serializable {
private Long id;
private Date timeStamp;
private Metaentity metaentityByEntity;
#IndexedEmbedded
private User user;
private Metaentity metaentityByProjectId;
private byte unfollow;
private Byte isAssociated;
}
But how to write lucene query which will search from multiple indexes? basically I am doing hibernate search.
Thanks.
Lucene is not a relational database, so the short answer is you shouldn't try doing joins; your specific use case happens to be implementable because your query can be greatly simplified.
Just create a query on the field which is following the link:
QueryBuilder queryBuilder = fullTextSession.getSearchFactory()
.buildQueryBuilder()
.forEntity( Activity.class )
.get();
Query query = queryBuilder.keyword()
.onField( "metaentityByEntity.user.id" )
.ignoreAnalyzer()
.matching( 1 )
.createQuery();
You'll have to adjust some details as you omitted some mapping details; for example it might need to be
.matching( "1" )
instead.