mybatis 批量处理更新(增删改)操作
进行处理操作的时候,又可能进行批量操作的时候,有可能产生错误,原因是因为有些数据库默认允许传递的数据量是有限制的,
mysql默认是1M,如果是这种情况,需要在mysql的配置文件(my.ini)中的mysqld这个组里面添加max_allowed_packet=10M
我们先来看下批量增加操作:
当然我这里只是说明在mapper映射文件的配置,至于dao层就要简单的多:
mapper.xml
<insert id="insertBatch">
insert into dept(deptname) values
<foreach collection="list" item="dept" separator=","> (#{deptname}) </foreach> </insert>
当然dao层的参数应该是一个List<dept>集合。
批量删除操作:
同样的删除操作是根据id来删除的,所以在dao层只需要传入一个List<Integer>的参数就好。
mapper.xml
<delete id="deleteBatch"> delete from dept where <foreach collection="list" item="key" separator="or"> id=#{key} </foreach> </delete>
下面来说说怎么在mybatis中批量修改操作的。这里有三种方法:
第一种:
利用分号分割多个sql语句来实现,需要在jdbc的连接url中添加 allowMultiQueries=true这个选项即可。但是这个操作,mysql和sqlserver是支持的,
oracle数据库好像不支持;
mapper.xml
<update id="updateBatch"> <foreach collection="list" item="dept" separator=";"> update dept set deptname=#{dept.deptname} where id=#{dept.id} </foreach> </update>
第二种,这种批量更新方式不需要设置jdbc的url的allowMultiQueries=true
因为它只有一条update语句。这也是比较推荐大家使用的。
先来个简单点的,数据库只有一个列(字段)的mapper.xml配置
mapper.xml
<update id="updateBatch"> update dept set <trim prefix="deptname=case" suffix="end"> <foreach collection="list" item="dept"> when id=#{dept.id} then #{dept.deptname} </foreach> </trim> <where> <foreach collection="list" item="dept" separator="or"> id=#{dept.id} </foreach> </where> </update>
下面这个就是对数据多个列(多个字段)的处理以及判断是否为空
<update id="updateBatch"> update employee <trim prefix="set" prefixOverride=";"> <trim prefix="user_name=case" suffix="end,"> <foreach collection="list" item="emp"> <if test="emp.username != null and emp.username != '''> when id=#{emp.id} then #{emp.username} </if> </foreach> </trim> <trim prefix="salary=case" suffix="end,"> <foreach collection="list" item="emp"> <if test="emp.salary!= null and emp.salary != '''> when id=#{emp.id} then #{emp.salary} </if> </foreach> </trim> <trim prefix="gender=case" suffix="end,"> <foreach collection="list" item="emp"> <if test="emp.gender!= null and emp.gender!= '''> when id=#{emp.id} then #{emp.gender} </if> </foreach> </trim> </trim> <where> <foreach collection="list" item="emp" separator="or"> id=#{emp.id} </foreach> </where> </update>
上面这两种批量操作的方式性能比较高,但是不是纯粹意义上jdbc的批处理,大家都可能了解jdbc的批处理是利用prepareStatement的
addBatch() ,executeBatch()这两个方法来完成的。
所以下面这个种就是利用mybatis对jdbc的批处理来完成的
先来看看映射文件的写法:
这里说下,因为这个批处理要用到java代码,所以说在xml配置方面会变得简单多。不像上面两种进行语句的拼接
mapper.xml
<update id="update"> update employee <set> <if test="username !=null and username != ''"> user_name = #{username} </if> <if test="salary!=null and salary != ''"> salary= #{salary} </if> <if test="gender!=null and gender != ''"> gender= #{gender} </if> </set> </update>
以上代码的dao层都需要一个用到interface下的方法,这里我就举例第三种的写法
public interface EmployeeDao{ //注意这个接口是不需要实现的 void update(Employee employee); }
然后utils层的写法
public class SqlSessionFactoryUtil { private static final String CONFIG_FILE_PATH = "mybatis-config.xml"; private static SqlSessionFactory factory = null; static { try { InputStream inputStream = Resources.getResourceAsStream(CONFIG_FILE_PATH); factory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("读取文件失败"); } } public static SqlSessionFactory getFactory(){ return factory; } public static SqlSession getSession(){ return factory.openSession(true); } }
然后是service层的写法
public class EmployeeService{ public void update(List<Employee> employees>{ SqlSessionFactory factory = SqlSessionFactoryUtil.getFactory(); SqlSession session = factory.openSession(ExecutorType.BATCH); EmployeeDao employeeDao = session.getMapper(EmployeeDao.class); try{ int size = employees.size(); for(int i = 0; i< size ;i++){ Employee employee = employees.get(i); //这下面这里调用这个方法的时候,在批处理的操作语境下等价于调用 prepareStatement.addBatch() employeeDao.update(employee); //这个if判断是避免积累太多的更新数据造成堆栈溢出的现象 if(i>0 && i%2 ==0 || i ==size-1){ //等价于调用prepareStatement的executeBatch方法 session.commit(); //调用commit的时候,其内部真正完成批量操作的方法其实是 flushStatement();这个方法的返回值可以获取 //每个更新语句影响的行数,这个数据 flushStatements()方法不会自动调用clearCache()方法来清理一级缓存 //但是调用commit这个方法的时候,会自动调用清空内部一级缓存的方法 } } }catch(Exception e){ session.rollback(); }finally{ session.close(); } } }