Mybatis多表操作
一:引言
在学习完前面的mybatis基本语法后,大家都有个认知,这个Mybatis太强大了,比之前使用JDBC写方便多了,但是你们当初在使用原生JDBC写SQL查询的时候有没有遇到过多表查询呢?肯定大部分人都遇到过,我刚学JDBC的时候遇到多表查询我懵了,不知道如何应对,所以我就默默的执行2条SQL语句,分别查询不同的表,然后对这2个查询出来的数据通过java代码控制,使封装到对象中,这个简直要崩溃的感觉,但是学完Mybatis多表查询后,你会爱上Mybatis的
准备工作:我已经准备好了一对一、一对多、多对多的数据表以及基本的Mybatis的搭建,后面的一些操作我会围绕这上面的数据表开展讲解,希望可以理解
二:Mybatis的高级映射
在前面的Mybatis基本使用中映射做过了初步的解释,但是在这我要把Mybatis的映射的参数做个详细的介绍,其实高级映射是通过resultMap标签完成的,而且也是最重要的部分,是为多表查询做个铺垫,所以搞懂Mybatis高级映射是很有必要的!
其实resultMap元素有很多子元素和一个值得讨论的结构。下面是 resultMap 元素的概念视图
- constructor – 类在实例化时,用来注入结果到构造方法中
- idArg – ID 参数;标记结果作为 ID 可以帮助提高整体效能
- arg – 注入到构造方法的一个普通结果
- id – 一个 ID 结果;标记结果作为 ID 可以帮助提高整体效能
- result – 注入到字段或 JavaBean 属性的普通结果
- association – 一个复杂的类型关联;许多结果将包成这种类型嵌入结果映射 – 结果映射自身的关联,或者参考一个
- collection – 复杂类型的集,嵌入结果映射 – 结果映射自身的集,或者参考一个
- discriminator – 使用结果值来决定使用哪个结果映射
- case – 基于某些值的结果映射,嵌入结果映射 – 这种情形结果也映射它本身,因此可以包含很多相同的元素,或者它可以参照一个外部的结果映射。
1:id和result
- <id column="sid" property="id"></id>
<result column="sname" property="name"></result>
- ①:property
- 映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同
- 的 JavaBeans 的属性,那么就会使用。否则 MyBatis 将会寻找给定名称
- 的字段。这两种情形你可以使用通常点式的复杂属性导航。比如,你
- 可以这样映射一些东西:“username”
- ②:column
- 从数据库中得到的列名,或者是列名的重命名标签。这也是通常和会
- 传递给 resultSet.getString(columnName)方法参数中相同的字符串。
- ③:javaType
- 一个 Java 类的全限定名,或一个类型别名(参加上面内建类型别名
- 的列表)。如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。
- 然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType
- 来保证所需的行为。
- ③:jdbcType
- 在这个后会列出所支持的 JDBC 类型列表中的类型。JDBC 类型是仅
- 仅需要对插入,更新和删除操作可能为空的列进行处理。这是 JDBC
- 的需要,而不是 MyBatis 的。如果你直接使用 JDBC 编程,你需要指定
- 这个类型-但仅仅对可能为空的值。
- ④:typeHandler
- 我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默
- 认的类型处理器。这个属性值是类的完全限定名或者是一个类型处理
- 器的实现,或者是类型别名
id和result属性介绍
- BIT FLOAT CHAR TIMESTAMP OTHER UNDEFINED
- TINYINT REAL VARCHAR BINARY BLOB NVARCHAR
- SMALLINT DOUBLE LONGVARCHAR VARBINARY CLOB NCHAR
- INTEGER NUMERIC DATE LONGVARBINARY BOOLEAN NCLOB
- BIGINT DECIMAL TIME NULL CURSOR
支持的JDBC类型
2:构造方法
- <constructor>
<idArg column="id" javaType="int"/>
<arg column=”username” javaType=”String”/>
</constructor>
二:Mybatis多表查询之一对一
什么是一对一呢?我先拿现实的例子来说,抛弃其它特殊情况,在我们上学的年代,学校总会统计每个学生的家庭状况,那我们也是可以认为一个学生有一个家庭信息,一个家庭信息里面有一个学生,两边是一对一关系,虽然我这个例子举得不是太好,但是产品和订单的例子就会很好的体现出来,比如一个产品对应一个订单,一个订单对应一个产品。
在Mybatis里面它不叫一对一查询,叫关联查询,关联元素处理“有一个”类型的关系,而且关联中不同的是你需要告诉 MyBatis 如何加载关联。MyBatis 在这方面会有两种不同的分别是:嵌套查询(通过执行另外一个 SQL 映射语句来返回预期的复杂类型)和嵌套结果(使用嵌套结果映射来处理重复的联合结果的子集)
①:嵌套查询
select属性:
使用嵌套结果映射来处理重复的联合结果的子集
我现在的要求是学生和家庭是一对一关联关系,这就好办了,我可以用嵌套查询来完成,那我们现在来创建2个实体类吧
- //学生实体类 注意要把private int fid;改为private Family family
- //因为是关联关系,我要在查到学生对象的时候通过外键把家庭也查询出来
- //放到学生对象里面
- public class Student {
- private int id; //id
- private String name; //姓名
- private String sex; //性别
- private int age; //年龄
- private double credit; //成绩/学分
- private double money; //零花钱
- private String address; //住址
- private String enrol; //入学时间
- private Family family; //外键 家庭
- private int tid; //外键 老师id
- //构造器/set/get/toString 你们补充一下
- }
- //家庭对象
- public class Family {
- private int id; //家庭主键
- private int member; //成员个数
- private String guardian; //监护人
- private String tel; //监护人号码
- private String dad; //爸爸姓名
- private String mom; //妈妈姓名
- private String address; //家庭住址
- //构造器/set/get/toString 你们补充一下
- }
Student对象和Family对象
- <!--配置家庭映射关系-->
- <resultMap id="familyMapper" type="family">
- <id column="fid" property="id"></id>
- <result column="fmember" property="member"></result>
- <result column="fguardian" property="guardian"></result>
- <result column="ftel" property="tel"></result>
- <result column="fdad" property="dad"></result>
- <result column="fmom" property="mom"></result>
- <result column="faddress" property="address"></result>
- </resultMap>
- <!--配置学生类映射关系-->
- <resultMap id="studentMapper" type="student">
- <id column="sid" property="id"></id>
- <result column="sname" property="name"></result>
- <result column="ssex" property="sex"></result>
- <result column="sage" property="age"></result>
- <result column="scredit" property="credit"></result>
- <result column="smoney" property="money"></result>
- <result column="saddress" property="address"></result>
- <result column="senrol" property="enrol"></result>
- <!--<result column="fid" property="fid"></result>-->
- <result column="tid" property="tid"></result>
- <association column="fid" property="family" select="findByIdInFamily" javaType="family"></association>
- </resultMap>
- <!--
- column:当前student表中连接family的外键字段名称
- property:当前封装family对象的student对象里的名称
- javaType:当前封装的对象类型
- 大家会看到里面有个association标签 但是在后面添加一个select属性就变成嵌套查询了 而且select="findByIdInFamily"
- 所以执行到这一步就找findByIdInFamily查询标签的ID
- -->
- <!--首先我们得建立一个查询学生的SQL语句和标签-->
- <!--查询全部学生-->
- <select id="findAll" resultMap="studentMapper">
- select * from student;
- </select>
- <!--可是现在的问题是数据库字段和对象属性不匹配 所以还要再上面定义映射关系 看上面-->
- <!--查询单个家庭信息-->
- <select id="findByIdInFamily" parameterType="Integer" resultMap="familyMapper">
- select * from family where fid=#{id};
- </select>
嵌套查询
注意:嵌套查询中被封装的对象(如上面的Family)一定要字段名和属性名一样,如果不一样,一定要手动映射,否则查询出来的family为空。虽然这种方式很简单,但是对于大型数据集合和列表将不会表现很好。问题就是我们熟知的“N+1 查询问题”。就是说如果我查询学生的数据有5000条,那么mybatis查询到学生的每条数据后都会去执行一个嵌套查询,获取指定id去查询关联的对象(family),5000条数据就会查询family表5000次+1次查询学生全部数据,但是这种嵌套查询也是有一种好处,能延迟加载这样的查询就是一个好处,因此你可以分散这些语句同时运行的消耗。然而,如果你加载一个列表,之后迅速迭代来访问嵌套的数据,你会调用所有的延迟加载,这样的行为可能是很糟糕的。
②:嵌套结果
嵌套结果查询是我们经常使用到的,这是结果映射的 ID,可以映射关联的嵌套结果到一个合适的对象图中。这是一种替代方法来调用另外一个查询语句。这允许你联合多个表来合成到一个单独的结果集。这样的结果集可能包含重复,数据的重复组需要被分解,合理映射到一个嵌套的对象图。为了使它变得容易,MyBatis 让你“链接”结果映射,来处理嵌套结果。
- <!--配置学生类映射关系-->
- <resultMap id="studentMapper" type="student">
- <id column="sid" property="id"></id>
- <result column="sname" property="name"></result>
- <result column="ssex" property="sex"></result>
- <result column="sage" property="age"></result>
- <result column="scredit" property="credit"></result>
- <result column="smoney" property="money"></result>
- <result column="saddress" property="address"></result>
- <result column="senrol" property="enrol"></result>
- <result column="tid" property="tid"></result>
- <association property="family" javaType="family" column="fid">
- <id column="fid" property="id"></id>
- <result column="fmember" property="member"></result>
- <result column="fguardian" property="guardian"></result>
- <result column="ftel" property="tel"></result>
- <result column="fdad" property="dad"></result>
- <result column="fmom" property="mom"></result>
- <result column="faddress" property="address"></result>
- </association>
- </resultMap>
- <!--property:bean对象名称 javaType:指定关联的类型 column:数据库字段名称 最少指定前2个-->
- <!--查询全部学生-->
- <select id="findAll" resultMap="studentMapper">
- select s.*,f.fmember,f.fguardian,f.ftel,f.fdad,f.fmom,f.faddress
- from student s inner join family f using(fid)
- </select>
- <!--using:表示主键和外键一样可以不用on xx=xx -->
三:Mybatis多表查询之一对多查询
什么是一对多呢?其实可以很容易理解,在学校每个学生都有辅导员,而且每个辅导员就有多个学生,通常辅导员是管理一个班学生的,所以呀,老师对学生就是一对多关系,我上面的表也可以很好的表现出来,teacher(辅导员)student(学生)
在Mybatis对与一对多称为集合查询 ,其实和关联查询差不多,集合查询是把映射嵌套的结果封装到List中,也可以分为集合嵌套查询和集合集合嵌套结果,争夺下面的案例我首先把teacher类和student类给改造一下
- //辅导员对象
- public class Teacher {
- private int id; //id
- private String name; //姓名
- private String sex; //性别
- private int age; //年龄
- private double salary; //工资
- private String address; //住址
- private List<Student> students; //学生对象
- //构造器/set/get/toString 你们补充一下
- }
- //学生对象
- public class Student {
- private int id; //id
- private String name; //姓名
- private String sex; //性别
- private int age; //年龄
- private double credit; //成绩/学分
- private double money; //零花钱
- private String address; //住址
- private String enrol; //入学时间
- //构造器/set/get/toString 你们补充一下
- }
- //辅导员接口
- public interface TeacherDao {
- //查询全部老师信息
- List<Teacher> findAll();
- }
- //辅导员xml配置
- <?xml version="1.0" encoding="UTF-8" ?>
- <!DOCTYPE mapper
- PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="cn.xw.dao.TeacherDao">
- <resultMap id="teacherMapper" type="teacher">
- <id column="tid" property="id"></id>
- <result column="tname" property="name"></result>
- <result column="tsex" property="sex"></result>
- <result column="tsalary" property="salary"></result>
- <result column="taddress" property="address"></result>
- </resultMap>
- <!--查询全部辅导员信息-->
- <select id="findAll" resultMap="teacherMapper">
- select * from teacher;
- </select>
- </mapper>
代码改造
①:集合的嵌套查询
- <!--辅导员关系映射-->
- <resultMap id="teacherMapper" type="teacher">
- <id column="tid" property="id"></id>
- <result column="tname" property="name"></result>
- <result column="tsex" property="sex"></result>
- <result column="tage" property="age"></result>
- <result column="tsalary" property="salary"></result>
- <result column="taddress" property="address"></result>
- <collection property="students" ofType="student" select="findById" column="tid"></collection>
- </resultMap>
- <!--查询全部辅导员信息-->
- <select id="findAll" resultMap="teacherMapper">
- select * from teacher;
- </select>
- <!--学生关系映射-->
- <resultMap id="studentMapper" type="student">
- <id column="sid" property="id"></id>
- <result column="sname" property="name"></result>
- <result column="ssex" property="sex"></result>
- <result column="sage" property="age"></result>
- <result column="scredit" property="credit"></result>
- <result column="smoney" property="money"></result>
- <result column="saddress" property="address"></result>
- <result column="senrol" property="enrol"></result>
- </resultMap>
- <!--查询单个学生-->
- <select id="findById" parameterType="Integer" resultMap="studentMapper">
- select * from student;
- </select>
②:集合的嵌套结果
- <!--辅导员关系映射-->
- <resultMap id="teacherMapper" type="teacher">
- <id column="tid" property="id"></id>
- <result column="tname" property="name"></result>
- <result column="tsex" property="sex"></result>
- <result column="tage" property="age"></result>
- <result column="tsalary" property="salary"></result>
- <result column="taddress" property="address"></result>
- <collection property="students" ofType="student" column="tid">
- <id column="sid" property="id"></id>
- <result column="sname" property="name"></result>
- <result column="ssex" property="sex"></result>
- <result column="sage" property="age"></result>
- <result column="scredit" property="credit"></result>
- <result column="smoney" property="money"></result>
- <result column="saddress" property="address"></result>
- <result column="senrol" property="enrol"></result>
- </collection>
- </resultMap>
- <!--查询全部辅导员信息-->
- <select id="findAll" resultMap="teacherMapper">
- select s.*,t.tid,tname,t.tsex,t.tage,t.tsalary,t.taddress from
- teacher t left join student s using(tid) ;
- </select>
四:总结(必看细节之我的错误总结)
1:自我认知
对于我刚接触Mybatis的多表操作遇到了好多错误bug,俗话说遇到的错误越多越要开心,因为这是对你以后的成长,我也觉得这句话有点道理,通过这2天的操作,我对Mybatis的多表操作不能说全部都懂吧,但是在日常对数据进行简单的增删改查应该问题不大。说实话我使用Mybatis多表操作遇到过好几个小时都没解决的bug,查过好多资料,也发现网上和我出现过一模一样的问题,如 CSDN的一个人发的问题 其实那些后面评论的解决方法都是不可行的,不能真正解决此问题,接下来我就和大家谈谈我遇到的一些问题吧,针对mybatis的多表操作
2:遇到的问题及注意事项
①:在做一对一关联查询的时候,查询出来的关联对象一直为空?
其实这个错误和我在上面发的CSDN的错误差不多,我先把我的代码展示出来,错误的代码
- <!--配置学生类映射关系-->
- <resultMap id="studentMapper" type="student">
- <id column="sid" property="id"></id>
- <result column="sname" property="name"></result>
- <result column="ssex" property="sex"></result>
- <result column="sage" property="age"></result>
- <result column="scredit" property="credit"></result>
- <result column="smoney" property="money"></result>
- <result column="saddress" property="address"></result>
- <result column="senrol" property="enrol"></result>
- <result column="tid" property="tid"></result>
- <association property="family" javaType="family" column="fid" select="findByIdInFamily"></association>
- </resultMap>
- <!--查询全部学生-->
- <select id="findAll" resultMap="studentMapper">
- select * from student;
- </select>
- <!--查询单个家庭信息 通过关联查询查询出来-->
- <select id="findByIdInFamily" parameterType="Integer" resultType="family">
- select * from family where fid=#{id};
- </select>
- <!--
- 为什么family都是为空呢?
- Student{id=1, name='王生安', sex='女', age=22, credit=11.0, money=401.1, address='安徽六安', enrol='2019-01-08', family=null, tid=4}
- Student{id=2, name='李鑫灏', sex='女', age=24, credit=4.0, money=902.8, address='安徽合肥', enrol='2019-03-17', family=null, tid=2}
- Student{id=3, name='薛佛世', sex='女', age=21, credit=52.0, money=532.1, address='安徽蚌埠', enrol='2018-10-16', family=null, tid=2}-->
查询为空的代码
其实遇到这个问题是最烦人的,即不报错,也在网上很难找到,如果有个师傅带着话,相信很快解决,废话不多说了
问题分析:
首先我的数据库字段和对象字段不一样,这个是最关键的,在做一对一关联查询,我忽略了对family字段的映射,这就直接导致出现空,但是上文CSDN发布的问题是他直接在关联查询标签里面写映射关系,他的思路是对的,但是他忘了他那个是嵌套查询,如果是嵌套结果查询,对sql语句重写,语法更改一下是没有问题的
- <!--配置学生类映射关系-->
- <resultMap id="studentMapper" type="student">
- <id column="sid" property="id"></id>
- <result column="sname" property="name"></result>
- <result column="ssex" property="sex"></result>
- <result column="sage" property="age"></result>
- <result column="scredit" property="credit"></result>
- <result column="smoney" property="money"></result>
- <result column="saddress" property="address"></result>
- <result column="senrol" property="enrol"></result>
- <result column="tid" property="tid"></result>
- <association property="family" javaType="family" column="fid" select="findByIdInFamily"></association>
- </resultMap>
- <!--查询全部学生-->
- <select id="findAll" resultMap="studentMapper">
- select * from student;
- </select>
- <!--映射家庭关系-->
- <resultMap id="familyMapper" type="family">
- <id column="fid" property="id"></id>
- <result column="fmember" property="member"></result>
- <result column="fguardian" property="guardian"></result>
- <result column="ftel" property="tel"></result>
- <result column="fdad" property="dad"></result>
- <result column="fmom" property="mom"></result>
- <result column="faddress" property="address"></result>
- </resultMap>
- <!--查询单个家庭信息 通过关联查询查询出来-->
- <select id="findByIdInFamily" parameterType="Integer" resultMap="familyMapper">
- select * from family where fid=#{id};
- </select>
问题解决
注意事项:
①:其实我们在写这些关联查询的时候,实体类是不用把外键字段纳入实体类里面,如果粗心的话,操作不当会导致错误,我们如果一个对象包含另一个对象就直接使用对象作为类型
②:一对多和多对多本质是是一样的,一对多是对一方设置数据为List集合而多对多则是双向的
Mybatis多表操作的更多相关文章
- 【MyBatis】MyBatis 多表操作
MyBatis 多表操作 文章源码 一对一查询 需求:查询所有账户信息,关联查询下单用户信息. 注意:因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询.如果从用户 ...
- mybatis单表操作实现完全java代码封装
之前在项目中用到mybtis操作数据库时都是手动写sql,对于我这种sql水平不是很好地人来说痛苦死了:动态查询的sql我表示到现在还不会写呀! 还好,利用数据库表反向生成的工具可以帮我解决大部分的s ...
- 通用mybatis单表操作接口
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "- ...
- Mybatis(四)多表操作
数据库如下: 一.创建数据库所对应的bean类 public class User { private Integer uId; private String username; private St ...
- Mybatis高级:Mybatis注解开发单表操作,Mybatis注解开发多表操作,构建sql语句,综合案例学生管理系统使用接口注解方式优化
知识点梳理 课堂讲义 一.Mybatis注解开发单表操作 *** 1.1 MyBatis的常用注解 之前我们在Mapper映射文件中编写的sql语句已经各种配置,其实是比较麻烦的 而这几年来注解开发越 ...
- Mybatis的多表操作
1.Mybatis多表查询 1.1 一对一查询 1.1.1 一对一查询的模型MapperScannerConfigurer 用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户 一对一 ...
- mybatis处理单表多表操作以及动态sql和批量操作
一.单表操作 建立了相应的mapper映射接口类后,在src/main/resources目录下的自己建立的mapper文件夹下创建映射类 select操作:用select标签 属性id为映射接口的方 ...
- MyBatis 单表CURD操作(五)
MyBatis的CURD操作 添加CURD接口方法 package mapper; import entity.UserEntity; import org.apache.ibatis.annotat ...
- MyBatis联表查询
MyBatis逆向工程主要用于单表操作,那么需要进行联表操作时,往往需要我们自己去写sql语句. 写sql语句之前,我们先修改一下实体类 Course.java: public class Cours ...
随机推荐
- ereg正则%00截断
0x01 <?php $flag = "xxx"; if (isset ($_GET['password'])) { if (ereg ("^[a-zA-Z0-9] ...
- 9、AutoResponder返回本地数据(mock)
前言 mock可以说是面试必问的话题的,我第一次接触mock的时候也是一脸懵逼.虽然fiddler工具用了很久,里面的打断点,设置自动返回数据功能都用过.mock说的通俗一点就是模拟返回数据,只是面试 ...
- Docker之镜像地址
转载自https://www.cnblogs.com/doraman/p/9570891.html 官方docker hub 官方:https://hub.docker.com/explore/ 常用 ...
- Lvs 调度算法
lvs scheduler:仅根据IP和端口进行调度 静态方法:仅根据算法本身进行调度,不考虑当前服务器实际负载情况:保证起点公平 RR:round robin, 轮调,轮询,轮叫: 调度器通过&qu ...
- mysql小白系列_10 mysql主从复制原理
1.如何解决主从复制延迟的问题? (1)主从复制原理 http://www.cnblogs.com/jenvid/p/8410922.html 1.salve执行slave start,salve服务 ...
- java中的上下问解释以及ServletContext介绍使用
摘抄的:所谓上下文,它是用来存储系统的一些初始化信息,例如在jboss中通过配置文件指定了数据源,那么在jboss启动的时候就把这个文件的相关信息上下文中,于是在我们使用这个数据源的时候,就需要先获得 ...
- Ubuntu下配置Hyperledger Fabric环境
在win10系统的台式机上安装配置Hyperledger Fabric环境 安装Ubuntu 16.04 双系统 镜像下载地址:https://www.ubuntu.com/download/desk ...
- 玩转Nginx location配置
原文链接:https://mp.weixin.qq.com/s/kaEtfmX9bVdKfCVY6gbOsQ 建议点击原文链接查看 nginx是一个跨平台的web服务器, 基于事件驱动的架构并发处理百 ...
- docker 日志查看与清洗
Linux下查看磁盘与目录的容量——df.du df:列出文件系统的整体磁盘使用量: du:评估文件系统的磁盘使用量(常用于评估目录所占容量) df参数: -a:列出所有的文件系统,包括系统特有的/p ...
- Pyqt5_QfileDialog
QfileDialog getOpenFileName getSaveFileName getExistingDirectory getOpenFileName: 就是调用窗口来读取用户选取的文件路径 ...