SpringBoot2 Jpa 批量删除功能的实现
前台处理
首先前台先要获取所有的要删除数据的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 批量删除引起的*
项目里有一处根据id,批量删除一些历史数据的代码(xxxrepository.deleteinbatch(list);),发现传入list过大时,出现栈溢出(*error) 。
解决方法:
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 方法。
由下图可知,该方法在①处递归调用自身,会不断的创建栈帧,当超出栈深度或者超出栈的大小后,会爆出 栈溢出。
至于① 处怎么跳出继续执行后面的代码,还没研究,有知道的小伙伴请指教,不正确的地方也请指正。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。