Spring boot jpa query can't update field - java

I have an update query on hibernate on this table
class PackEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String status;
private String oldStatus;
#ManyToOne
#JoinColumn(name = "order_id")
private OrderEntity order;
...
}
And on OrderEntity I have there another relationship to another table when I have machine names.
On the JPA repository, I have the query. Basically first I search by machine and status and then I want to update the old status to put the current value of the status field and in status to put the new status. This is it:
#Transactional
#Modifying(clearAutomatically = true)
#Query("UPDATE PackEntity p " +
"SET p.oldStatus= p.status, p.status = ?3 " +
"WHERE p.id IN " +
" ( SELECT p2" +
" FROM PackEntity p2" +
" JOIN p2.order " +
" JOIN p2.order.machine" +
" WHERE p2.order.machine.name = ?1 AND p2.status = ?2)")
List<PackEntity > updateAllWithStatusByMachineNameAndStatus(String machineName, String status, String newStatus);
Now I'm having this error .QueryExecutionRequestException: Not supported for DML operations [UPDATE com.pxprox.entities.PackEntity with root cause ...

Why not create a method that does this for you? Initializing the entity and updating everything, changes will be flushed automatically at the end of the transaction. You can have a look at updating entity with spring-data-jpa
It should basically be something like:
#Autowired
private PackEntityRepository packEntityRepository;
public void updatePackEntity(PackEntity newPE) {
PackEntity packEntity = packEntityRepository.findById(newPE.getId());
packEntity.setOldStatus = packEntity.getStatus();
packEntity.setStatus = newPE.getStatus();
packEntityRepository.save(packEntity);
}

The return type of the method is wrong and also the query should be a little adapted. Use the following:
#Transactional
#Modifying(clearAutomatically = true)
#Query("UPDATE PackEntity p " +
"SET p.oldStatus = p.status, p.status = ?3 " +
"WHERE EXISTS " +
" ( SELECT 1" +
" FROM PackEntity p2" +
" JOIN p2.order o " +
" JOIN o.machine m" +
" WHERE m.name = ?1 AND p2.status = ?2 AND p2.id = p.id)")
void updateAllWithStatusByMachineNameAndStatus(String machineName, String status, String newStatus);
or even better
#Transactional
#Modifying(clearAutomatically = true)
#Query("UPDATE PackEntity p " +
"SET p.oldStatus = p.status, p.status = ?3 " +
"WHERE p.status = ?2 AND EXISTS " +
" ( SELECT 1" +
" FROM p.order o " +
" JOIN o.machine m" +
" WHERE m.name = ?1)")
void updateAllWithStatusByMachineNameAndStatus(String machineName, String status, String newStatus);

Related

Bind #param in a Jpa repository native query inside single quotations

I am looking for a way to bind a given param in a native query where the value has to be inside single quotations, like so:
#Transactional(readOnly = true)
#Query(value = " SELECT c.ID " +
" FROM table1 clh " +
" LEFT JOIN table2 nks " +
" on clh.SERIAL = nks.SERIAL_REF " +
" WHERE clh.CREATED_DATE >= :now - interval ':timeThreshold' HOUR " +
" AND nks.SERIAL_REF IS NULL" , nativeQuery = true)
List<Long> getIdsWithoutAnswer (#Param("timeThreshold") Integer timeThreshold, #Param("now") LocalDateTime now);
However, when I try to run this, it results in hibernate not being able to bind the timeThreshold value as it is provided inside the single quotations ''.
Does anyone know how this can be resolved?
The problem you are having with your native Oracle query has to do with trying to bind a value to an interval literal. You can't do that. Instead, use the NUMTODSINTERVAL() function:
#Transactional(readOnly = true)
#Query(value = " SELECT c.ID " +
" FROM table1 clh " +
" LEFT JOIN table2 nks " +
" on clh.SERIAL = nks.SERIAL_REF " +
" WHERE clh.CREATED_DATE >= :now - numtodsinterval(:timeThreshold, 'hour') " +
" AND nks.SERIAL_REF IS NULL" , nativeQuery = true)
List<Long> getIdsWithoutAnswer (#Param("timeThreshold") Integer timeThreshold, #Param("now") LocalDateTime now);

Converting MySQL query into JPA to get count

Here I'm having a SQL query which is working fine to get count from the MySQL database which is as
#Query("SELECT count(is_accepted) as value, post_type, is_accepted from agent_activities where "
+ "YEAR(created_date_time) as year and post_type = 'ticket' " + "GROUP BY is_accepted")
And when I'm trying into Java as JPA query it's not working.
public interface AgentActivitiesRepo extends JpaRepository<AgentActivitiesEntity, Long> {
#Query("select new ProductIssuesModel"
+ "(count(data.isAccepted) as acceptCount) "
+ "from data where YEAR(data.createdDate) as :year "
+ "and data.postType = :postType " + "group by data.isAccepted")
public List<ProductIssuesModel> findAgentActivitiesYearly(Long year, String postType);
}
Here ProductIssuesModel is like:
public class ProductIssuesModel {
private Long acceptCount;
private Long rejectCount;
private Long year;
private Long month;
...}
By running above query, I face an error as:
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: as near line 1, column 137 [select new com.accenture.icoe.fm.model.ProductIssuesModel(count(data.isAccepted) as acceptCount) from data where YEAR(data.createdDate) as :year and data.postType = :postType group by data.isAccepted]
Please let me know if you see any error.
There are two ways to write a JPQL -
Indexed Query Parameters
#Query("select new ProductIssuesModel"
+ "(count(data.isAccepted) as acceptCount) "
+ "from data where YEAR(data.createdDate) = ?1 "
+ "and data.postType = ?2 " + "group by data.isAccepted")
public List<ProductIssuesModel> findAgentActivitiesYearly(Long year, String postType);
The values of the parameters will be bind to the query based on the position/index.
Named Parameters
#Query("select new ProductIssuesModel"
+ "(count(data.isAccepted) as acceptCount) "
+ "from data where YEAR(data.createdDate) = :year "
+ "and data.postType = :postType " + "group by data.isAccepted")
public List<ProductIssuesModel> findAgentActivitiesYearly(#Param("year") Long year, #Param("postType") String postType);
In your case, the second approach is applicable.
Here , YEAR(data.createdDate) as :year is not a condtional expression.
You may be try to do this YEAR(data.createdDate) = :year.
And use #Param to use parameter in JPQL
#Query("select new ProductIssuesModel"
+ "(count(data.isAccepted) as acceptCount) "
+ "from data where YEAR(data.createdDate) = :year "
+ "and data.postType = :postType " + "group by data.isAccepted")
public List<ProductIssuesModel> findAgentActivitiesYearly(#Param("year") Long year, #Param("postType") String postType);

Could not translate MS Sql query to Hibernate #Query

I have the following MS SQL Query that works perfectly.
select u.id, u.username, r2.authority, em.hrt02_first_name, em.hrt02_last_name from users as u
inner join group_members gm
on u.id = gm.user_id
inner join groups g
on gm.group_id = g.id
inner join group_authorities ga
on ga.group_id = g.id
inner join roles r2
on ga.role_id = r2.id
inner join hrt02_employee_name em
on em.id = u.id
where u.username = 'john'
The output is as follows
+----+----------+------------+------------------+-----------------+
| id | username | authority | hrt02_first_name | hrt02_last_name |
+----+----------+------------+------------------+-----------------+
| 1 | john | ROLE_ADMIN | fname | lname |
+----+----------+------------+------------------+-----------------+
| 1 | john | ROLE_USER | fname | lname |
+----+----------+------------+------------------+-----------------+
But When I tried to convert it to Hibernate Query or `#Query(..., nativeQuery=true) it throws exception. (Failed to Lazy Initialize and Path expected for join).
This is the My Schema Design
#Entity
public class Users {
// id, username omitted
#ManyToMany
#JoinTable(name="group_members", joinColumns=#JoinColumn(name="user_id", referencedColumnName="id"), inverseJoinColumns=#JoinColumn(name="group_id", referencedColumnName="id"))
private List<Groups> groups;
}
#Entity
public class Groups {
// id omitted
#ManyToMany
#JoinTable(name="group_authorities", joinColumns=#JoinColumn(name="group_id", referencedColumnName="id"), inverseJoinColumns=#JoinColumn(name="role_id", referencedColumnName="id"))
private List<Roles> roles;
}
#Entity
public class Roles {
// id omitted, authority
}
#Entity
public class Hrt02EmployeeName {
// id, firstname, lastname omitted
}
Update 1 - Queries I've tried,
All examples here throws an error, but if you written them in native query and run them, they works as expected. So, It's probably me not knowing how to convert it from nativeQuery to Hibernate Query.
public interface UsersRepository extends JpaRepository<Users, Long> {
#Query(value =
"select * from users as u" +
" inner join group_members gm" +
" on u.id = gm.user_id" +
" inner join groups g" +
" on gm.group_id = g.id" +
" inner join group_authorities ga" +
" on ga.group_id = g.id" +
" inner join roles r2" +
" on ga.role_id = r2.id" +
" inner join hrt02_employee_name em" +
" on em.id = u.id" +
" where u.username = :qryusername", nativeQuery = true)
public Users findRoleByUsername(#Param("qryusername") String username);
#Query("select distinct u.username, r2.authority from Users as u " +
" inner join group_members gm " +
" on u.id = gm.user_id " +
" inner join Groups g " +
" on gm.group_id = g.id " +
" inner join group_authorities ga " +
" on ga.group_id = g.id " +
" inner join Roles r2 " +
" on ga.role_id = r2.id" +
" inner join hrt02_employee_name em" +
" on em.id = u.id" +
" where u.username = :username")
public Users findRoleByUsername(#Param("username") String username);
#Query(value =
"select u from Users u" +
" inner join GroupMembers gm" +
" on u.id = gm.user_id" +
" inner join Groups g" +
" on gm.group_id = g.id" +
" inner join GroupAuthorities ga" +
" on ga.group_id = g.id" +
" inner join Roles r2" +
" on ga.role_id = r2.id" +
" inner join hrt02_employee_name em" +
" on em.id = u.id" +
" where u.username = :username")
public Users findRoleByUsername(#Param("username") String username);
}
Since you want to cast it to User, you would have to construct it, not u.username, r2.authority. Secondly you need to fetch what you're using to avoid LazyInitializationException:
#Query("select distinct u from Users u " +
" left join fetch u.groups g "
" left join fetch g.roles r " +
" ... "
" where u.username = :username")
public Users findRoleByUsername(#Param("username") String username);
It's the beginning, because your entity Role has no mapping. You would have to write the same way other entities where the dots are.

Order By in #Query select not working

I have this Query in my JPA repository - and it works EXCEPT the " order by " part. Am i doing this wrong ? is it different in hql ?
#Query(value = "select wm.WagerIdentification, wm.BoardNumber, wm.MarkSequenceNumber, wm.MarkNumber," +
" pt.CouponTypeIdentification, pt.WagerBoardQuickPickMarksBoard " +
"from WagerBoard wb " +
"inner join wb.listOfWagerMarks wm " +
"inner join wb.poolgameTransaction pt " +
"where wb.WagerIdentification = wm.WagerIdentification and wb.BoardNumber = wm.BoardNumber and wb.GameIdentification = wm.GameIdentification and wm.meta_IsCurrent = 1 " +
"and wb.TransactionIdentification = pt.TransactionIdentification and pt.meta_IsCurrent = 1 " +
"and wb.meta_IsCurrent = 1 order by wm.WagerIdentification asc, wm.BoardNumber asc, wm.MarkNumber asc")
Instead of ordering result within the #Query, you can add a method parameter of type Sort, like in Spring Data JPA reference

HQL Query with LEFT JOIN and WHERE not giving results

Working with HQL, on this simplified scenario:
String query = "SELECT new CustomUser(" +
"user.userID AS id," +
"user.username AS username)" +
"FROM User AS user" +
" LEFT JOIN user.friends as friend " +
" where user.username like (:query)" +
" OR " +
" friend.username like (:query) ";
This is giving back only those Users that have at least one friend, but I want to get Users by a condition, beyond of having Friends or not.
Dynamic instantiation is used because of domain requirements
I've noticed that it gives all Users, having Friends or not, if there is no condition on the joined table (friend.username like (:query))
These are my tables:
User
#Id...
protected Integer userID;
#Column(name = "username")
private String username;
#OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE)
private List<Friend> friends;
Friend
#Id
private Integer friendID;
#Column(name = "username")
private String username;
#ManyToOne(cascade = CascadeType.DETACH)
#JoinColumn(name="userid")
private User user;
Note: it works as I expect on native SQL
You can try this query.
String query = "SELECT new CustomUser(" +
"user.userID AS id," +
"user.username AS username)" +
"FROM User AS user" +
" LEFT OUTER JOIN user.friends as friend " +
" where user.username like (:query)" +
" OR " +
" friend.username like (:query) ";

Categories

Resources