Can I limit the result of JPQL to just return one result? - spring

I would like to do something like this (in Spring Data JPA) in a repository interface:
interface myRepository extends JpaRepository<A, Long> {
#Query("select a from A a where a.x = :x")
A findFirstBySomeCondition(int x);
}
But I only need the first result. (Edited: The actual query condition is much complex so I would prefer to use #Query instead of findFirst or findTop...)
I don't want to use criteria api, because it's verbose.
I don't want to use the the native query cause I will have to compose the query string manually.
So, is there an solution left though, given the restricted requirement above?
Thanks!

The results of query methods can be limited via the keywords first or top, which can be used interchangeably. An optional numeric value can be appended to top/first to specify the maximum result size to be returned.
interface myRepository extends JpaRepository<A, Long> {
#Query("select a from A a where a.x = :x")
Page<A> findFirstBySomeCondition(#Param("x") int x,Pageable pageable);
}
Impletation Class:
Page<A> results = repository.findFirstBySomeCondition(x,new PageRequest(0, 1));
A object = results.getContent.get(0);

You can use a Pageable for that. The documentation schows different ways of using it: http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.limit-query-result

Related

How to fetch multiple entities by a list of natural ids with Hiberate or JPA repository?

If I have a list of natural Ids, how can I fetch all the records in the DB associated with these natural Ids at once?
All I've seen are methods that let you find an entity by a natural Id, an example of one is shown below.
#Override
public Optional<T> findBySimpleNaturalId(ID naturalId) {
Optional<T> entity = entityManager.unwrap(Session.class)
.bySimpleNaturalId(this.getDomainClass())
.loadOptional(naturalId);
return entity;
}
I am looking for a method that can take a list natural Ids and fetch all the entities with these natural Ids. My current Entity has a autogenerated UUID and a naturalId and I'd like to keep it this way.
Is there something like this below
List<Song> songs = entityManager
.unwrap(Session.class)
.byMultipleSimpleNaturalIds(Song.class)
.multiLoad(songGroup.getSongIds());
// or using the repository
customRepository.findAllByNaturalId(...)
The examples you've seen are showing you how to build them yourself, as spring does not provide individual methods for you; it knows nothing about properties in your entity other than it must have an id. If you want a findAllByNaturalId method, you have to define it in the interface.
Specifying this in your customRepository:
public List<Song> findByNaturalIdIn(List<Int> naturalIds);
Spring should generate an implementation that creates a query similar to "Select s from Song s where s.naturalId In :naturalIds".
If it doesn't, just add that JPQL query string as an annotation and it will execute it for you:
#Query(value = "Select s from Song s where s.naturalId In :naturalIds")
public List<Song> findByNaturalIdIn(List<Int> naturalIds);
Or you can write your own implementation method to execute your loadOptional calls, or any query you wish, but you still must define the method in your repository.

SpringData JPA: Query with collection of entity as parameter

I have a list of entities on which I want to perform an update, I know I could update the table with list of String/Integer.. etc as the parameter with something like
#Query("update tableName i set i.isUpdated = true where i.id in :ids")
void markAsUpdated(#Param("ids") List<Integer> itemIds);
I'm trying to avoid repeated conversion of list of entities to list of Ids for making the query in DB. I know there are deleteAll and deleteInBatch commands which accept parameter as list of entities.
How do I do this in JPA Query, I tried the following but it didn't work yet.
#Modifying(flushAutomatically = true, clearAutomatically = true)
#Query("update tableName i set i.updated = true where i in :items")
void markAsUpdated(#Param("items") List<Item> items)
The query needs ids, it doesn't know how to deal with entities.
You have multiple options:
Just pass ids to the method, the client is responsible for extracting ids.
Pass entities and use SpEL for extracting ids
As suggested in the comments use a default method to offer both APIs and to delegate from one to the other.
As for the question that came up in the comments: You can move the method for extracting the id into a single method by either have relevant entities implement an interface similar to this one:
interface WithId {
Long getId();
}
Or by passing a lambda to the method, doing the conversion for a single entity:
List<ID> extractIds(List<E> entities, Function<E, ID> extractor) {
// ...
}

Find by id=1 spring data jpa

I have a table component and entity Component. I want to select id = 1 record from jpa query. Can I write 'findByIdOne' or 'findByIdEqualtoOne'? will that give me id = 1 record? Please let me know, Thanks.
No, you cannot. Refer to the Spring Data JPA documentation which documents the exact keywords that you can use.
You are free to specify the query that you want a method to execute though. Something like
#Query("select c from Component c where c.id=1")
Component findByIdOne();
I do have to put a disclaimer: by providing this solution I assume that you are really sure that ID 1 is always going to exist and will always point to exactly the same Component record in any environment that you may be running the application against. Hardcoded database IDs in your application code is not something I would ever recommend.
Direct writing the query dsl you cant, but there is a 'way' with Java 8 using default methods:
Let's say you have the query:
public interface ComponentRespository extends CrudRepository<Component, Long> {
#Query("select c from Component c where c.id=:id")
Component findById(#Param("id") Long id);
default Component findByIdOne() {
return findById(1L);
}
//eventually
default Component findByIdTwo() {
return findById(2L);
}
}
This way you can use:
private ComponentRespository componentRepository;
.....
Component componentOne = componentRepository.findByIdOne();
Component componentTwo = componentRepository.findByIdTwo();
You can use Optional<EntityName> findById(long id) or List<EntityName> findById(long id) if it's not unique.
You can use Optional<EntityName> findById(long id) or List<EntityName> findAllById if it's not unique.
We have several options:
findByIdIs(long id) or findByIdEquals(long id)

Spring Pagination sorting with cases

I have a problem with Sorting in Spring. I have an enum class
public enum Category {
A,
B,
C,
D,
E,
F;
private static final List<Category> THRILLER;
static {
THRILLER = Arrays.asList(A, F, D);
}
private static final List<Category> DRAMA;
static {
DRAMA = Arrays.asList(C, E, B);
}
}
The Movie Entity:
#Entity
#Table
public class Movie {
...
private Category category;
private Timestamp realizationDate;
....
}
Now I have a rest endpoint that return movies List. I want to Sort this list according to the following order: Movies they belong to THRILLER should be displayed first. When two movies belong to the same category then this should be sorted by realization date.
#GetMapping(value = "/movies")
public ResponseEntity<PagedResources<Movie>> list(#PageableDefault() Pageable pageable) {
.......
}
I tried it with:
#SortDefault.SortDefaults({SortDefault(sort="category", direction=Sort.Direction.ASC), SortDefault(sort="realizationDate", direction=Sort.Direction.ASC)})
But this Sort the enum only alphabetically.
I need a possibility to do something like: Sort.by(Category.THRILLER.contains(movie.getCategory())). It is possible that I achieve that with Pageable or should I do it another way?
Spring Data directly only supports sorting by simple attributes.
So in order for this to work you need to provide an isThriller attribute.
You can do this with the existing object structure by using:
a) a trigger to fill the column in the database
b) use a view to compute the desired value
c) if you use Hibernate you may use the #Formula annotation to instruct it to compute the value.
In either case you are hardcoding the categories that make up thrillers in a String or in the database.
If you don't want that you could make Category an entity with an isThriller attribute which you then can query and sort by in the usual fashion.
AFAIK Sort in spring pagination sorts fields only in lexicographical order (same like in SQL, uneless you use some procedural version of SQL like PSQL) so you can't introduce some custom logic of sort.
You have two options:
introduce custom procedure/function into your DB and call it from code (ugly & bad approach)
Sort list of movies by playing with lambdas and custom comparators, after fetching it from DB (clean & simpler approach)

Run two #NamedNativeQuery query on same entity Class

I want to define two #NamedNativequery on entity class . When tying to define eclipse gives a error.
Duplicate annotation of non-repeatable type #NamedNativeQuery. Only
annotation types marked #Repeatable can be used multiple times at one
target.
From that error , I know we cannot define two define two #NamedNativeQuery of the entity class like
#Entity
#Table(name = "abc")
#NamedNativeQuery(name = "ABC.getSomeMethod1" query = "some_query",resultSetMapping ="abcDTO")//1st name query
// #NamedNativeQuery(name = "some_name" query = "some_query",resultSetMapping ="some_dto")//try to define second query , but gives error
public class ABC {
}
I am using spring repository at dao layer to called the method which bind with this query
public interface SomeInterface extends JpaRepository<ABC, Long> {
#Query(nativeQuery =true)
List<ABCDTO> getSomeMethod1(#Param("someParam1") long someParam1, #Param("someParam2") String someParam2);
}
The senario is that I want to run the 1st native sql (which run fine) query and then run the 2nd native sql query(want to run this also from same). How to solve this or What is the possible solution.
If this way I cannot run the two native sql query then is there any other to achive this.
You can define multiple named queries like this
#NamedNativeQueries({
#NamedNativeQuery(name = "ABC.getSomeMethod1"
query = "some_query",resultSetMapping ="abcDTO"
),
#NamedNativeQuery(name = "some_name"
query = "some_query",resultSetMapping ="some_dto"
)
})
Then in the business layer under the transaction you can call these two queries one after another,
If its a simple join between two entities and select and display better go with join's. Always remember to have those columns index in the Table ;)

Resources