Spring Data Rest findByIdIn - spring-data-rest

We use Spring Data Rest to build simple API for our internal webapp.
Our repositories as below:
public interface BaseRepository<T, ID extends Serializable> {
List<T> findByIdIn(#Param("ids") Collection<ID> ids);
}
public interface FooRepository extends BaseRepository<Foo, String> {}
public interface BarRepository extends BaseRepository<Bar, Long> {}
Our client fetch data, passing ids parameters as below:
GET http://example.com/api/foos/search/findByIdIn?ids=ABC,XYZ --> It works well for String ids.
GET http://example.com/api/bars/search/findByIdIn?ids=1,2,3 --> Got exception for numeric ids.
We got the exception: Parameter value element[1] did not match expected type [java.lang.Long (n/a)]
What's wrong with above repositories? What is the correct way to pass numeric ids?

You may use this library which lets you build advanced search queries: https://github.com/turkraft/spring-filter
You can then simply do:
?filter= ids in (1, 2, 3, 4)
It supports enums, dates, booleans, logical operations, comparisons, and even joins (nested fields).

Related

Jpa createSQLQuery returns List<Object> instead of List<Employee>

Trying to make an sql query to get as a result a list of Class "EmployeeCardOrderLink". But this code always returns me an list of Object. Casts doesn't working. I got the right data in this list, but it's just object. In debug i can call methods(Idea suggest according interface of my class), but then i got "class Object doesn't have a such method". And i can't use TypedQuery cause i have old JPA version, it doesn't support this.
#Repository
public class EmployeeCardOrderLinkDAOImpl extends AbstractBasicDAO<EmployeeCardOrderLink> implements EmployeeCardOrderLinkDAO {
//....
#Override
public List<EmployeeCardOrderLink> getLinksByOrderNumber(Integer num) {
List<EmployeeCardOrderLink> result = (ArrayList<EmployeeCardOrderLink>) getSessionFactory().getCurrentSession().createSQLQuery("select * from employee_card_order_links " +
"where trip_order_id = " + num).list();
return result;
}}
You use Hibernate (not JPA), if you are using Session. Hibernate is JPA provider of course. You have to use EntityManager and other related things to use JPA.
You don't need SQL here. SQL always returns list of objects (if you don't use transformers to DTO objects).
Just use HQL (JPQL in JPA)
To get all EmployeeCardOrderLink
getSessionFactory().getCurrentSession()
.createQuery("select link from EmployeeCardOrderLink link").list();
Query "from EmployeeCardOrderLink" will work for Hibernate too (for JPA will not work).

Pageable usage with Query Annotation

Can I use Pageable attribute in Spring Data R2dbc repositories with #Query annotation? For example;
public interface PartyRepository extends ReactiveCrudRepository<Party,String> {
#Query("select * from party order by id")
Flux<Party> getParties(Pageable pageable);
}
It gives "org.springframework.data.repository.query.ParameterOutOfBoundsException : Invalid parameter index! You seem to have declared too little query method parameteres!"
Is there any way to use pagination in spring Data R2dbc repositories?
Thanks.
This is not supported by R2DBC and probably never will.
But you can adjust the query string with SpEL expressions manually to include paging. For MySQL, in your example, this could look like this:
#Query("SELECT * FROM party ORDER BY id LIMIT :#{[0].offset},:#{[0].pageSize}")
Flux<Party> getParties(Pageable pageable);
The [0] part indicates which argument in your argument list is the Pageable.

Id Encryption in spring-hateoas or Spring Rest Data

I have a question about a standard pattern or mechanism in spring-hateoas or Spring Rest Data about encrypting the IDs of the Resources/Entities.
The reason I am asking, a requirement to our project is that we don't deliver the id's of our objects to the outside world and they should not be used in GET Requests as Parameters.
I know, Spring Rest Data and spring-hateoas does not give the ids of the objects unless they are configured so but even that case I can see the ids in links.
I know I can use PropertyEditors or Converters to encrypt/decrypt ids before and after Json serialisation/deseritalisation but I just like to know is there a more standard way?
Thx for answers...
If you have the unique 'business id' property of your resource you can configure SDR to use it instead of the entity ID.
First you have to create lookup method of your entity with this unique property:
public interface MyEntityRepo extends JpaRepository<MyEntity, Long> {
#RestResource(exported = false)
Optional<CatalogResource> findByMyUniqueProperty(String myUniqueProperty);
}
Then use it to configure SDR:
#Component
public class DataRestConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.withCustomEntityLookup()
.forRepository(MyEntityRepo.class, MyEntity::getMyUniqueProperty, MyEntityRepo::findByMyUniqueProperty);
super.configureRepositoryRestConfiguration(config);
}
}
After this customization you will have resource URI like this:
http://localhost:8080/myEntities/myUniquePropertyValue1

Spring data rest - expose default methods

I have a Person Repository as follows
#RepositoryRestResource
public interface PersonRepository extends Repository<Person, String> {
List<Person> findAll();
default List<Person> findNewPersons() {
return findByStartDateAfter(LocalDate.now().minusMonths(3));
}
List<Person> findByStartDateAfter(LocalDate date);
}
I am not able to expose the default method through rest.. is there a way to do it without creating an implementation of the repo ?
I faced a similar problem, and was able to solve it using a SpEL expression inside an HQL query in a #Query annotation.
While nowhere near as clean as using a default method, this was the tidiest way I could find without writing a custom controller or introducing a custom implementation with a new DSL library or something for just this one query.
#Query("select p from Person p where p.startDate > :#{#T(java.time.LocalDate).now().minusMonths(3)}")
List<Person> findNewPersons();
My actual query was different so I might have typoed the syntax here, but the idea is the same and it worked for my case (I was using a LocalDate parameter and finding timestamps on that day by using a findByTimestampBetween style query).

findBy URI not working in Spring Data Rest

By default, in Spring Data Rest the #Id of the entity is not exposed. In line with the REST rules, we're supposed to use the URI of the resource to refer to it. Given this assumption, the findBy queries should work if you pass a URI to them, but they don't.
For example, say I have a one-to-many relationship between Teacher and Student. I want to find students by teacher.
List<Student> findByTeacher(Teacher teacher)
http://localhost:8080/repositories/students/search/findByTeacher?teacher=http://localhost:8080/repositories/teachers/1
This doesn't work because the framework is attempting to convert the teacher URI to a Long.
I get this error that says "Failed to convert from type java.lang.String to type java.lang.Long".
Am I missing something?
You could expose #Id s by configuring web intializer
//Web intializer
#Configuration
public static class RespositoryConfig extends
RepositoryRestMvcConfiguration {
#Override
protected void configureRepositoryRestConfiguration(
RepositoryRestConfiguration config) {
config.exposeIdsFor(Teacher.class);
}
}
Its good to change List to Page
List findByTeacher(Teacher teacher)
to
Page<Student> findByTeacher(#Param("teacher) Teacher teacher, Pageable pageable);
Also note #Param annotation is required along with Pageable. The latter is required because return type "Page"
3.Latest snapshots, not milestones work fine
See https://jira.spring.io/browse/DATAREST-502
Depending of your version of Spring Data, it would work as you want or not. If you are with Spring Data 2.4, you need to pass the URI. If you are with a previous version, you need to pass the id.