Difference between findBy and findOneBy in Spring data JPA - java

All i know so far is that FindBy can return multiple results while FindOneBy will return a single result or null when we use it the following way.
List<Department> findByDepartmentName(String name);
Department findOneByDepartmentId(Long Id);
now, my question is, can i use findBy this way?
Department findByDepartmentId(Long Id);
If yes,
Lets assume there are multiple records for given Id.
On what basis does findBydepartmentId return a single record?
Finally, When or Why should i not use findBy in place of findOneBy?

Can I use findBy this way? Department findByDepartmentId(Long Id);
Yes, this syntax is technically correct from Spring JPA point of view. Although Spring JPA infers what you're trying to achieve with your query looking at the return type as well.
Basically these are the cases for return types:
with your query you want to return a single value - you can specify basic type, Entity T, Optional<T>, CompletableFuture<T> etc.
with your query you want to return a collection of T - you can specify List<T>, Stream<T>, Page<T>, Slice<T> etc.
That being said, your query definition:
Department findByDepartmentId(Long Id);
means that you expect a single result (because you've specified Entity T as a return type). This will reflect on how Spring JPA executes the query - it will call getSingleResult() on the javax.persistence.Query interface, which will throw an exception if more than one objects satisfy the criteria.
On what basis does findBydepartmentId return a single record?
On the basis that there's a single object with that Id, otherwise it will throw an exception.
When or Why should i not use findBy in place of findOneBy?
Those two have different meanings and are not interchangeable.
findOneBy always results in getSingleResult() being invoked.
findBy has different behavior depending on the return type - as per the definitions given above.

findOneByXX will ensure that there is only one or no value, if there are 2 values an exception will be thrown.
However findByXX doesn't make this check of uniqueness.

I have done some tests and Spring Data ignore all characters between the method (find, delete,...) and By.
In https://github.com/spring-projects/spring-data-commons/blob/14d5747f68737bb44441dc511cf16393d9d85dc8/src/main/java/org/springframework/data/repository/query/parser/PartTree.java#L65 it is the \p{Lu}.*? part.
Spring Data only use return type to decide how handle responses.
So it is possible to define these following methods even if it is not correct semantically.
Department findAllByDepartmentId(Long Id);
List<Department> findOneByDepartmentName(String name);

Related

How to retrieve only part (few) of pojo fields in Hibernate?

I have a situation where I need to return only few fields of a POJO.
Here is a SOF Question: retrieve-single-field-rather-than-whole-pojo-in-hibernate question regarding the same, but few things still seems to be obscure.
1) The answer suggests to use -
String employeeName = session.createQuery("select empMaster.name from EmployeeMaster empMaster where empMaster.id = :id").setInteger("id",10).uniqueResult();
So, here is my concern - Every pojo field is normally private, so "empMaster.name" will simply not work. And am not sure if empMaster.getName() is the solution for this. Will calling the getter methods work?
2) If i am querying multiple fields, (which is my case) (assuming getter methods work) the query will be some thing like -
List<String> employeeDetails = session.createQuery("select empMaster.getName(), empMaster.getDesignation() from EmployeeMaster empMaster where empMaster.id = :id").setInteger("id",10).uniqueResult();
Note the return type has changed from String to List<String>.
2(a) Hope this is right?
2(b) what if i am interested in age/salary of employee which will be of int type. I think the return type will be List<String> or List<Object>. Well any how in the application i can recast the String or Object to the proper type int or float etc. So this should not be a problem.
3) Now what if I am querying multiple employee details (no where clause), so the query will be something like - (not sure if the part of query after from is correct)
List<List<<String>> employeesDetails = session.createQuery("select empMaster.getName(), empMaster.getDesignation() from EmployeeMaster;
Anyway, point here is to emphasise the change in the return type to : List<List<<String>> employeesDetails. Does it work this way ???.
(The question quoted above also has answers pointing to use Projections. I have questions about it but will post them on another question, don't want to mixup.)
I will list the points in the order you mentioned them:
The query has nothing to do with the POJO's field visibility. You are doing a simple query to the database, as if you were doing a query using SQL, and columns in a table have nothing to do with the fact that their mapped POJOs' fields in an application are public or private. The difference is only the language that you're using: now you're using the Hibernate Query Language (HQL), which allows you to express your query with respect to the POJOs' definitions instead of the database's tables' definitions. In fact, doing
session.createQuery("select empMaster.getName() from EmployeeMaster...");
will throw a syntax error: there can be no parenthesis in an object's field name.
By the way, you have to parse your query result to a String, otherwise there would be a compiler semantics error.
String name = (String) session.createQuery("select empMaster.name from EmployeeMaster...").setYourParameters().uniqueResult();
When you do a query whose SELECT clause contains more than one field, and you call uniqueResult(), you're obtaining an array of Objects (Object[]). You iterate through each element in the array and cast it to the corresponding type.
First of all, you probably forgot to add a .list() method call at the end of your query.
session.createQuery("select empMaster.name, empMaster.designation from EmployeeMaster").list();
Otherwise, the query will not be executed, and you would just have a QueryImpl object waiting to be triggered.
Now, when you're expecting multiple results in your query and selecting several fields, you're getting back a List<Object[]>. The elements of the list will be the array of fields for a specific record, and the array per se will be the fields.

spring data findBy return type

What are the possible return types of spring data findBy methods? I haven't found any consolidated list anywhere.
like findByXXX can return
1) Entity
2) Optional<Entity>
3) List<Entity>
4) Stream<Entity>
and many more, but do we have a definite list?
Also, List<Entity> returns emptyList when no data found but Stream<Entity> returns null when no data found, so when you apply map/filter etc on stream, you get NPE.
Is there a better way to use Stream without causing NPE, other than physically checking null before calling stream functions.
Though a bit late, but the supported return types are documented here:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repository-query-return-types
Generally, the return types could be any of the following categories:
a (Optional) single item, could be a primitive, wrapped, or object, or void
a collection of items, or paged list
a stream, Iterator, Mono/Flux, or those similar.
Future, or its variants
And, the complete list of currently supported return types:
void
Primitives
Wrapper types
T
Iterator
Collection
List
Optional
Option
Stream
Streamable
Types that implement Streamable and take a Streamable constructor or factory method argument
Vavr Seq, List, Map, Set
Future
CompletableFuture
ListenableFuture
Slice
Page
GeoResult
GeoResults
GeoPage
Mono
Flux
Single
Maybe
Flowable
Your list of possible return type is corret. You can use too the type Page and Slice when you need pagination the result. See example:
Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
"The first method allows you to pass an org.springframework.data.domain.Pageable instance to the query method to dynamically add paging to your statically defined query. A Page knows about the total number of elements and pages available. It does so by the infrastructure triggering a count query to calculate the overall number. As this might be expensive depending on the store used, Slice can be used as return instead. A Slice only knows about whether there’s a next Slice available which might be just sufficient when walking thought a larger result set." - See more in: Spring Data Documentation
For another question, you can use Guava/Java 8 Optional. This way, your query method will return an Optional that contains the found object or an empty Optional. Optional is a way of replacing a nullable T reference with a non-null value, see example:
Optional<User> findById(Long id);
I hope I have help you. =)

Spring: Derived Queries using 2 clauses?

I am currently using spring derived queries in my application. E.g:
Dog findById(String id);
Is there a way to add 2 or more clauses to the derived queries? e.g.:
Dog findByIdAndOwnerOrderByOwner();
You should be able to do something like this,
Dog findByIdAndOwnerOrderByOwnerDesc(String id, String owner);
As long as you use AND or OR to concatenate your query, it should handle multiple clauses. I also believe you need to specify the order by order (ASC/DESC).
Check out Spring Data JPA for more information regarding this topic.

How to specify default sort in Spring Data #Repository find method?

Is below the only way (not so "clean" IMHO)? I'd expect something like #SortDefault and #PageableDefault, but it's only for (REST) controllers and not applicable to Spring Data REST.
#Query("SELECT e FROM FacebookPost e WHERE e.commentsPaging.next IS NOT NULL ORDER BY e.creationTime ASC")
Page<FacebookPost> findByCommentsHasNext(Pageable pageable);
Additionally:
How to specify multiple columns for default sorting?
How to specify calculated column(s), i.e. involving CASE WHEN or functions (which can be indexed in PostgreSQL)?
I'm not sure if #SortDefault is what you're looking for but you can directly specify the type of ordering you'd like with the repository method name. For example, from reading your query, I'd name the method something as follows:
Page<FaceBookPost> findByCommentHasNextOrderByCreationTimeAsc(Pageable pageable);
If you dictate elsewhere that the field commentsPaging cannot be null, I don't think there would even be a need for a #Query above your method name since Spring will automatically go and perform the query for you.

Inject attribute into JPQL SELECT clause

Let's depict the following use case: I have a JPQL Query which on the fly creates data objects using the new keyword. In the SELECT clause I would like to inject an attribute which is not known to the database but to the layer which queries it.
This could look like
EntityManager em; // Got it from somewhere
boolean editable = false; // Value might change, e.g. depending on current date
Query q = em.createQuery("SELECT new foo.bar.MyDTO(o, :editable) FROM MyObject o")
.setParameter("editable", editable);
List<MyDTO> results = (List<MyDTO>) q.getResultList();
Any ideas how this kind of attribute or parameter injection into the SELECT clause might work in JPQL? Both JPA and JPA 2.0 solutions are applicable.
Edit: Performance does not play a key role, but clarity and cleanness of code.
Have you measured a performance problem when simply iterating over the list of results and call a setter on each of the elements. I would guess that compared to
the time it takes to execute the query over the database (inter-process call, network communication)
the time it takes to transform each row into a MyObject instance using reflection
the time it takes to transform each MyObject instance into a MyDTO using reflection
your loop will be very fast.
If you're so concerned about performance, you should construct your MyDTO instances manually from the returned MyObject instances instead of relying on Hibernate and reflection to do it.
Keep is simple, safe, readable and maintainable first. Then, if you have a performance problem, measure to detect where it comes from. Then and only then, optimize.
It will not work without possible vendor extensions, because according specification:
4.6.4 Input Parameters
...
Input parameters can only be used in the
WHERE clause or HAVING clause of a query.

Categories

Resources