Is there any difference when using Spring Data JPA keywords between:
List<SomeEntity> findBySomeCondition();
and
List<SomeEntity> findAllBySomeCondition();
No, there is no difference between them, they will execute exactly the same query, the All part is ignored by Spring Data when deriving the query from the method name. The only important bit is the By keyword, anything following it is treated as a field name (with the exception of other keywords like OrderBy which incidentially can lead to some strange looking method names like findAllByOrderByIdAsc).
This means something like this is perfectly valid:
List<SomeEntity> findAnythingYouWantToPutHereBySomeCondition();
And will execute exactly the same SQL query as:
List<SomeEntity> findBySomeCondition();
or
List<SomeEntity> findAllBySomeCondition();
The documentation for the 2.3.6 release of Spring Data discusses this feature:
Any text between find (or other introducing keywords) and By is considered to be descriptive unless using one of the result-limiting keywords such as a Distinct to set a distinct flag on the query to be created or Top/First to limit query results.
The purpose of feature was explained in a blog post about the then-upcoming 2.0 release of Spring Data:
Spring Data’s method parsing uses prefix keywords like find, exists, count, and delete and a terminating By keyword. Everything you put in between find and By makes your method name more expressive and does not affect query derivation.
To illustrate the difference lets look at the two functions:
1. Set<Policy> findAllByRoleIn(Iterable<Role> role);
2. Set<Policy> findByRoleIn(Iterable<Role> role);
The query generated by 1st function:
1. select policy.id, policy.role from policy where (policy.role in (? , ? , ? , ?))
The query generated by 2nd function:
2. select policy.id, policy.role from policy where (policy.role in (? , ? , ? , ?))
Conclusion: Clearly, if we look at the queries generated by both functions. We can clearly see, there is no difference between the two function definitions, they execute exactly the same query.
one difference is that with findAllBy Hibernate filters (#Filters from org.hibernate.annotations) are applied and so a different sql.
Actually, the difference between findallBy and findby, is that :
findAllBy returns a Collection but
findBy returns Optional.
so it's preferable to write List findAllBy instead of writing List findBy (but it will work also :p).
and to write Optional findBy instead of Optional findAllBy.
check this doc https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.core-concepts
findBy method is used if we want to find by name or some other criteria like findByFirstName(String firstName);
findAll methods generally finds by providing specification
List<T> findAll(Specification<T> spec);
Please see docs below for more clarity:
http://docs.spring.io/spring-data/jpa/docs/1.4.3.RELEASE/reference/html/jpa.repositories.html
Related
The case is:
I have a repository and two methods, one of them looks like:
//There is no warning about: Activity domain type or valid projection interface expected here...
#Query("select distinct a.creatorId from Activity a")
Optional<List<String>> findAllCreatorIds();
The second method looks like:
//There i have warning about: Activity domain type or valid projection interface expected here
#Query("select distinct a.creatorId from Activity a join a.categories c where c.name in ?1")
Optional<List<String>> findAllCreatorIdsByCategoryNames(Set<String> categoryNames);
It looks workable and tests are passed and the generated query is the following:
SELECT DISTINCT activity0_.created_by AS col_0_0_
FROM activities activity0_
INNER JOIN category_item categories1_ ON
activity0_.id=categories1_.activity_id
INNER JOIN categories category2_ ON
categories1_.category_id=category2_.id
WHERE category2_.name IN (?)
I have made the changes to use projection interface. The simple projection interface is:
public interface CreatorIdProjection {
String getCreatorId();
}
The query was changed:
#Query("select a from Activity a join a.categories c where c.name in ?1")
Optional<List<CreatorIdProjection>> findAllCreatorIdsByCategoryNames(Set<String> categoryNames);
Works too. And here is the generated query:
SELECT activity0_.id AS id1_0_,
activity0_.price AS price2_0_,
activity0_.created_by AS created_3_0_,
activity0_.expiration_date AS expirati4_0_,
activity0_.insertts AS insertts5_0_,
activity0_.name AS name6_0_,
activity0_.start_date AS start_da7_0_,
activity0_.updatets AS updatets8_0_
FROM activities activity0_
INNER JOIN category_item categories1_ ON activity0_.id=categories1_.activity_id
INNER JOIN categories category2_ ON categories1_.category_id=category2_.id
WHERE category2_.name IN (?)
I have a few questions related to this case:
Why there is no warning for the first method ?
Why the query have a lot of fields after SELECT when we use
projection? (to be more precise - why we cannot use "select
a.creatorId from Activity a...")
What is the reason of the warning "...domain type or valid
projection interface expected here" if as a result we have a query
that querying the table data instead of what we need.
The warning comes because you're using 'By' in your method name which triggers Spring Data to use (magic) Query methods.
Remove 'By' or replace it with 'Using' or something else and the warning will disappear.
See also https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.details
About question #1:
Why there is no warning for the first method?
There is no Spring Data Projection involved here. You just execute a query returning a list of scalar values, that then is past on (and wrapped in an Optional) by Spring Data. Therefore you should not get such a warning. As a matter of fact I couldn't reproduce the warning at all, nor could I find it in the source code and in any case, the same argument holds for the second method. If it actually produces such a warning please submit a bug.
For question #2a
Why the query have a lot of fields after SELECT when we use projection?
You specify the query exactly using the #Query annotation, and it gets executed as such. Spring Data does not parse your query and removes unnecessary parts, which would be a huge amount of effort for little effect since you provided the query in the first place you might as well provide one that fits your needs.
For question #2b
why we cannot use select a.creatorId from Activity a...?
You can (almost). You just have to specify aliases because JPA will otherwise mangle the column names and Spring Data wouldn't know what columns it is looking at. It actually tells you so when you don't specify aliases. You should get an exception No aliases found in result tuple! Make sure your query defines aliases!. So this should work:
select a.creatorId as creatorId from Activity a...
An alternative is to actually invoke the constructor of a class in the statement:
#Query("select new my.super.cool.projection.CreatorId(a.creatorId)")
Both variants will only query the columns specified.
For question #3
What is the reason for the warning ...domain type or valid projection interface expected here if as a result we have a query that querying the table data instead of what we need.
I was not able to reproduce the warning. I also searched the source code and couldn't find it, so I can't answer this one.
Is any possibility to execute parametrized N1QL query with turned off adhoc flag using annotation #Query?
I.e. for given query:
#Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} and author = $author")
List<Comment> getCommentsByAuthor(#Param("author") String author);
If not, is any other way to force couchbase to use secondary index (in this example index on author field) in annotation queries?
You seem to mix a few things:
Parametrized Query will work with the #Query annotation as in your snippet. The documentation mentions it (below the 1st warning block in this section)
N1QL secondary indexes should be automatically picked up by N1QL, depending on the statement. The subtlety here is that n1ql.filter. SDC needs that to limit such queries to the correct set of documents in an heterogeneous bucket.
adhoc is something else: it is about prepared statements. SDC doesn't use that feature, and it will only produce N1QL query with the default value for adhoc (which is still true as far as I know).
If you've logged the query produced by this annotation and run an EXPLAIN on it to effectively see that the index is not picked up, maybe try inverting the two expressions in the WHERE clause?
While building a query using Hibernate, I noticed something rather odd. If I use sequential named parameters for the ORDER BY clause, Hibernate throws a QuerySyntaxException (the colon prefix being an unexpected token):
createQuery("FROM MyEntity ORDER BY :orderProperty :orderDirection");
However, when this is done with a plain SQL query the query is created without a problem:
createSQLQuery("SELECT * FROM my_entity_table ORDER BY :orderProperty :orderDirection");
I know Hibernate is doing more String evaluation for the HQL query, which is probably why the SQL query is created without an error. I am just wondering why Hibernate would care that there are two sequential named parameters.
This isn't a huge issue since it is simple to work around (can just append the asc or desc String value to the HQL instead of using a named paramater for it), but it struck my curiosity why Hibernate is preventing it (perhaps simply because 99% of the time sequential named parameters like this result in invalid SQL/HQL).
I've been testing this in my local, and I can't get your desired outcome to work with HQL.
Here is quote from the post I linked:
You can't bind a column name as a parameter. Only a column value. This name has to be known when the execution plan is computed, before binding parameter values and executing the query. If you really want to have such a dynamic query, use the Criteria API, or some other way of dynamically creating a query.
Criteria API looks to be the more useful tool for your purposes.
Here is an example:
Criteria criteria = session.createCriteria(MyEntity.class);
if (orderDirection.equals("desc")) {
criteria.addOrder(Order.desc(orderProperty));
}
else {
criteria.addOrder(Order.asc(orderProperty));
}
According to the answer accepted in this question, you can only define parameters in WHERE and HAVING clauses.
The same answer also gives you some ways to have a workaround for your problem, however I will add one more way to do this:
Use the CASE - WHEN clause in your ORDER BY, this would work by the following way:
SELECT u FROM User u
ORDER BY
CASE WHEN '**someinputhere**' = :orderProperty
AND '**someotherinput**' = :orderDirection
THEN yourColumn asc
ELSE yourColumn desc END
Please, note that in this approach would required you to write all the possible inputs for ordering. Not really beautiful but really useful, especially because you would not need to write multiple queries with different orderings, plus with this approach you can use NamedQueries, which would be possible by writing the query dinamically using string concats.
Hope this can solve your problem, good luck!
I'm using openJPA as implementation, and i'm facing the following issue.
In one of our services, we use a namedQuery to select value in a range, so something like that:
query = "select xxx from xxx where xxx in (:param)"
This service / query is called by another process which is responsible of building/providing this parameter value. Sometime - for bad reasons - this process give us a list with a length greater than the maximum one authorized by DB2.
So i'm wondering if it's possible for us to implement a mecanism to automatically split this parameter into several list of vlaues, execute for each internal list this namedQuery and then aggregate results before returning.
The main question now is: can i re-use several times my built TypedQuery to re-execute same request but with a different parameter value ?
Code example (For the example, not a real nor existing code):
TypedQuery<MyClass> query = em.createNamedQuery("myQueryName", MyClass.class);
foreach (...) {
query.setParameter(...);
res = query.getResultList();
// Query re-executed and results are different ?
}
Is it a correct way to fix this kind of issue, if not is there any better (in term of performances) way to do this ?
No, you can't do that. You must recreate the TypedQuery for each execution.
I'm using JOOQ for half a year now and must say that it's quite superb :)
The problem I encountered is: I'm trying to generate plain SQL query containing order by clause with case ... when statements to achieve custom sorting order as described in this part of JOOQ tutorial.
However, when I'm using Field.sortAsc(Collection< T> sortList) method and passing Collection of Strings I get following order by clause in query:
order by
case `type`
when ? then ?
when ? then ?
when ? then ?
when ? then ?
when ? then ? end asc
Because I'm not using JOOQ for executing queries, I need to specify these values myself which is quite inconvenient.
Partial solution to this problem was to pass Collection of Param<String> values obtained from original values using DSL.inline(T) method. However, in that case placeholders still exist and order by clause in query looks like:
order by
case `type`
when 'type_a' then ?
when 'type_b' then ?
when 'type_c' then ?
when 'type_d' then ?
when null then ? end asc
Is this expected behaviour or this can be considered as bug and should be reported?
For now, I worked this problem around by using Field.sort(Map sortMap) method, passing Map<Param<String>, Param<Integer>> to it with incremented integer values. There is minor problem with this method as well, because it returns SortField instance which doesn't have methods like asc(), desc() or sort(SortOrder) so in order to sort with different orders this map should be regenerated.
Thanks in advance for any help!
In order to extract all bind values from a query, use Query.getBindValues(). The sort indirection values should be in there. See also the relevant section of the manual.
Note, it might be generally useful to generate "inline" sort indirection values instead of bind values to prevent this in the future. Execution-plan-wise, I don't think there will be any difference. I have registered Issue #3147 for this.