MyBatis 接口代理方式实现 Dao 层
接口代理方式-实现规则
- 传统方式实现 Dao 层,我们既要写接口,还要写实现类。而 MyBatis 框架可以帮助我们省略编写 Dao 层接
口实现类的步骤。程序员只需要编写接口,由 MyBatis 框架根据接口的定义来创建该接口的动态代理对象。 - 实现规则
1. 映射配置文件中的名称空间必须和 Dao 层接口的全类名相同。
2. 映射配置文件中的增删改查标签的 id 属性必须和 Dao 层接口的方法名相同。
3. 映射配置文件中的增删改查标签的 parameterType 属性必须和 Dao 层接口方法的参数相同。
4. 映射配置文件中的增删改查标签的 resultType 属性必须和 Dao 层接口方法的返回值相同。
接口代理方式-代码实现
- 删除 mapper 层接口的实现类。
- 修改映射配置文件。
- 按照实现规则修改相应标签的属性:
- 接口的方法名称以及参数类型返回值类型必须和SQL映射配置文件中的标签对应
- namespace的值一定得和接口的权限定类名相同
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
mapper:核心根标签
namespace属性:名称空间
-->
<mapper namespace="com.itheima.mapper.StudentMapper">
<!--<sql id="select" >SELECT * FROM student</sql>-->
<!--
select:查询功能的标签
id属性:唯一标识
resultType属性:指定结果映射对象类型
parameterType属性:指定参数映射对象类型
-->
<select id="selectAll" resultType="student">
SELECT * FROM student
</select>
<select id="selectById" resultType="student" parameterType="int">
SELECT * FROM student WHERE id = #{id}
</select>
<insert id="insert" parameterType="student">
INSERT INTO student VALUES (#{id},#{name},#{age})
</insert>
<update id="update" parameterType="student">
UPDATE student SET name = #{name},age = #{age} WHERE id = #{id}
</update>
<delete id="delete" parameterType="int">
DELETE FROM student WHERE id = #{id}
</delete>
</mapper>
- 修改 service 层接口的实现类,采用接口代理方式实现功能。
/*
持久层接口
*/
public interface StudentMapper {
//查询全部
public abstract List<Student> selectAll();
//根据id查询
public abstract Student selectById(Integer id);
//新增数据
public abstract Integer insert(Student stu);
//修改数据
public abstract Integer update(Student stu);
//删除数据
public abstract Integer delete(Integer id);
//多条件查询
public abstract List<Student> selectCondition(Student stu);
//根据多个id查询
public abstract List<Student> selectByIds(List<Integer> ids);
}
/*
业务层实现类
*/
public class StudentServiceImpl {
public List<Student> selectAll() {
List<Student> list = null;
SqlSession sqlSession = null;
InputStream is = null;
try{
//1.加载核心配置文件
is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
sqlSession = sqlSessionFactory.openSession(true);
//4.获取StudentMapper接口的实现类对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();
//5.通过实现类对象调用方法,接收结果
list = mapper.selectAll();
} catch (Exception e) {
} finally {
//6.释放资源
if(sqlSession != null) {
sqlSession.close();
}
if(is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//7.返回结果
return list;
}
/*
控制层测试类
*/
public class StudentController {
//创建业务层对象
private StudentService service = new StudentServiceImpl();
//查询全部功能测试
@Test
public void selectAll() {
List<Student> students = service.selectAll();
for (Student stu : students) {
System.out.println(stu);
}
}
重复代码抽取
- 什么时候初始化配置信息拿到SQLSession(静态代码块儿, 构造方法)
- 抽取StudentMapper作为属性字段在构造方法重初始化
代码实现:
/*
业务层实现类
*/
public class StudentServiceImpl implements StudentService {
private StudentMapper studentMapper;
public StudentServiceImpl() {
InputStream is = null;
SqlSession sqlSession = null;
try {
is = Resources.getResourceAsStream("MyBatisproperties.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
sqlSession = build.openSession(true);
studentMapper = sqlSession.getMapper(StudentMapper.class);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public List<Student> selectAll() {
return studentMapper.selectAll();
}
@Override
public Student selectById(Integer id) {
return studentMapper.selectById(id);
}
@Override
public Integer insert(Student stu) {
return studentMapper.insert(stu);
}
@Override
public Integer update(Student stu) {
return studentMapper.update(stu);
}
@Override
public Integer delete(Integer id) {
return studentMapper.delete(id);
}
接口代理方式-源码分析
- 分析动态代理对象如何生成的?
通过动态代理开发模式,我们只编写一个接口,不写实现类,我们通过 getMapper() 方法最终获取到
org.apache.ibatis.binding.MapperProxy 代理对象,然后执行功能,而这个代理对象正是 MyBatis 使用了
JDK 的动态代理技术,帮助我们生成了代理实现类对象。从而可以进行相关持久化操作。 - 分析方法是如何执行的?
动态代理实现类对象在执行方法的时候最终调用了 mapperMethod.execute() 方法,这个方法中通过
switch 语句根据操作类型来判断是新增、修改、删除、查询操作,最后一步回到了 MyBatis 最原生的
SqlSession 方式来执行增删改查。
接口代理方式小结
- 接口代理方式可以让我们只编写接口即可,而实现类对象由 MyBatis 生成。
- 实现规则
- 映射配置文件中的名称空间必须和 Dao 层接口的全类名相同。
- 映射配置文件中的增删改查标签的 id 属性必须和 Dao 层接口的方法名相同。
- 映射配置文件中的增删改查标签的 parameterType 属性必须和 Dao 层接口方法的参数相同。
- 映射配置文件中的增删改查标签的 resultType 属性必须和 Dao 层接口方法的返回值相同。
- 获取动态代理对象
SqlSession 功能类中的 getMapper() 方法。
MyBatis 映射配置文件 - 动态 SQL
动态 SQL 介绍
- MyBatis 映射配置文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL 就是动态变化的,此时在前面学习的 SQL 就不能满足要求了。
- 多条件查询

- 动态 SQL 标签
< if >:条件判断标签。
< foreach>:循环遍历标签。
< if >标签
- < where>:条件标签。如果有动态条件,则使用该标签代替 where 关键字。
- < if>:条件判断标签。

- 代码实现:
<select id="selectCondition" resultType="student" parameterType="student">
SELECT * FROM student
<where>
<if test="id != null">
id = #{id}
</if>
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
< foreach >标签
- < foreach>:循环遍历标签。适用于多个参数或者的关系。

- 属性
- collection:参数容器类型,(list-集合,array-数组)。
- open:开始的 SQL 语句。
- close:结束的 SQL 语句。
- item:参数变量名。
- separator:分隔符。
<select id="selectByIds" resultType="student" parameterType="list">
<include refid="select"/>
<where>
<foreach collection="list" open="id IN (" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
SQL 片段抽取
- 我们可以将一些重复性的 SQL 语句进行抽取,以达到复用的效果。
- < sql>:抽取 SQL 语句标签。

- < include>:引入 SQL 片段标签。

- 代码实现:

注意:在SQL语句中我们使用 * 会影响查询性能,这时我们可以将SELECT * FROM student 内的 * 符号替换为相应的字段即可
动态 SQL 小结
- 动态 SQL 指的就是 SQL 语句可以根据条件或者参数的不同进行动态的变化。
- < where>:条件标签。
- < if>:条件判断的标签。
- < foreach>:循环遍历的标签。
- < sql>:抽取 SQL 片段的标签。
- < include>:引入 SQL 片段的标签。
MyBatis 核心配置文件 – 分页插件
分页插件介绍

- 分页可以将很多条结果进行分页显示。
- 如果当前在第一页,则没有上一页。如果当前在最后一页,则没有下一页。
- 需要明确当前是第几页,这一页中显示多少条结果。
- 在企业级开发中,分页也是一种常见的技术。而目前使用的 MyBatis 是不带分页功能的,如果想实现分页的
功能,需要我们手动编写 LIMIT 语句。但是不同的数据库实现分页的 SQL 语句也是不同的,所以手写分页
成本较高。这个时候就可以借助分页插件来帮助我们实现分页功能。 - PageHelper:第三方分页助手。将复杂的分页操作进行封装,从而让分页功能变得非常简单。
分页插件实现步骤
- 导入 jar 包。
pagehelper-5.1.10.jar
jsqlparser-3.1.jar

- 在核心配置文件中集成分页助手插件。
<!--集成分页助手插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
- 在测试类中使用分页助手相关 API 实现分页功能。
package com.itheima.paging;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itheima.bean.Student;
import com.itheima.mapper.StudentMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class Test01 {
@Test
public void selectPaging() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取StudentMapper接口的实现类对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//通过分页助手来实现分页功能
// 第一页:显示3条数据
//PageHelper.startPage(1,3);
// 第二页:显示3条数据
//PageHelper.startPage(2,3);
// 第三页:显示3条数据
PageHelper.startPage(3,3);
//5.调用实现类的方法,接收结果
List<Student> list = mapper.selectAll();
//6.处理结果
for (Student student : list) {
System.out.println(student);
}
//获取分页相关参数
PageInfo<Student> info = new PageInfo<>(list);
System.out.println("总条数:" + info.getTotal());
System.out.println("总页数:" + info.getPages());
System.out.println("当前页:" + info.getPageNum());
System.out.println("每页显示条数:" + info.getPageSize());
System.out.println("上一页:" + info.getPrePage());
System.out.println("下一页:" + info.getNextPage());
System.out.println("是否是第一页:" + info.isIsFirstPage());
System.out.println("是否是最后一页:" + info.isIsLastPage());
//7.释放资源
sqlSession.close();
is.close();
}
}
分页插件相关参数
- PageInfo:封装分页相关参数的功能类。
- 核心方法

分页插件小结
- 分页:可以将很多条结果进行分页显示。
- 分页插件 jar 包:pagehelper-5.1.10.jar jsqlparser-3.1.jar
- < plugins>:集成插件标签。
- 分页助手相关 API
PageHelper:分页助手功能类。
startPage():设置分页参数
PageInfo:分页相关参数功能类。
getTotal():获取总条数
getPages():获取总页数
getPageNum():获取当前页
getPageSize():获取每页显示条数
getPrePage():获取上一页
getNextPage():获取下一页
isIsFirstPage():获取是否是第一页
isIsLastPage():获取是否是最后一页
MyBatis 多表操作
多表模型
- 我们之前学习的都是基于单表操作的,而实际开发中,随着业务难度的加深,肯定需要多表操作的。
- 多表模型分类
一对一:在任意一方建立外键,关联对方的主键。
一对多:在多的一方建立外键,关联一的一方的主键。
多对多:借助中间表,中间表至少两个字段,分别关联两张表的主键。
一对一
- 一对一模型:人和身份证,一个人只有一个身份证。
- < resultMap>:配置字段和对象属性的映射关系标签。
id 属性:唯一标识
type 属性:实体对象类型 - :配置主键映射关系标签。
-< result>:配置非主键映射关系标签。
column 属性:表中字段名称
property 属性: 实体对象变量名称 - < association>:配置被包含对象的映射关系标签。
property 属性:被包含对象的变量名
javaType 属性:被包含对象的数据类型
一对一映射文件
<?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:被包含对象的数据类型
-->
<association property="p" javaType="Person">
<id column="pid" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</association>
</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>
一对一接口
public interface OneToOneMapper {
//查询全部
public abstract List<Card> selectAll();
}
一对一测试类
package com.itheima.table01;
import com.itheima.bean.Card;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class Test01 {
@Test
public void selectAll() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取OneToOneMapper接口的实现类对象
OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class);
//5.调用实现类的方法,接收结果
List<Card> list = mapper.selectAll();
//6.处理结果
for (Card c : list) {
System.out.println(c);
}
//7.释放资源
sqlSession.close();
is.close();
}
}
一对多
- 一对多模型:班级和学生,一个班级可以有多个学生。
- < resultMap>:配置字段和对象属性的映射关系标签。
id 属性:唯一标识
type 属性:实体对象类型 - < id>:配置主键映射关系标签。
- < result>:配置非主键映射关系标签。
column 属性:表中字段名称
property 属性: 实体对象变量名称 - < collection>:配置被包含集合对象的映射关系标签。
property 属性:被包含集合对象的变量名
ofType 属性:集合中保存的对象数据类型
一对多映射文件
<?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="classes">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
<!--
collection:配置被包含的集合对象映射关系
property:被包含对象的变量名
ofType:被包含对象的实际数据类型
-->
<collection property="students" ofType="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>
一对多接口
public interface OneToManyMapper {
//查询全部
public abstract List<Classes> selectAll();
}
一对多测试类
package com.itheima.table02;
import com.itheima.bean.Classes;
import com.itheima.bean.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class Test01 {
@Test
public void selectAll() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取OneToManyMapper接口的实现类对象
OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class);
//5.调用实现类的方法,接收结果
List<Classes> classes = mapper.selectAll();
//6.处理结果
for (Classes cls : classes) {
System.out.println(cls.getId() + "," + cls.getName());
List<Student> students = cls.getStudents();
for (Student student : students) {
System.out.println("\t" + student);
}
}
//7.释放资源
sqlSession.close();
is.close();
}
}
多对多
- 多对多模型:学生和课程,一个学生可以选择多门课程、一个课程也可以被多个学生所选择。
- < resultMap>:配置字段和对象属性的映射关系标签。
id 属性:唯一标识
type 属性:实体对象类型
-< id>:配置主键映射关系标签。 - < result>:配置非主键映射关系标签。
column 属性:表中字段名称
property 属性: 实体对象变量名称 - < collection>:配置被包含集合对象的映射关系标签。
property 属性:被包含集合对象的变量名
ofType 属性:集合中保存的对象数据类型
多对多映射文件
<?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="student">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="sage" property="age"/>
<collection property="courses" ofType="course">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</collection>
</resultMap>
<resultMap id="AllCoures" type="course">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
<collection property="students" ofType="student">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="sage" property="age"/>
</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>
<select id="selectAllCoures" resultMap="AllCoures">
SELECT sc.cid,c.name cname,sc.sid,s.name sname,s.age sage FROM student s,course c,stu_cr sc WHERE sc.sid=s.id AND sc.cid=c.id
</select>
</mapper>
多对多接口
public interface ManyToManyMapper {
//查询全部
public abstract List<Student> selectAll();
List<Course> selectAllCoures();
}
多对多测试类
@Test
public void selectAll() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取ManyToManyMapper接口的实现类对象
ManyToManyMapper mapper = sqlSession.getMapper(ManyToManyMapper.class);
//5.调用实现类的方法,接收结果
List<Student> students = mapper.selectAll();
//6.处理结果
for (Student student : students) {
System.out.println(student.getId() + "," + student.getName() + "," + student.getAge());
List<Course> courses = student.getCourses();
for (Course cours : courses) {
System.out.println("\t" + cours);
}
}
//7.释放资源
sqlSession.close();
is.close();
}
多表操作小结
- 多表模型分类:一对一、一对多、多对多。
- < resultMap>:配置字段和对象属性的映射关系标签。
id 属性:唯一标识
type 属性:实体对象类型 - :配置主键映射关系标签。
-< result>:配置非主键映射关系标签。
column 属性:表中字段名称
property 属性: 实体对象变量名称 - < association>:配置被包含对象的映射关系标签。
property 属性:被包含对象的变量名
javaType 属性:被包含对象的数据类型 - < collection>:配置被包含集合对象的映射关系标签。
property 属性:被包含集合对象的变量名
ofType 属性:集合中保存的对象数据类型
712

被折叠的 条评论
为什么被折叠?



