SpringBoot2 Jpa 批量删除功能的实现
- 作者: 怎么什么好么30225486
- 来源: 51数据库
- 2021-09-01
前台处理
首先前台先要获取所有的要删除数据的id,并将id拼接成字符串 例如: 2,3,4,5,然后通过get请求返送到后台。
后台处理
控制器接收
/**
* @function 批量删除
* @param stu_id
* @return
*/
@getmapping("/del_stu")
@responsebody
public msg batch_del_stu(@requestparam("stu_id") string stu_id){
// 接收包含stuid的字符串,并将它分割成字符串数组
string[] stulist = stu_id.split(",");
// 将字符串数组转为list<intger> 类型
list<integer> lstring = new arraylist<integer>();
for(string str : stulist){
lstring.add(new integer(str));
}
// 调用service层的批量删除函数
studentsservice.deletebatch(lstring);
return msg.success().add("数组", lstring);
}
service层
@override
public void deletebatch(list<integer> stulist) {
// 第一种批量删除方法,是通过spring data中继承jparepository接口后,通过关键字拼接的方法进行删除,删除时候是通过id一条一条删除的
// studentsrepository.deletestudentsbystuidin(stulist);
// 第二种批量删除方法, 是通过自定义jpql语句进行删除,使用的是 where stuid in ()的操作
studentsrepository.deletebatch(stulist);
}
repository接口层
public interface studentsrepository extends repository<students, integer>, jparepository<students, integer> {
/**
* @function 自定义jpql
* @param ids
*/
// 注意更新和删除是需要加事务的, 并且要加上 @modify的注解
@modifying
@transactional
@query("delete from students s where s.stuid in (?1)")
void deletebatch(list<integer> ids);
// 这个是通过spring data拼接关键字进行的操作
void deletestudentsbystuidin(list<integer> ids);
}
附加
@modifying注解
1、在@query注解中编写jpql实现delete和update操作时候必须加上@modifying注解,通知spring data这是一个delete或者updata操作
2、 updata和delete操作需要使用事务,此时需要定义service层,在service方法上添加事务操作
3、 注意jpql不支持insert操作
@query 如果在注解中添加 nativequery=true 是支持原生sql查询
补充:springjpa 批量删除引起的stackoverflow
项目里有一处根据id,批量删除一些历史数据的代码(xxxrepository.deleteinbatch(list);),发现传入list过大时,出现栈溢出(stackoverflowerror) 。
解决方法:
list切分成多份,循环批量删除。
下面是简单的了解一下执行流程。
deleteinbatch(iterable<t> entities)
/**
点进源码 看看。
org.springframework.data.jpa.repository.support.simplejparepository#deleteinbatch
*/
@transactional
public void deleteinbatch(iterable<t> entities) {
assert.notnull(entities, "the given iterable of entities not be null!");
if (!entities.iterator().hasnext()) {
return;
}
// 继续跟踪
applyandbind(getquerystring(delete_all_query_string, entityinformation.getentityname()), entities, em)
.executeupdate();
}
/**
org.springframework.data.jpa.repository.query.queryutils#applyandbind
*/
public static <t> query applyandbind(string querystring, iterable<t> entities, entitymanager entitymanager) {
// ... 省略一些code
// 最后会形成 delete from xx表 x(表别名) where x.id =? or x.id=?... 一条sql语句
string alias = detectalias(querystring);
stringbuilder builder = new stringbuilder(querystring);
builder.append(" where");
int i = 0;
while (iterator.hasnext()) {
iterator.next();
builder.append(string.format(" %s = ?%d", alias, ++i));
if (iterator.hasnext()) {
builder.append(" or");
}
}
query query = entitymanager.createquery(builder.tostring());
iterator = entities.iterator();
i = 0;
while (iterator.hasnext()) {
query.setparameter(++i, iterator.next());
}
}
结合日志记录的错误信息,进入到org.hibernate.hql.internal.antlr.hqlsqlbasewalker#logicalexpr 方法

下面贴一下调用栈

org.hibernate.hql.internal.antlr.hqlsqlbasewalker#deletestatement 方法中 whereclause()调用到了logicalexpr 方法。
由下图可知,该方法在①处递归调用自身,会不断的创建栈帧,当超出栈深度或者超出栈的大小后,会爆出 栈溢出。
至于① 处怎么跳出继续执行后面的代码,还没研究,有知道的小伙伴请指教,不正确的地方也请指正。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。
