Fork me on GitHub

mybatis多表关联批量操作

  最近在开发一个可持续集成平台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<>();

//通过主体名称查询主体编号,然后将主体编号存入cm_host_system
String projectName=appInfoFilter.getProjectName();
CmProject cmProject=cmProjectMapper.selectByName(projectName);

//通过组件名称去应用组件表中查询组件类型,然后将组件类型存入cm_app_components
String appType=appInfoFilter.getAppType();
List<CmComponents> list1=cmComponentsMapper.selectByName(appType);

//通过systemName去cm_system表中查询system_id,然后存入cm_host_system
String systemName=appInfoFilter.getSystemName();
List<CmSystem> list2=cmSystemMapper.selectByName(systemName);

//将AppInfo封装成一个list集合
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);
}
//先向关联表cm_host_system中添加数据
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的使用熟悉了很多。虽然这是我做的第二个项目,但是却走了整个的流程。学到的东西挺多的。

本文标题:mybatis多表关联批量操作

文章作者:Jeremy

发布时间:2018年02月11日 - 13:02

最后更新:2018年04月19日 - 17:04

原始链接:http://yoursite.com/2018/02/11/mybatis多表关联批量操作/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------
你的支持是我最大的动力