Mybatis(plus)使用过程中的一些小问题

问题1:多表关联的条件查询、分页、排序

在使用mybatis-plus自带的分页查询时,我们只能查询出实体类的相关信息。但是如果需要同时查出其他表的信息,那么就得查两次。在没有其他限制条件的情况下,查两次也没有什么问题。但是如果加上排序以及分页,那么两次查询就不能满足需求了。

example

我们有三张表 A学生表 B学生在课程下的排名 C课程表

我们要查符合某些条件的学生的信息以及在某门课程下的排名信息,并且根据排名来排序,最后在加上分页查。这显然是一个比较复杂的查询了。(如何在业务逻辑去处理??)

1、根据B表来分页查出10条数据(假设分页大小就是10)

2、用10条数据再去查A表的基本信息

3、根据条件再去筛选查出来的A

到了这一块,可能筛选过后,我们返回的数据就不够10条了。那么该如何处理?

并且如果排序不是B表,而是根据A表的某些字段去排序,那么业务代码又该怎么去写?

所以我们可以使用mybatis+mybatis-plus来使用xml去写sql来实现这个功能,这样就可以轻松实现这个需求

problem

sql应该怎么写?resultMap应该怎么映射?

resultType的使用

因为这里我们不会涉及到typeHandler的类型处理,也不会涉及到List 等复杂类型的处理,所以我们就不需要自定义resultMap,直接只用resultType就好,mybatis会将我们的结果封装到我们给定的resultType类型的bean中

sql的编写


select A.*, B.* from A left join B
	on A.id = B.A_ID
<where>
	<if test = "A.name!= null and A.name != ''">
		A.name = #{A.name} and
	</if>

	......  to Many if 判断  注意!最后一个 if 判断里面不能加 and

	group by A.id
	<chose>
		<when test = "sortField!= null and sortField!= ''">
			order by ${sortField}
			<chose>
				<when test = "sortMethod != null and sortMethod != ''">
					${sortMethod}
				</when>
			</chose>
		</when>
	</chose>
</where>

这样,我们可以实现分页查询的功能。分页的参数在哪里传递呢?


	在我们的mapper接口里面只需要将page对象传递进去。mybatisPlus会帮我们实现分页功能
	
	page(@Param("field") String field, Page page);

稍微提一点!我们在xml中取值的时候用的是#{}, 但是对于前端给我们传的排序的字段以及排序的规则都是字符串,这个时候我们就得使用 ${}去取值。

问题2:对于createTime以及updateTime的自动处理

在这一块,我们既可以使用mysql的触发器来帮我们实现,也可以使用mybatis的自动填充功能去实现

mybatis-plus自动填充功能参考文档mybatis-plus字段自动填充

mysql的触发器:

ALTER TABLE table_name
ADD COLUMN create_time timestamp NULL DEFAULT CURRENT_TIMESTAMP,
ADD COLUMN update_time timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER create_time;

这里简单使用mybatis的自动填充功能来填充时间

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

	//这里会给createTime与updateTime自动填充值,无需我们在代码里再去处理
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
        // 或者
        this.strictUpdateFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
        // 或者
        this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐)
        // 或者
        this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
        // 或者
        this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
    }
}

最后得在字段上标注填充的类型即可 不然框架不会对这些字段去处理的

public class User {

    // 注意!这里需要标记为填充字段
    @TableField(.. fill = FieldFill.INSERT)
    // 时间序列化与反序列化机制
    @JsonSerialize
    @JsonDeserialize
    private LocalDateTime createTime;
    ....
}

问题3:如何根据多个字段的集合去查询多个数据

在工作时,碰到了这样一个问题:

我的入参是list,我需要根据list中的对象Student的两个字段去判断这个对象是不是在数据库中。
如果在的话,我需要更新这个对象,不在的话,我需要插入这个对象。

List list = new ArrayList();

前提条件是不能循环去调用数据库。要是数据量小的话还行,数据量大的话,这个循环调用会出大问题的。

循环调用会消耗大量的数据库连接资源,并且如果sql有关联查询的话,查询会锁表,导致写操作无法进行,从而影响其他业务的速度. 所以能避免就不要循环调!

解决办法:

1、将对象中的多个字段处理成map,并且收集起来

List<Map<String,Object>> list = Lists.newArrayListWithExpectedSize(list.size());
list.forEach(student -> {
	Map<String,Object> paramMap = new HashMap<>(字段的数量);
	paramMap.put("field1", student.getField1());
	......to many field put
	paramMapList.add(paramMap);
)

2、mapper接口

list(@Param("paramMapList") List<Map<String,Object>> paramMapList);

3、xml文件的编写

select * from student where

	<foreach collection = "paramMapList" item = "paramMap" open = "(" close = ")" separator = "or">
		field1 = #{paramMap.field1}, field2 = #{paramMap.field2}   to many field ...
	</foreach>

4、@Transaction注解与批量、非批量的之间的冲突

接着我们第三个问题讨论:
在我们有一大批数据需要处理的时候,我们首先需要考虑的是将其作为一个批量操作。
但是这样在某些情况下会有问题:

(1)、mybatis-plus的批量操作不会返回主键id

有的时候,我们需要批量将数据导入数据库。如果导入的数据它存在一个相互的关联关系:比如将以下数据导入数据库

[
	{"field1":"field1",
	 "field2":"field2",
	 ......to many field
	 "list":[
			{"subField1":"subField1",...to many field}
		]
	 },
	 {"field1":"field1",
	 "field2":"field2",
	 ......to many field
	 "list":[
			{"subField1":"subField1",...to many field}
		]
	 },
]

可以看到,保存的数据是一个集合,集合中的对象可能会存在一个集合字段。
那么在保存对象的list字段的时候,我们需要知道对象的id,这样才会将对象与自己的list字段建立起关联关系。

而外层集合的批量保存不会生成主键id,那么list字段保存的时候就没法得知外层对象的id。

(2)、外层使用循环解决问题

所以,我们将外层对象的保存设置为一个循环,那么在保存过后我们就可以知道他的主键id。从而将其与list字段建立关系。

那么在保存list字段的时候 ,我们就可以得知与他相关联的是哪一个对象了。这时候只需要直接保存就好。

所以list字段我们就可以使用批量保存了。因为我们也不需要去获取其他的信息了。

(3)、@Transaction注解的加入

通常情况下,我们的这种关联操作要么执行成功,要么就得数据回滚,所以我们需要加入@Transaction注解来保证数据的一致性。
但是在我们加入@Transaction注解的时候,会发现保存list字段的时候就会报错!

Cannot change the ExecutorType when there is an existing transaction

如果有事务的情况下:我们外层对象是循环插入,那么在处理list的时候就不能使用批量操作的方法。
而list的插入也使用循环的话,那么这个插入的循环次数将是很恐怖的n*n。
所以需要使用xml文件,自己去写sql来实现批量操作,不能使用mybatis-plus的批量操作

同样,对于外层的循环应该怎么解决?

5、根据map参数去查询

某些情况下,我们需要去批量更新数据,如果存在的数据则更新,不存在的插入。而有时候这一批数据可能没有主键id,我们需要根据某两个或者三个字段去看数据库中是否存在。这时候,需要将这些数据收集起来去做查询,进而分别来更新或者插入即可。

(1)、根据两个字段检查是是否存在

如果这一批数据需要查询的两个字段中有一个肯定不会重复,那么我们可以将其封装成map来查

将需要查的封装成map

我这里的keyWord肯定不会重复,所以我是用它作为map的key  type作为value

Map<String, TypeEnum> keywordTypeMap = keywordReqs.stream()
.collect(Collectors.toMap(KeywordReq::getKeyWord, HmKeywordEntity::getType));

查数据,这时候返回值肯定是一个list

期望sql应该是这个样子的

select * from tableName 
where (key_word = ? and type = ?) or (key_word = ? and type = ?)......

再来写mapper文件

这个collection中的值就是我们mapper接口里面的入参,别忘记标注@Param注解!

index就是map的key
item就是map的value

select * from tableName
        <where>
            <foreach collection="keywordTypeMap" index="key" item="value" separator="or">
                (key_word = #{key} and type = #{value})
            </foreach>
        </where>

否则使用下面的方法去查

(2)、根据两个以上字段检查是否存在

这个请看标题三即可!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值