How to use QueryDslJpaRepository? - spring-data

In my current project setup I'm defining repositories as:
public interface CustomerRepository extends JpaRepository<Customer, Long>, QueryDslPredicateExecutor<Customer> {
}
The QueryDslPredicateExecutor provides additional findAll methods which return e.g. an Iterable.
It e.g. does not contain a method to only specify an OrderSpecifier.
I just came across the QueryDslJpaRepository which contains more variants of these Predicate and OrderSpecifier aware methods, and also return Lists instead of Iterables.
I wonder why QueryDslPredicateExecutor is limited and if it is possible to use QueryDslJpaRepository methods?

I used a custom BaseRepository already so It was easy to make sure my repositories use the List variant (instead of Iterable) using:
#NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID>, QueryDslPredicateExecutor<T> {
#Override
List<T> findAll(Predicate predicate);
#Override
List<T> findAll(Predicate predicate, Sort sort);
#Override
List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders);
#Override
List<T> findAll(OrderSpecifier<?>... orders);
}
Note that my commnent reagarding missing methods in QueryDslPredicateExecutor was incorrect.

QueryDslJpaRepository extends SimpleJpaRepository
SimpleJpaRepository is used when you want to adding custom behavior to all repositories. It takes three steps to do so:
Step 1: Create an interface (eg CustomRepository) that extends JpaRepository, then add your own interface methods
Step 2: Create a class (eg CustomRepositoryImpl) that implements your CustomRepository, which naturally requires you to supply concrete method implementations to each and every method defined in not only CustomRepository but also JpaRepository as well as JpaRepository's ancestor interfaces. It'd be a tedious job, so Spring provide a SimpleJpaRepository concrete class to do that for you. So all you need to do is to make CustomRepositoryImpl to extend SimpleJpaRepository and then only write concrete method for the method in your own CustomRepository interface.
Step 3: make CustomRepositoryImpl the new base-class in the jpa configuration (either in xml or JavaConfig)
Similarly, QueryDslJpaRepository is the drop-in replacement for SimpleJpaRepository when your CustomRepository extends not only JpaRepository but also QueryDslPredicateExecutor interface, to add QueryDsl support to your repositories.
I wish Spring Data JPA document made it clear what to do if someone is using QueryDslPredicateExecutor but also wants to add his/her own customized methods. It took me some time to figure out what to do when the application throws errors like "No property findAll found for type xxx" or "No property exists found for type xxx".

Check your Predicate import in Service, for my case it was because auto import brings import java.util.function.Predicate; instead of import com.querydsl.core.types.Predicate;. This gives confusion like findall with predicate function gives error.

Related

Issue while implementing a interface which extends to MongoRepository interface in Kotlin

I am trying to use the in built methods of MongoRepository<T,ID> interface to interact with mongo.
interface MovieRepository : MongoRepository<Movie, String> {
}
But when I try to implement the "MovieRepository" using class. Its asking me to implement all the member functions defined in "MongoRepository" as well
class ControllerClass(private val MovieRepository: MovieRepository): MovieRepository {}
This is what i get when i initialize my controller class:
Class 'ControllerClass' is not abstract and does not implement abstract member public abstract fun <S : Movie!> save(entity: S): S
Is there any way so that i do not need to defined all those MongoRepository's functions again in my ControllerClass?
You don't usually implement a repository interface yourself: you let Spring do it for you!
First, you define your interface, as you have done:
interface MovieRepository : MongoRepository<Movie, String> {
// Add any abstract methods you'll need here…
}
Then, you autowire a property of that type. In Kotlin, you can either do it in the primary constructor, e.g.:
#Controller
class ControllerClass #Autowired constructor(
private val movieRepository: MovieRepository
) {
// …code…
}
Or as a plain property. (In this case, because you can't specify an initial value, you have to make the property a var; it must either be nullable — requiring !! everywhere you use it — or, better, make it lateinit.)
#Controller
class ControllerClass {
#Autowired private lateinit var movieRepository: MovieRepository
// …code…
}
Spring will then create some synthetic class implementing that interface, and set your property to it. (You don't need to worry about how it does that — just as you don't need to worry about all the other magic it does, much of which involves creating synthetic subclasses. That's why Spring objects generally need to be made open — and why there's a Spring plugin which takes care of doing that.)
It's more usual to use the repository in a service class, and then call that from your controller class — at least, that pattern tends to scale better, and be easier to follow and to test. But doing so directly should work too. Either way, you can call whichever repository method you need, e.g. movieRepository.findAll().
See the Spring docs; they use Java, but it's mostly trivial to convert to Kotlin.

Disable query creation from method name - use of projections

I would like to use the Spring Data Projection technique in order to extract from a table only some fields (and not all fields of the table).
As described in the documentation (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections) I created a simple interface, for example:
interface NamesOnly {
String getFirstname();
String getLastname();
}
But I have some problems to use it.
Problem 1:
First of all, I would like to use the name findAll() to create a query that finds all rows with only two fields (firstName and lastName):
#Repository
public interface PersonaRepository extends JpaRepository<Persona, Long> {
List<NamesOnly> findAll();
}
But in this case I have these errors (maybe because findAll() is a method of the JpaRepository):
implements org.springframework.data.jpa.repository.JpaRepository.findAll
The return type is incompatible with JpaRepository.findAll()
Problem 2:
Ok, so I try to change the name of the method to findAllOnlyNames():
#Repository
public interface PersonaRepository extends JpaRepository<Persona, Long> {
List<NamesOnly> findAllOnlyNames();
}
But now I have this error:
Caused by:
org.springframework.data.mapping.PropertyReferenceException: No
property findAllOnlyNames found for type Persona!
Because Spring tries to create a query from the name.
1) Could it be possible to reuse the method name findAll() without having problems with JpaRepository?
2) Could it be possible to turn off the query creation from the method name (only for some queries, not for all projects or repositories)?
You are on the right track, your findAll() is in conflict with the ones specified on the existing Spring Data interfaces and you can rename it (as you tried) but it still has to be a name that is compatible with the query derivation mechanism. Try this instead:
#Repository
public interface PersonaRepository extends JpaRepository<Persona, Long> {
List<NamesOnly> findAllOnlyNamesBy();
}
This part of the Spring Data JPA documentation explains how the query creation process works:
The mechanism strips the prefixes find…By, read…By, query…By, count…By, and get…By from the method and starts parsing the rest of it.
So you just need to add the By keyword in the method name, anything after that keyword is treated as a condition, in this case there is no condition so it fetches everything.
To disable the query derivation from the method name you would need to add an #Query(...) annotation to the method and specify either a JPA or native query instead.
You can specify an explicit query rather than rely on it being derived from the method name.
#Repository
public interface PersonaRepository extends JpaRepository<Persona, Long> {
#Query("select p from Persona p")
List<NamesOnly> findAllOnlyNames();
}
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query
Overriding findAll() (even in the unlikely event it is possible) is probably a bad idea.

Why we need to create xxxCustom and xxxImpl class when we create repository?

From this information, When we create repository class, it is better to create 1 classes and 2 interfaces for one repository UserRepository(interface), UserRepositoryCustom(class), UserRepositoryImpl(interface).
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations
But we can create repository class without these classes... why we need to create those classes and what is the merit(or demerit) if we create those classes?
If you checkout the Spring Data core concepts, UserRepository interface definition which extends CrudRepository or JPAReposiory provides you are all basic CRUD operations for free on an entity.
public interface UserRepository extends JpaRepository<User, Long>
You can add your own basic custom queries in this repository interface using the naming convention approach or by using #Query attribute.
In case you want to execute some custom logic which cannot be easily managed and defined in the UserRepository, e.g. complex joins and stored procedure and you need access to underlying EntityManager you need UserCustomRepository interface. UserRepository will extend this interface for inheriting the methods.
public interface UserRepository extends JpaRepository<User, Long>, UserCustomRepository {
void myCustomMethod();
}
You need to provide the implementation of these methods yourself in UserRepositoryImpl class. Spring data look for custom method implementation in this class and invoke them when they are called.
Hope this explanation helps.

Serialization Exception while making an RPC call

I have created a very basic application. I have only one service class and a corresponding Async class which contains only Java types and no custom classes. But still I get the serialization exception.
My service class looks like this.
public interface MyService extends RemoteService {
public String getName();
public Object getAdditionalDetials(ArrayList<String> ids);
public Date getJoiningDate();
}
My async interface looks like this
public interface MyServiceAsync {
public void getName(AsyncCallback<String> callback);
public void getAdditionalDetials(ArrayList<String> ids, AsyncCallback<Object> callback);
public void getJoiningDate(AsyncCallback<Date> callback);
}
I know I am making some stupid mistake.
I am Naive in gwt rpc and serialization mechanism, but will try to answer your question.
Whenever you write classes involving an RPC, GWT creates a Serialization Policy File. The serialization policy file contains a whitelist of allowed types which may be serialized.
In your Service methods, all the types you mention and refer will be automatically added to this list if they implements IsSerializable. In your case you have used the following two methods,
public String getName();
public Date getJoiningDate();
Here you have used String and Date as your return types and hence it is added to your Serialization Policy File. But in the below method their lies a problem,
public Object getAdditionalDetials(Arraylist<String> ids);
Here you have used ArrayList and String that is not a problem and they will be added to your whitelist, but the problem is you have mentioned return type as Object. Here GWT Compiler does not know what type to be added to whitelist or Serialization Policy and hence it wont pass your RPC call. The solution is use mention a class which implements IsSerializable instead of mentioning the return type of type Object.
FWIW, I was having this problem but my 'Object' type was hidden behind generified classes.
So if one of your rpc methods involves a class:
class Xxx<T> implements IsSerializable {...
It needs to change to:
class Xxx<T extends IsSerializable> implements IsSerializable {...

Where do the Locator methods go when using a ServiceLocator?

If I use separate service classes with a ServiceLocator, do I still need to define the Locator methods somewhere?:
T create(Class<? extends T> clazz)
I getId(T domainObject)
T find(Class<? extends T> clazz, I id)
Object getVersion(T domainObject)
Do they go on the service class?
In general, these two types serve orthogonal purposes: A ServiceLocator finds code; a Locator finds entities.
Using a ServiceLocator does not change where the entity support methods are declared. The entity support methods are still searched for in the domain types.