Spring Data:是否支持“按照...删除”的操作?

135

我正在使用Spring JPA进行数据库访问。我能够找到诸如findByName和countByName之类的示例,对于这些示例,我不必编写任何方法实现。我希望找到根据某些条件删除一组记录的示例。

Spring JPA是否支持像deleteByName一样的删除操作?非常感谢任何指导。

此致敬礼。


使用派生查询进行批量删除时要小心。这并不是你所期望的:DeleteExecution - Andrii Ofim
9个回答

234

已废弃的答案(Spring Data JPA <=1.6.x):

@Modifying注解来帮助你。不过你需要提供自定义的SQL行为。

public interface UserRepository extends JpaRepository<User, Long> {
    @Modifying
    @Query("delete from User u where u.firstName = ?1")
    void deleteUsersByFirstName(String firstName);
}

更新:

在现代版的Spring Data JPA(>=1.7.x)中,可以使用查询派生来进行deleteremovecount操作。

public interface UserRepository extends CrudRepository<User, Long> {

    Long countByFirstName(String firstName);

    Long deleteByFirstName(String firstName);

    List<User> removeByFirstName(String firstName);

}

2
@AndreyAtapin 因为它不再是一个好的答案而被踩了。也许应该删除它?Stackoverflow 的一个缺陷是处理与所讨论的库相关的版本更改/错误修复。 - Ben George
1
@Webgeek,我曾经用 DELETE FROM x WHERE id = ?1 or parent_id = ?1 来解决这个问题。顺便问一下,你确定 parent__id 没有打错吗(你是有意使用双下划线的吗)?不过,你为什么要使用本地查询选项呢? - Andrey Atapin
4
即使我使用1.7.4版本,为了成功删除,查询方法上方仍需要使用@Transactional注解。 - gokhansari
54
在一个应用程序中,通常会有@Service类/方法,这些类/方法将调用Repositories。@Service公共方法应该被标记为@Transactional,因为事务是基于用例的。这意味着一个用例需要完全提交或回滚,而不是每个单独的Repository方法。因此,请不要在Repository方法上使用@Transactional,而是在使用Repository的Service方法上使用它。 - bytor99999
1
将 @Transactional 注释放在仓库内意味着如果在服务中多次调用仓库,则每个调用都将有不同的事务,在一个查询中进行回滚。如果您将其放在服务级别,则任何错误都会导致整个函数回滚。 - Satish Patro
显示剩余11条评论

91

从Spring Data JPA版本1.6.0.RC1开始,支持使用给定的方法名称来导出删除查询。关键字removedelete均被支持。作为返回值,可以选择已移除实体的数量或列表。

Long removeByLastname(String lastname);

List<User> deleteByLastname(String lastname);

23

2种方法:

第一种:自定义查询

@Modifying
@Query("delete from User where firstName = :firstName")
void deleteUsersByFirstName(@Param("firstName") String firstName);

第二个 JPA 方法查询

List<User> deleteByLastname(String lastname);

当您使用第二种方法进行查询时,它将首先执行一个GET调用。

select * from user where last_name = :firstName

接下来它将把它加载到一个列表中 然后逐个调用删除ID

delete from user where id = 18
delete from user where id = 19

首先获取对象列表,然后通过循环一个个删除id。

但是,第一个选项(自定义查询),

只需要一个单独的查询语句 它将删除值存在的任何地方。

由于第二个选项会进行多个数据库查询,请尝试使用第一个选项。

也可以查看此链接 https://www.baeldung.com/spring-data-jpa-deleteby


8
如果您查看Spring Data JPA的源代码,特别是PartTreeJpaQuery类,您将看到它尝试实例化PartTree。在该类中,以下正则表达式 private static final Pattern PREFIX_TEMPLATE = Pattern.compile("^(find|read|get|count|query)(\\p{Lu}.*?)??By") 应该指示了什么是允许的,什么是不允许的。
当然,如果您尝试添加这样的方法,您会发现它实际上不起作用,并且会获得完整的堆栈跟踪。
我应该注意到,我正在使用Spring Data JPA版本1.5.0.RELEASE

3

如果您将直接使用Spring JPA提供的预定义删除方法,则框架将执行以下两个查询。

  • 首先,通过执行带有删除查询where子句的选择查询来收集数据(如id和其他列)。

  • 然后,在获取第一个查询结果集之后,将为所有id逐个执行第二个删除查询。

    注意:这不是应用程序的优化方式,因为对于单个MYSQL删除查询将执行多个查询。

以下是另一种优化了的删除查询代码方式,因为只使用一个自定义方法只会执行一个删除查询。



@NamedNativeQueries({

@NamedNativeQuery(name = "Abc.deleteByCreatedTimeBetween",
            query = "DELETE FROM abc WHERE create_time BETWEEN ?1 AND ?2")
    ,

    @NamedNativeQuery(name = "Abc.getByMaxId",
            query = "SELECT max(id) from abc")
})

@Entity
public class Abc implements Serializable {

}

@Repository
public interface AbcRepository extends CrudRepository {

    int getByMaxId();

    @Transactional
    @Modifying
    void deleteByCreatedTimeBetween(String startDate, String endDate);
}


1

是的,deleteBy方法得到支持。 要使用它,您需要在方法上注释@Transactional。


1

它正常工作

import org.springframework.transaction.annotation.Transactional;

    @Transactional
    Long removeAddressByCity(String city);

0
@Query(value = "delete from addresses u where u.ADDRESS_ID LIKE %:addressId%", nativeQuery = true)
void deleteAddressByAddressId(@Param("addressId") String addressId);

3
欢迎来到 Stack Overflow。在回答已有采纳答案的旧问题(找绿色的 ✓)或其他答案之前,请确保您的答案提供了新的信息或以其他方式对它们有帮助。仅仅发布代码而没有任何解释并不是很有帮助,因为它不能解释为什么或如何回答问题。请[包含您代码的解释](https://meta.stackoverflow.com/q/392712/5698098),以提高您的帖子质量。请还参考 如何撰写一个好的答案? - Ivo Mori

0

这是我的建议。你也可以使用原生查询,如下所示:

@Modifying
@Query(value="delete from rreo r where r.cod_ibge = ?1 and r.exercicio= ?2", nativeQuery = true)
void deleteByParameters(Integer codIbge, Integer exercicio);

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