jpa query with optional parameters - spring

I m using Spring Data Rest JPA which implements the Query internally on the basis of method name.
I wrote the following method in my repository interface which should list all the users in a state and if the name and/or age is present, it should filter the result.
StateId is mandatory but name and age are optional filter parameters
public List<User> findByStateIdAndNameOrAge(Integer stateId, String name , Integer age, Pageable pageable);
I am not getting any results. Where am I doing wrong?

You can try
There is no mistake in your method defination.
public List<User> findByStateIdAndNameOrAge(Integer stateId, String name , Integer age, Pageable pageable);
but you can't pass null parameter to this method so it will not work if you are putting any parameter is blank.

Related

Spring Data JDBC - Pageable on custom Query

On my Project I have a Repository that extends CrudRepository. Inside there I have a custom query:
public interface CustomerRepository extends CrudRepository<Customer, Long> {
#Query("select * from person where firstname = :firstname")
List<Customer> findByFirstname(#Param("firstname") String firstname, Pageable pageable);
}
in my Service-Class I try to put the List in a Pageable - Object like:
... getPageableCustomer(String firstname, Pageable pageable){
// => By using "Sol" I got 90 matching entries
List<Customer> custList = customerRepository.findByFirstname(firstname, pageable);
Page<Customer> custPage = new PageImpl<Customer>(custList, pageable, custList.size());
return custPage;
}
the return value includes the complete List "custList". What would be the best way to get a pageable object with specified offset and size?
One option could be to use
customer.subList(fromIndex, toIndex)
but that feels wrong. Also because of Loading all Data inside the list instead of just getting data by size and offset as parameterized with pageable.
Remark: In case of using Page inside the Repository I ´ll get org.springframework.dao.IncorrectResultSizeDataAccessException: Incorrect result size: expected 1, actual 88
There is also a open Improvement on Jira that could be found here:
https://jira.spring.io/browse/DATAJDBC-554?filter=-3
hope for some help...
I got a response on the JIRA-Issue from Dirk Luijk (Thx Dirk :))
https://jira.spring.io/browse/DATAJDBC-554?filter=-3
interface FooRepository extends PagingAndSortingRepository<FooEntity, Long> {
List<FooEntity> findAllByBar(String bar, Pageable pageable);
Long countAllByBar(String bar);
}
And then combining those 2 queries like this:
List<FooEntity> fooList = repository.findAllByBar("...", pageable);
Long fooTotalCount = repository.countAllByBar("...");
Page<FooEntity> fooPage = PageableExecutionUtils.getPage(fooList, pageable, () -> fooTotalCount);
"the mistake in your workaround is your custom query. In Spring Data JDBC 2.0 you don't need to use that, except for special queries but they won't support pageables."
Possible Parameters could be found:
https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/#jdbc.query-methods
Thx Dirk,
I also find a workaround to get it running with a custom Query. Just use limit, offset and orderBy as additional Parameter like so:
#Query("select * from person where firstname = :name order by :order limit :size offset :offset")
List<Customer> findByFirstNameCustomQuery(#Param("name") String name, Pageable page, #Param("offset") long offset,
#Param("size") long size, #Param("order") String order);
And than change the call inside the Service like:
List<Customer> custList = customerRepository.findByFirstNameCustomQuery(firstname, pageable, ....

How to collect all fields annotated with #RequestParam into one object

I would like to gather all of my query parameters into a pojo and perform additional validation of the fields.
I have read that I can simply create an object and spring-boot will automatically set those request parameters on it.
#GetMaping
public ResponseEntity<?> listEntities(#RequestParam(value = "page-number", defaultValue = "0") #Min(0) Integer pageNumber,
#RequestParam(value = "page-size", defaultValue = "100") #Min(1) Integer pageSize ... )
I am thinking to create a class called RequestParamsDTO, where I'd have my query params responsible for the pagination.
But in order to have those fields set on the RequestParamsDTO, I'd have to match the name of the request param with the field name. But it won't be a valid variable name: page-size.
There must be some workaround, similar to #RequestParam's value attribute, that would set given request param on my field in the DTO.
Please advise.
Someone already purposed this feature before such that you can do the following .But unfortunately it is declined due to inactivity response :
public class RequestParamsDTO{
#RequestParam(value="page-number",defaultValue="0")
#Min(0)
private Integer pageNumber;
#RequestParam(value = "page-size", defaultValue = "100")
#Min(1)
Integer pageSize
}
The most similar things that you can do is using its #ModelAttribute which will resolve the parameter in the following orders:
From the model if already added by using Model.
From the HTTP session by using #SessionAttributes.
From a URI path variable passed through a Converter (see the next example).
From the invocation of a default constructor.
From the invocation of a “primary constructor” with arguments that match to Servlet request parameters. Argument names are determined through JavaBeans #ConstructorProperties or through runtime-retained parameter names in the bytecode.
That means the RequestParamsDTO cannot not have any default constructor (constructor that is without arguments) .It should have a "primary constructor" which you can use #ConstructorProperties to define which request parameters are mapped to the constructor arguments :
public class RequestParamsDTO{
#Min(0)
Integer pageNumber;
#Min(1)
Integer pageSize;
#ConstructorProperties({"page-number","page-size"})
public RequestParamsDTO(Integer pageNumber, Integer pageSize) {
this.pageNumber = pageNumber != null ? pageNumber : 0;
this.pageSize = pageSize != null ? pageSize : 100;
}
}
And the controller method becomes:
#GetMaping
public ResponseEntity<?> listEntities(#Valid RequestParamsDTO request){
}
Notes:
There is no equivalent annotation for #RequestParam 's defaultValue,so need to implement in the constructor manually.
If the controller method argument does not match the values in this , it will resolved as #ModelAttribute even though #ModelAttribute is not annotated on it explicitly.
To be honest this seems like a lot of effort for a functionality that exists already in spring-boot. You can either extend your repositories from PagingAndSortingRepository and have pagination added whenever you call a collection resource.
Or you can write a custom query method (or overwrite an existing one) and add this:
Page<Person> findByFirstname(String firstname, Pageable pageable);
This way spring boot will automatically add all those parameters you want to the Request.

Spring- How to remove a property from a list of Objects?

I have an Entity "Person" which has following properties,
Id
Name
F-Name
Age
Address
When I call a repository function findAll() on Person, it returns a list of Persons.
List<Person> list = somefuntionToConvertIterableToList(personRepository.findAll());
this list has multiple objects of Person Type.
Person ...... Id1,Name1, F-Name1, Age1, Address1
Person .......IdN,NameN, F-NameN, AgeN, AddressN
I need to remove "Id" from all Persons, what should I do?
I know we can use "remove" to delete an element of list, but how to delete a property with in an element?
I think you need used : #JsonIgnore in Id look like:
#JsonIgnore
Field you want remove in reponse.
Or you can create DTO and put all field you want return look like :
public class PersonDTO {
//all field you want return
}
You can of course set id null or add ignores in serialization but maybe you do not want to load id at all. Usually you then would use DTO or Tuple to decide what fields to populate. So not first to populate all and then remove unneeded (I have not used your Person but just one simplified example class).
Tuple query in your repository would simply be like (JPQL):
#Query("SELECT te.name AS name, te.created as created FROM TestEntity te")
List<Tuple> findAllTuple();
This would need then do extra work to have tuple to correspond original entity when serialized. So preferably with a DTO, like:
// This class would be exactly as your Person but without that id
#AllArgsConstructor // you need the constructor for new in jpql
public class TestEntityDto {
private String name;
private LocalDateTime created;
}
while the query in your repository would be like:
#Query("SELECT NEW org.example.data.entity.dto.TestEntityDto(te.name, te.created) FROM TestEntity te")
List<TestEntityDto> findAllDto();

Spring data jpa query to return with multiple properties

I have to fetch Users by id and state.The method takes a set of usersIds as input.
I need a jpa query like below:
public Set<Users> fetchUsersByIdsContainingStateId(Set<Integer> userIds, Integer stateId)
Is there a query like the above in Spring Data JPA or I need to resort to named query
you can use spring data query:
public interface UserRepository extends CrudRepository<User, Long> {
Set<User> findUserByIdInAndStageId(Set<Long> userIds , Long stageId);
#Query("select u from User u where u.id in (:userIds) and u.stage.id=:stageId")
Set<User> findUserByUserIdsAndStageId(#Param("userIds") Set<Long> userIds , #Param("stageId") Long stageId);
}
for method findUserByIdInAndStageId spring data generates something
like
-- whereuser0_.id in (? , ? , ?...)) and stage1_.id=?
method findUserByUserIdsAndStageId do the same as findUserByIdInAndStageId , but in Query annotation you should write your query.
second search method findUserByUserIdsAndStageId named just for example , but I'll change it into something more readable like findUserWithStage(......)
PC don't use for domain model plural name like Users , use singular User

Spring data JPA for returning specific fields

Does Spring Data have a mechanism for returning specific fields?
I'm familiar with the syntax:
Invoice findByCode(String code);
How about this:
Integer findIdByCode(String code);
which returns the id field only. Or
Tuple findIdAndNameByCode(String code);
which returns a tuple. Or
Invoice findIdAndNameByCode(String code);
which returns an entity only populated with specific fields. Can use a constructor taking only those field if defined - else construct empty and populate the fields.
EDIT
To qualify some more, I'm aware of solutions like #Query, constructor expressions and now, #NamedEntityGraph. My question is simply - does Spring data support such a shorthand syntax as I'm suggesting?
If not, perhaps this is a cool enhancement for a later version...
I'm not looking for workarounds.
You can use JPQL Constructor Expressions:
SELECT NEW com.company.PublisherInfo(pub.id, pub.revenue, mag.price)
FROM Publisher pub JOIN pub.magazines mag WHERE mag.price > 5.00
The constructor name must be fully qualified
If you want to return just 1 field from table and it's primitive(or autoboxing), you can use next:
#Query("select distinct t.locationId from Table t")
List<Long> findAllWashroomLocationId();
Where:
Table - name of class which represent your table
t - alias
locationId - name of field(in your Table object)
Long - type of locationId (Integer, String, ...)
Not sure if what you're trying to achieve is the same as using multiple projections on the same JPA generated query (where method name are the same). I have posted an answer in this post.
https://stackoverflow.com/a/43373337/4540216
So I've managed to figure out how to use multiple projections with a
single query.
<T> T getByUsername(String username, Class<T> projection) This allows the method caller to specified the type of projection to be
applied to the query.
To further improve this so it is less prone to error, I made a blank
interface that the projection will have to extend in order to be able
to insert class into the parameter.
public interface JPAProjection {
}
public interface UserRepository extends CrudRepository<UserAccount, Long> {
<T extends JPAProjection > T getByUsername(String username, Class<? extends JPAProjection> projection);
}
Projection Interface
public interface UserDetailsProjection extends JPAProjection{
#Value("#{target.username}")
String getUsername();
#Value("#{target.firstname}")
String getFirstname();
#Value("#{target.lastname}")
String getLastname();
}
Then I can call the query method by
getByUsername("...", UserDetailsProjection.class)
i have a nativequery,
this is a insert and i going to return all fields after insert whit "RETURNING *"
this query return all fields of my database, and this data going to save in my entity
"Perfil Detalles"
my entity have all configurations of my fields of my database
#Query(
value= "INSERT INTO \"USUARIO\".\"PERFIL_CONFIGURACION\" (id_perfil, id_group, id_role) VALUES(:id_perfil, :id_group, :id_role) returning *",
nativeQuery = true)
public PerfilDetalles insertPerfilDetalles(
#Param("id_perfil") Long id_perfil,
#Param("id_group") int id_group,
#Param("id_role") int id_role);

Resources