一、关联关系分类
1.一对一关联:一个人有一张身份证,一张身份证对应一个人
2.一对多关联:一个城市有很多街道
3.多对多关联:一个老师有多个学生,一个学生有多个老师
二、一对一关联
mybatis中处理一对一关联的方法有四种.
①包装类方式(仅了解)
1.数据库表的创建
#身份证表
CREATE TABLE tb_card (
cid INT PRIMARY KEY AUTO_INCREMENT,
CODE VARCHAR(18)
);
#测试数据
INSERT INTO tb_card (CODE) VALUES('432801198009191038');
COMMIT;
#公民表
CREATE TABLE tb_person (
pid INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(50),
age INT,
sex VARCHAR(20),
idcard INT UNIQUE,
FOREIGN KEY (idcard) REFERENCES tb_card (cid)
);
#测试数据
INSERT INTO tb_person (NAME,sex,age,idcard) VALUES('张三',23,'男',1);
COMMIT;
公民表
身份证表
接口:
public interface PersonMapper {
public List<PersonCard> findAll();
}
Person类:
@Data
public class Person {
private int pid;
private String name;
private int age;
private String sex;
private Card card;
}
PersonCard类:
@Data
public class PersonCard extends Person {
private String code;
}
映射文件:
<mapper namespace="com.acoffee.maven.mapper.PersonMapper">
<resultMap id="PersonMap" type="PersonCard">
<id column="pid" property="pid"></id>
<id column="name" property="name"></id>
<id column="age" property="age"></id>
<id column="sex" property="sex"></id>
<id column="code" property="code"></id>
</resultMap>
<select id="findAll" resultMap="PersonMap">
SELECT p.*,c.`CODE`
FROM tb_person AS p INNER JOIN tb_card c ON p.idcard=c.`cid`
</select>
</mapper>
执行结果:
这种方法本质上是在数据库内连接进行查询。
②resultMap方式一(仅了解):
Person对象:
@Data
public class Person {
private int pid;
private String name;
private int age;
private String sex;
private Card card;
}
Card 对象:
@Data
public class Card {
private int cid;
private String code;
}
接口:
public interface PersonMapper {
public List<Person> selectAllPersons();
}
映射文件:
<mapper namespace="com.acoffee.maven.mapper.PersonMapper">
<resultMap id="PersonMap" type="Person">
<id column="pid" property="pid"></id>
<result column="name" property="name"></result>
<result column="age" property="age"></result>
<result column="sex" property="sex"></result>
<result column="cid" property="card.cid"></result>
<result column="code" property="card.code"></result>
</resultMap>
<select id="selectAllPersons" resultMap="PersonMap">
SELECT p.*,c.*
FROM tb_person AS p INNER JOIN tb_card c ON p.idcard=c.cid
</select>
</mapper>
测试文件:
@Test
public void test1(){
SqlSession sqlSession = MyBatisUtil.createSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
List<Person> people = personMapper.selectAllPersons();
for (Person person: people) {
logger.info(person.getName()+"\t"+ person.getSex()+"\t"+ person.getCard().getCode());
}
MyBatisUtil.closeSession(sqlSession);
}
执行结果:
③ association嵌套映射【掌握】
实体对象:
@Data
public class Card {
private int cid;
private String code;
}
@Data
public class Person {
private int pid;
private String name;
private int age;
private String sex;
private Card card;
}
接口:
public interface PersonMapper {
public List<Person> selectAllPersons();
}
映射文件:
association:一对多的节点属性,常用属性如下
property:关联对象的名称
javaType: 关联对象的类型(类全名)
<mapper namespace="com.acoffee.maven.mapper.PersonMapper">
<resultMap id="PersonMap" type="Person">
<result column="pid" property="pid"></result>
<result column="name" property="name"></result>
<result column="age" property="age"></result>
<result column="sex" property="sex"></result>
<!--
association:一对多的节点属性,常用属性如下
property:关联对象的名称
havaType: 关联对象的类型(类全名)
-->
<association property="card" javaType="Card">
<id property="cid" column="cid"></id>
<result property="code" column="code"></result>
</association>
</resultMap>
<select id="selectAllPersons" resultMap="PersonMap">
SELECT p.*,c.*
FROM tb_person AS p INNER JOIN tb_card c ON p.idcard=c.cid
</select>
</mapper>
测试文件:
public class AppTest {
private Logger logger = Logger.getLogger(this.getClass());
@Test
public void test1() {
SqlSession sqlSession = MyBatisUtil.createSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
List<Person> people = personMapper.selectAllPersons();
for (Person person : people) {
logger.info(person.getName()+"\t"+ person.getSex()+"\t"+person.getCard().getCode());
}
MyBatisUtil.closeSession(sqlSession);
}
}
执行结果:
本质上还是连接查询。
④分步查询(重点掌握)
我们首先修改数据库表,修改如下:
tb_person表:
tb_card表:
PersonMapper接口:
public interface PersonMapper {
public List<Person> selectAllPersons();
}
CardMapper接口:
public interface CardMapper {
public Card selectCardByCidWithStep(@Param("cid") int cid);
}
PersonMapper.xml映射文件:
<mapper namespace="com.acoffee.maven.mapper.PersonMapper">
<resultMap id="PersonMap" type="Person">
<id column="pid" property="pid"></id>
<result column="name" property="name"></result>
<result column="age" property="age"></result>
<result column="sex" property="sex"></result>
<!--
association:一对多的节点属性,常用属性如下
property:关联对象的名称
select: 分步查询的方法
column:外键的值
-->
<association property="card" select="com.acoffee.maven.mapper.CardMapper.selectCardByCidWithStep"
column="idcard"></association>
</resultMap>
<select id="selectAllPersons" resultMap="PersonMap">
select * from tb_person
</select>
</mapper>
CardMapper.xml映射文件:
<mapper namespace="com.acoffee.maven.mapper.CardMapper">
<select id="selectCardByCidWithStep" resultType="Card">
select * from tb_card where cid=#{cid}
</select>
</mapper>
测试文件:
public class AppTest {
private Logger logger = Logger.getLogger(this.getClass());
@Test
public void test1() {
SqlSession sqlSession = MyBatisUtil.createSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
List<Person> people = personMapper.selectAllPersons();
for (Person person : people) {
logger.info(person.getName()+"\t"+ person.getSex()+"\t"+person.getCard().getCode());
}
MyBatisUtil.closeSession(sqlSession);
}
}
执行结果:
对比 ③ association嵌套映射的查询结果我们可以清楚的看出他们之间本质的sql语句的数目是不同的,④分步查询发送的sql语句数目是1+N次,我们在这里推荐使用④分步查询的方法,它在性能优化上面是有优势的。
三、association分步查询的延迟加载问题(掌握)
立即加载: 在多表查询中,如果我们查询A表的时候,不管有没有使用B表中的数据,依然会查询B表
注意: MyBatis的默认加载方式就是立即加载。
延迟加载: 俗称懒加载,需要的采取查询的这种情况,在多表查询中,如果我们查询A表的时候,我们如果用到了B表中的数据,就会去查询B表,如果我们没有用到B表中的数据,就不会去查询B表。
一对一、多对一查询:推荐立即加载
需求场景:查询账户时,基本需要显示用户信息
一对多查询:推荐延迟加载(减少内存浪费)
需求场景:查询商品时,不需要查询全部商品,只需要部分商品,推荐使用延迟加载
为什么需要延迟加载: 为了提高性能,把需要的信息不利己立即查出来,需要的时候再查
如下操作虽然我只是需要人员本身信息,但我们观察SQL语句,大家会发现,依然做的是立即加载。
mybatis中如何设置延迟加载:
第一种方式:
第一步:在mybatis-config.xml中设置全局启用延迟加载
<settings>
<setting name="logImpl" value="LOG4J"/>
<!--lazyLoadingEnabled是设置值开启延迟加载的,默认是false
表示没有开启延迟加载,这里设置true表示开启延迟加载
-->
<setting name="lazyLoadingEnabled" value="true"></setting>
</settings>
测试代码:
@Test
public void test1() {
SqlSession sqlSession = MyBatisUtil.createSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
List<Person> people = personMapper.selectAllPersons();
for (Person person : people) {
logger.info(person.getName()+"\t"+ person.getSex()+"\t"+person.getCard().getCode());
}
MyBatisUtil.closeSession(sqlSession);
}
此时测试代码中没有查询关联信息,只是查询Person本身的信息。
延迟加载执行结果:
立即加载执行结果:
我们将测试代码中的person.getCard().getCode()
代码部分删除(即删除关联信息):
@Test
public void test1() {
SqlSession sqlSession = MyBatisUtil.createSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
List<Person> people = personMapper.selectAllPersons();
for (Person person : people) {
logger.info(person.getName()+"\t"+ person.getSex()+"\t"+person.getCard().getCode());
}
MyBatisUtil.closeSession(sqlSession);
}
在mubatis-config.xml文件中,设置属性不按需加载:
<!--设置不按需加载,默认不按需加载-->
<setting name="aggressiveLazyLoading" value="true"></setting>
<!--设置按需加载-->
<setting name="aggressiveLazyLoading" value="false"></setting>
我们可以发现当我们设置不按需加载的时候它也去查询它依然会去查询 cid 当数据多了的时候会不必要的麻烦
第二种方式:通过在association中设置fetchType的值来设置延迟加载
fetchType属性中eager为立即加载.
fetchType属性中lazy为延迟加载.
setting设置全局,fetchType设置局部,且都只对分布查询有效。
总结: 如果在association中通过fetchType设置延迟加载和在mybatis-config.xml文件通过setting节点设置延迟加载同时存在时,fetchType设置延迟加载的级别要高于setting中设置的级别。
四、一对多关联
1.一对多广泛存在于实际应用中,部门和员工,县区和街道
2.一对多的分类,以方向来可以分为
- 单向一对多:只在一的一方配置多方的关联
- 单向多对一:只在多的一方配置一方的关联
- 双向一对多:在两方都配置
3.mybatis 中配置一对多
在mybatis配置一对多的方式有两种
数据库脚本:
drop table emp;
CREATE TABLE emp(
empno NUMBER(4),
ename VARCHAR2(10),
job VARCHAR2(9),
mgr NUMBER(4),
hiredate DATE,
sal NUMBER(7,2),
comm NUMBER(7,2),
deptno NUMBER(2)
);
INSERT INTO emp VALUES(7369,'SMITH','CLERK',7902,to_date('17-12-1980','dd-mm-yyyy'),800,NULL,20);
INSERT INTO emp VALUES(7499,'ALLEN','SALESMAN',7698,to_date('20-2-1981','dd-mm-yyyy'),1600,300,30);
INSERT INTO emp VALUES(7521,'WARD','SALESMAN',7698,to_date('22-2-1981','dd-mm-yyyy'),1250,500,30);
INSERT INTO emp VALUES(7566,'JONES','MANAGER',7839,to_date('2-4-1981','dd-mm-yyyy'),2975,NULL,20);
INSERT INTO emp VALUES(7654,'MARTIN','SALESMAN',7698,to_date('28-9-1981','dd-mm-yyyy'),1250,1400,30);
INSERT INTO emp VALUES(7698,'BLAKE','MANAGER',7839,to_date('1-5-1981','dd-mm-yyyy'),2850,NULL,30);
INSERT INTO emp VALUES(7782,'CLARK','MANAGER',7839,to_date('9-6-1981','dd-mm-yyyy'),2450,NULL,10);
INSERT INTO emp VALUES(7788,'SCOTT','ANALYST',7566,to_date('19-4-87','dd-mm-yyyy'),3000,NULL,20);
INSERT INTO emp VALUES(7839,'KING','PRESIDENT',NULL,to_date('17-11-1981','dd-mm-yyyy'),5000,NULL,10);
INSERT INTO emp VALUES(7844,'TURNER','SALESMAN',7698,to_date('8-9-1981','dd-mm-yyyy'),1500,0,30);
INSERT INTO emp VALUES(7876,'ADAMS','CLERK',7788,to_date('23-5-87','dd-mm-yyyy'),1100,NULL,20);
INSERT INTO emp VALUES(7900,'JAMES','CLERK',7698,to_date('3-12-1981','dd-mm-yyyy'),950,NULL,30);
INSERT INTO emp VALUES(7902,'FORD','ANALYST',7566,to_date('3-12-1981','dd-mm-yyyy'),3000,NULL,20);
INSERT INTO emp VALUES(7934,'MILLER','CLERK',7782,to_date('23-1-1982','dd-mm-yyyy'),1300,NULL,10);
①嵌套映射的配置方式
案例:配置单向一对多
实体对象:
@Data
public class Dept {
private byte deptno;
private String dname;
private String loc;
private List<Emp> emps;
}
@Data
public class Emp {
private short empno;
private String ename;
private String job;
private short mgr;
private Date hiredate;
private double sal;
private double comm;
}
接口:
public interface DeptMapper {
public Dept selectDeptByDeptno(@Param("deptno") byte deptno);
}
映射文件:
<mapper namespace="com.acoffee.maven.mapper.DeptMapper">
<resultMap id="deptMap" type="dept">
<id column="deptno" property="deptno"></id>
<result column="dname" property="dname"></result>
<result column="loc" property="loc"></result>
<!--
collection:配置一对多
property:关联对象名称
ofType:集合中元素的类型
-->
<collection property="emps" ofType="com.acoffee.maven.pojo.Emp">
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="mgr" column="mgr"></result>
<result property="hiredate" column="hiredate"></result>
<result property="sal" column="sal"></result>
<result property="comm" column="comm"></result>
</collection>
</resultMap>
<select id="selectDeptByDeptno" parameterType="byte" resultMap="deptMap">
SELECT e.*,d.dname,d.`LOC`
FROM dept AS d INNER JOIN emp e ON d.`DEPTNO`=e.`DEPTNO`
WHERE d.`DEPTNO`=#{deptno};
</select>
</mapper>
测试文件:
public class AppTest {
private Logger logger = Logger.getLogger(this.getClass());
@Test
public void shouldAnswerWithTrue() {
SqlSession sqlSession = MyBatisUtil.createSession();
DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = deptMapper.selectDeptByDeptno((byte)10);
logger.info(dept.getDeptno()+"\t"+dept.getDname()+"\t"+dept.getLoc());
List<Emp> emps = dept.getEmps();
logger.info(dept.getDname()+"共有"+emps.size()+"个人");
for (Emp emp: emps) {
logger.info(emp.getEmpno()+"\t"+emp.getEname()+"\t"+emp.getSal()+"\t"+emp.getHiredate());
}
MyBatisUtil.closeSession(sqlSession);
}
}
执行结果:
②一对多关联(分步查询)
接口:
public interface EmpMapper {
public List<Emp> selectEmpsByDeptno(@Param("deptno") byte deptno);
}
映射文件:
DeptMapper.xml文件:
<mapper namespace="com.acoffee.maven.mapper.DeptMapper">
<resultMap id="deptMap" type="dept">
<id column="deptno" property="deptno"></id>
<result column="dname" property="dname"></result>
<result column="loc" property="loc"></result>
<!--
collection:配置一对多
property:关联对象名称
ofType:集合中元素的类型
-->
<collection property="emps" select="com.acoffee.maven.mapper.EmpMapper.selectEmpsByDeptno" column="deptno" fetchType="lazy">
</collection>
</resultMap>
<select id="selectDeptByDeptno" parameterType="byte" resultMap="deptMap">
select * from dept where deptno=#{deptno}
</select>
</mapper>
EmpMapper.xml文件:
<mapper namespace="com.acoffee.maven.mapper.EmpMapper">
<select id="selectEmpsByDeptno" parameterType="byte" resultType="emp">
select * from emp where deptno=#{deptno}
</select>
</mapper>
测试文件:
public class AppTest {
private Logger logger = Logger.getLogger(this.getClass());
@Test
public void shouldAnswerWithTrue() {
SqlSession sqlSession = MyBatisUtil.createSession();
DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = deptMapper.selectDeptByDeptno((byte)10);
logger.info(dept.getDeptno()+"\t"+dept.getDname()+"\t"+dept.getLoc());
List<Emp> emps = dept.getEmps();
logger.info(dept.getDname()+"共有"+emps.size()+"个人");
for (Emp emp: emps) {
logger.info(emp.getEmpno()+"\t"+emp.getEname()+"\t"+emp.getSal()+"\t"+emp.getHiredate());
}
MyBatisUtil.closeSession(sqlSession);
}
}
执行结果:
2.双向一对多
实体对象:
@Data
public class Dept {
private byte deptno;
private String dname;
private String loc;
private List<Emp> emps;
}
@Data
public class Emp {
private short empno;
private String ename;
private String job;
private short mgr;
private Date hiredate;
private double sal;
private double comm;
private Dept dept;
}
接口:
public interface EmpMapper {
public List<Emp> selectEmpsByDeptno(@Param("deptno") byte deptno);
public Emp selectEmpByEmpno(@Param("empno") short empno);
}
public interface DeptMapper {
public Dept selectDeptByDeptno(@Param("deptno") byte deptno);
public Dept selectDeptByDeptnoWithStep(@Param("deptno") byte deptno);
}
映射文件:
EmpMapper.xml文件:
<mapper namespace="com.acoffee.maven.mapper.EmpMapper">
<resultMap id="empMap" type="emp">
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="mgr" column="mgr"></result>
<result property="hiredate" column="hiredate"></result>
<result property="sal" column="sal"></result>
<result property="comm" column="comm"></result>
<association property="dept" select="com.acoffee.maven.mapper.DeptMapper.selectDeptByDeptnoWithStep" column="deptno">
</association>
</resultMap>
<select id="selectEmpsByDeptno" parameterType="byte" resultType="emp">
select * from emp where deptno=#{deptno}
</select>
<select id="selectEmpByEmpno" parameterType="short" resultMap="empMap">
select * from emp where empno=#{empno}
</select>
</mapper>
DeptMapper.xml文件:
<mapper namespace="com.acoffee.maven.mapper.DeptMapper">
<resultMap id="deptMap" type="dept">
<id column="deptno" property="deptno"></id>
<result column="dname" property="dname"></result>
<result column="loc" property="loc"></result>
<!--
collection:配置一对多
property:关联对象名称
ofType:集合中元素的类型
-->
<collection property="emps" select="com.acoffee.maven.mapper.EmpMapper.selectEmpsByDeptno" column="deptno">
</collection>
</resultMap>
<select id="selectDeptByDeptno" parameterType="byte" resultMap="deptMap">
select * from dept where deptno=#{deptno}
</select>
<select id="selectDeptByDeptnoWithStep" parameterType="byte" resultType="dept">
select * from dept where deptno=#{deptno}
</select>
</mapper>
测试文件:
public class AppTest {
private Logger logger = Logger.getLogger(this.getClass());
@Test
public void shouldAnswerWithTrue2() {
SqlSession sqlSession = MyBatisUtil.createSession();
EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = empMapper.selectEmpByEmpno((short)7369);
logger.info("员工的信息" + emp.getEmpno() + "\t" + emp.getEname() + emp.getDept().getDname() + "\t" + emp.getDept().getLoc());
MyBatisUtil.closeSession(sqlSession);
}
}
五、多对多关联
1.多对多的应用场景
医生和患者、老师和学生 等。
2.具体的案例
①分析
②数据库表的创建
#创建教师表
CREATE TABLE teacher(
tid INT PRIMARY KEY AUTO_INCREMENT,
tname VARCHAR(20),
step VARCHAR(20)
);
#创建学生表
CREATE TABLE student(
sid INT PRIMARY KEY AUTO_INCREMENT,
sname VARCHAR(20),
telephone VARCHAR(11)
);
#创建中间表
CREATE TABLE t_s(
id INT PRIMARY KEY AUTO_INCREMENT,
tid INT,
sid INT,
FOREIGN KEY fk_tid(tid) REFERENCES teacher(tid),
FOREIGN KEY fk_sid(sid) REFERENCES student(sid)
);
#测试数据
INSERT INTO teacher(tname,step)VALUES('zt','一阶段');
INSERT INTO teacher(tname,step)VALUES('pf','三阶段');
INSERT INTO teacher(tname,step)VALUES('zjz','三阶段');
INSERT INTO student(sname,telephone)VALUES('jl','12354356654');
INSERT INTO student(sname,telephone)VALUES('zl','15721471910');
INSERT INTO student(sname,telephone)VALUES('lch','15533951605');
INSERT INTO t_s(tid,sid)VALUES(1,1);
INSERT INTO t_s(tid,sid)VALUES(1,2);
INSERT INTO t_s(tid,sid)VALUES(2,3);
INSERT INTO t_s(tid,sid)VALUES(3,1);
INSERT INTO t_s(tid,sid)VALUES(3,2);
INSERT INTO t_s(tid,sid)VALUES(3,4);
COMMIT;
teacher表:
student表:
t_s表:
③实体类的设计
@Data
public class Student {
private int sid;
private String sname;
private String telephone;
private List<Teacher> teachers;
}
@Data
public class Teacher {
private int tid;
private String tname;
private String step;
private List<Student> students;
}
接口:
TeacherMapper
public interface TeacherMapper {
public Teacher selectTeacherByTid(@Param("tid") int tid);
}
StudentMapper
public interface StudentMapper {
public List<Student> selectStudentsByTid(@Param("tid") int tid);
}
映射文件:
TeacherMapper.xml
<mapper namespace="com.acoffee.maven.mapper.TeacherMapper">
<resultMap id="teacherMap" type="teacher">
<id property="tid" column="tid"></id>
<result property="tname" column="tname"></result>
<result property="step" column="step"></result>
<collection property="students" select="com.acoffee.maven.mapper.StudentMapper.selectStudentsByTid"
column="tid">
</collection>
</resultMap>
<select id="selectTeacherByTid" parameterType="int" resultMap="teacherMap">
SELECT * FROM teacher WHERE tid=#{tid};
</select>
</mapper>
StudentMapper.xml
<mapper namespace="com.acoffee.maven.mapper.StudentMapper">
<select id="selectStudentsByTid" parameterType="int" resultType="student">
SELECT * FROM student WHERE sid IN(
SELECT sid FROM t_s WHERE tid=#{tid}
)
</select>
</mapper>
测试文件:
public class AppTest {
private Logger logger = Logger.getLogger(this.getClass());
@Test
public void test1(){
SqlSession sqlSession = MyBatisUtil.createSession();
TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = teacherMapper.selectTeacherByTid(1);
logger.info(teacher);
MyBatisUtil.closeSession(sqlSession);
}
}
执行结果:
执行流程图:
六、完整的多表查询流程
数据表
student表
teacher表
s_t 表(关联表)
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias type="com.acoffee.pojo.Student" alias="student"></typeAlias>
<typeAlias type="com.acoffee.pojo.Teacher" alias="teacher"></typeAlias>
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="jdbc"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/t_s"></property>
<property name="username" value="root"></property>
<property name="password" value="7777"></property>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com/acoffee/mapper"></package>
</mappers>
</configuration>
方式一(嵌套映射):
实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private int id;
private String name;
List<Teacher> teachers;
}
接口
public interface StudentMapper {
List<Student> queryAll();
}
StudentMapper.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="com.acoffee.mapper.StudentMapper">
<resultMap id="queryTeacherByStudent" type="Student">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<collection property="teachers" column="id" ofType="teacher">
<id property="id" column="tid"></id>
<result property="name" column="tname"></result>
</collection>
</resultMap>
<select id="queryAll" resultMap="queryTeacherByStudent">
SELECT s.*,t.`id` tid,t.`name` tname
FROM student s
INNER JOIN s_t st
ON s.`id`=st.`sid`
INNER JOIN teacher t
ON st.`tid` = t.`id`
</select>
</mapper>
查询结果:
方式二(分步查询):
实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private int id;
private String name;
List<Teacher> teachers;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
}
接口
StudentMapper
public interface StudentMapper {
List<Student> queryAll();
}
TeacherMapper
public interface TeacherMapper {
Teacher queryTeacherById(int id);
}
配置文件
StudentMapper.xm
<?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="com.acoffee.mapper.StudentMapper">
<resultMap id="queryTeacherByStudent" type="Student">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<collection property="teachers" column="id" ofType="teacher" select="com.acoffee.mapper.TeacherMapper.queryTeacherById"></collection>
</resultMap>
<select id="queryAll" resultMap="queryTeacherByStudent">
select * from student
</select>
</mapper>
TeacherMapper.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="com.acoffee.mapper.TeacherMapper">
<select id="queryTeacherById" parameterType="int" resultType="Teacher">
select * from teacher t,s_t st where t.id = st.tid and st.sid = #{sid}
</select>
</mapper>
执行结果:
思考题
上述我们方式二中使用分步查询时,我们将TeacherMapper.xml 配置文件中的 sql 语句改为右联或者左联时我们可以思考一下结果为什么是这样。
右联
<mapper namespace="com.acoffee.mapper.TeacherMapper">
<select id="queryTeacherById" parameterType="int" resultType="Teacher">
select t.id,t.name from teacher t right join s_t st on st.tid = t.id and st.sid = #{id}
</select>
</mapper>
左联
<mapper namespace="com.acoffee.mapper.TeacherMapper">
<select id="queryTeacherById" parameterType="int" resultType="Teacher">
select t.id,t.name from teacher t left join s_t st on st.tid = t.id and st.sid = #{id}
</select>
</mapper>
我们可以看到右联的结果中teachers 打印的是一个完整的集合并且集合中有四个元素,并且每个几个中有三个null值,而左联的结果中每个集合中有三个元素,并且是全部打印出来了。
这是因为右联时是以 s_t 表为基准的 而恰好 s_t 表中有四个值并且每次查询中 teacher只有一个元素与s_t表中的元素匹配 而其他三个元素与s_t 是找不到匹配的,所以就打印出来了三个null.
而同理在左联时是以teacher表为基准的不管你s_t表是什么情况 teacher 都会全部打印,所以才展现出每个teachers集合中又有三个元素。