实现关联表查询续
一对一关联:
这里我们假设一个老师只负责教一个班级,那么老师和班级之间的关系就是一种一对一的关系。
那么我们可以创建一张教师表和班级表:
CREATE TABLE teacher(
t_id INT PRIMARY KEY AUTO_INCREMENT,
t_name VARCHAR(20)
);
CREATE TABLE class(
c_id INT PRIMARY KEY AUTO_INCREMENT,
c_name VARCHAR(20),
teacher_id INT
);
ALTER TABLE class ADD CONSTRAINT fk_teacher_id FOREIGN KEY (teacher_id) REFERENCES teacher(t_id);
INSERT INTO teacher(t_name) VALUES('teacher1');
INSERT INTO teacher(t_name) VALUES('teacher2');
INSERT INTO class(c_name, teacher_id) VALUES('class_a', 1);
INSERT INTO class(c_name, teacher_id) VALUES('class_b', 2);
表之间的关系如下图:
定义实体类:
1.Teacher类,Teacher类是teacher表对应的实体类。
//定义实体类的属性,与teacher表中的字段对应
private int id; //id===>t_id
private String name; //name===>t_name
//省略部分代码
2.Classes类,Classes类是class表对应的实体类
//定义实体类的属性,与class表中的字段对应
private int id; //id===>c_id
private String name; //name===>c_name
/**
*class表中有一个teacher_id字段,所以在Classes类中定义一个teacher属性,
*用于维护teacher和class之间的一对一关系,通过这个teacher属性就可以知道这个班级是由哪个老师负责的
*/
private Teacher teacher;
//省略部分代码
定义sql映射文件classMapper.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="me.gacl.mapping.classMapper">
<!--查询方式一:嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集
封装联表查询的数据(去除重复的数据)
select * from class c, teacher t where c.teacher_id=t.t_id and c.c_id=1-->
<select id="getClass" parameterType="int"
resultMap="ClassResultMap">
select * from class c,teacher t where c.teacher_id=t.t_id and c.c_id =#{id}
</select>
<resultMap type="me.gacl.domain.Classes" id="ClassResultMap">
<id property="id" columni=c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="me.gacl.domain.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
<!--查询方式二:嵌套查询:通过执行另外一个SQL映射语句来返回预期的复杂类型
SELECT * FROM class WHERE c_id=1;
SELECT * FROM teacher WHERE t_id=1 //1是上一个查询得到的teacher_id的值-->
<select id="getClass2" parameterType="int"
resultMap="ClassResultMap2">
select * from class where c_id=#{id}
</select>
<resultMap type="me.gacl.domain.Classes" id="ClassResultMap2">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" select="getTeacher"/>
</resultMap>
<select id="getTeacher" parameterType="int"
resultType="me.gacl.domain.Teacher">
SELECT t_id id, t_name name FROM teacher WHERE t_id=#{id}
</select>
</mapper>
在conf.xml文件中,注册classMapper.xml
<mappers>
<mapper resource="me/gacl/mapping/classMapper.xml"/>
</mappers>
编写单元测试代码
@Test
public void testGetClass(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
String statement = "me.gacl.mapping.classMapper.getClass";
Classes clazz = sqlSession.selectOne(statement,1);
sqlSession.close();
System.out.println(clazz);
//打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1]]
}
@Test
public void testGetClass2(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
String statement = "me.gacl.mapping.classMapper.getClass2";
Classes clazz = sqlSession.selectOne(statement,1);
sqlSession.close();
System.out.println(clazz);
//打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1]]
}
一对多查询
在上面我们假设了一个老师只负责教一个班级,但一个班级会有多个学生,那么班级对于学生的关系就是一种一对多的关系。
CREATE TABLE student(
s_id INT PRIMARY KEY AUTO_INCREMENT,
s_name VARCHAR(20),
class_id INT
);
INSERT INTO student(s_name, class_id) VALUES('student_A', 1);
INSERT INTO student(s_name, class_id) VALUES('student_B', 1);
INSERT INTO student(s_name, class_id) VALUES('student_C', 1);
INSERT INTO student(s_name, class_id) VALUES('student_D', 2);
INSERT INTO student(s_name, class_id) VALUES('student_E', 2);
INSERT INTO student(s_name, class_id) VALUES('student_F', 2);
在上面的一对一关联查询中我们已经创建了班级表和教师表,在创建一张学生表之后的关系图:
定义实体类
Student类,Student类是表student对应的实体类
private int id; //id===>s_id
private String name; //name===>s_name、
//省略部分代码
修改Classes类,添加一个List<Student>
students属性,使用一个List<Student>
集合属性表示班级拥有的学生,如下:
private int id; //id===>c_id
private String name; //name===>c_name
private Teacher teacher;
//使用一个List<Student>集合属性表示班级拥有的学生
private List<Student> students;
修改sql映射文件classMapper.xml
添加如下的SQL映射信息:
<select id="getClass3" parameterType="int"
resultMap="ClassResultMap3">
select * from class c, teacher t,student s where c.teacher_id=t.t_id and c.C_id=s.class_id and c.c_id=#{id}
</select>
<resultMap type="me.gacl.domain.Classes" id="ClassResultMap3">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" javaType="me.gacl.domain.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
<!-- ofType指定students集合中的对象类型 -->
<collection property="students" ofType="me.gacl.domain.Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
<select id="getClass4" parameterType="int"
resultMap="ClassResultMap4">
select * from class where c_id=#{id}
</select>
<resultMap type="me.gacl.domain.Classes" id="ClassResultMap4">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id"
javaType="me.gacl.domain.Teacher" select="getTeacher2"></association>
<collection property="students" ofType="me.gacl.domain.Student"
column="c_id" select="getStudent"></collection>
</resultMap>
<select id="getTeacher2" parameterType="int"
resultType="me.gacl.domain.Teacher">
SELECT t_id id, t_name name FROM teacher WHERE t_id=#{id}
</select>
<select id="getStudent" parameterType="int"
resultType="me.gacl.domain.Student">
SELECT s_id id, s_name name FROM student WHERE class_id=#{id}
</select>
编写单元测试代码
@Test
public void testGetClass3(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
String statement = "me.gacl.mapping.classMapper.getClass3";
Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录
sqlSession.close();
//打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1], students=[Student [id=1, name=student_A], Student [id=2, name=student_B], Student [id=3, name=student_C]]]
System.out.println(clazz);
}
@Test
public void testGetClass4(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
String statement = "me.gacl.mapping.classMapper.getClass4";
Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录
sqlSession.close();
//打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1], students=[Student [id=1, name=student_A], Student [id=2, name=student_B], Student [id=3, name=student_C]]]
System.out.println(clazz);
}
总结:
MyBatis中使用collection标签来解决一对多的关联查询
collection标签可用的属性如下:
(1) property:对象属性的名称
(2) ofType属性指定集合中元素的对象类型
(3) column:所对应的外键字段名称
(4) select:使用另一个查询封装的结果
调用存储过程
存储过程的定义
存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。
提出一个需求
查询得到男性或女性的数量, 如果传入的是0就女性否则是男性
准备数据库表和存储过程
create table p_user(
id int primary key auto_increment,
name varchar(10),
sex char(2)
);
insert into p_user(name,sex) values('A',"男");
insert into p_user(name,sex) values('B',"女");
insert into p_user(name,sex) values('C',"男");
-- 创建存储过程(查询得到男性或女性的数量, 如果传入的是0就女性否则是男性)
DELIMITER $
CREATE PROCEDURE mybatis.ges_user_count(IN sex_id INT, OUT user_count INT)
BEGIN
IF sex_id=0 THEN
SELECT COUNT(*) FROM mybatis.p_user WHERE p_user.sex='女' INTO user_count;
ELSE
SELECT COUNT(*) FROM mybatis.p_user WHERE p_user.sex='男' INTO user_count;
END IF;
END
$
-- 调用存储过程
DELIMITER ;
SET @user_count = 0;
CALL mybatis.ges_user_count(1, @user_count);
SELECT @user_count;
编辑userMapper.xml
在userMapper.xml文件中添加如下的配置项:
<!--查询得到男性或女性的数量, 如果传入的是0就女性否则是男性-->
<select id="getUserCount" parameterMap="getUserCountMap"
statementType="CALLABLE">
CALL mybatis.ges_user_count(?,?)
</select>
<!--parameterMap.put("sexid", 0);
parameterMap.put("usercount", -1);
-->
<parameterMap type="java.util.Map" id="getUserCountMap">
<parameter property="sexid" mode="IN" jdbcType="INTEGER"/>
<parameter property="usercount" mode="OUT" jdbcType="INTEGER"/>
</parameterMap>
编写单元测试代码###、
@Test
public void testGetUserCount(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
String statement = "me.gacl.mapping.userMapper.getUserCount";
Map<String, Integer> parameterMap = new HashMap<String, Integer>();
parameterMap.put("sexid", 1);
parameterMap.put("usercount", -1);
sqlSession.selectOne(statement, parameterMap);
Integer result = parameterMap.get("usercount");
System.out.println(result);
sqlSession.close();
}
重点:###
1.其中DELIMITER 定好结束符为”$$”, 然后最后又定义为”;”, MYSQL的默认结束符为”;”. 默认情况下,delimiter是分号;
2.调用存储过程 delimiter定义结束符为”;” SET赋初始值, 使用CALL调用 ,SELECT作用是输出
Mybatis缓存
正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持
1.一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
2. 二级缓存:二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。
3.对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。
如何开启二级缓存
开启二级缓存,只需要在userMapper.xml文件中添加如下配置:
<mapper namespace="me.gacl.mapping.userMapper">
<!-- 开启二级缓存 -->
<cache/>
二级缓存补充说明
- 映射语句文件中的所有select语句将会被缓存。
- 映射语句文件中的所有insert,update和delete语句会刷新缓存。
- 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
- 缓存会根据指定的时间间隔来刷新。
- 缓存会存储1024个对象
cache标签常用属性:
<cache eviction="FIFO" <!--回收策略为先进先出-->
flushInterval="60000" <!--自动刷新时间60s-->
size="512" <!--最多缓存512个引用对象-->
readOnly="true"/> <!--只读-->