Dynamic like query in spring data jpa - spring-data-jpa

Can a dynamic query be written in spring data rest as follows. If not then how to achieve a similar functionality:
#Query("select s from Screen s where s.#searchColumn like:searchValue%")
#RestResource(path="byString")
Page findAll(
#Param("searchColumn") String searchColumn,
#Param("searchValue") String searchValue,
Pageable pageable);

Solved
Repo
#Query("select o from Screen o where "
+ "(o.screenName like :val% and :prop = 'screenName') or "
+ "(o.address like :val% and :prop = 'address')")
#RestResource(path="byString")
Page findAll(
#Param("prop") String prop,
#Param("val") String val,
Pageable pageable);
Query:
/api/screens/search/byString?prop=address&val=a
Tested it with prop=address as well as prop=screenName. Working :)

No. Spring Data JPA support only entityName variables inside SpEL based query templates
For dynamic queries use:
Specifications
Query by Example
Querydsl Extension

Related

Spring Data JDBC and specification pattern

We got an Spring Boot 2 application using Spring Data JDBC. Its displaying a list of orders which needs to be filtered for many different aspects and combinations. Therefore, our Repository is growing more and more, because for new combinations find* methods are added or new parameters are applied.
public interface OrderJdbcRepository extends PagingAndSortingRepository<OrderEntity, Long> {
String ORDER_FILTER = "WHERE ..."; // big custom sql statement
String NOT_BILLED = "...";
Optional<OrderEntity> findById(String orderId);
#Query(
"SELECT o.* FROM `ORDER` AS o "
+ ORDER_FILTER
+ "ORDER BY o.ORDER_ID "
+ "LIMIT :limit OFFSET :offset"
)
List<OrderEntity> findFiltered(String customerId, LocalDate startDate,
LocalDate endDate, ...some other filter criterias, int limit, long offset);
#Query(
"SELECT o.* FROM `ORDER` AS o "
+ ORDER_FILTER + NOT_BILLED // additional filter for not billed orders
+ "ORDER BY o.ORDER_ID "
+ "LIMIT :limit OFFSET :offset"
)
List<OrderEntity> findFilteredAndNotBilled(String customerId, LocalDate startDate,
LocalDate endDate, ...some other filter criterias, int limit, long offset);
// many other methods which looks similar
I'm afraid of that this leads to unmaintainable code. Therefore I'm trying to use the composite specification pattern like its described in the link. For example i just need to combine CustomerIdSpecification, DateBetweenSpecification and NotBilledSpecification. However, the linked tutorial is for Spring Data JPA with Criteria API, which does not exist in Spring Data JDBC. So I'm thinking of how i could solve this problem in Spring Data JDBC.
I need a way to define the custom sql in each specification and pass it to my OrderJdbcRepository. An alternative I thought is to use JdbcTemplate for building a custom sql and accessing my data by hand. Are there other solutions, which I did not think of?
Thanks so far!

How to compose query using the #Query annotation and HQL

Can somebody help how to compose a HQL query with JPA #Query on the following line?
"get me all books which has this author and are published after year X"
Consider a simple entity without relationship - more concern on the JPA #Query and HQL combination:
#Entity
public class Book{
#Id
public int id;
public String title,
public String authorName;
public String yearOfPublish;
.....
}
The query would be:
select b from Book b where b.authorName = :authorName and yearOfPublish > :year
And as you are using Spring Data JPA you don't even need to write the Query! Simply write a method:
List<Book> findAllByAuthorNameAndYearOfPublishGreaterThan(String authorName, String yearOfPublish);
This will create the query for you.
Read for about Query methods in the official documentation: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods

How to avoiding AND condition if parameter is null in Spring Data JPA query

I am trying to get the result of one query using Spring Data JPA. Here I am sending some parameter and receiving result according to that.
My repository query is,
#Query("select u.username,p.pname from Users u join u.priviJoin p where u.username = :uname AND p.pname = :pname")
List<Users> findByUsername(#Param("uname") String uname , #Param("pname") String pname );
And calling from controller like the following,
#RequestMapping(value = "/joinResult", method = RequestMethod.GET)
public List<Users> joinResultShow()
{
return (List<Users>) userRepo.findByUsername("test_user","testRole");
}
Here we can see that if I am passing some value then only checking according to that parameter. Here I need to modify my query like if parameter is null, then not need to use AND condition in query.
How can I modify this query for avoiding AND condition if parameter is null? I am new to Spring Data JPA world.
Here are some possible options for you
1. Create multiple methods in your repository like
#Query("select u.username,p.pname from Users u join u.priviJoin p where u.username = :uname AND p.pname = :pname")
List<Users> findByusernamewithRole(#Param("uname") String uname , #Param("pname") String pname );
#Query("select u.username,p.pname from Users u join u.priviJoin p where u.username = :uname")
List<Users> findByUsernameWithoutRole(#Param("uname") String uname);
Write a custom respository and use EntityManager. With this you can create a dynamic queries based on your input using CriteriaBuilder and use this criteria in querying.
Last and the most preferred option in case of dynamic inputs(like you have) is Querydsl.
Some articles about querydsl
http://www.baeldung.com/querydsl-with-jpa-tutorial
http://www.querydsl.com/static/querydsl/latest/reference/html/ch02.html

Calling StoredProcedure using JPA in Spring Boot

I am trying to call a stored procedure, which is built in mysql, in my Spring boot app using JPA. My stored procedure returns the result which cant be contain in single model as it fetches data from combination of tables.
I can do this with "call " but i guess that is not JPA's way. COuld you please let me know what is the best way to do it?
In case you're using plain JPA you need to do a native query call. Something like below.
Query q = em.createNativeQuery("select my_store_pro(?, ?)");
List<Object[]> results = q.getResultList();
for (Object[] a : results) {
System.out.println("result " + a[0] + " " + a[1]);
}
If you're using Spring Data repositories then you want something like below.
#Query(nativeQuery = true, value = "select my_store_pro(?, ?)")
Date callMyStoreProc(int val1, int val2);

I am trying to use dynamic order by but the list retrieved is not ordered

public List<Series> findSeries(int period, String fieldname, int num) {
TypedQuery<Series> query = em.createQuery(
"select s from Series s where s.period = ?1 order by ?2",
Series.class);
query.setParameter(1, period);
query.setParameter(2, fieldname);
query.setMaxResults(num);
return query.getResultList();
}
This is the method I am using. I think order by isn't even getting executed, it doesn't give any error even when I pass incorrect fieldname.
When it comes to dynamic limit and ordering, its best to use PagingAndSortingRepository so now my Repository extends this repository. I can simply use JPA criteria query as below.
If u want to learn more about JPA criteria query i found this very helpful http://docs.spring.io/spring-data/data-jpa/docs/1.0.x/reference/html/#jpa.query-methods.query-creation
#Repository
public interface SeriesRepository extends PagingAndSortingRepository<Series,Long>{
List<Series> findByPeriod(int period, Pageable pageable);
}
And then when I call this method from my dao i can just instantiate PageRequest which is one of the implementation of Pageable. I can add limit and sorting order to this instance.
public List<Series> getSeriesByFilter(int period, String fieldname, int num) {
Sort sort = new Sort(Sort.Direction.ASC, fieldname);
Pageable pageable = new PageRequest(0, num, sort);
return seriesRepo.findByPeriod(period, pageable);
}
You cannot pass variables as column name in order by.
There is a work around which may help you achieve what you are trying.
public List<Series> findSeries(int period, String fieldname, int num) {
String query = "select s from Series s where s.period = "+period+" order by "+fieldname;
return entityManager.createQuery(query).getResultList();
}
Check this question Hibernate Named Query Order By parameter
There are ways to pass column name in order by in ASP, however I am not able to find anything in Spring or JPA.
"Order By" using a parameter for the column name
http://databases.aspfaq.com/database/how-do-i-use-a-variable-in-an-order-by-clause.html