Spring Data JPA QueryDslPredicateExecutor find distinct results - java

I have a Spring Data & JPA QueryDSL based project in which I have many repository interfaces extending QueryDslPredicateExecutor like below:
public interface ProductRepository extends JpaRepository<Product, Long>,
QueryDslPredicateExecutor<Product> {
}
I am performing findAll() queries with BooleanExpressions all over my application to fetch data. However I now need to find the distinct results of a query based on a particular column.
I am also using Projections & Custom repositories in some cases to select particular columns based on this post.
Is there a way to select distinct so that I only get the distinct values of a particular column for a query, based on any of the above approaches?

Today I've encountered the same issue and it seems that there's no direct repository approach to solve it.
I ended using Querydsl in order to accomplish what I wanted: being able to use Page<T> findAll(Predicate var1, Pageable var2); using distinct.
A simple snippet:
public Page<LocalizedMessage> findAll(Predicate predicate, Pageable pageable) {
QMessage qMessage = QMessage.message;
Querydsl querydsl = new Querydsl(entityManager, new PathBuilder<>(Message.class, qMessage.getMetadata()));
JPAQuery countQuery = querydsl.createQuery(qMessage).distinct().where(predicate);
JPAQuery query = querydsl.createQuery(qMessage).distinct().where(predicate);
querydsl.applyPagination(pageable, query);
return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount);
}
This code is based on QuerydslJpaRepository's findAll(Predicate, Pageable) method. I presumed that it could be easy to extend this repository in order to add findAllDistinct methods using JPQLQuery.distinct().
I've filed a feature request at spring-data's JIRA.
Hope this helps someone.

If you use Querydsl queries directly in your repository you can call query.distinct() to get distinct results.

Related

How to query only limited columns in Hibernate, and map them to a given POJO?

My project involves using GraphQL within a Spring Boot app. For demonstration purposes, here is my GraphQL schema:
type Company{
name: String,
parentOrganization: String,
flag:Int
}
I'm still learning Spring Boot and JPA, so I use spring-boot-starter-data-jpa for all the JPA, Hibernate, etc.
My problem is, when someone queries only for name and organization, Hibernate queries for all the columns and GraphQL picks the columns requested.
#Repository
#Transactional
public interface CompanyRepository extends JpaRepository<Company,Long> {
}
The above code doesn't really give me any flexibility in limiting the columns that are queried. I've tried using Hibernate's Criteria API as well, but whichever way I go, I get this error:
Unable to locate appropriate constructor on class [packagee.entity.company]. Expected arguments are: java.lang.String, java.lang.String [select new package.entity.Company(generatedAlias0.company, generatedAlias0.organization) from package.entity.Company as generatedAlias0]
Below is the code for my Criteria implementation:
public static List<Company> get(EntityManager em, List<String> fieldsAsked){
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Company> cq = cb.createQuery(Company.class);
Root<Company> root = cq.from(Company.class);
List<Selection<?>> selectionList = new LinkedList<Selection<?>>();
for(String name: fieldsAsked){
selectionList.add(root.get(name));
}
cq.multiselect(selectionList);
return em.createQuery(cq).getResultList();
}
How do I get limited columns from Hibernate? I've seen many answers online that ask to make appropriate constructor in the entity class, but that's not really possible for me because my entity parameters are mostly Strings and I cant make constructors for all the permutations possible (because I'm using GraphQL, the control of what to query really goes to the end user of my project).
What should I do? Thanks in advance!
What you want to do is not really possible with Hibernate directly, but you can checkout Blaze-Persistence Entity-Views which also has a GraphQL integration that supports exactly what you are looking for. See https://persistence.blazebit.com/documentation/1.6/entity-view/manual/en_US/#graphql-integration
Here is a sample project that shows how you can use this: https://github.com/Blazebit/blaze-persistence/tree/master/examples/spring-data-graphql
Solution 1:
You can create a new DTO class which will be returned by your query.
The DTO class:
public class CompanyDTO(){
//fields,constructor
}
And in the repository:
#Query(value = "SELECT new com.example.dto.companyDTO" +
"(c.name,c.parentOrganization)" +
" FROM Company c")
List<CompanyDTO>findCompanySelectedColumns(PageRequest pageable);
Solution 2(clean solution):
You can use interface. Do not implement the interface.
interface customCustomer{
String getName();
String getParentOrganization();
}
In repository:
List<CustomCustomer>findAllByNameAndParentOrganization();

how to write not in clause using springboot findAll() method

I am try to get data from the database using spring boot JPA and CRUD Repository findAll() method with using not in clause but not find any where. There is any solution making dynamic query using findAll() method.
Example:
hql="select * from urlMaster urlmast where urlmast.urlcd not in (select distinct acturl.acuCd from ActionUrl acturl) order by urlmast.urlcd";
Currently I am just getting data from the urlMaster data. But i want acturl.acuCd not in ActionUrl table.
Repository class:
public interface ActionUrlRepository extends CrudRepository<urlMaster, Long>,JpaRepository<urlMaster, Long> {
}
Service implementation:
actionurlrepository.findAll();
If there is any provision? Please guide me. Thanks!
You could put your query string in an #Query annotation, as described in https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query
So something like :
public interface ActionUrlRepository extends JpaRepository<urlMaster, Long> {
#Query("select * from urlMaster urlmast where urlmast.urlcd not in (select distinct acturl.acuCd from ActionUrl acturl) order by urlmast.urlcd")
List<urlMaster> findByNotIn();
}
By the way, you don't need CrudRepository on the declaration, since JpaRepository already extends from it (via PagingAndSortingRepository).

Change default behaviour of mongoTemplate.find

Is there any way to modify every query prepared by mongotemplate.find spring boot 2?
I want to modify every find query prepared by spring data mongo find . I want to add custom query after every find query to mongo.
How can I achieve that in spring boot 2.
I want to override find methods of mongo template with my own implementation and want to add some custom query on top of find query which was built by crud repository methods.
I want to add some custom query by overriding methods of mongotemplate to every find method of crud repository
you would need to customize the repository class implementation for the mongoDB.
below is the sample code to help you understand how to add customized query in the repository. you would need to twik the code as per your requirement after getting the result.
public interface PersonRepository extends PagingAndSortingRepository<Person, String> {
List<Person> findByLastname(String lastname);
Page<Person> findByFirstname(String firstname, Pageable pageable);
Person findByShippingAddresses(Address address);
}
you can visit this page for proper understanding
https://docs.spring.io/spring-data/mongodb/docs/1.2.0.RELEASE/reference/html/mongo.repositories.html

Spring Data: limit result for custom query

In my Spring Data repository I (have to) use custom queries using the #Query annotation. I know I can limit the number of results in a named query like that
Iterable<Person> findFirst5OrderByLastName()
or that it is possible to limit the number of results by passing a pageable like that
Iterable<Person> findByLastName(String lastName, Pageable pageable)
But is it possible to achieve the same when using a custom #Query annotation?
TIA
EDIT
as I see my question is a little confusing, some clearification:
What I want is to limit the number of the results I get, when using a custom query, so that I neither
1) need to specify the result-size through a pageable
2) need to use a named query to specify the result-size
In fact I want the limitation of the number of results to be completely transparent when invoking method (hence not passing a Pageable) and to not rely on the naming-scheme of Spring Data (as the meaning/function of the method is best conveyed through a custom name)
You can try this:
#Entity
#Table(name = "persons")
public class Person {
//...
}
#Query(value = "select * from persons limit 50", nativeQuery = true)
List<Person> getFirst50();
Don't forget to check whether your SQL server supports the limit keyword.
Of course you can use #Query to order a result, it is an JPQL query e.g.
#Query("SELECT u FROM User u ORDER BY u.name ASC")
Further sorting can be done be either providing a PageRequest or using Sort directly.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.sorting
I would suggest to add "By" but without parameter, it would work:
List<Person> findTop100By();
You need to extend PagingAndSortingRepository
and add method
Page<Person> listAllByPage(Pageable pageable)
See the example
Hello you can try this
#Query("...")
List<YourDTO>getData(Pageable pageable)
In your serviceImpl
List<YourDTO> getData(){
int limit=10;
Pageable pageable = PageRequest.of(0, limit);
return this.repo.getData(pageable);
}
You should avoid nativeQuery

Spring data repositories - parameter what to retrieve all records?

#Repository
public interface UserDao extends User {
public List<User> findByFirstname(String firstname);
}
How could I use above code to retrieve all records?
I tried findByFistname(null);, it doesn't work...
I don't want to use findByFirstname(); because it's possible to have parameter.
Hope you all understand.
Have you considered using a spring data specification? In spring data a specification is a way to wrap the JPA criteria api.
The idea behind the JPA Criteria api is to generate queries programatically, by defining query objects.
Once you have encapsulated the criteria in a specification objects, a single findAll method can be used in a number of scenarios. For example programatically add criteria based input form the user, such as additional search filters etc.
To use this feature a repo will need to extend "JpaSpecificationExecutor"
public interface UserRepository extends JpaRepository<User>, JpaSpecificationExecutor {
List<T> findAll(Specification<T> spec);
}
The find method can then be called with multiple criteria, or the criteria can be built dynamically based on the situation:
List<User> users = userRepository.findAll(where(userLastNameIs("John")).and(userIsArchived()));
Alternatively you can also try query by exampe. The idea here is to provide the actual domain object with the populated search fields to an example matcher. Configure the example matcher to control the search and pass it to the findAll method.
Again the repo will need to implement an interface. Check the documentation for the detailed pros/cons of each approach.
Person person = new Person();
person.setFirstname("Dave");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("lastname")
.withIncludeNullValues()
.withStringMatcherEnding();
Example<Person> example = Example.of(person, matcher);
List<Person> people = personRepository.findAll(example);
You should extend your repository from JpaRepository. Be careful with name of repository (It should follow convention). After you inject your UserRepository bean you will have already implemeted by spring data crud methods like findOne(), findAll(), delete() etc.
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
//assume your primary key is Long type
}
Also will be useful documentation
As I got from comments you're trying to achieve ignorance of null values of passed parameters (instead of retrieving all records by findAll()).
Unfortunately, currently, it's not supported by Spring .
You could leverage the #Query annotation and write the query manually in such manner:
#Query("select u from User u where "
+ "(:firstname is null or u.firstname = :firstname)"
+ "(:lastname is null or u.lastname = :lastname)"
)
public List<User> findUserByFirstNameAndLastName(
#Param("firstname") String firstname,
#Param("lastname") String lastname
);
https://spring.io/blog/2011/02/10/getting-started-with-spring-data-jpa/
This is very good tutorial of Spring Data. I suggest you to start with it. tutorial of Spring Data. If you want to go deeper you can read the documentation.
http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html

Categories

Resources