I have a 'Role' table with a 'name' column. I need to get all roles where names are either 'role1' or 'role2'. Role repository method looks like this:
Set<Role> findByNameIsIn(Set<String> roleNames);
My database contains only 'role1'. The request that is generated looks like this:
SELECT ID, NAME FROM ROLE WHERE (NAME IN ((?,?)))
bind => [role1, role2]
Please notice the double brackets around the parameters. Result set is empty. When I try this query manually through the h2 console - no results as well. The following query works:
SELECT ID, NAME FROM ROLE WHERE (NAME IN ('role1', 'role2'))
My set contains two elements exactly. Sets should be supported as a parameter type. See:https://dzone.com/refcardz/core-spring-data
And finally the question: What am I missing?
As Oliver Gierke mentioned - there is a bug opened for this issue in EclipseLink (this is what I'm using as a persistence provider) issue tracker. Since 2011!.. Here is the workaround:
#Query("select r from Role r where r.name in ?1")
Set<Role> findByNameIsIn(Set<String> roleNames);
And here is the valid generated query:
SELECT ID, NAME FROM ROLE WHERE (NAME IN (?,?))
bind => [role1, role2]
Related
I need to lowercase all emails when querying my table, but the documentation specifies only method-name-keyowrd for UPPER():
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)
How the LOWER() could be used?
I have debug it and can see that PredicateBuilder doesn't seem to be considering it.
Are you aware if that is a limitation? Or could this be achieved in different way?
As per the Spring JPA reference guide, findXXXByIgnoreCase(...) by default uses UPPER(...) to perform case insensitive search.
To force it to use LOWER keyword we can use a custom query with #Query annotation and specifying SELECT .... LOWER(email)= LOWER(?1).
In my case I have used custom query as below to force it to use LOWER keyword for email column:
#Query("SELECT p from Person p where LOWER(email) = LOWER(?1)")
List<Person> findByEmailIgnoreCase(#Param("email") String email);
and this resulted in creating following query:
Hibernate: select person0_.id as id1_0_, person0_.email as email2_0_, person0_.first_name as first_na3_0_, person0_.last_name as last_nam4_0_ from person person0_ where lower(person0_.email)=lower(?)
This should help the query to use the function index i.e., LOWER(email).
The Spring Data JPA docs say this about case sensitivy of properties:
// Enabling ignoring case for an individual property
List<Person> findByLastnameIgnoreCase(String lastname);
// Enabling ignoring case for all suitable properties
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
Using IgnoreCase in your method name will automatically generate a query which is case insensitiv for one or all specified properties.
Otherwise you always have the possibility to specify a custom query for your method by annotating it like this:
#Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();
By using the #Query annotation you can use the plain old JPQL to query your database.
I have a User entity and a Role entity. The relationship is defined like this:
#OneToMany
#JoinTable(name="USER_ROLES", inverseJoinColumns=#JoinColumn(name="ROLE_ID"))
private List<Role> roles = null;
Now, when I delete a role, I need to delete the role from all the users that have that role. Normally you'd do something like this by looking up all the users with this role, removing the role from the list, and saving the user. However, when there could be over a million users, I don't want to be looping over this many entities in the app. So, I'm wanting to use a native query to remove rows from the USER_ROLES join table. I tried adding this to my repository:
#Query(value="DELETE FROM user_roles WHERE role_id = ?1", nativeQuery=true)
public void deleteRoleFromUsersWithRole(Long roleId);
However, when I do this, I see the following in the logs:
[EL Fine]: sql: 2013-11-02 14:27:14.418--ClientSession(707349235)--Connection(2096606500)--Thread(Thread[http-bio-8080-exec-4,5,main])--DELETE FROM user_roles WHERE role_id = ?
bind => [1000110139999999953]
[EL Fine]: sql: 2013-11-02 14:27:14.478--ClientSession(707349235)--Thread(Thread[http-bio-8080-exec-4,5,main])--SELECT 1
[EL Warning]: 2013-11-02 14:27:14.482--UnitOfWork(1795045370)--Thread(Thread[http-bio-8080-exec-4,5,main])--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: No results were returned by the query.
Error Code: 0
Call: DELETE FROM user_roles WHERE role_id = ?
bind => [1000110139999999953]
Query: DataReadQuery(sql="DELETE FROM user_roles WHERE role_id = ?")
I don't understand what No results were returned by the query. is saying. The record does get deleted from the database, but this exception is causing everything to blow up.
Can someone please tell me what I'm doing wrong here?
A method annotated with #Query executes a query in order to read from the database. Not to update the database. To do that, as the documentation indicated, you need to add the #Modifying annotation to the method:
All the sections above describe how to declare queries to access a given entity or collection of entities. Of course you can add custom modifying behaviour by using facilities described in Section 1.3, “Custom implementations for Spring Data repositories”. As this approach is feasible for comprehensive custom functionality, you can achieve the execution of modifying queries that actually only need parameter binding by annotating the query method with #Modifying:
Example 2.13. Declaring manipulating queries
#Modifying
#Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);
This will trigger the query annotated to the method as updating query instead of a selecting one.
use this two annotations
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.annotation.Transactional;
#Modifying
#Transactional
I would like to clarify few finding.
It's true when define Filter, it's required to define FilterDefs?
There's scenario that i does't required any parameter, because the filter it's self is sufficient.
eg: filterName="filter1" condition="ID in (select id from table1"), filterName="filter2" condition="ID in (select id from table2)"
It's true when define a Filter, filter name should not contain dot "." character?
When I define a class name as filterName, hibernate can't find FilterDefs
eg: filterName="org.my.company.Class1" condition="ID in (select id from table1")
Is the following condition is correct:
filterName="filter3" condition="ID in (select id from table1 where column1 like '%:param1%')"
question: What I'm tries to do?
Answer: I'm using Spring ACL and I want to query all granted entity for given sid. I had create Spring ACL entity object.
My domain and sid is my ACL session query parameter.
Then I'm using my domain name as a filter name so that a would easily enable the required filter
eg: session.enableFilter(myclass.getCanonicalName());
session.createQuery("select count(distinct aoi.id) from AclObjectIdentity aoi join aoi.entries e where ......"
Thanks
I have 3 tables, Role[roleId, roleName], Token[tokenID, tokenName] & ROLETOKENASSOCIATION[roleId, tokenID]. The 3rd one was created automatically by hibernate. Now if i simply write a Query to get all the objects from Role class means, it gives the all role objects along with the associated tokenID & tokenName.
I just wanted the association as unidirectional. i.e: Roles--->Tokens
So the annotation in the Role class looks like,
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int roleId;
private String roleName;
#ManyToMany
#JoinTable(name="ROLE_TOKEN_ASSOCIATION",
joinColumns={#JoinColumn(name="roleId")},
inverseJoinColumns={#JoinColumn(name="tokenID")})
private List<Token> tkns;
//Getters & Setters
Now i want the tokenNames for the specific roleId.
First i made a query like this SELECT tkns.tokenName FROM Role WHERE Role.roleId=:roleId
But, i ended up with some dereference error.
Then i changed the query to SELECT tkns FROM Role r WHERE r.roleId=:roleId
Now i have got what i wanted. But it comes with roleId too.
How shall i get tokenName itself?
Actually my problem is solved, but i would like to know how to do it.
It ll be helpful to me, if anyone explains the Query Construction.
Any suggestions!!
Have you tried
SELECT t.tokenName FROM Role r JOIN r.tkns t WHERE r.roleId = :roleId
EDIT: This query almost directly maps to the corresponding SQL query where Role r JOIN r.tkns t is a shorthand syntax for the SQL join via the link table Role r JOIN ROLETOKENASSOCIATION rt ON r.roleId = rt.roleId JOIN Token ON rt.tokenId = t.tokenId. Affe's answer is another syntax for the same query.
See also:
Chapter 14. HQL: The Hibernate Query Language
You want a scalar list of just the name field? You should be able to get that like this
select t.name from Roles r, IN(r.tkns) t where r.roleId = :id
I have this class mapped
#Entity
#Table(name = "USERS")
public class User {
private long id;
private String userName;
}
and I make a query:
Query query = session.createQuery("select id, userName, count(userName) from User order by count(userName) desc");
return query.list();
How can I access the values returned by the query?
I mean, how should I treat the query.list()? As a User or what?
To strictly answer your question, queries that specify a property of a class in the select clause (and optionally call aggregate functions) return "scalar" results i.e. a Object[] (or a List<Object[]>). See 10.4.1.3. Scalar results.
But your current query doesn't work. You'll need something like this:
select u.userName, count(u.userName)
from User2633514 u
group by u.userName
order by count(u.userName) desc
I'm not sure how Hibernate handles aggregates and counts, but I'm not sure if your query is going to work at all. You're trying to select a aggregate (i.e. the "count(userName)"), but you don't have a "group by" clause for userName.
If the query does in fact work, and Hibernate can figure out what to do with it, the results you get back will most likely be a raw Object[], because Hibernate will not be able to map your "count(userName)" data into any field on your mapped objects.
Overall, when you get into using aggregates in queries, Hibernate can get a little more tricky, since you're no longer mapping tables/columns directly into classes/fields. It might be a good idea to read up more on how to do aggregates in Hibernate, from their documentation.