Spring Data JPA delete native query throwing exception - java

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

Related

How to make insert custom query in spring data jpa by using #Query annotation

I am using mssql and spring data JPA, I want to insert new records to a table by using custom #Query annotation.
public interface CustomerRepository extends JpaRepository<Customers, String>{
#Modifying
#Query("insert into Customers values (?1 , ?2)")
public void saveCutomer(int custId, Customer cust);
}
Its giving error,
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: expecting OPEN, found 'values' near line 1, column 23 [insert into Customers values (?1 , ?2)]
I tried below also, same error.
#Modifying
#Query("insert into Customers select ?1 , ?2")
public void saveCutomer(int custId, Customer cust);
You don't do that using JPQL. You have batch DELETE and UPDATE but that's it.
Your options are:
1) Mark the query as native if you really make explicit insert (not recommended unless you intend to use some database-specific syntax not supported by JPQL.
2) Use standard save(Entity) method on your repository which is of course preferable.

Can #Query annotation in Spring Data JPA take in a list of enums?

I've been trying to figure out how to take an input of a list of enums for my sql query. This is what it looks like:
#Query(value = "Select * from employees where city in :cities", nativeQuery = true)
List<Employee> findByCities(#Param("cities") List<City> cities);
I understand that for simple queries we can rely on the JPA Criteria API but I want to know if I can actually do it this way instead. Because if so, i can create more complicated queries (such as joins with another table) if I could have this flexibility of specifying the list.
Yes spring-data-jpa's #Query can take a list of enums.
This is my repository method
#Query("Select u from User u where u.userType in :types")
List<User> findByType(#Param("types") List<UserType> types);
This is my repository call
userRepository.findByType(Arrays.asList(AppConstant.UserType.PRINCIPLE)))
And this is the query logs
SELECT user0_.id AS id1_12_,
user0_.date_created AS date_created2_12_,
...
...
FROM users user0_
WHERE user0_.user_type IN ( ? )
Hope this helps.
PS: I tested this in my local machine with positive results.
Update 1
The same doesn't work with nativeQuery=true. I tested it on my local system and it doesn't seem to be working with native queries. However with JPQL it works fine as mentioned in the above answer.
May be this answer will help.

Bulk update with HibernateTemplate and IN clause

I would like to perform a database bulk update using Spring's HibernateTemplate (Hibernate 5.1).
HibernateTemplate offers the following interface: public int bulkUpdate(String,Object...).
My query is UPDATE entity item SET item.attribute.id = ? WHERE item.id in (?.....?).
I had a lot of troubles and want to ask what is the proper way to use HibernateTemplate
The above query results in the deprecation warning [DEPRECATION] Encountered positional parameter near line 1, column 172 in HQ
Replacing the above query with JPA-style parameters (UPDATE entity item SET item.attribute.id = ?1 WHERE item.id in (?2,?3.....?N)) results in NullPointerException thrown by Hibernate when building parameter metadata
As seen on one of the most authoritative Hibernate sources, named parameters result in a misleading exception
Question is: how do I properly formulate a bulk update query using Spring's HibernateTemplate? As correctly reported by Mykong, HibernateTemplate automagically sets query parameters 0-based, but eventually the author got the program working with non-positional parameters without mentioning (or having at all) any warning.
I think that the recommended way to do that now a days is with Spring Data JPA. There is a getting started tutorial here.
So if you have an entity, you can add an interface that extends any of the reposiotry interfaces supported in SpringDataJpa and add a modifying query.
public interface CustomerRepository extends CrudRepository<Customer, Long> {
#Transactional
#Modifying
#Query("update Customer c set c.firstName = ?1 where c.id = ?2")
int updateNameById(String nameToUpdate, long id);
#Transactional
#Modifying
#Query("update Customer c set c.firstName = ?1 where c.id in (?2)")
int updateNameByIds(String nameToUpdate, List<Long> ids);
}
Then Spring will implement that method and you can use the it as:
customerRepo.updateNameByIds("newName", Arrays.asList(cust.getId()));
This will generate the following sql:
update customer set first_name=? where id in (?)
Here is the project I used to test with

OneToMany association with JoinTable returning null

In short: A user has many "not clients" (these are the clients the user cannot serve). User is associated to multiple clients through a join table.
User class: https://gist.github.com/dd99690fcaaba2c834d6
Client class: https://gist.github.com/10de71bcd1914ded5fb9
DAO: https://gist.github.com/dd4a369d60a05460d0c0
the "notClients" attribute in User is always null, can anyone help me understand why?
In short, because you're not including it in your select query. Not sure why you're writing out SQL queries and using the bean transformer? That's a very strange way to use hibernate. In the end it means what you're getting back is not a hibernate managed entity. It's just an object with the specific stuff that you selected mapped onto it.
The "normal"/"correct" way to use hibernate would be something like this:
private User getUser(int id, String userType)
{
User result;
session = HibernateUtil.getWilsonsSessionFactory().getCurrentSession();
session.beginTransaction();
Query query = session.createQuery("select u from User u
where u.id = :id and u.role = :role");
query.setParameter("id", id);
query.setParameter("role", userType);
result = (User)query.uniqueResult();
session.getTransaction().commit();
return result;
}
Then what you get back is an Hibernate entity that will have all its mapped properties populated.
(would not personally use uniqueResult in that way either, but I am willing to admit that is largely a style preference.)

Java-Hibernate-Newbie: How do I acces the values from this list?

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.

Categories

Resources