Spring JPA #Query Annatotion disadvantages - spring-boot

Hi I use spring jpa to access my data in my spring boot project.I am wondering that is there any difference between #Query annatotation and critearia api in jpa.Are they totaly same or is there any difference(Their writing styles are different ,and I mean any performance or other issue between them)
Mostly I prefer #Query annotation it looks simple.Or any other option some one can advice like #Query or criteria api in spring jpa.And is there any disadvantages of #Query style?
#Query("SELECT u FROM User u WHERE u.status = 1")
Collection<User> findAllActiveUsers();
List<Book> findBooksByAuthorNameAndTitle(String authorName, String title) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Book> cq = cb.createQuery(Book.class);

Using #Query we can pass static query or pre compiled query so we can perform both select and non-select operations on the data
where as Criteria is suitable for executing Dynamic Queries such requirements occurs when data are know at run time
but using criteria api we can only perform select operations on the data.
For example
#Query(value = "SELECT u FROM User u")
List<User> findAllUsers(Sort sort);
We can also work with pre compiled query using #Query
For Example
#Query("SELECT u FROM User u WHERE u.status = :status and u.name = :name")
User findUserByStatusAndNameNamedParams(
  #Param("status") Integer status,
  #Param("name") String name);)
Dynamic queries like
Criteria cr = session.createCriteria(Employee.class);
// To get records having salary more than 2000
cr.add(Restrictions.gt("salary", 2000));
// To get records having salary less than 2000
cr.add(Restrictions.lt("salary", 2000));
Actual use of Dynamic queries comes when we'll encounter the need for building SQL statements based on conditions or data sets whose values are only known at runtime. And, in those cases, we can't just use a static query So we can't just use the #Query annotation since we can't provide a static SQL statement.In such case we use Criteria API
For more info follow the link provided
#Query and Criteria

Related

Spring Data JPA #Query with Specification

I have a requirement to create a REST api. Api allows user to provide dynamic search criteria in URL. For example, let say I have NOTES table with column as Note_ID, NOTE_TEXT, STATUS, PERSON_ID. This table is used to keep notes of every person.
Now I want my REST api to be as https://server:host/MyApi/Notes?search=NoteText=='My Java adventure'. API should provide all notes having NOTE_TEXT as 'My Java adventure'. Similarly user can provide status also in url and also he can use operators as LIKE. I was able to do it via rsql parser as mentioned in https://www.baeldung.com/rest-api-search-language-rsql-fiql
Now I have additional requirement that based on user security person_id filter should be applied on query automatically.
I found that we can't have findBy method which can take Specification, Pageable and extra personId. For example I can't have a repository function as
findByPersonId(Specification spec, Pageable page, Long personId);
I thought of using SpEL to use it, but then I found that if we use #Query annotation on findBy method, Specifications are ignored.
Seems like there is no way I can have Specification and #Query both. I need to add more clauses using specification only. In reality my where clause is very complex which I have to append and getting it with Specification seems to be difficult. Its something like
Select * from NOTES where exists (select 'x' from ABC a where n.person_id = a.person_id)
Is there a way I can write #Query and also have Specification working on top of it?
Ideally I have achieve a query like
select * from test.SCH_FORUM_THREAD t
where exists (select 'x' from test.FORUM_THREAD_ACCESS fta, school.SCH_GROUP_PERSON gp
where gp.GROUP_ID = fta.GROUP_ID
and t.THREAD_ID = fta.THREAD_ID
and gp.PERSON_ID = :personId)
or exists (select 'x' from test.FORUM_THREAD_ACCESS fta
where fta.THREAD_ID = t.THREAD_ID
and fta.PERSON_ID = :personId);
So there are two exists clauses with or condition. I was able to make second exists by following How to write query(include subquery and exists) using JPA Criteria Builder
Now struggling with first exists as it has join also. Any idea how to do that with Specification.
Also as there are two exists, does that mean I need two specifications. Can I achieve it in one specification.
I was able to resolve it by creating a complex specification code. Something like
#Override
public Predicate toPredicate(Root<ForumThread> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
Subquery<ForumThread> subQuery = query.subquery(ForumThread.class);
Root<ForumThread> subRoot = subQuery.from(ForumThread.class);
Join<ForumThreadAccess, GroupPerson> fragpjoin = subRoot.join("groupPersons");
Predicate threadPredicate = builder.equal(root.get("threadId"), subRoot.get("threadId"));
Predicate personPredicate = builder.equal(fragpjoin.get("personId"), personId);
subQuery.select(subRoot).where(threadPredicate, personPredicate);
Predicate existsGroupPredicate = builder.exists(subQuery);
Subquery<ForumThreadAccess> subQuery1 = query.subquery(ForumThreadAccess.class);
Root<ForumThreadAccess> subRoot1 = subQuery1.from(ForumThreadAccess.class);
Predicate threadPredicate1 = builder.equal(root.get("threadId"), subRoot1.get("threadId"));
Predicate personPredicate1 = builder.equal(subRoot1.get("personId"), personId);
subQuery1.select(subRoot1).where(threadPredicate1, personPredicate1);
Predicate existsPersonPredicate = builder.exists(subQuery1);
return builder.or(existsGroupPredicate,existsPersonPredicate);
}
To make it work your entities should also have proper #OneToMany and #ManyToMany in place.
Thanks

Spring JPA - How to create a Pageable with a NativeQuery?

I try to do the following inside a Spring Boot application : create a native query and page it so it can returns a page of a given number of elements from a #RestController.
Here's the snippet of my code, where em is the #PersistanceContext EntityManager, and the repository method is the following, knowing that queryString is the native query :
Query searchQuery = em.createNativeQuery(this.queryString, MyEntity.class);
List<MyEntity> resultsList = searchQuery.getResultList();
return new PageImpl<>(resultsList, PageRequest.of(index,size), resultsList.size());
My problem is that the Page returned has a content of the complete query result, not a content of the size of size parameter inside the PageRequest.of.
Has anybody faced the same issue and could give a working example on how to paginate a nativeQuery please ?
Thanks for your help
You are mixing Spring Data JPA (Pageable) with JPA EntityManager. You can't do that. If you are already using a native query then simply put the pagination in the query. You can use what your database supports, for example the standard:
SELECT [a_bunch_of_columns]
FROM dbo.[some_table]
ORDER BY [some_column_or_columns]
OFFSET #PageSize * (#PageNumber - 1) ROWS
FETCH NEXT #PageSize ROWS ONLY;
this is example of using native query with pagination:
#Query("SELECT c FROM Customer As c INNER JOIN Offer as f on f.id=c.specialOffer.id inner join User As u on u.id=f.user.id where u.id=?1 And c.status=?2")
Page<Customer> getAllCustomerToShop(Integer shopId,String status,Pageable pageable)
and then you can call it as:
getAllCustomerToShop(shopId,"status",PageRequest.of(index, PAGE_SIZE));
Modify your code as follows
Query searchQuery = em.createNativeQuery(this.queryString, MyEntity.class)
.setFirstResult(pageable.getPageNumber() * pageable.getPageSize())
.setMaxResults(pageable.getPageSize());

Mapping many-to-many IN statement into JPA (Spring Boot)

I have created two entities in JPA, Listing and ItemType - these exist in a many-to-many relationship (Hibernate auto-generates a junction table). I'm trying to find the best way to create a query which accepts a dynamic list of item type Strings and returns the IDs of all listings which match the specified item types, but I am a recent initiate in JPA.
At present I'm using JpaRepository to create relatively simple queries. I've been trying to do this using CriteriaQuery but some close-but-not-quite answers I've read elsewhere seem to suggest that because this is in Spring, this may not be the best approach and that I should be handling this using the JpaRepository implementation itself. Does that seem reasonable?
I have a query which doesn't feel a million miles away (based on Baeldung's example and my reading on WikiBooks) but for starters I'm getting a Raw Type warning on the Join, not to mention that I'm unsure if this will run and I'm sure there's a better way of going about this.
public List<ListingDTO> getListingsByItemType(List<String> itemTypes) {
List<ListingDTO> listings = new ArrayList<>();
CriteriaQuery<Listing> criteriaQuery = criteriaBuilder.createQuery(Listing.class);
Root<Listing> listing = criteriaQuery.from(Listing.class);
//Here Be Warnings. This should be Join<?,?> but what goes in the diamond?
Join itemtype = listing.join("itemtype", JoinType.LEFT);
In<String> inClause = criteriaBuilder.in(itemtype.get("name"));
for (String itemType : itemTypes) {
inClause.value(itemType);
}
criteriaQuery.select(listing).where(inClause);
TypedQuery<Listing> query = entityManager.createQuery(criteriaQuery);
List<Listing> results = query.getResultList();
for (Listing result : results) {
listings.add(convertListingToDto(result));
}
return listings;
}
I'm trying to understand how best to pass in a dynamic list of names (the field in ItemType) and return a list of unique ids (the PK in Listing) where there is a row which matches in the junction table. Please let me know if I can provide any further information or assistance - I've gotten the sense that JPA and its handling of dynamic queries like this is part of its bread and butter!
The criteria API is useful when you need to dynamically create a query based on various... criteria.
All you need here is a static JPQL query:
select distinct listing from Listing listing
join listing.itemTypes itemType
where itemType.name in :itemTypes
Since you're using Spring-data-jpa, you just need to define a method and annotate it with #Query in your repository interface:
#Query("<the above query>")
List<Listing> findByItemTypes(List<String> itemTypes)

Add dynamic criteria to a JPA custom Query

I have a complex query(using multiple joins and subqueries) written in HQL which I have used in a Repository class. Similar to one below -
#Repository
public interface DataRepository extends PagingAndSortingRepository<Data,String> {
public List<Data> findByService(#Param("service")Service service, Pageable page);
#Query("SELECT DISTINCT d from Data d "
+" WHERE (d.working in (SELECT d1 from Data d1 "
+" JOIN d1.working d1w "
+" JOIN d1.service s WITH (s in (:serviceList)))"
+" OR d.cleared IS NOT NULL) AND [..several other CRITERIA]")
public Page<Data> findForServices(#Param("serviceList")Set<Service> serviceList, Pageable page);
....
Now I need to add criteria to it dynamically. These criteria are flexible in number which is holding me from including it into the HQL straightaway. Is it anyhow possible?
Sifting through the internet I have come across solutions for dynamic query. But, I guess they would be working only for cases where I do not have a custom query i.e.- no #Query at the query in the repository.
There was another interesting question I found. But that also suits for a case where you have a single table to query.
I do not want to be switching over to raw SQL queries. How do I solve this?
The mentioned Criteria API with specifications and predicates is a little bit difficult to get used to but it is a good way to handle dynamic conditions.
I don't think it is possible to mix the annotation based query with programmatic query creation.

Inner Join and Group By using Specification in Spring Data JPA

I am trying to fetch the employee details whose empltype is clerk and whose joining date is the recent one.
For which the query looks like following in SQL Server 2008:
select
*
from
employee jj
inner join
(
select
max(join_date) as jdate,
empltype as emptype
from
employee
where
empltype='clerk'
group by empltype
) mm
on jj.join_date=mm.jdate and jj.empltype=mm.emptype;
I am using SpringData JPA as my persistence layer using QuerylDSL,Specification and Predicate to fetch the data.
I am trying to convert the above query either in QueryDSL or Specification, but unable to hook them properly.
Employee Entity :
int seqid;(sequence id)
String empltype:
Date joindate;
String role;
Predicate method in Specifcation Class :
Predicate toPredicate(Root<employee> root,CriteriaQuery <?> query,CriteriaBuilder cb)
{
Predicate pred=null;
// Returning the rows matching the joining date
pred=cb.equal(root<Emplyoee_>.get("joindate"));
//**//
}
What piece of code should be written in //**// to convert about SQL query to JPA predicate. any other Spring Data JPA impl like #Query,NamedQuery or QueryDSL which returns Page also works for me.
Thanks in advance
I wrote this in notepad and it hasn't been tested but I think you're looking for something like
QEmployee e1 = new QEmployee("e1");
QEmployee e2 = new QEmployee("e2");
PathBuilder<Object[]> eAlias = new PathBuilder<Object[]>(Object[].class, "eAlias");
JPASubQuery subQuery = JPASubQuery().from(e2)
.groupBy(e2.empltype)
.where(e2.empltype.eq('clerk'))
.list(e2.join_date.max().as("jdate"), e2.emptype)
jpaQuery.from(e1)
.innerJoin(subQuery, eAlias)
.on(e1.join_date.eq(eAlias.get("jdate")), e1.emptype.eq(eAlias.get("emptype")))
.list(qEmployee);

Resources