Queries in EJB QL Collection Member Comparison - ejbql

I need to write the following sql query, in EJBQL.
SELECT * FROM teacher t left join student_class sc on t.id_teacher = p.id_student_class where t.login = 01325 and sc.id_student_class = 3;
In the class bean Teacher, there is the following relantionship:
#Column(name = "login")
private String mLogin;
#OneToMany(mappedBy = "mTeacher", fetch = FetchType.LAZY)
private List<StudentClass> mStudentClassList ;
I know how start it:
SELECT mTeacher FROM Teacher mTeacher INNER JOIN mTeacher.mStudentClassList mStudentClassList WHERE mTeacher.mLogin = 01325 AND mStudentClassList
... ???
Can anyone help me?

Related

JPA find first by created in given list

I have a model class which has these fields:
class Student {
Long roleNo;
Long version;
Date createdAt;
Date updateAt;
}
Now I'm trying to write a JPA query in which I can pass a list of role numbers and for every role number I can get a latest record by version.
I tried to do this but I'm only getting one record:
findFirstByRoleNoIn(List<Long> roleNo);
One way is to make use of #Query annotation to write simple query. Sample query for your case:
#Query(nativeQuery = true, value = """
SELECT s.* FROM student s
INNER JOIN (SELECT
role_no,
MAX(version) AS latest
FROM student GROUP BY role_no) t_version
ON s.role_no = t_version.role_no
AND s.version = t_version.latest
WHERE s.role_no IN (:roleNo)
""")
findLatestVersion(#Param("roleNo") List<Long> roleNo);

Named Query with map values in Spring Data JPA

i have a hashmap like which has key value pair such as :
testMap = new HashMap(){{
put(iD, sID);
put(lEVEL, sLevel);
put(tYPE, sType);
put(vALUE, sValue);
Now i have my entity Class which has fields like
#Column(name = "ID")
private String id;
#Column(name = "LEVEL")
private String level;
#Column(name = "PROGRAM")
private String program;
#Column(name = "TYPE")
private String type;
#Column(name = "VALUE")
private String value;
#Column(name = "STARTDATE")
private Date start;
Now i need to form a named query where for each column i have to set values by extracting them from map key value pair (something like this :tesMap.get(iD) and setting in ID column) and also comparing the start date with the current date and setting the largest date in startdate column .
Tried the following using Named query but didn't work .
#Query(value = "SELECT * FROM OVERRIDES s " +
"WHERE s.ID =?1 AND s.LEVEL=?2 AND s.TYPE=?3 AND s.VALUE=?4", nativeQuery = true)
Also ,unable to decide how to compare dates in this select query .
Comparing the dates can be easily done with Date.after() method.
E.g.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date1 = sdf.parse("2022-07-01");
Date date2 = sdf.parse("2022-08-31");
if(date1.after(date2)) {
//set largest date in date column
}
As for the rest question, please provide the whole Entity, or maybe more infos about the query
In SQL, you can compare dates/timestamps with relational operators >, <, >=, <= i.e.
#Query(value = "SELECT * FROM OVERRIDES s " +
"WHERE s.ID =?1 AND s.LEVEL=?2 AND s.TYPE=?3 AND s.VALUE>=?4", nativeQuery = true)

spring jpa query SQLGrammarException on join #ManyToMany

I have Way POJO in my Spring Boot application, here is what it looks like:
public class Way {
#Id
private Long wayID;
#ToString.Exclude
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY,cascade=CascadeType.ALL)
private List<Relation> relations=new ArrayList<>();
Now I want to select all the ways that has relationID of a given value.
relationID is a member of Relation POJO.
So here is my query
#Query("select w from Way w join Relation relations where relations.relationID=?1")
List<Way> selectAllWaysByRelationID(Long relationID);
But the error seems to be in my JOIN portion, it crashes and says:
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [select way0_.wayid as wayid1_5_ from way way0_ inner join relation relation1_ on where relation1_.relationid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
How can I resolve this ?
I also tried this:
#Query("select way from Way.relations full join Way.relations rel where rel.relationID=?1")
List<Way> selectAllWaysByRelationID(Long relationID);
This too:
#Query("select way from Way way join Way.relations rel where rel.relationID=?1")
List<Way> selectAllWaysByRelationID(Long relationID);
When using JOIN you need to use alias of Way with relations field like this, w.relations r instead of Way.relations.
The complete query is below:
#Query("select w from Way w join w.relations r where r.relationID=?1")
List<Way> selectAllWaysByRelationID(Long relationID);

Spring Data - Custom DTO Query with filtering

I have a complexe application and I need to retrieve and filter 1000~5000 object for an xls export. Each object having multiple eager relationship (I need them for the export).
If I retrieve all the objects and their relationship as it is, I got some stackoverflow error.
Generaly when I need to make a big export, in order to make it efficient I use a DTO object with an #Query like this :
public interface myRepository extends JpaRepository<Car, Long> {
#Query("SELECT new com.blabla.myCustomObject(p.name, p.surname, c.model, c.number ...) "
+ "FROM Car c "
+ "LEFT JOIN c.person p "
+ "WHERE ... ")
List<myCustomObject> getExportCustomObject();
}
The problem is that the #Query is static and I want to add dynamic filter to my Query (Specifications, Criteria or some other system...)
How to do it ?
Specification cannot be used because this is only the where clause.
But you can use Criteria API. Here's an example. The BasicTeacherInfo is the DTO:
CriteriaQuery<BasicTeacherInfo> query = cb.createQuery(BasicTeacherInfo.class);
Root<Teacher> teacher = query.from(Teacher.class);
query.multiselect(teacher.get("firstName"),teacher.get("lastName"));
List<BasicTeacherInfo> results = em.createQuery(query).getResultList();
You can use #Param annotation to pass dynamic values to HQL, something like:
#Query("SELECT new com.blabla.myCustomObject(p.name, p.surname, c.model, c.number ...) "
+ "FROM Car c "
+ "LEFT JOIN c.person p "
+ "WHERE c.status = :status AND p.name = :name")
List<myCustomObject> getExportCustomObject(
#Param("status") Integer status,
#Param("name") String name
);
Below is one of the possible way where you can try to add offset and limit into your query you can make it dynamic with the help off placeholders.
Below is an sample pseudo code for reference:
Dao Layer:
#Query(value="SELECT e FROM tablename e WHERE condition_here ORDER BY e.id offset :offset limit:limit ")
public returnType yourMethod(String name, int offset, int limit);
Service Layer:
long count = number of records in db.
int a = // number of records to be fetched on each iterations
int num_iterations = count % a ;
int additionalrecords = count / a;
int start= 0;
while(num_iterations>0)
{
dao.yourMethod(start,a);
start = start+a;
count--;
// write your data to excel here
}
dao.yourMethod(start,additionalrecords);
Hope it is helpful.

DTO Projection of Query with #ElementCollection causes 'Unable to locate appropriate constructor on class' error

I'm writing custom query using projection to reduce amount of queries in one session, when couple of field from antity are needed and using Fetch join.
Unfortunately got stuck into a problem when one types in returned dto is a collection.
I have following class with #ElementCollection (siplified version for this purpose):
#Entity
class MyClass(
val someString: String,
#ElementCollection
#Enumerated(EnumType.STRING)
var enums: MutableSet<Enum>,
#ManyToOne
var condition: Condition
//And so on...
)
And DTO used for projection:
data class ProjectionDTO(
val aString: String,
val enumList: List<Enum>
)
But when using query:
fun query(condition: Condition): List<ProjectionDTO> =
entityManager.createQuery(
"""SELECT NEW com.xxx.ProjectionDTO( m.someString, e ) FROM MyClass m
INNER JOIN FETCH m.enums e
WHERE m.condition = :condition""", ProjectionDTO::class.java)
.setParameter("condition", condition)
.resultList
}
I get following exception:
Exception:[org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate appropriate constructor on class [com.xxx.ProjectionDTO]. Expected arguments are: com.xxx.String, com.xxx.Enum [SELECT NEW com.xxx.ProjectionDTO( m.someString, e ) FROM MyClass m
INNER JOIN FETCH m.enums e
WHERE m.condition = :condition]]
Already tried different types of collection, additional constructors and calling field of given #ElementCollection like e.enum in query params.
Is it possible to return a list (or other collection) from this kind of query? If so, how do I tackle it?
It is not allowed to use collection path expressions in constructor query. link
Put just root entity into constructor:
SELECT NEW com.xxx.ProjectionDTO(m) WHERE m.condition = :condition
In constructor assign m.enums, m.someString to fields.

Resources