如何在Spring Data JPA中通过ID数组批量删除数据?

31

现在我有一个名为User的类,我从jsp或html获得一个数组的请求数据。

列出这个Integer[] arr=[5,6,9,10,62,52,21]

然后我使用两种方法来完成批量删除操作。

@Transactional
@Override
public void deleteUser(Integer id) {

    oneRepository.delete(id);
}


@Transactional
@Override
public void deleteSomeUser(Integer[] ids) {

    for (Integer id : ids) {

        deleteUser(id);

    }

}

我想知道是否有更有效的方法来完成这个操作。

你可以看看我的日志: 似乎效果不太好!

[94, 95, 91, 92, 93]
Hibernate: 
    delete 
    from
        sshh_user 
    where
        ID=?


Hibernate: 
    delete 
    from
        sshh_user 
    where
        ID=?



Hibernate: 
    delete 
    from
        sshh_user 
    where
        ID=?



Hibernate: 
    delete 
    from
        sshh_user 
    where
        ID=?



Hibernate: 
    delete 
    from
        sshh_user 
    where
        ID=?



Hibernate: 
    select
        count(practice0_.ID) as col_0_0_ 
    from
        sshh_user practice0_
6个回答

71

只需将以下内容添加到您的用户存储库接口即可:

void deleteByIdIn(List<Integer> ids);

Spring会根据方法名的派生自动生成适当的查询。

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods

编辑:更详细地说明

使用Spring Repository接口,如CrudRepositoryJpaRepository,带来了基本的数据库操作,例如创建、读取、更新、删除、分页、排序等。

要手动添加一些简单的查询,例如搜索用户名称或邮件地址,Spring提供了一个很好的机制,无需注释任何基于字符串的HQL查询或类似内容。

Spring仅分析您的方法名称,搜索关键字。请阅读上面链接中提供的关键字文档。

对于CrudRepository<User>的示例方法:

Iterable<User> findByNameLike(String search)解析为select * from user where name like '<search>'

void deleteByIdIn(List<Integer> ids)解析为delete from user where id in ([ids])

更新:

这只适用于Spring Boot版本<2.0的真正批量删除!

从Spring Boot 2.0开始,它将产生单个删除查询,以遵循JPA实体生命周期事件,如preRemovepostRemove

如果您真的想要批量删除,请使用接受的答案。


你好,请在代码中添加一些解释,例如这段代码的作用等。虽然提供文档链接很有用,但包含这些信息对每个人都有益处。 - Chait
关于这个解决方案,我要提醒一下,由于Eclipselink中存在一个bug,它无法正常工作(会生成格式不正确的查询),因此如果您正在使用EclipseLink,您必须退而求其次,在方法名称中添加查询。 - PaulNUK
1
到目前为止,它与Hibernate完美地配合工作。另一种(更加灵活)的方法是使用QueryDSL(http://www.baeldung.com/rest-api-search-language-spring-data-querydsl)。 - LazyProphet
7
在2.0.5.RELEASE版本中,这并不起作用。deleteByIdIn将根据提供的id数量生成相同数量的删除查询。 - Yuriy Kravets
1
请注意,当 ids 为空时,会抛出异常。 - User1291
1
尝试使用deleteByIdIn()方法并查看Hibernate调试日志,发现该方法被转换为多个删除查询而不是上述单个查询。最终我按照被接受的答案所述编写了自定义查询。 - Yuming Cao

45

假设您有一个像这样的UserRepository

public interface UserRepository extends JpaRepository<User, Integer> {}

然后,您可以将以下修改查询方法添加到您的 UserRepository 中:

/**
 * Delete all user with ids specified in {@code ids} parameter
 * 
 * @param ids List of user ids
 */
@Modifying
@Query("delete from User u where u.id in ?1")
void deleteUsersWithIds(List<Integer> ids);

最后,您可以按以下方式更改批量删除服务:

@Transactional
@Override
public void deleteSomeUser(Integer[] ids) {
    oneRepository.deleteUsersWithIds(Arrays.asList(ids));
}

这将生成一个类似于删除查询的语句:

Hibernate: delete from users where id in (? , ? , ?)

在调用一个public通知方法时,要注意自我调用问题。


更新了关于自我调用的Spring文档链接:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-understanding-aop-proxies - Hein Blöd
1
请注意,这不会自动触发与已删除实体相关联的关系中的级联删除,参见 https://dev59.com/Zn_aa4cB1Zd3GeqP11QN - Hein Blöd
还要注意,有SQL中的in子句限制。例如,在PostgreSQL中https://dev59.com/lnNA5IYBdhLWcg3wVcFx - Woland
1
注意,如果 ids 为空,则会抛出异常。 - User1291
我们能否为使用复合键的人提供一个示例?(我们在实体上使用@IdClass - payne
显示剩余3条评论

1

我已经使用这个函数来删除JPA仓库中的元素列表。

void deleteInBatch(List<Integer> list);

1
您可以使用不同的方法删除项目列表,而无需调用存储库两次。当调用 deleteAll 方法时,CrudRepository 或 JpaRepository 只查找对象的 id。
List<User> userList = userIdList.stream().map(id -> {
            User user = new User();
            user.setId(id);
            return user;
        }).collect(Collectors.toList());

userRepository.deleteAll(userList);

0
如果您的存储库接口扩展了 CrudRepository,则可以简单地使用其 deleteAll(Iterable<? extends T> var1) 方法来删除实体集合:
@Repository
public interface UserRepository extends CrudRepository<User, Long> {
    
    void deleteAll(List<User> usersToDelete);

}

你应该发布实际的代码并标记它,因为答案是正确的。编辑器顶部有一个按钮,可以将你高亮的文本创建为代码块。 - G_V
他正在使用主键进行工作。这个答案假设他正在使用实体,或者将使用主键构建一些实体。 - payne
作者希望避免单独使用Hibernate查询。根据我的测试,deleteAlldeleteAllById似乎无法解决这个问题,它们仍然会进行单独的查询。被接受的答案提供的解决方案按照描述的方式执行。 - syydi

0
感谢rayrayj92提供的解决方案。您不需要编写任何自定义查询,只需获取对象列表并通过该列表删除所有对象即可。
@DeleteMapping("/deleteproduct")
public ResponseEntity<?> deleteProduct(@Valid @RequestBody Map<String,Object> userMap){

    List<String> idList=(List<String>) userMap.get("id_list");

    List<Product> productList=(List<Product>) productRepository.findAllById(idList);
    productRepository.deleteAll(productList);
    return ResponseEntity.status(HttpStatus.OK).body("Deleted item : "+productList);

}

不建议在DELETE方法中使用RequestBody。 - Rishabh Ryber

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接