Spring data jpa - compicated query - spring-data-jpa

I am trying to prepare compliacted query using Spring Data JPA.
Page<Line> findByIdIn_AndNumberIgnoreCaseContainingAndActualTrueAndOrNameIgnoreCaseContainingInAndActualTrueOrderById(List<Long> idList, String number, String name, Pageable pageable);
My goal is to build query like:
select * from line where id in (?,?,?) and (number like ? and actual = true) or (name like ? and actual = true) order by id
Unfortunately I have following error:
No property in found for type Long! Traversed path: Line.id.
Whats wrong with my method name?
And also I made a method:
Page<Line> findByNumberIgnoreCaseContainingAndActualTrueOrNameIgnoreCaseContainingAndActualTrueOrderById(String number, String name, Pageable pageable);
And that one works good. The problem is with id in..

Related

Pass list in JPA named native query

We want to pass list of strings into JPA named native query.How we can achieve this.it is throwing invalid query parameter exception.
#Query(value = "select e from student so where so.name in ?1", nativeQuery = true)
List<Student> someFunName(List<String> stringList);
U can try something like this for a nativeQuery, for list of string as param.
(I just gave an example for Entity Student)

Can Spring JPA projections have Collections?

I have a Customer entity from which I only want to select a few fields and their associated CustomerAddresses. I've defined a Spring Data JPA projection interface as follows:
public interface CustomerWithAddresses {
Integer getId();
String getFirstName();
String getLastName();
String getBrandCode();
String getCustomerNumber();
Set<CustomerAddress> getCustomerAddresses();
}
But from my Repository method:
CustomerWithAddresses findCustomerWithAddressesById(#Param("id") Integer id);
I keep getting NonUniqueResultException for Customers with multiple CustomerAddresses. Do projections have to have a flat structure, i.e. they don't support Collections the same way true Entities do?
you have Set<CustomerAddress> getCustomerAddresses(); it's X-to-Many relation. When spring data do select for CustomerWithAddresses it does join , in result set N-records (N - amount of CustomerAddress for CustomerWithAddresses with id = id). You can check if it you change CustomerWithAddresses to List of CustomerWithAddresses .
List<CustomerWithAddresses> findCustomerWithAddressesById(#Param("id") Integer id);
when you use entity sping data gropu multiply result into one element , gouped it by id as id it's unique identifier.
you can do :
1) add into CustomerWithAddresses interface
#Value("#{target.id}")
Integer getId();
and use your query
2) use #Query
#Query("select adr from CustomerWithAddressesEntity adr where adr.id=:id")
CustomerWithAddresses findCustomerWithAddressesById(#Param("id") Integer id);

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

Passing List<Integer> in spring data jpa native query

Using spring data JPA, I am trying to make this sort of query (it is more complex, this is a simple case)
#Query(nativeQuery = true,
value = "SELECT * FROM events WHERE typeId IN (?1)")
List<Event> findEventsByType(List<Integer> types);
When I launch the query, an exception raises:
org.postgresql.util.PSQLException: Can't infer the SQL type to use for an instance of java.util.ArrayList. Use setObject() with an explicit Types value to specify the type to use.
I have tried List < Integer >, Integer[], Object[] and String but it is not working...
Can't I pass list of values?
Which is the best approach to make this sort of queries?
Thanks!
Try taking away the #Query and make the method name:
public List<Event> findByTypeIn(List<Integer> types);
See table 2.2 in the link: http://docs.spring.io/spring-data/jpa/docs/1.2.0.RELEASE/reference/html/
I tried like below and it works for me.
#Query(value = "select * from events where type_id in :types", nativeQuery = true)
List<Event> findEventsByType(#Param("types") List<Integer> types);
#Query(value = "SELECT c from Company c where " +
"c.companyName IN (:company_names)")
List<Company> findCompaniesByName(#Param("company_names") List<String> companyNames);
This is the solution to your problem.
Here I am passing List which contains company names and I am querying DB and storing result in List.
Hope this hepls!
Use JPQL. A native query is or should be passed to the database exactly as you have created the SQL string, and unless your driver can take a serialized collection and understand that the single parameter needs to be interpreted as many, it just won't work. The collection you pass in needs the SQL expanded from (?) to (?, ?,...) based on the number of elements in a collection, and JDBC drivers just are not able to do this, and JPA providers are required to execute the string as is.
A JPQL query allows the JPA provider to create the SQL it needs dynamically based on the list passed in, so it can expand the collection for you.
Try this. It will work for Native Query in SpringBoot JPA:
#Query(value = "SELECT * FROM table WHERE columnName IN (:inputList)" ,
nativeQuery = true)
List<Object> findByObjectList(#Param("inputList") List<Object> inputList);
And also in case of JPA, try the below :
List<Object> findByObjectList(List<Object> inputList)
I know this is a little bit out of context (we use Update and not Select), but this can be usefull for others :
/**
* Update the state of list of entities using their ids
* #param ids request ids
* #param state new state
* #return
*/
#Modifying
#Query(value = "UPDATE AbstractRequest SET state = :state WHERE id IN (:ids)")
int updateStates(#Param("ids") List<Long> ids, #Param("state") InternalRequestStep state);
pass array this way inside IN Clause
#Query(value = "select name from teams where name in :names", nativeQuery = true)
List<String> getNames(#Param("names") String[] names);
Call this way
String[] names = {"testing team","development team"};
List<String> teamtest = teamRepository.getNames(names);
Remove brackets around (?1) parameter. Your query value should look like that:
#Query(value = "SELECT * FROM events WHERE typeId IN ?1")
try querying like this:
#Query(nativeQuery = true,
value = "SELECT * FROM events WHERE typeId = ?1")
List<Event> findEventsByType(List<Integer> types);
did it work?

Is it possible to be able to accept many matrix parameters in my rest URL without having to declare them in my code?

I am developing a Restful WS which does the simple job of querying a DB and bringing back some data. The table that is querying has around 20 columns.
I want to be able to filter the my returned records by using the matrix parameters in the WHERE clause of my SQL statements.
For Example:
Lets say that we have the table People with the columns id, firstname, lastname
I want the URL http://localhost:808/myservice/people;firstname=nick
to bring me back all the people with firstname equals Nick (select * from people where firsname='Nick').
First of all, is this the correct practice to do that?
Second, in my tablet that I have 20 columns I must create a method in my Java code that will contain all the possible matrix parameters (see below) or there is a better way to do this?
public Response getPeople(#MatrixParam("id") String id,
#MatrixParam("firstname") String firstname,
#MatrixParam("lastname") String lastname,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,
#MatrixParam("antoherColumn") String antoherColumn,) {
}
Thanks in advance
First of all do not create your query by concatenating strings:
String q = "select * from where firstName = " + firstName //BAD!
You are asking for troubles like SQL injection attacks. If you use JDBC, use query parameters.
Since probably you want to use GET request, you can stick to your approach, use query parameters instead (#QueryParam). You might also consider the following approach:
http://localhost:808/myservice/people?filter=firstname:nick,lastName:smith
and method:
public Response getPeople(#QueryParam("filter") String filter) {
//if filter is not null, tokenize filter string by ',' then by ':'
//to get needed parameters
}
You should use #BeanParam in order to map the MatrixParam to an object.
This way you can keep the resource pretty simple, but still have the possibility to add more matrix parameters. Also, adding matrix params doesn't involve changing the resource at all. The #BeanParam works also with #PathParam and #QueryParam.
Example:
Consider this:
http://localhost:8081/myservice/people;firstname=nick,lastName=smith/?offset=3&limit=4
and then the resource:
#GET
public Response get(#BeanParam Filter filter, #BeanParam Paging paging) {
return Response.ok("some results").build();
}
and the Filter class looks like:
public class Filter {
public Filter(#MatrixParam("firstname") String firstname, #MatrixParam("lastname") String lastname) {}
}
and the Paging class:
public class Paging {
public Paging(#QueryParam("offset") int offset, #QueryParam("limit") int limit) { }
}
You can also use more filters, like Filter1, Filter2 etc in order to keep it more modular.
Using the matrix parameters the biggest advantage is caching. It makes even more sense if you have more than one level in you API, like ../animals;size=medium/mamals;fur=white/?limit=3&offset=4, because the query params would apply otherwise to all collections.