(Spring boot) can Optional<> Class be like List<> Class? - java

Im trying to put the RoomEntity Class in the List as its generic parameter but the List Class turns red(Error) and the only thing that it suggests is for me to change the List Class to Optional Class.
public interface RoomRepository extends CrudRepository<RoomEntity, Long> {
List<RoomEntity> findById(Long id);
}
RoomEntity Class
#Entity
#Table(name = "Room")
public class RoomEntity {
}
are they the same?
List<RoomEntity> findById(Long id);
Optional<RoomEntity> findById(Long id);

Optional and List are two very different concepts.
The CrudRepository findAllById method returns an Iterable.
The findById method returns an Optional.
An iterable can be used to access one by one the elements of a collection and so can be used in conjunction with the List class.
An Optional is a container object which may or may not contain a non-null value (zero or one elements). This will have a single element in it, not many elements like in a List, if there is one.
The CrudRepository::findAllById can have more than one ID sent to it and so can return more than one element, the way it does this is to return an iterable you can use to select each of the returned results one by one. The findById method can only be sent a single ID and so returns that element if it is present (wrapped in an Optional), or an Optional.none if it is not.
If you are looking for a list to be returned because you intend to send in multiple IDs then use the findAllById method. If you want a specific element by ID only, then use the findById method but then you will have to unwrap the Optional object it is returned in before you can use it outside of a stream pipeline using Optional.get, Optional.isPresent, or using a map or flatmap call over it in a streams pipeline.

Spring data JPA will fit the query result to your desired container
You ask a List<>, Spring will initialize a list and add any row data to that list and return it for you. Hence it will:
Return empty list if no items found
Return populated list if items found
When you ask an Optional<>, Spring will understand that you want at most one row data. It will interpreted as getSingleResult() on javax.persistence.Query. Hence it will:
Return Optional.empty() if no items found
Return Optional.of(result) if exactly one match
Throw exceptions if there are more than one match (The one I remember is NonUniqueResultException)
In your case, you find by id. It's unique on your table so Optional<> should fit your purpose.
But note that your List<RoomEntity> findById(Long id); definition is correct and it won't give you compiler error (turn red). Have you imported the List interface?

The findById method is supposed to look for a single entity by it’s id. After all, ids are unique for every entity.
You can try to use findAllById,
but I doubt it’ll make much difference.
What Optional means is that there may or may not be a result. The isPresent method of Optional will indicate this.

Your findById by definition should always return 1 or 0 entities(according to documentation for spring data method naming), as your id is a unique key, and there cannot be more then one entry in your repository with such key value. So Optional suits perfectly well for this situation, because its either empty(no entry with such key in repository) or present with specific value(there is entry in repository). If you want to query all entities by some not unique key, lets say name column, you can name your method findByName, with return value of Iterable<Entity>, thus when generating implementation for your repository spring will understand that there can be more than 1 entity in result set.
Method findById is already predefined in interface you are extending, so you couldn't change it return type anyway.
This also might be usefull: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.core-concepts

Related

How does Spring Data decide what is returned by FindBy

How does Spring Data's findBy decide which database record to return if there are multiple matches?
I realised if I have more than one entry in my Elastic Search database with the same attribute code (ie: "123"), Spring only returns one entry when I call a 'findByAttributeCode'.
If I use a findById, its self explanatory as Id's are unique, however with other findBys, there can be many matches. Note: attributeCode is NOT unique.
How does Spring decide which one to return?
My call would be something like this:
Attribute attribute = findByAttribute(attributeCode);
The repo would look like this:
public interface AttributeRepository extends ElasticsearchRepository<Attribute, String> {
Attribute findByAttributeCode(String attributeCode);
}
This is taken from the return type that you define for your function. If you specify a collection, all matching documents are returned for your query.
If you define a single object as return type, the first entry returned from the underlying store - here Elasticsearch - is returned. What this first entry is, depends on your query criteria, sort parameters - whatever Elasticsearch returns first, is returned to the caller.
What you should be doing, if there are more than one possibility is creating the method stub like this:
<Iterable>Attribute findByAttributeCode(String attributeCode);
This way you return them all. If you don't do that, you are beholden to the RDBMS in how it builds it swap to return a single entry from the multiple tuples it will return from the query it builds, which should be something like:
select * from table where attributeCode = ?;

How do I retrieve particular List of element from MongoDB?

I want to retrieve particular list of element from the Mongodb Table.
Lets suppose i have two variables in my Employee class-:
public Class Employee
{
private String Id;
private String Name;
.
.
now when i will make the fetch query it will be something like -:
List<Employee> list=mongoTemplate.findAll();
and then i will loop through each employee object to fetch the employee ID and save in the List<String>
Now, I want the solution in such a way that i can retrieve all the ID's in one go.Something like-:
List<String> employeeId = someCodeHere;
Please help if you can
Thanks in advance.
According to Mongos Reference documentation on distinct operation:
Finds the distinct values for a specified field across a single collection or view and returns the results in an array.
In Spring Data MongoDB this can be achieved like this:
DistinctIterable<String> distinctIds =
mongoTemplate.getCollection(mongoTemplate.getCollectionName(Employee.class))
.distinct("id", String.class);
return Lists.newArrayList(distinctIds);
// or
BasicDBObject dbObject = new BasicDBObject();
dbObject.append("name", new BasicDBObject("$regex", ".*and.*"));
DistinctIterable<String> distinctIds =
mongoTemplate.getCollection(mongoTemplate.getCollectionName(Employee.class))
.distinct("id", dbObject, String.class);
return Lists.newArrayList(distinctIds);
MongoTemplate offers here a couple of overloads for distinct. The primer query will collect all IDs for entries of a employee collection directly while the latter one will perform a filtering for only IDs of employees that contain an and within their name.
In order to convert the iterable result set to the requested List of String objects you can make use of Guava's newArray(...) feature.
As #Veeram also mentioned in his comment you can of course also make use of a projected query like
Query query = Query.query(Criteria.where(...));
query.fields().include("id");
return mongoTemplate.find(query, String.class);
where query.fields().include("id") is used to specify the fields you are actually interested in.
In contrast to distinct, this approach will contain duplicate entries in the result list, if there are any. While an ID should in general be unique, performing these two queries on names might produce a result that contains multiple identical entries however.
While the answer given by #Boris is technically valid as well, it might have some performance impact, unfortunately, especially if lots of embedded and referenced documents need to get retrieved as well. I therefore would not recommend such an approach.
Final note: throughout the examples I have kept the Id and Name fields in lower-case letters as this is basically Java naming convention.
After a year, what you want can be done with the following code:
List<String> employeeIds= mongoTemplate.query(Employee.class).distinct("id").as(String.class).all();
Without the need of making any conversion. I had the same situation and resolved it doing that.
You can use Java Stream API:
private List<String> getEmployeeIds() {
return mongoTemplate.findAll().stream()
.map(Employee::getId)
.filter(Objects::nonNull)
.collect(toList());
}
First you query for all employees, then convert to a stream, map Employee to Id and then aggregate all non-null values to a list.
If your Repository uses a Java Stream query method:
Stream<Employee> findAll();
then you don't need to call stream() method inside getEmployeeIds().
EDIT: added filtering a null value from the Stream

Spring Rest: when pathVariable matches multiple entities should spring return a list or throw error

Say, a persons collection contain more than one person with the same first and last name. (i.e ex: more than one person with name Andrew Thomas). What will/should be the response of the below rest request
/persons/{firstName}/{lastName} ie. /persons/andrew/thomas
or
/persons/{firstName lastName} i.e /persons/andrew thomas (not sure if spaces are allowed in path variable)
Now since a db query will return more than one record, what should I expect (or what is the correct response to return)
It depends completly on the return type of your method or what you are returning as a output of this API.
If you have returned only person object as
public Person getperson(){}
it can return only single person object .
if you get multiple result at this instant so as the return type can return only single object it will give error.
So if you are getting multiple object with same first name and last name you should use return type as list as
public List<Person> getperson(){}
In the above case it will work either you get one result many result
or no result.
Thanks hope it will help you out
If you are not searching on a primary or unique key, the return type of REST API should always be List.

How to use natural sort with Spring Data Jpa

I have a table column that I want to order on. The problem is that the column value contains both numbers and text. For example, the result is now ordered like this.
1. group one
10. group ten
11. group eleven
2. group two
But I'd like the result to be ordered naturally, like this
1. group one
2. group two
10. group ten
11. group eleven
When looking at the Spring configuration I can't seem to find an option that allows you to do this. I use the Spring Pageable class to set my order field and direction, with an additional use of JPA specifications. The method itself returns a Page with the first 20 results by default.
I don't believe that Oracle supports Natural ordering out of the box so I have a stored procedure for that. Running the query using this procedure I get the desired result.
select ... from group order by NATURALSORT(group.name) asc
As you might expect I'd like to use that procedure by wrapping it around every ordered column that contains text. While maintaining to use pageables/pages and specifications. The research I done this far points me to a solution that might include
Either creating and implementing a custom Specification
Or extending the SimpleJpaRepository to change the way the Sort object is transformed.
But I didn't seem to find a method that allows me to set the order using native SQL. The only way I found to set the order was by calling orderBy and including an Order object.
So in general.
Is there a way to globally enable natural ordering when using Spring Data Jpa, hibernate and the Oracle database
If not, how can I wrap a single order by column with my stored procedure while still being able to use the Page findAll(Pageable, Specifications) method?
Thanks in advance.
After some digging into the source code of both Spring JPA and Hibernate I managed to find a solution to my problem. I'm pretty sure this isn't a nice way to solve it, but it's the only one I could find.
I ended up implementing a wrapper for the 'order by' part of the query by extending the SingularAttributePath class. This class has a render method that generates the string which gets inserted into the actual query. My implementation looks like this
#Override
public String render(RenderingContext renderingContext) {
String render = super.render(renderingContext);
render = "MYPACKAGE.NSORT(" + render + ")";
return render;
}
Next I extended the Order conversion functionality in the SimpleJpaRepository class. By default this is done by calling QueryUtils.toOrders(sort, root, builder). But since the method calling it was impossible to override I ended up calling the toOrder method myself and altering the result to my liking.
This means replacing all orders in the result by my custom implementation of the SingularAttributePath class. As an extra I extended the Sort class which is used by the Pageable class to have control over what gets wrapped and what doesn't (called NaturalOrder). But I'll get to that in a second. My implementation looks close to this (some checks are left out)
// Call the original method to convert the orders
List<Order> orders = QueryUtils.toOrders(sort, root, builder);
for (Order order : orders) {
// Fetch the original order object from the sort for comparing
SingularAttributePath orderExpression = (SingularAttributePath) order.getExpression();
Sort.Order originalOrder = sort.getOrderFor(orderExpression.getAttribute().getName());
// Check if the original order object is instantiated from my custom order class
// Also check if the the order should be natural
if (originalOrder instanceof NaturalSort.NaturalOrderm && ((NaturalSort.NaturalOrder) originalOrder).isNatural()){
// replace the order with the custom class
Order newOrder = new OrderImpl(new NaturalSingularAttributePathImpl(builder, expression.getJavaType(), expression.getPathSource(), expression.getAttribute()));
resultList.add(newOrder);
}else{
resultList.add(order);
}
}
return resultList;
The return list then gets added to the query by calling query.orderBy(resultlist). That's it for the back-end.
In order to control the wrap condition I also extended the Sort class used by the Pageable (mentioned this a few lines back). The only functionality I wanted to add was to have 4 types in the Direction enum.
ASC (default ascending)
DESC (default descending)
NASC (normal ascending)
NDESC (normal descending)
The last two values only act as placeholders. They set the isNatural boolean (variable of the extended Order class) which gets used in the condition. At the time they are converted to query they are mapped back to their default variants.
public Direction getNativeDirection() {
if (this == NaturalDirection.NASC)
return Direction.ASC;
if (this == NaturalDirection.NDESC)
return Direction.DESC;
return Direction.fromString(String.valueOf(this));
}
Lastly I replaced the SortHandlerMethodArgumentResolver used by the PageableHandlerMethodArgumentResolver. The only thing this does is creating instances of my NaturalSort class and passing them into the Pageable object , instead of the default Sort class.
In the end I'am able to call the same REST endpoint, while the result differs in the way it's sorted.
Default Sorting
/api/v1/items?page=0&size=20&sort=name,asc
Natural Sorting
/api/v1/items?page=0&size=20&sort=name,nasc
I hope this solution can help those who have the same or a derived problem regarding natural sort and spring JPA. If you have any question or improvements ,please let me know.
I am not aware of any such feature. You could however store the numbers in a separate column, and then order by that column, which should give a better sorting performance as an additional benefit.

Return value of JPA query when no matches found

I'm using Spring JPA named querys in my repository. My problem is, that I can't find anywhere information what would be returned value for a query that wouldn't match any results. I assume it'll be null for findOne() but I have no idea what would it be for findAllByName() function.
Does anyone know from his/her experience or know a place in documentation?
From my little and personal experience, if you search for an object on your repo, for example by Id or Name the named query method returns an object of type T, but if no results are found from your repo, it will return null.
Methods that can return more than one element, will produce an empty collection List<T>(not null).
Some documentation here:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repository-query-keywords
Appendix D: Repository query return types
Supported query return types
Query return types:
T An unique entity. Expects the query method to
return one result at most. In case no result is found null is
returned. More than one result will trigger an
IncorrectResultSizeDataAccessException.
Iterator An Iterator.
Seems like only when return type is of type T is the only one that specify a null is returned if no matches.

Categories