关联映射关系
我们拿学生和年级的关系来举例:
一对一:因为一个学生只能属于一个年级,所以从学生的角度一个学生只能对一个年级,一对一关系(实际上从模块的角度出发,多个学生同属一个年级,应该是多对一的关系,但是此处我们要从实际操作查询的角度出发,如果此处把他们看多多对一去查询就没有意义了)。
一对多:一个年级可以有多个学生,所有从年级的角度是一对多关系。
案例准备
1)在数据库中新建student和grade并添加数据
#先进grade
CREATE TABLE `NewTable` (
`grade_id` int(4) NOT NULL AUTO_INCREMENT ,
`name` varchar(7) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
PRIMARY KEY (`grade_id`)
)ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci;
#添加数据
INSERT INTO `mybatis`.`grade` (`grade_id`, `name`) VALUES ('1', '大一');
INSERT INTO `mybatis`.`grade` (`grade_id`, `name`) VALUES ('2', '大二');
INSERT INTO `mybatis`.`grade` (`grade_id`, `name`) VALUES ('3', '大三');
#新建student
CREATE TABLE `NewTable` (
`student_id` int(4) NOT NULL AUTO_INCREMENT ,
`name` varchar(7) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`age` int(3) NULL DEFAULT NULL ,
`grade_id` int(4) NOT NULL ,
PRIMARY KEY (`student_id`),
FOREIGN KEY (`grade_id`) REFERENCES `grade` (`grade_id`) ON DELETE CASCADE ON UPDATE CASCADE,
INDEX `grade_id` (`grade_id`) USING BTREE
)ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci;
#添加数据
INSERT INTO `mybatis`.`student` (`student_id`, `name`, `age`, `grade_id`) VALUES ('1', '张三', NULL, '1');
INSERT INTO `mybatis`.`student` (`student_id`, `name`, `age`, `grade_id`) VALUES ('2', '李四', NULL, '2');
INSERT INTO `mybatis`.`student` (`student_id`, `name`, `age`, `grade_id`) VALUES ('3', '王五', NULL, '1');
INSERT INTO `mybatis`.`student` (`student_id`, `name`, `age`, `grade_id`) VALUES ('4', '赵六', NULL, '3');
2)新建项目mybatis_demo03,搭建项目环境
导入jar包,db.properties,log4j.properties和SqlMapConfig.xml(不会的同学翻我前两章文章)
首先在SqlMapConfig.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>
<!-- db.properties配置文件 -->
<properties resource="db.properties"/>
<!-- 和spring整合后environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${user}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
</configuration>
3)在com.oak.po中新建两个pojo类Student和Grade
public class Student {
private Integer StudentId;
private String name;
private Integer age;
private Integer gradeId;
//方法略
}
public class Grade {
private Integer gradeId;
private String name;
//方法略
}
一对一关联映射
需求:查询所有学员信息,关联查询所属年级信息
方法一
使用resultType,定义学生信息PO类,此PO类中包括了学生信息和年级信息。在po包下新建一个StudentGrade类,让此类继承自Student类,然后添加Grade类的信息字段即可:
public class StudentGrade extends Student{
private String gradeName;
//方法略
}
注意:此处定义该类与vo的含义类似
按照需求,在com.oak.mapper包中新建StudentMapper接口,然后定义查询方法:
public interface StudentMapper {
/**
* 查询所有学生信息--包括所属年级
* @return
*/
List<StudentGrade> findAll();
}
接在在同包下新建UserMapper.xml,在映射文件中添加select元素
<!-- 查询所有学生信息 -->
<select id="findAll" resultType="com.oak.po.StudentGrade">
select stu.student_id studentId
,stu.name name
,stu.age
,stu.grade_id gradeId
,gra.name gradeName
from `student` stu left join `grade` gra on stu.grade_id=gra.grade_id
</select>
在SqlMapConfig.xml中加载映射文件:
<!-- 加载mapper映射文件 -->
<mappers>
<package name="com.oak.mapper"/>
</mappers>
测试类中测试:
public class StuTest {
// 工厂对象一般在我们的系统中是单例的
private SqlSessionFactory sqlSessionFactory=null;
@Before
public void init() throws IOException{
// 第一步,创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 第二步,加载配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 第三步,创建SqlSessionFactory对象
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void testFindAll(){
SqlSession sqlSession=sqlSessionFactory.openSession();
StudentMapper stuMapper=sqlSession.getMapper(StudentMapper.class);
List<StudentGrade> stus=stuMapper.findAll();
System.out.println(stus);
}
}
查看结果:
小结:定义专门的po类作为输出类型,其中定义了sql查询结果集所有的字段。此方法较为简单,企业中使用普遍,类似于vo。
方法二
使用resultMap,定义resultMap用于映射一对一查询结果。
首先在学生类中添加一个grade属性,grade属性用于存储关联查询的年级信息,因为学生关联年级是一对一,所以这里使用单个Grade对象来存储关联信息
public class Student {
private Integer StudentId;
private String name;
private Integer age;
private Integer gradeId;
private Grade grade;
//添加getter和setter方法
}
在UserMapper接口中定义方法findStuResutlMap:
/**
* 查询所有的学生信息,使用resultMap封装关联信息
* @return
*/
List<Student> findStuResultMap();
在UserMapper.xml中编写sql:
<select id="findStuResultMap" resultMap="student_grade_resultmap">
select stu.student_id
,stu.name
,stu.age
,stu.grade_id
,gra.name gname
from `student` stu left join `grade` gra on stu.grade_id=gra.grade_id
</select>
定义id为student_grade_resultmap的ResultMap:
<resultMap type="student" id="student_grade_resultmap">
<id property="studentId" column="student_id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="gradeId" column="grade_id"/>
<!-- 配置一对一关联映射 -->
<association property="grade" javaType="grade">
<id property="gradeId" column="grade_id"/>
<!-- 因为查询结果列名中有重名的name 所以起个别名用于区分 -->
<result property="name" column="gname"/>
</association>
</resultMap>
-
association:表示进行关联查询单条记录。
-
property:表示关联查询的结果存储在student的grade属性中。即 property对应Grade类里面一对一关联映射的那个属性,即grade属性。
-
javaType:表示关联查询的结果类型。即grade属性的数据类型,可使用别名。
-
< id property=“id” column=“grade_id”/>:查询结果的grade_id列对应关联对象的id属性,这里是表示grade_id是关联查询对象的唯一标识。
-
< result property=“name” column=“gname”/>:查询结果的gname列对应关联对象的name属性。
测试:
@Test
public void testFindAll(){
SqlSession sqlSession=sqlSessionFactory.openSession();
StudentMapper stuMapper=sqlSession.getMapper(StudentMapper.class);
List<Student> stus=stuMapper.findStuResultMap();
System.out.println(stus);
}
一对多关联映射
需求:查询所有年级,关联查询所有学生信息
类似于上面在在Student中加入一个Grade对象,因为一个Student对应一个Grade,是一对一关系。那么在此,一个年级对应多个学生,是一对多关系,所以在Grade中加入一个Student集合作为属性:
public class Grade {
private Integer gradeId;
private String name;
private List<Student> students;
//添加getter和setter方法
}
在新建GradeMapper接口,并定义查询方法:
/**
* 查询所有年级信息--关联学生信息
* @return
*/
List<Grade> findAll();
在同包中新建GradeMapper.xml映射文件
<select id="findStuResultMap" resultMap="grade_student_resultmap">
select g.grade_id
,g.name
,s.student_id
,s.name sname
,s.age
from `grade` g left join `student` s on g.grade_id=s.grade_id
</select>
<!--指定映射resultMap-->
<resultMap type="grade" id="grade_student_resultmap">
<id property="gradeId" column="grade_id"/>
<result property="name" column="name"/>
<!-- 配置一对一关联映射 -->
<collection property="students" ofType="student">
<id property="studentId" column="student_id"/>
<!-- 因为查询结果列名中有重名的name 所以起个别名用于区分 -->
<result property="name" column="sname"/>
<result property="age" column="age"/>
<result property="gradeId" column="grade_id"/>
</collection>
</resultMap>
这里resultMap指定为grade_student_resultmap。
-
collection部分年级关联的学生信息,表示关联查询结果集。
-
property=”students”:关联查询的结果集存储在Grade对象的哪个属性上。即property对应Grade对象中的集合属性。
-
ofType=”student”:指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。
-
< id />及< result/>的意义同一对一查询。
测试:
@Test
public void testGradeFindAll(){
SqlSession sqlSession=sqlSessionFactory.openSession();
GradeMapper graMapper=sqlSession.getMapper(GradeMapper.class);
List<Grade> grades=graMapper.findAll();
System.out.println(grades);
}