Modify SQL based on DAO method param in Spring Data #Query - spring

I have a Spring Data query method where the SQL/HQL can vary depending on a method param passed to the method, in this case the boolean methodParam. Is there a way to achieve the below?
#Query("select ... " +
".... " +
"...." +
(methodParam ? "variant1" : "variant2") +
"....")
public List<MyObject> findObjects(boolean methodParam);

It's better to create the query to use OR and two different conditions e.g.
select
...
where
...
AND (
(methodParam = 1 AND <variant1 conditions>)
OR
(methodParam = 0 AND <variant2 conditions>)
)
...
OR in case of boolean you can just declare two methods.

Related

Spring Data JPA native query parameters are omitted

How do I check that parameters are correctly passed into an Hibernate native query ?
This is my Spring Data repository:
public interface TasksRepository extends JpaRepository<Task, Long> {
#Query(value = "SELECT * FROM tasks_table WHERE jsonb_path_exists(task_tags, "
+ "'$[*] ? (#.entityCode == $code && #.entityId == $id)', "
+ "'{\"code\":\"?1\", \"id\":\"?2\"}')",
nativeQuery = true)
List<Task> findTasksByEntityCodeAndEntityId(String entityCode, UUID entityId);
}
And these are my logs:
Hibernate:
SELECT
*
FROM
tasks_table
WHERE
jsonb_path_exists(task_tags, '$[*] ? (#.entityCode == $code && #.entityId == $id)', '{"code":"?1", "id":"?2"}')
But I can see no means to check that the variables are replaced before the query is passed to PostgreSQL 13; which seems odd, but consistent with classical derived queries I also logged, ending up with traces like "where task0_.task_name=?" where the parameters values aren't shown either.
Does anyone know why my positional parameters are not interpreted with this native query, since I understood that they should be?
Thanks for any tip!

Hibernate Formula doesn't allow any custom Java code in Getter

I have the following Hibernate formula, which works, but the custom WordUtils.capitalizeFully modifier in the Getter does not work. I need to not only get the value of the formula but then also apply the custom Java code to it. Is there a way to do it? My result does not have the modification.
#Formula("case when preferred_first_name is not null then preferred_first_name else first_name end || ' ' || case when preferred_last_name is not null then preferred_last_name else last_name end")
public String getFullNameFL() {
return WordUtils.capitalizeFully(fullNameFL);
}
This is used in a JPA Projection,
CompoundSelection<ResultBean> selection = cb.construct(
ResultBean.class,
...
nvRoot.get("fullNameFL"),
...
criteriaQuery.select(selection);
TypedQuery<ResultBean> query = entityManager.createQuery(criteriaQuery);
List<ResultBean> results = query.getResultList();

Spring Data JPA: Parameterize #Query Annotation String, Refactor Two Similar #Query Methods

In Spring Data I have 2 very large queries which are essentially identical, but with small differences. I don't want to duplicate the methods for both queries. Suppose I have
Method 1
#Query(value = ".. " +
" .. " +
//... big query
"...")
public List<Bean> getResult(#Param("studyId") long studyId);
Method 2
#Query(value = ".. " +
" .. " +
//... big query, after WHERE:
" and (:startDate is null or :startDate = '' or r.recall_date >= to_date(cast(:startDate as TEXT) " +
"...")
public List<Bean> getResult(#Param("studyId") long studyId, #Param("startDate" String startDate);
My goal is:
1) Parameterize the #Query string so that it can either take or omit the optional additional WHERE as a sub-string.
2) Somehow refactor the methods so they don't call separate SQL. The only difference is the additional parameter in Method 2.
Is this possible?
Something like this should work
interface ReportTypeRepository extends PagingAndSortingRepository<ReportType,String> {
final String report = " select r from ReportType r ";
final String where = " where r.active=:active ";
final String sort = " order by r.id asc ";
#Query(report + sort) // <-- all with sort
List<ReportType> findByQuery();
#Query(report + where + sort) // <-- all with where and sort
List<ReportType> findByActiveQuery(#Param("active") boolean active);
}
Another (probably better) solution is to use Spring Data JPA with Querydsl or the JPA 2 Criteria API where you can define some Predicates and combine then to use multiple constraints.
You could also take a look on Specification if you plan to do dynamic queries.

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.

How to pass Byte value to JPQL in Spring?

I have an Interface wich
extends JpaRepository
I have method in it:
#Query("SELECT a FROM ArticlesEntity a " +
"WHERE ((:approved = 2 ) or (a.approved = :approved)) ")
Page<ArticlesEntity> filterByAllPage(
#Param("approved") Byte approved,
Pageable pageable);
When this method is invoked I have an exeption:
java.lang.IllegalArgumentException: Parameter value [2] did not match expected type [java.lang.Integer (n/a)]
When I change
(:approved = 2 )
to
(:approved is null)
or
#Param("approved") Byte approved
to
#Param("approved") Integer approved
or
"WHERE ((:approved = 2 ) or (a.approved = :approved))"
to
"WHERE (a.approved = :approved) "
it works.
It seems that should be something like
(:approved = (byte) 2 )
or
(:approved=2.byteValue())
but both didn't work also.
Is there any way to use Byte here or it's better to switch to Integer ?
The database is MySQL and it has table "articles" with column "approved" of "TINYINT(1)" type.

Resources