Mybatis-多表操作
MyBatis的多表操作
多表模型介绍
我们之前学习的都是基于单表操作的,而实际开发中,随着业务难度的加深,肯定需要多表操作的。
我们在下面的这些案例中全部的项目骨架如图所示
多表模型分类
-
一对一:在任意一方建立外键,关联对方的主键。
-
一对多:在多的一方建立外键,关联一的一方的主键。
-
多对多:借助中间表,中间表至少两个字段,分别关联两张表的主键。
多表模型一对一操作
数据准备
在db2数据库下创建了两张数据表,card表,person表
CREATE DATABASE db2;
USE db2;
CREATE TABLE person(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT
);
INSERT INTO person VALUES (NULL,'张三',23);
INSERT INTO person VALUES (NULL,'李四',24);
INSERT INTO person VALUES (NULL,'王五',25);
CREATE TABLE card(
id INT PRIMARY KEY AUTO_INCREMENT,
number VARCHAR(30),
pid INT,
CONSTRAINT cp_fk FOREIGN KEY (pid) REFERENCES person(id)
);
INSERT INTO card VALUES (NULL,'12345',1);
INSERT INTO card VALUES (NULL,'23456',2);
INSERT INTO card VALUES (NULL,'34567',3);
person表
card表
card表中的pid指向了person表中的id
完整代码实现
项目骨架
测试类
public class Test01 {
@Test
public void selectAll() throws IOException {
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class);
List<Card> list = mapper.selectAll();
for (Card card : list) {
System.out.println(card);
}
sqlSession.close();
is.close();
}
}
核心配置文件
数据库连接的那一些不用变,只要改一下mappers中映射文件地址的那里
<!-- mappers引入映射配置文件 -->
<mappers>
<!-- mapper 引入指定的映射配置文件 resource属性指定映射配置文件的名称 -->
<mapper resource="com/itheima/one_to_one/OneToOneMapper.xml"/>
</mappers>
Person(后面的getter、setter方法、构造方法就不展示了)
Card
映射配置文件OneToOneMapper.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.itheima.table01.OneToOneMapper">
<!--配置字段和实体对象属性的映射关系 -->
<resultMap id="oneToOne" type="card">
<id column="cid" property="id"/>
<result column="number" property="number"/>
<!--
association:配置 被 包含对象的映射关系
property:被包含对象的变量名
javaType:被包含对象的数据类型
-->
<!-- Person 与 person-->
<association property="p" javaType="person">
<id column="pid" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</association>
</resultMap>
<!-- 涉及到两张表,不能使用resultType,应该使用resultMap-->
<select id="selectAll" resultMap="oneToOne">
SELECT c.id cid,number,pid,name,age FROM card c,person p WHERE c.pid=p.id;
</select>
</mapper>
※代码分析※
我们上述实现的是多表模型中1:1的案例。真正需要我们注意的只有映射配置文件OneToOneMapper.xml
由于是多表所以我们不能使用resultType进行封装,要使用resultMap!!!
我们现在就这个案例进行逐行分析:
<!--配置字段和实体对象属性的映射关系 -->
<resultMap id="oneToOne" type="card">
</resultMap>
<!-- 涉及到两张表,不能使用resultType,应该使用resultMap-->
<select id="selectAll" resultMap="oneToOne">
SELECT c.id cid,number,pid,name,age FROM card c,person p WHERE c.pid=p.id;
</select>
<resultMap>标签是用来配置字段和对象属性的映射关系标签。
<resultMap>的id 属性是唯一标识,<select>标签通过resultMap的属性值来找到id为其值的<resultMap>标签。
<resultMap>的type 属性:实体对象类型,即是要和哪一个实体对象配置映射关系,这里就是给card对象。注:可以写成这样子type="com.itheima.bean.Card"
<resultMap id="oneToOne" type="card">
<id column="cid" property="id"/>
<result column="number" property="number"/>
</resultMap>
<resultMap>标签里有两个子标签——<id>、<result>,它们都有俩个属性column、property
其中<id>:配置主键映射关系标签。<result>:配置非主键映射关系标签。
column 属性:数据表中字段名称,我们在<select>标签中将card表的id取了别名为cid,所以上面column的值为cid
SELECT c.id cid,number,pid,name,age FROM card c,person p WHERE c.pid=p.id;
property 属性: 实体对象变量名称
之前创建了一个实体类Card,里面定义了id,所以这里是将数据表的字段cid映射到实体类Card的id属性值。
<resultMap id="oneToOne" type="card">
<id column="cid" property="id"/>
<result column="number" property="number"/>
<!--
association:配置 被 包含对象的映射关系
property:被包含对象的变量名
javaType:被包含对象的数据类型
-->
<association property="p" javaType="person">
<id column="pid" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</association>
</resultMap>
由于SQL中的后面三个值——pid,name,age不属于card对象!所以不能想上面那样,这里我们要用到一个新的标签<association>
SELECT c.id cid,number,pid,name,age FROM card c,person p WHERE c.pid=p.id;
<association>是用来配置被包含对象的映射关系标签。为什么是被包含?
如下所示:
在<association>标签中有两个属性——property、javaType
property 属性:被包含对象的变量名,显示这里包含对象的变量名就是p
javaType 属性:被包含对象的数据类型就是person,注意:这里的person就是其全类名的省略,Mybatis底层会帮你该回去的!如下:
<association property="p" javaType="com.itheima.bean.Person">
还是不太明白的看看这里:
MyBatis resultMap元素 (biancheng.net)http://m.biancheng.net/mybatis/resultmap.html
多表模型一对多操作
数据准备
CREATE TABLE classes(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
INSERT INTO classes VALUES (NULL,'黑马一班');
INSERT INTO classes VALUES (NULL,'黑马二班');
CREATE TABLE student(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
age INT,
cid INT,
CONSTRAINT cs_fk FOREIGN KEY (cid) REFERENCES classes(id)
);
INSERT INTO student VALUES (NULL,'张三',23,1);
INSERT INTO student VALUES (NULL,'李四',24,1);
INSERT INTO student VALUES (NULL,'王五',25,2);
INSERT INTO student VALUES (NULL,'赵六',26,2);
student表
classes表
我们自然要根据上面的数据表在bean层创建对应的类对象
Student类
Classes类
完整代码实现
测试类
public class Test02 {
@Test
public void selectAll() throws IOException {
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class);
List<Classes> list = mapper.selectAll();
for (Classes cls : list) {
System.out.println(cls.getId() + "," + cls.getName());
List<Student> students = cls.getStudents();
for (Student student : students) {
System.out.println("\t" + student);
}
}
sqlSession.close();
is.close();
}
}
映射配置文件OneToManyMapper.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.itheima.table02.OneToManyMapper">
<resultMap id="oneToMany" type="com.itheima.bean.Classes">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
<!--
collection: 配置被包含的集合类对象映射关系
property: 被包含对象的变量名
ofType: 被包含对象的实际数据类型
-->
<collection property="students" ofType="com.itheima.bean.Student">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="sage" property="age"/>
</collection>
</resultMap>
<select id="selectAll" resultMap="oneToMany">
SELECT c.id cid,c.name cname,s.id sid,s.name sname,s.age sage
FROM classes c,student s
WHERE c.id=s.cid;
</select>
</mapper>
运行结果
※代码分析※
大多数标签与上述的一对一案例一样,需要提一下的是这个<collection>标签
由于这个案例是一对多模型,所以不能使用<association>标签!只能使用<collection>
<collection>:配置被包含集合对象的映射关系标签。在班级类Classes中,包含了一个Student的List集合。
property 属性:被包含集合对象的变量名。
在Classes类中定义了Student类集合——students,所以property="students"
private List<Student> students;
ofType 属性:集合中保存的对象数据类型。
<collection property="students" ofType="com.itheima.bean.Student">
注:取ofType="student"也是可以的!
多表模型多对多操作
数据准备
CREATE TABLE course(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
INSERT INTO course VALUES (NULL,'语文');
INSERT INTO course VALUES (NULL,'数学');
CREATE TABLE stu_cr(
id INT PRIMARY KEY AUTO_INCREMENT,
sid INT,
cid INT,
CONSTRAINT sc_fk1 FOREIGN KEY (sid) REFERENCES student(id),
CONSTRAINT sc_fk2 FOREIGN KEY (cid) REFERENCES course(id)
);
INSERT INTO stu_cr VALUES (NULL,1,1);
INSERT INTO stu_cr VALUES (NULL,1,2);
INSERT INTO stu_cr VALUES (NULL,2,1);
INSERT INTO stu_cr VALUES (NULL,2,2);
这里使用到了3张表(student表在上一个案例已经创建过了!)
student表
course表
stu_cr表
三张表的关系如下:
完整代码
注:由于我们是简单的使用一下,只有查询的方法使用就没有创建stu_cr的类
Classes类
Student类
测试类
public class Test03 {
@Test
public void selectAll() throws IOException {
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
ManyToManyMapper mapper = sqlSession.getMapper(ManyToManyMapper.class);
List<Student> list = mapper.selectAll();
for (Student student : list) {
System.out.println(student.getId() + "," + student.getName());
List<Course> courses = student.getCourses();
for (Course c : courses) {
System.out.println("\t" + c);
}
}
sqlSession.close();
is.close();
}
}
映射配置文件OneToManyMapper.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.itheima.table03.ManyToManyMapper">
<resultMap id="manyToMany" type="com.itheima.bean.Student">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="sage" property="age"/>
<collection property="courses" ofType="com.itheima.bean.Course">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</collection>
</resultMap>
<select id="selectAll" resultMap="manyToMany">
SELECT sc.sid,s.name sname,s.age sage,sc.cid,c.name cname
FROM student s,course c,stu_cr sc
WHERE sc.sid=s.id
AND sc.cid=c.id
</select>
</mapper>
运行结果
※代码分析※
同上述一样,没有新的东西,就不做赘述了!与一对多的模型很相似。