为何JpaRepository.delete()先查实体再删除?如何避免该行为?
Let’s break this down clearly, since those extra database calls add unnecessary cost when using pay-per-request services like AWS’s database offerings.
Why the Pre-Deletion Select Happens
When you delete an entity using methods like EntityManager.remove() or Spring Data JPA’s delete(T entity), Hibernate (the JPA implementation behind Spring) first fetches the entity from the database. Here’s the reasoning:
- Persistence Context Tracking: Hibernate needs the entity loaded into its persistence context to confirm its current state (e.g., ensuring it hasn’t already been deleted).
- Lifecycle Callback Support: It triggers entity lifecycle hooks like
@PreRemoveor@PostRemoveif those annotations are defined on your entity. - Cascade Handling: If your entity has relationships marked with
cascade=CascadeType.REMOVEorCascadeType.ALL, Hibernate needs the loaded entity to automatically delete associated child entities.
All these checks require fetching the entity first, which leads to that extra select query you’re seeing.
How to Skip the Pre-Deletion Query
You have reliable ways to execute a direct delete without fetching the entity, cutting down on database costs:
1. Use Spring Data JPA’s deleteById() (With Caveats)
If your repository extends CrudRepository or JpaRepository, the deleteById(ID id) method may skip the select query in simple scenarios (no lifecycle callbacks, no cascading relationships). However, this behavior isn’t guaranteed—Hibernate will still fetch the entity if it needs to handle lifecycle events or cascades.
2. Define a Custom Delete Query with @Modifying
This is the most consistent approach to run a direct delete. Create a custom method in your repository interface with a JPQL or native SQL delete statement, annotated with @Modifying and @Query:
@Repository public interface CommentRepository extends JpaRepository<Comment, Long> { @Modifying @Query("delete from Comment c where c.id = :id") void deleteCommentById(@Param("id") Long id); // Alternative: Use native SQL if you prefer @Modifying @Query(value = "delete from comments where id = :id", nativeQuery = true) void deleteCommentByIdNative(@Param("id") Long id); }
- Key Notes:
- This won’t trigger
@PreRemove/@PostRemovecallbacks. If you need these events, you’ll have to handle them manually before calling the delete method. - Cascaded deletes won’t be handled automatically. If your entity has child relationships that need deletion, add those steps to your query or handle them separately.
- Always invoke this method within a transaction (add
@Transactionalto the service method calling it).
- This won’t trigger
3. Use EntityManager for Direct Delete Queries
If you’re working directly with EntityManager, you can create and execute a delete query directly:
@Autowired private EntityManager entityManager; @Transactional public void deleteCommentById(Long id) { entityManager.createQuery("delete from Comment c where c.id = :id") .setParameter("id", id) .executeUpdate(); }
This works exactly like the custom repository method—no entity fetch, no lifecycle events, no automatic cascades.
Which Option to Pick?
- Use the custom
@Modifyingquery for most cases: it’s clean, declarative, and consistent. - Stick with
deleteById()only if you have no lifecycle callbacks or cascades and want minimal repository code. - Use
EntityManagerdirectly if you need fine-grained control over query execution.
内容的提问来源于stack exchange,提问作者Maurice

