If there is only one record matching the query ,then query.list() will only return one record.Then what is the use of query.uniqueResult()? because in case there are more than one record matching the query it would throw NonUniqueResultException.What is use of this method?
As documentation states:
Convenience method to return a single instance that matches the query, or null if the query returns no results.
Returns:
the single result or null
Throws:
NonUniqueResultException - if there is more than one matching result
So if you are sure that only one result is going to be returned, then you can use this convenience method to obtain directly the result instead of a list (and so avoiding the verbose use of result.get(0)). If you failed in your suposition of uniqueness then the exception is thrown.
You can use query.uniqueResult() if you are sure you get only one record. For instance when you search for a unique identifier.
If you do something like
select * from person where id =1
you can use query.uniqueResult() because you get only one or zero records.
If you do somethink like
select * from person where name = 'Jens'
you have to use query.list() because you can get more than one record.
Related
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 = ?;
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
I am trying to use createNativeQuery to get a result set from my database and map it to a list of Entity objects.
When i do
List<MyObject> results = this.em.createNativeQuery(query, MyClass.class).getResultList();
I end up with an array of Entity objects that is the correct size, and mapped to the columns correctly, but they are all the same object. The first result returned gets duplicated for every entry in my list.
In the picture above you can see that I get data back, and if i inspect the ovbjects they are mapped to my columns properly. But they are all the same object.
But if i do
List<Object[]> objects = this.em.createNativeQuery(query, Object.class).getResultList();
I get a list of distinct Objects like it should be. But they are not mapped to my Entity class.
In the above pic, you can see that when I use Object and stead of my Entity class type, I get a result set of distinct objects for each row returned. I cannot figure out why when i use my Entity class it just repeats the same row over and over.
Does anyone know what could be causing me to get a list of the same object each time?
My query is selecting from a Pipelined function in a stored procedure
String query = "select * from table(myschema.mypackage.myfunction(input_var))";
UPDATE
The pipelined function I am calling inside my procedure returns about 200 rows. For testing purposes, I have only mapped the first few of the columns.
My Entity class has column mappings for the first 13 of the 200 columns.
My query that calls the function is doing a select *, so, it is returning all 200 columns and mapping the first 13. I thought this was a possible issue, but I just tried it with a new test class I made and doing a select * from a regular table into an Entity that only had 2 of the columns mapped. There was no issue. It returned a list of distinct objects.
My conclusion is that there must be something preventing Hibernate from iterating through the result set of my pipelined function. It returns the correct number of rows, but it gets stuck on the first row and just creates that object over and over.
The issue doesnt seem to have anything to do with my column mappings
Solved it.
Turns out I was just not paying attention and over thinking what I had done.
I had my #Id annotation in my Entity class on a column that turned out was not distinct. When i was checking me work I was only paying attention to the size of the list being returned.
After fixing my #Id on the right column, it is now returning the full list of distinct objects.
Did you trying using something like this?
StoredProcedureQuery q = em.createStoredProcedureQuery("stored_procedure_name");
q.registerStoredProcedureParameter("empList", something.class, ParameterMode.REF_CURSOR);
List<MyObject> myObj= (List<>)q.getOutputParameterValue("paramValue");
// ...
Can you try to provide explicit Result Set mapping
#SqlResultSetMapping(
name="myMapping",
entities={#EntityResult(entityClass=MyClass.class)}
And then
em.createNativeQuery(query, "myMapping").getResultList();
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.
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.