本文摘要:
文章介绍了房屋租赁系统中几个关键接口的技术实现细节,主要包括:1)分页查询房间列表接口使用MyBatis的复杂映射,通过<resultMap>实现嵌套查询,避免分页问题;2)房间详情接口采用手写SQL和多表查询;3)浏览历史功能通过异步调用优化性能;4)看房预约模块处理用户ID绑定和嵌套查询。重点分析了MyBatis映射的注意事项,如<collection>标签使用ofType、column参数传递,以及分页时需采用嵌套查询而非嵌套结果映射等技术要点,并提出了SQL优化和异步处理的改进方案。
一,找房功能的分页查询房间列表接口的复杂sql条件查询
返回前端VO:
@Schema(description = "房间图片列表")
private List<GraphVo> graphVoList;
@Schema(description = "房间标签列表")
private List<LabelInfo> labelInfoList;
@Schema(description = "房间所属公寓信息")
private ApartmentInfo apartmentInfo;
因此必循使用<resultMap>标签以及<association>映射实体类ApartmentInfo ,<collection>映射集合List
<resultMap id="RoomItemVoMap" type="com.atguigu.lease.web.app.vo.room.RoomItemVo" autoMapping="true">
<id column="id" property="id"/>
<association property="apartmentInfo" javaType="com.atguigu.lease.model.entity.ApartmentInfo" autoMapping="true">
<id column="apartment_id" property="id"/>
</association>
<collection property="graphVoList" ofType="com.atguigu.lease.web.app.vo.graph.GraphVo"
select="selectGraphVoListByRoomId" column="id"/>
<collection property="labelInfoList" ofType="com.atguigu.lease.model.entity.LabelInfo"
select="selectLabelInfoListByRoomId" column="id"/>
</resultMap>
Mybatis-Plus分页插件注意事项
使用Mybatis-Plus的分页插件进行分页查询时,如果结果需要使用`<collection>`进行映射,只能使用“嵌套查询(Nested Select for Collection)”,而不能使用“嵌套结果映射(Nested Results for Collection)”。
(1)嵌套结果映射:通过join多表连接,只需要一条查询语句,执行流程是两张表先进行join得到一张表,然后对这张表进行结果映射,如果分页limit会添加到join后,导致只能获取到join后表的前size行,并非我们需要的结果

(2)嵌套查询: 先单独查询主表,然后根据分页参数在主表中查询结果,然后再对主表中查询出的结果与其他表匹配,这样就避免了分页结果出错

xml文件编写易错点:
1,<collection property="graphVoList" ofType="com.atguigu.lease.web.app.vo.graph.GraphVo" select="selectGraphVoListByRoomId" column="id"/>集合标签的类型用ofType,声明是返回多条数据,colum表示传递给对应查询的参数
2,忘记添加autoMapping="true"开启自动映射,如果有自定义属性名字段需要使用标签单独指定
代码可优化部分:
1,房间价格区间筛选xml条件编写:由and (ri.rent >= #{queryVo.minRent} and ri.rent <= #{queryVo.maxRent})优化到and ri.rent between #{queryVo.minRent} and #{queryVo.maxRent}
二,根据id获取房间的详细信息接口
因为基本都涉及到两张表及以上,所以都采用手写SQL的方式来实现
易错点一:要求返回的类RoomDetailVo里的有关公寓信息的类是ApartmentItemVo而不是ApartmentInfo,所以要在apartmentInfoService实现类里实现返回ApartmentItemVo的方法
易错点二:对于sql语句的编写,特别是嵌套查询需要保证每个查询都有逻辑删除is_deleted=0的条件判断,且join连接为左连接
设计思想:尽量使得手写SQL更加通用,方便后续直接使用该方法,例如:
// 根据apartmentId和ItemType查询GraphVo列表
List<GraphVo> graphVoList = graphInfoMapper.getGraphVoList(apartmentId, ItemType.APARTMENT);
三,根据公寓id分页查询房间列表接口
该接口与上述的分页查询房间列表的接口类似,只是没有查询过滤条件,较前者的复杂SQL去掉了<where>标签,增加了判断ri.apartment_id=#{id},直接使用到了前者的resultMap="RoomItemVoMap"来调用两个SQL查询graphList和labelList
四,浏览历史和添加浏览历史接口
<resultMap id="HistoryItemVoMap" type="com.atguigu.lease.web.app.vo.history.HistoryItemVo" autoMapping="true">
<id column="id" property="id"/>
<result column="room_id" property="roomId"/>
<collection property="roomGraphVoList" ofType="com.atguigu.lease.web.app.vo.graph.GraphVo"
select="selectRoomGraphVoListByRoomId" column="room_id"/>
</resultMap>
(1)必须添加<result column="room_id" property="roomId"/>重新对room_id字段进行映射,否则因为<collection>标签中的colum传递从而导致忽略掉room_id的正常映射
(2)最初该功能实现在“根据id获取房间的详细信息”接口的实现类中,如图:

第一,这样设计影响了原有的功能(必须执行完保存历史记录后才能返回roomDetailVo)从而导致功能紊乱,不美观且不好维护;第二,这样可能会拖慢原来功能的执行速度
改进方式:采用异步调用的方式,开启一个子线程执行saveHistory方法,主线程则继续执行,从而实现异步的方式(在需要异步的实现方法上添加@Async,启动类添加@EnableAsync启动异步支持)
其他方法:aop切面编程或者使用真正的异步通信工具Rabbitmq或者kafka
五,看房预约信息模块所有接口的开发
1,保存或更新看房预约接口
前端过来的ViewAppointment的userId值为null,所以要在本地线程中取出userId放入ViewAppointment然后再保存到数据库:
viewAppointment.setUserId(LoginUserHolder.getLoginUser().getUserId());
2,查询个人预约看房列表
因为内含图片集合List<GraphVo>,所以采用手写sql,使用嵌套查询方式完成,注意<collection>标签内的column值是主查询中的公寓id别名apartment_id,方法中传入本地线程的userId:
viewAppointmentMapper.selectAppointmentItemVo(LoginUserHolder.getLoginUser().getUserId());
<result column="apartment_name" property="apartmentName"/>
<collection property="graphVoList" ofType="com.atguigu.lease.web.app.vo.graph.GraphVo" select="selectGraphVo" column="apartment_id"/>
3,根据ID查询预约详情信息
先通过id得到预约信息,取出公寓id调用之前的queryApartmentItemVo方法

被折叠的 条评论
为什么被折叠?



