I have this spring data repository method
public FileActivity findTopByFileIDOrderByCreatedDesc(String fileID);
This works fine . But how do I make it work for list of parameters ?
This doesn't work (I can have many FileActivity for file id - but I want only the last one) :
public List<FileActivity> findTopByFileIDOrderByCreatedDesc(List<String> fileIDs);
Spring Data's support for derived queries is useful but for anything other than simple queries it is probably easier and clearer just to define your own JPQL query.
#Query("select f from File f where f.id in :fileIds order by f.created desc")
public Page<FileActivity> findTopFilesById(
#Param("fileIDs") List<String> fileIDs, Pageable pageable);
As JPQL does not have a limit keyword you can simply pass in a Page.
List<String> fileIds = //;
Page<File> page = repository.findTopFilesById(fileIds, new PageRequest(0, fileIds.size());
List<File> files = page.getContent();
You could also dynamically specify the sort order in the PageRequest rather than in the JPQL giving a bit more flexibility:
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/PageRequest.html
public FileActivity findTopByFileIDInOrderByCreatedDesc(Collection<String> fileIDs);
See more examples here
Note - if you use 'Top' then you get only one record. To get more than one record you have to add a number to 'Top' for example: 'Top10'.
More info here
Related
I'm requesting paged and sorted data from my database, specified via Pageable with following repository API:
org.springframework.data.jpa.repository.JpaSpecificationExecutor.findAll(Specification<MyEntity> filter, Pageable pageable)
Pageable contains a Sort with simple sorting criteria, such as sort column X in descending order.
Sometimes, Sort contains one column which actually doesn't exist in the underlying table, which must be replaced by a complex conditional ORDER BY expression:
ORDER BY CASE WHEN entity_to='CUSTOMER' THEN entity_to_id ELSE (CASE WHEN entity_from='CUSTOMER' THEN entity_from_id END) END
Indeed, I can rebuild the Sort instance in Pageable so that this non-existing column gets removed, but I didn't find a way to inject the complex ORDER BY expression in that case.
I tried to find a way, to enrich the resulting query from this invocation of the Repository. I hoped that I can do it by adding another Specification filter, but I got an error (No explicit selection and an implicit one could not be determined), and I'm not sure that the order-by will be applied to the final query built by the Repository implementation:
...
filter = filter.and(
(root, query, cb) -> {
final var customerLiteral = cb.literal("CUSTOMER");
final var caseExpression = cb.selectCase(root.get(MyEntity_.ENTITY_TO))
.when(customerLiteral, root.get(MyEntity_.ENTITY_TO_ID)).otherwise(
cb.selectCase(root.get(MyEntity_.ENTITY_FROM))
.when(customerLiteral, root.get(MyEntity_.ENTITY_FROM_ID)).otherwise(0));
final var order = ascElseDesc ? cb.asc(caseExpression) : cb.desc(caseExpression);
final var cq = cb.createQuery(MyEntity.class);
return cb.exists(cq.select(root).orderBy(order).subquery(MyEntity.class));
}
);
repository.findAll(filter, pageable);
I don't want to drop the current implementation which leverages Spring Data's findAll(filter, pageable). I only would like to enrich it in one case. How can it be done?
For that case you can create #Formula column in MyEntity with custom SQL and then refer it in Pageable as regular entity field
I'm building REST API connected to ORACLE 11G DB. API sends data to Android client using JSON. To get data I'm using JpaRepository, and #Query annotations.
I want to provide data for charts: number of contracts in years.
I have native SQL query:
select aa.ROK, count(aa.NUMER_UMOWY)
from (select distinct NUMER_UMOWY, ROK from AGR_EFEKTY) aa
group by aa.ROK order by aa.ROK
Result of query using SQL Developer look like this:
I tried to get result using native query:
But result is always like this:
or error depending what I try.
Is it possible to obtain list of count() results using #Query?
If not, what should I use?
Thanks in advance :-)
I think What you are trying to use here is spring data projection.
As mentioned in the reference doc:
Spring Data query methods usually return one or multiple instances of
the aggregate root managed by the repository. However, it might
sometimes be desirable to create projections based on certain
attributes of those types. Spring Data allows modeling dedicated
return types, to more selectively retrieve partial views of the
managed aggregates.
and particularly closed projection where all accessor methods match the target attributes. In your case the count is not an attribute of your aggregate.
To perform what you want you can use constructor as follow :
class ContractsDto{
private String rok;
private int count;
public ContractsDto(String rok, int count) {
this.rok=rok;
this.count =count;
}
// getters
}
The query will be:
#Query(value = "select new ContractsDto(aa.rok , /*count */) from fromClause")
List<ContractsDto> getContractsPerYear();
Utilizing the Spring Framework's Page interface I'm able to retrieve 50 users at a time with the code below:
int page = 0;
int pageSize = 50;
Page<User> userPage = userRepo.findByFirstName("Bob", page, pageSize);
List<User> userList = userPage.getContent();
This runs some query in the background to give me my first 50 users in my database. What I want to do is populate my userList without paging so that every is returned from the underlying query - is this possible?
I've tried the hasNext() method on my Page object but it runs in an endless loop
while(userPage.hasNext()){
logger.info("Next page found");
}
Any ideas?
If you don't want paging you can ignore spring Page and directly call userRepo.findByFirstName("Bob");
This will return all users in database whose firstName is bob
Your method signature should be
List<User> users = userRepo.findByFirstName("Bob");
Hope this solves your question
If you always want all users and not them by page, you need to adjust this on the Repository side. Change the signature of findByFirstName to return List<User> instead of Page<User>, and remove the Pageable parameter. Then, it will always return all. See more information on how parameters are handled in the Spring Data documentation.
If you need to sometimes paginate and sometimes get all data, then just add the List<User> version. The return type will depend on whether you pass a Pageable parameter or not based on Java's usual overloading.
The hasNext() method on Page merely tells you if a next page is available (for use in presenting navigation), it doesn't run another query. You would need to construct a new Pageable object and iterate through several calls to the Page<User> version in order to access all data through the Pageable interface, which is less efficient than just querying for what you actually want in the first place.
I have a question regarding querying in hibernate. If there is a way to search without specifying an where cause explicitly?
So what I mean is: let's say I have a search form with 10 columns that are bound to my dto fields. So the user can fill some of them and left the rest as nulls. And now I would like to search only by fields that are specified (filled) and left the nulls behind (they doesn't matter).
So the query would be like this:
select e
from entity e
where e.entity = e.searchedCriteriaEntityGivenInDTO
Or a better example via jpg: I'd like to have all sample entities without specifying "where number, where name, where firstanme", but over my dto by "where dtoFields". As mentioned the nulls should be ignored.
Thanks a lot in advance.
[EDIT]: Thanks to Dragon I have a great solution how to do it, but I have one more question: What about I have 2 row's, I can search? My query should look like:
select e
from example e,
where (e.entity = example) OR (e.entity = example2);
I tried to put the
session.createCriteria(MyEntity.class).add(Example.create(myEntityExample))
into an OR-Predicate but it seems it does not work.
Any suggestions?
No, you can't do it with DTOs, but you can use a prototype (example) entity instance for it:
MyEntity myEntityExample = new MyEntity();
myEntityExample.setNumber(12);
myEntityExample.setName("AA");
myEntityExample.setFirstName("BB");
List<MyEntity> results = session.createCriteria(MyEntity.class)
.add(Example.create(myEntityExample))
.list();
I'm just getting to grips with JPA in a simple Java web app running on Glassfish 3 (Persistence provider is EclipseLink). So far, I'm really liking it (bugs in netbeans/glassfish interaction aside) but there's a thing that I want to be able to do that I'm not sure how to do.
I've got an entity class (Article) that's mapped to a database table (article). I'm trying to do a query on the database that returns a calculated column, but I can't figure out how to set up a property of the Article class so that the property gets filled by the column value when I call the query.
If I do a regular "select id,title,body from article" query, I get a list of Article objects fine, with the id, title and body properties filled. This works fine.
However, if I do the below:
Query q = em.createNativeQuery("select id,title,shorttitle,datestamp,body,true as published, ts_headline(body,q,'ShortWord=0') as headline, type from articles,to_tsquery('english',?) as q where idxfti ## q order by ts_rank(idxfti,q) desc",Article.class);
(this is a fulltext search using tsearch2 on Postgres - it's a db-specific function, so I'm using a NativeQuery)
You can see I'm fetching a calculated column, called headline. How do I add a headline property to my Article class so that it gets populated by this query?
So far, I've tried setting it to be #Transient, but that just ends up with it being null all the time.
There are probably no good ways to do it, only manually:
Object[] r = (Object[]) em.createNativeQuery(
"select id,title,shorttitle,datestamp,body,true as published, ts_headline(body,q,'ShortWord=0') as headline, type from articles,to_tsquery('english',?) as q where idxfti ## q order by ts_rank(idxfti,q) desc","ArticleWithHeadline")
.setParameter(...).getSingleResult();
Article a = (Article) r[0];
a.setHeadline((String) r[1]);
-
#Entity
#SqlResultSetMapping(
name = "ArticleWithHeadline",
entities = #EntityResult(entityClass = Article.class),
columns = #ColumnResult(name = "HEADLINE"))
public class Article {
#Transient
private String headline;
...
}
AFAIK, JPA doesn't offer standardized support for calculated attributes. With Hibernate, one would use a Formula but EclipseLink doesn't have a direct equivalent. James Sutherland made some suggestions in Re: Virtual columns (#Formula of Hibernate) though:
There is no direct equivalent (please
log an enhancement), but depending on
what you want to do, there are ways to
accomplish the same thing.
EclipseLink defines a
TransformationMapping which can map a
computed value from multiple field
values, or access the database.
You can override the SQL for any CRUD
operation for a class using its
descriptor's DescriptorQueryManager.
You could define a VIEW on your
database that performs the function
and map your Entity to the view
instead of the table.
You can also perform minor
translations using Converters or
property get/set methods.
Also have a look at the enhancement request that has a solution using a DescriptorEventListener in the comments.
All this is non standard JPA of course.