MyBatis 的高级映射及延迟加载
1. 准备工作
- 新建一个模块
- 配置 pom 文件
- 打包方式:
<packaging>jar</packaging>
- 导入依赖:mybatis mysql junit logback lombok
- 拷贝工具类 SqlSessionUtil:
package com.dsl.mybatis.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/**
* MyBatis工具类
*
* @author DSL
* @version 1.0
* @since 1.0
*/
public class SqlSessionUtil {
private static SqlSessionFactory sqlSessionFactory;
/**
* 类加载时初始化sqlSessionFactory对象
*/
static {
try {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (Exception e) {
e.printStackTrace();
}
}
private static ThreadLocal<SqlSession> local = new ThreadLocal<>();
/**
* 每调用一次openSession()可获取一个新的会话,该会话支持自动提交。
*
* @return 新的会话对象
*/
public static SqlSession openSession() {
SqlSession sqlSession = local.get();
if (sqlSession == null) {
sqlSession = sqlSessionFactory.openSession();
local.set(sqlSession);
}
return sqlSession;
}
/**
* 关闭SqlSession对象
* @param sqlSession
*/
public static void close(SqlSession sqlSession){
if (sqlSession != null) {
sqlSession.close();
}
local.remove();
}
}
- 准备数据库:t_student t_clazz
2. 多对一(多个学生对应一个班级)
多种方式,常见的包括三种:
- 第一种方式:一条SQL语句,级联属性映射。
- 第二种方式:一条SQL语句,association。
- 第三种方式:两条SQL语句,分步查询。(这种方式常用:优点一是可复用。优点二是支持懒加载。)
2.1 级联属性映射
在 Student 中添加一个属性:Clazz ,表示学生关联的班级对象
在 StudentMapper.xml 文件编写如下代码,这里再 idea 中可能会标红,但是不影响查询结果
<?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.dsl.mybatis.mapper.StudentMapper">
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="studentName" column="student_name"/>
<result property="clazz.cid" column="cid"/>
<result property="clazz.clazzName" column="clazz_name"/>
</resultMap>
<select id="selectStudentAndClazz" resultMap="studentResultMap">
select ts.sid, ts.student_name, ts.cid, tc.clazz_name
from t_student ts
join t_clazz tc on ts.cid = tc.cid
where ts.sid = #{sid};
</select>
</mapper>
在 StudentMapper 中添加对应的接口:Student selectStudentAndClazz(Integer id);
最后编写测试类对代码进行测试:
public class TestMapper {
@Test
public void testSelectStudentAndClazz(){
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.selectStudentAndClazz(1);
System.out.println(student);
}
}
观察结果:
Student(sid=1, studentName=zs, clazz=Clazz(cid=101, clazzName=一班))
成功查询了所有数据
2.2 association
使用该方式其他条件不用更改,只需要改变 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.dsl.mybatis.mapper.StudentMapper">
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="studentName" column="student_name"/>
<association property="clazz" javaType="Clazz">
<id property="cid" column="cid"/>
<result property="clazzName" column="clazz_name"/>
</association>
</resultMap>
<select id="selectStudentAndClazz" resultMap="studentResultMap">
select ts.sid, ts.student_name, ts.cid, tc.clazz_name
from t_student ts
join t_clazz tc on ts.cid = tc.cid
where ts.sid = #{sid};
</select>
</mapper>
再次使用上述测试代码进行测试,得到相同结果
2.3 分布查询
分布查询本质上就是通过两步查询出所有数据:第一步是先通过学生 id 查询出学生的所有信息(学生id,学生姓名name,学生班级号cid),然后通过班级号 cid 查询出学生的班级信息,最后封装结果返回。
- 编写
StudentMapper.xml
和对应的接口,在 association 中 select 位置填写 sqlId。sqlId = namespace + id。其中 column 属性作为这条子 sql 语句的条件。
<?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.dsl.mybatis.mapper.StudentMapper">
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="studentName" column="student_name"/>
<association property="clazz" select="com.dsl.mybatis.mapper.ClazzMapper.selectByCid" column="cid"/>
</resultMap>
<select id="selectStudentAndClazz" resultMap="studentResultMap">
select sid, student_name, cid
from t_student
where sid = #{sid};
</select>
</mapper>
在 StudentMapper
接口中添加对应的方法 Student selectStudentAndClazz(Integer id);
- 编写
ClazzMapper.xml
和对应的 mapper 接口
<?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.dsl.mybatis.mapper.ClazzMapper">
<resultMap id="BaseResultMap" type="com.dsl.mybatis.pojo.Clazz">
<id column="cid" jdbcType="INTEGER" property="cid"/>
<result column="clazz_name" jdbcType="VARCHAR" property="clazzName"/>
</resultMap>
<select id="selectByCid" resultMap="BaseResultMap">
select cid, clazz_name
from t_clazz
where cid = #{cid};
</select>
</mapper>
并在对应接口上添加 Clazz selectByCid(Integer cid);
方法
- 执行结果,可以观察到两条语句先后执行
- 使用该方法的优点:
- 代码复用性增强
- 支持延迟加载。【暂时访问不到的数据可以先不查询。提高程序的执行效率。】
2.4 多对一的延迟加载
要想支持延迟加载,只需要在分布查询的基础上在 association 标签中添加 fetchType="lazy" 即可。
添加后如果我们只需要查询出部分信息,如上面测试程序的学生姓名,可以通过控制台看出只有一天语句执行。
@Test
public void testSelectStudentAndClazz(){
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.selectStudentAndClazz(1);
System.out.println(student.getStudentName());
}
如果后续需要使用到学生班级名称,这时候会执行相关的 sql 语句,通过控制台可以看出关联的语句执行了,这就是延迟加载。
上面开启的是局部延迟加载,我们可以通过配置 mybatis-config.xml 文件开启全局延迟加载,具体代码如下:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
把之前加的 fetchType="lazy" 测试,我们会看到相同的结果。
开启全局延迟加载之后,所有的 sql 都会支持延迟加载,如果某个 sql 你不希望它支持延迟加载怎么办呢?将 fetchType 设置为 eager。
3. 一对多(一个班级对应多个学生)
一对多的实现,通常是在一的一方中有List集合属性。
在Clazz类中添加 List<Student> stus
; 属性。这里需要去掉之前在 Student 类中加入 Clazz 属性。
一对多的实现通常包括两种实现方式:
- 第一种方式:collection
- 第二种方式:分步查询
3.1 collection
- 编写
ClazzMapper.xml
和对应Mapper接口
<?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.dsl.mybatis.mapper.ClazzMapper">
<resultMap id="resultMap" type="Clazz">
<id property="cid" column="cid"/>
<result property="clazzName" column="clazz_name"/>
<collection property="students" ofType="student">
<id property="sid" column="sid"/>
<result property="studentName" column="student_name"/>
</collection>
</resultMap>
<select id="selectClazzAndStudentsByCid" resultMap="resultMap">
select *
from t_clazz tc
join t_student ts on tc.cid = ts.cid
where tc.cid = #{cid};
</select>
</mapper>
这里ofType,表示的是集合中的类型