Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPQL query delete not accept a declared JOIN?

I'm trying to understand why the Hibernate not accepts this follow JPQL:

@Modifying
@Query("delete from Order order JOIN order.credit credit WHERE credit.id IN ?1")
void deleteWithListaIds(List<Long> ids);

The error that I receive is:

Caused by: java.lang.IllegalArgumentException: node to traverse cannot be null!
    at org.hibernate.hql.internal.ast.util.NodeTraverser.traverseDepthFirst(NodeTraverser.java:46)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:284)

But accepts this:

@Modifying
@Query("delete from Order order WHERE order.credit.id IN ?1")
void deleteWithListaIds(List<Long> ids);

The entity Order (the entity Credit does not map the Orders):

@Entity
public class Order {

    @Id
    @Setter
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
    @SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE, allocationSize = 1)
    @Column(name = "id", nullable = false)
    private Long id;

    @JoinColumn(name = "credit_id", foreignKey = @ForeignKey(name = "fk_order_credit"))
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Credit credit;


}

In select statements, the two approaches are accepted, but I don't understand why Hibernate have this limitation or if I'm doing something wrong in my DELETE Jpql. I would like to declare the JOIN in the query.

The only way that I know to resolve this problem in more complex queries is create a subselect:

delete from Order order WHERE order.id IN (
    SELECT order.id FROM Order order
    JOIN order.credit credit
    WHERE credit.id in ?1)

Is this the right approach for more complex delete queries?

I'm using the Spring Jpa Repository in the code above and Spring Boot 1.5.10.RELEASE.

like image 742
Dherik Avatar asked Mar 13 '18 12:03

Dherik


People also ask

How do I delete an entity in JPA?

To delete a record from database, EntityManager interface provides remove() method. The remove() method uses primary key to delete the particular record.

Why do we use @query annotation?

The @Query annotation can only be used to annotate repository interface methods. The call of the annotated methods will trigger the execution of the statement found in it, and their usage is pretty straightforward. The @Query annotation supports both native SQL and JPQL.

How does @query annotation work?

The @Query annotation gives you full flexibility over the executed statement, and your method name doesn't need to follow any conventions. The only thing you need to do is to define a method in your repository interface, annotate it with @Query, and provide the statement that you want to execute.


1 Answers

I don't understand why Hibernate have this limitation.

It is specified as such in the JPA Spec in section 4.10:

delete_statement ::= delete_clause [where_clause]
delete_clause ::= DELETE FROM entity_name [[AS] identification_variable]

So joins aren't allowed in delete statements. Why this was decided this way is pure speculation on my side. But the select_clause or delete_clause specify what the query operates on. While it is totally fine for a select statement to operate on a combination of multiple entities a join for a delete doesn't really make much sense. It just forces you to specify which entity to delete.

The only way that I know to resolve this problem in more complex queries is to create a subselect:

Is this the right approach for more complex delete queries?

If you can't express it using simpler means then yes, this is the way to go.

like image 156
Jens Schauder Avatar answered Sep 22 '22 09:09

Jens Schauder