写点什么

【MyBatis 系列 4】一对一, 一对多, 多对多查询及延迟加载 (N+1 问题) 分析

  • 2021 年 11 月 11 日
  • 本文字数:3204 字

    阅读完需:约 11 分钟


前言


===============================================================


上一篇分析了MyBatis中的配置的使用,而 MyBatis 中动态标签功能也非常强大,本文不会介绍全部标签,主要是针对 resultMap 来介绍复杂查询该如何利用 sql 标签来配置动态 sql。


固定参数的查询


====================================================================


首先我们来看一个带有固定参数的查询语句该如何实现:


UserMapper.java 中新增如下两个方法:


List<LwUser> listUserByUserName(@Param("userName") String userName);


List<LwUser> listUserByTable(@Param("tableName") String tableName);


对应 UserMapper.xml 中的 sql 语句为:


<select id="listUserByUserName" resultType="lwUser">


select user_id,user_name from lw_user where user_name=#{userName}


</select>


<select id="listUserByTable" resultType="lwUser">


select user_id,user_name from ${tableName}


</select>


然后执行查询:


package com.lonelyWolf.mybatis;


import com.alibaba.fastjson.JSONObject;


import com.lonelyWolf.mybatis.mapper.UserMapper;


import com.lonelyWolf.mybatis.model.LwUser;


import org.apache.ibatis.io.Resources;


import org.apache.ibatis.session.SqlSession;


import org.apache.ibatis.session.SqlSessionFactory;


import org.apache.ibatis.session.SqlSessionFactoryBuilder;


import java.io.IOException;


import java.io.InputStream;


import java.util.List;


public class MyBatisQueryByParam {


public static void main(String[] args) throws IOException {


String resource = "mybatis-config.xml";


//读取 mybatis-config 配置文件


InputStream inputStream = Resources.getResourceAsStream(resource);


//创建 SqlSessionFactory 对象


SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);


//创建 SqlSession 对象


SqlSession session = sqlSessionFactory.openSession();


/**


  • 相比较于 session.selectList("com.xxx.UserMapper.listAllUser")来实现查询,

  • 下面这种通过先获取 mapper 再挑用 mapper 中方法的方式会更灵活


*/


UserMapper userMapper = session.getMapper(UserMapper.class);


List<LwUser> userList = userMapper.listUserByUserName("孤狼 1 号");


System.out.println(null == userList ? "": JSONObject.toJSONString(userList));


List<LwUser> userList2 = userMapper.listUserByTable("lw_user");


System.out.println(null == userList2 ? "": JSONObject.toJSONString(userList2));


}


}


查询结果输出如下:



#和 $区别




从上面的输出 sql 语句截图可以看到,如果使用 #的话,那么 sql 语句会先在 sql 语句中使用占位符,也就是预编译,对应 JBDC 中的 PreparedStatem


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


ent。而使用 $,则会直接把参数拼到 sql 语句上,相当于 JDBC 中的 Statement。


一般情况下不建议使用 $,因为这种直接拼接的方式容易被 sql 注入攻击。


比如,上面的 sql 语句:


select user_id,user_name from ${tableName}


假如 tableName 传入的是:lw_user;delete from lw_user;那么这时候执行的 sql 语句就会变成:


select user_id,user_name from lw_user;delete from lw_user;


这时候整张表的数据都会被删除,而如果使用的是 #{tableName},最终执行的是如下 sql:


select user_id,user_name from 'lw_user;delete from lw_user;'


产生的后果只是查询了一张不存在的表而已。


动态参数的查询


====================================================================


上面的例子中参数是固定的,那么假如我们参数不固定呢?比如有 2 个参数,但是我可能一个都不用,也可能只用 1 个,或者 2 个都用。这种又该如何实现呢?


如下图所示,可以通过 where 和 if 标签结合使用,两个条件都写了 and,这是因为 Mybatis 会帮我们处理掉多余的 and 关键字。


<select id="list" parameterType="com.lonelyWolf.mybatis.model.LwUser" resultType="lwUser">


select user_id,user_name from lw_user


<where>


<if test="userId !=null and userId !=''">


and user_id=#{userId}


</if>


<if test="userName !=null and userName !=''">


and user_name=#{userName}


</if>


</where>


</select>


或者说我们对同一个参数需要进行不同取值拼接不同的 sql,那么可以通过 choose 标签根据不同的参数拼接不同的 sql


select user_id,user_name from lw_user


<where>


<choose>


<when test="userId ='1'">


and user_id=#{userId}


</when>


<when test="userId='2'">


and user_id=#{userId}


</when>


<otherwise>


and user_id=#{userId}


</otherwise>


</choose>


</where>


</select>


当然,Mybatis 还提供了其他许多标签,用来处理更加复杂的组合,在这里就不举例说明了。


一对一查询


==================================================================


假如我们现在有两种表,是一对一关系,我们想同时查询出来,当然最简单的办法是再写一个类,把两张表的结果属性都放到一个类里面,但是这种方式无疑会造成了很多重复代码,而且体现不出层级关系,假如我们有一张表 lw_user 表,存储用户信息,另一张表 lw_user_job 存储了用户的工作经历,那么很明显,job 对应类应该包含在 user 类内,这种应该怎么实现呢?


请看!


1、新建一个实体类 UserJob 来映射 lw_user_job 表属性:


package com.lonelyWolf.mybatis.model;


public class LwUserJob {


private String id;


private String userId; //用户 id


private String companyName; //公司名


private String position; //职位


public String getId() {


return id;


}


public void setId(String id) {


this.id = id;


}


public String getUserId() {


return userId;


}


public void setUserId(String userId) {


this.userId = userId;


}


public String getCompanyName() {


return companyName;


}


public void setCompanyName(String companyName) {


this.companyName = companyName;


}


public String getPosition() {


return position;


}


public void setPosition(String position) {


this.position = position;


}


}


2、在原先的 LwUser 类增加一个引用属性来引用 LwUserJob:


package com.lonelyWolf.mybatis.model;


public class LwUser {


private String userId; //用户 id


private String userName; //用户名称


private LwUserJob usreJobInfo;//用户工作信息


public String getUserId() {


return userId;


}


public void setUserId(String userId) {


this.userId = userId;


}


public String getUserName() {


return userName;


}


public void setUserName(String userName) {


this.userName = userName;


}


public LwUserJob getUsreJobInfo() {


return usreJobInfo;


}


public void setUsreJobInfo(LwUserJob usreJobInfo) {


this.usreJobInfo = usreJobInfo;


}


}


3、UserMapper.java 中新增一个方法:


List<LwUser> listUserAndJob();


这时候 UserMapper.xml 需要自定义一个 ResultMap:


<resultMap id="JobResultMap" type="lwUser">


<result column="user_id" property="userId" jdbcType="VARCHAR" />


<result column="user_name" property="userName" jdbcType="VARCHAR" />


<association property="userJobInfo" javaType="com.lonelyWolf.mybatis.model.LwUserJob">


<result column="id" property="id" jdbcType="VARCHAR" />


<result column="company_Name" property="companyName" jdbcType="VARCHAR" />


<result column="position" property="position" jdbcType="VARCHAR" />


</association>


</resultMap>


<select id="listUserAndJob" resultMap="JobResultMap">


select * from lw_user u inner join lw_user_job j on u.user_id=j.user_id


</select>

评论

发布
暂无评论
【MyBatis系列4】一对一,一对多,多对多查询及延迟加载(N+1问题)分析