How to find all by number that starts with Spring JPA? - spring-boot

MyStore class contains itemNbr Integer. I want to find all where itemNbr starts with xxxxxx
I'm using Spring Repository like this . This is what I tried
#Repository
public interface TestRepository extends CrudRepository<MyStore, Long> {
List<MyStore> findByItemNbrStartsWith(int itemNbr);
}
But this throws an exception
java.lang.IllegalArgumentException: Parameter value [2%] did not match expected type [java.lang.Integer (n/a)]
at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:54) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
a

With default methods you can't.
However you can write your own #Query something like this,
and then pass the itemNbr as a String.
#query("from MyStore where CAST(itemNbr as text) like CONCAT(:itemNbr, '%')")
List<MyStore> findByItemNbrStringStartsWith(String itemNbr);

Related

Spring boot not able to read custom #Query annotation

I have requirement when we apply #CustomQUery annotation, then I need to intercept this method and append the query predicate which I already know.
Created
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
#Documented
public #interface CustomQuery {
Query query();
}
#Repository
public interface FloorRepository extends JpaRepository<TnFloor, Integer> {
public String query="select distinct tnFloor from TnFloor tnFloor where tnFloor.tnBuilding.buildingId in ?1 ";
#CustomQUery(query=#Query(query))
public List<TnFloor> findByBuildingIds(List<Integer> buildingIds);
}
Here, Spring is unable to read this #CustomQUery because I have not mentioned anywhere to read this annotation.
Is this the correct way to create custom query annotation ?
I am getting below exception on application startup.
Could not create query for public abstract java.util.List
FloorRepository.findByBuildingIds(java.util.List)!
Reason: Failed to create query for method public abstract java.util.List FloorRepository.findByBuildingIds(java.util.List)!
No property buildingIds found for type TnFloor!
Did you mean 'buildingId'?;
nested exception is java.lang.IllegalArgumentException: Failed to c
As other people have already said in comments, I think your way of extending the Query annotation is usefull only if you need to do some things more than just extending it.
If you need some paths to enhance the behavior of the #Query annotation, maybe using #Modifying annotation could get the point, or using #NamedQuery and #NamedNativeQuery annotations too.
If it is a requirement you could not resolve with these other annotations, maybe make some click on them to see how they are declared and raised in the Spring IoC ecosystem using Aspect programming.
The problem here, to my point of view, seems not to be related to your annotation, but a missing property, as the error message has told to you :
No property buildingIds found for type TnFloor! Did you mean 'buildingId'?;
Maybe because of a typo error in your annotation when you are using it, which is not found :
declared as public #interface CustomQuery and used as #CustomQUery(query=#Query(query)). Write the things as they are declared will work better I think.
Did you try using native query feature like that too ?
Query q = em.createNativeQuery("SELECT a.firstname, a.lastname FROM Author a");
// of course, update with your own code.
You can look at what JPA is capable of and switch to native query as I have just added if it is not supported.

Spring Slice Specification Pagable

I want to use Slice with Specification and Pagable; not List or Page.
The following is working in a JpaRepository:
public interface PersonRepository extends JpaRepository<Person, UUID>,
JpaSpecificationExecutor<Person> {
Slice<Person> findAllBy(Pageable pageable);
}
But if i add a Specification at least 1 parameter is provided and it will be failed:
Slice<Person> findAllBy(Specification<Person> specification, Pageable pageable);
java.lang.IllegalArgumentException: At least 1 parameter(s) provided but only 0 parameter(s) present in query.
This is currently not supported by Spring Data JPA. See https://github.com/spring-projects/spring-data-jpa/issues/1311
You'd have to create a custom method to achieve this.

Spring Data - Build where clause at runtime

In Spring Data, how can I append more conditions to an existing query?
For example, I have the CrudRepository below:
#RepositoryRestResource
public interface MyRep extends CrudRepository<MyObject, Long> {
#Query("from MyObject mo where mo.attrib1 = :attrib1")
List<MyObj> findMyObjects(String attrib1, String conditions);
}
At runtime, I will need to call "findMyObjects" with two params. The first param is obviously the value of attrib1. the second param will be a where clause that would be determined at runtime, for example "attrib2 like '%xx%' and attrib3 between 'that' and 'this' and ...". I know this extra where condition will be valid, but I don't know what attributes and conditions will be in it. Is there anyway to append this where clause to the query defined in the #Query annotation?
Unfortunately, no. There is no straightforward way to achieve that.
You'll want to use custom reporistory methods where you'll be able to inject an EntityManager and interact with EntityManager.createQuery(...) directly.
Alternatively, you can build dynamic queries using Specifications or QueryDsl.
I ended up injecting an EntityManager that I obtained in the rest controller. Posting what I did here for criticism:
The repository code:
#RepositoryRestResource
public interface MyRepo extends CrudRepository<MyObject, Long> {
default List<MyObject> findByRuntimeConditions(EntityManager em, String runtimeConditions) {
String mySql = "<built my sql here. Watch for sql injection.>";
List<MyObject> list = em.createQuery(mySql).getResultList();
return list
}
}
The Rest controller code:
#RestController
public class DataController {
#Autowired
EntityManager em;
// of course watch for sql injection
#RequestMapping("myobjects/{runtimeConditions}")
public List<MyObject> getMyObjects(#PathVariable String runtimeConditions) {
List<MyObject> list = MyRepo.findByRuntimeConditions(em, runtimeConditions);
return list;
}
}

Spring Boot WebFlux Converter

I am trying to migrate my project from the Spring MVC to the Spring WebFlux.
The repository I am currently using is ReactiveCrudRepository.
In order to achieve the post-redirect-get pattern, which I have used within Spring MVC, I need to rewrite the current converter to work with ReactiveCrudRepository.
I was trying to do that with this aproach:
#Component
public class ObjByIdConverter implements Converter<String, Obj> {
#Autowired
private IObjRepository objRepository;
#Override
public Obj convert(String id) {
return objRepository.findById(id).block();
}
}
When I implement converter in this way, I am getting the following error:
block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-xxx.
When I was using CrudRepository instead of ReactiveCrudRepository everything was worked fine.
Is there a way to implement converter to work with ReactiveCrudRepository?
~~~ Edit 1 ~~~
The controller class:
#PostMapping
public Mono<String> processOrder(#ModelAttribute("newCar") Car car) {
webDataBinder.validate();
BindingResult bindingResult = webDataBinder.getBindingResult();
if (bindingResult.hasErrors()) {
return Mono.just("orderForm");
}
return this.carRepository.save(car).thenReturn("redirect:/");
}
The model class:
#Document(collection = "cars")
#ToString
#EqualsAndHashCode
public class Car {
#Id
private String id;
private List<Obj> objs = new ArrayList<>();
// constructor, getters, setters, ...
}
I am using the Thymeleaf view technology.
I have to provide the implementation for ObjByIdConverter because I am getting the following error message: [Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property 'objs'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.example.app.model.Obj' for property 'objs[0]': no matching editors or conversion strategy found]
You should not use block in any case in reactive development. If you have ReactiveRepository and Spring Webflux, use them together with Mono/Flux from repository to controller to leverage the reactive way of doing.
But I think the main reason why you try to convert the result to a standard type is for the post-redirect-get pattern, could you detail this in the spring controller context ?

Modify argument value with the Spring Data JPA Repository

I do have a Spring Data JPA repository with a custom method:
#Repository
public interface EntityRepository extends PagingAndSortingRepository<Entity, Long> {
List<Entity> findByNameIgnoreCase(String name);
}
And I would like to somehow modify the name (e.g. escape the % and _, see https://jira.spring.io/browse/DATAJPA-216) value before calling the method.
My proposed solution was to create a CustomString and a Converter<CustomString, String> with required business logic in it. But even if I change the signature to findByNameIgnoreCase(CustomString name), the converter is not used and the original CustomString is passed to the SimpleJpaRepository.
Is there any other way how to do that without creating any extra Services and wrapping the repository call?
Can't you just convert the string before invoking the repository method?
Smth like this:
entityRepository.findByNameIgnoreCase(transformNameIntoSomethingElse(name));

Resources