最近在开发一个可持续集成平台devops,在开发时涉及到了批量操作,关键是数据库还都是多张表关联的,由于以前对这方面接触比较少,所以还是感觉比较困难的。经过几天的学习和实践,最终还是成功实现了。代码还未优化,所以有些粗糙,先写篇博客记录下,后续会进行代码的优化。
为什么要执行批量操作 MyBatis操作数据库时经常会出现批量操作,例如:批量新增,批量修改,批量删除。批量操作要比数据循环执行的效率要高很多,特别是当需要操作的数据量特别大的情况下。循环执行每次都需要跟数据库建立一个连接,当连接数达到连接池上限时,系统会直接瘫痪。所以循环执行操作数据库尽量不要采用。
foreach标签 foreach主要是用在构建in条件中,它主要是在SQL语句中进行循环迭代一个集合。 foreach标签的主要属性有collection、index、item、separator、open、close。 collection最容易出错,该属性在使用前必须先指定。根据传过来的参数,一般可以有三种类型(list、array、map)。 index指定一个名字,用于在迭代的过程中迭代到的位置。 item表示集合中每一个元素进行迭代时的别名。 separator指定在迭代时以什么符号作为分隔符。 open、close表示语句以什么开始,以什么结束。
多表关联 mybatis关于多表关联有相应的处理办法,提供了association和collection两个标签。 association通常是用来对应一对一的关系。 collection通常是用来对应一对多关系或者是多对多的关系。 但是因为项目是前后端分离,公司自己封装了框架,对复杂类型的数据不太友好,加上项目数据量也不算太大,所以没有使用这两个标签,而是直接新建一个实体类包含两张表里面的共同数据,然后在mapper中映射到创建的实体即可。
批量增加 在添加的过程中,其它的字段都相同,但是可以输入多个IP,有多少个IP,就在数据库中添加多少条数据实现批量新增。在项目中,前台传给后台一个对象AppInfoFilter,因为有部分字段需要转换,所以需要将字段转换后封装到集合然后返回前台。核心代码如下:
数据的转换 先将前台传过来的数据进行转换,然后将多个IP进行拆分,将所有字段封装到一个新的对象,最后将这个对象封装成集合传到mapper中进行批量增加。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 @Transactional (isolation = Isolation .READ_UNCOMMITTED , propagation = Propagation .REQUIRED )@Override public ResList <AppInfo > save (AppInfoFilter appInfoFilter ) { List <String > record=appInfoFilter.getHostIp (); List <AppInfo > list=new ArrayList <>(); String projectName=appInfoFilter.getProjectName (); CmProject cmProject=cmProjectMapper.selectByName (projectName); String appType=appInfoFilter.getAppType (); List <CmComponents > list1=cmComponentsMapper.selectByName (appType); String systemName=appInfoFilter.getSystemName (); List <CmSystem > list2=cmSystemMapper.selectByName (systemName); for (String hostIp :record) { AppInfo appInfo = new AppInfo (); appInfo.setUuid (UUID .getUUID ()); appInfo.setHostId (hostIp); appInfo.setHostIp (hostIp); appInfo.setCreateTime (new Date ()); appInfo.setAppId (appInfo.getUuid ()); convert (appInfoFilter); appInfo.setProjectNum (cmProject.getNumber ()); appInfo.setType (list1.get (0 ).getType ()); appInfo.setSystemId (list2.get (0 ).getNumber ()); list.add (appInfo); } hostMapper.batchInsert (list); paramMapper.batchInsert (list); componentsMapper.batchInsert (list); ResList <AppInfo > resList=new ResList <AppInfo >(list); return resList; }
代码未进行优化,所以看起来有点乱。由于是三张表关联,所以分别想三张表中插入数据(找了好久资料也想了很久也没实现一条SQL实现三张表插入)。
将集合中的数据插入到数据库中 执行单表批量增加的SQL语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <insert id="batchInsert" parameterType="java.util.List"> insert into cm_app_components (UUID , APP_TYPE, APP_VERSION, TYPE , HOST_ID, HOST_IP, PORT, DESCRIPTION, CREATE_TIME, UPDATE_TIME, SCRIPT_PATH ) VALUES <foreach collection="list" item="item" index ="index" separator=","> (#{item.uuid,jdbcType=VARCHAR }, #{item.appType,jdbcType=VARCHAR }, #{item.appVersion,jdbcType=VARCHAR }, #{item.type ,jdbcType=VARCHAR }, #{item.hostId,jdbcType=VARCHAR }, #{item.hostIp,jdbcType=VARCHAR }, #{item.port,jdbcType=VARCHAR }, #{item.description,jdbcType=VARCHAR }, #{item.createTime,jdbcType=TIMESTAMP }, #{item.updateTime,jdbcType=TIMESTAMP }, #{item.scriptPath,jdbcType=VARCHAR }) </foreach > </insert >
对于单表来说,批量增加还是比较简单的。但是这样非常消耗连接池资源。长久肯定是不可能的。
批量修改 由于项目中此模块涉及到的字段太多,但是客户要求只有几个字段能够修改,其它字段全部设为不可修改状态,所以批量修改还是比较轻松的。类似于批量增加,还是先将要批量修改的对象封装成集合。
数据的封装 AppUpdateInfo 实体中只有四个字段,,替换掉AppInfo中的旧数据,将AppInfo封装成一个集合,然后利用SQL语句进行多表关联修改即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 //批量修改 @Transactional(isolation = Isolation .READ_UNCOMMITTED, propagation = Propagation.REQUIRED) @Override public ResList<AppInfo> batchUpdate(AppUpdateInfo updateInfo) { List<String> record =updateInfo.getUuid(); List<AppInfo> list=new ArrayList<AppInfo>(); for (String uuid :record ){ AppInfo appInfo=new AppInfo(); appInfo.setUuid(uuid ); appInfo.setAppVersion(updateInfo.getAppVersion()); appInfo.setScriptPath(updateInfo.getScriptPath()); appInfo.setDeployDir(updateInfo.getDeployDir()); appInfo.setAppDir(updateInfo.getAppDir()); appInfo.setUpdateTime(new Date ()); list.add (appInfo); } appMapper.batchUpdate(list); ResList<AppInfo> resList=new ResList<AppInfo>(list); return resList; }
将数据库中的数据进行修改 先将几张表进行关联,然后进行批量更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <update id="batchUpdate" parameterType="java.util.List" > <foreach collection ="list" separator="," item="item" index ="index" > UPDATE cm_app_components c LEFT JOIN cm_app_components_param p ON c.UUID =p.APP_ID SET c.APP_VERSION = #{item.appVersion}, c.SCRIPT_PATH = #{item.scriptPath}, p.DEPLOY_DIR = #{item.deployDir}, p.APP_DIR = #{item.appDir} WHERE c.UUID = #{item.uuid } </foreach> </update >
批量删除 批量删除根据传入的多个uuid进行删除,只需要在SQL中将多张表关联,然后用foreach遍历封装了uuid的list集合进行删除即可。 核心代码如下:
1 2 3 4 5 6 7 8 9 <delete id="batchDelete" parameterType="java.util.List"> DELETE c,s FROM cm_db_components c LEFT JOIN cm_db_system s ON c.UUID=s.P_UUID WHERE c.UUID IN <foreach collection="list" item="item" index ="index" separator="," open ="(" close =")"> #{item} </foreach > </delete >
技术太low,代码写的很差,很多地方都可以优化,而且有些控制都没有加上。但是写这份代码对我的帮助挺大的,对mybatis的使用熟悉了很多。虽然这是我做的第二个项目,但是却走了整个的流程。学到的东西挺多的。