1. MyBatis接口代理方式实现Dao层

1.1 代理开发方式介绍

  • 传统方式实现Dao层,我们既要写接口,还要写实现类。而MyBatis框架可以帮助我们省略编写Dao层接口实现类的步骤。程序猿只需要编写接口,由MyBatis框架根据接口的定义来创建该接口的动态代理对象。

采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。
Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper 接口开发需要遵循以下规范:
1) Mapper.xml文件中的namespace与mapper接口的全限定名相同
2) Mapper接口方法名和Mapper.xml中定义的每个statement的id相同

3) Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
4) Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

总结:
接口开发的方式: 程序员只需定义接口,就可以对数据库进行操作,那么具体的对象怎么创建?
1.程序员负责定义接口
2.在操作数据库,mybatis框架根据接口,通过动态代理的方式生成代理对象,负责数据库的crud操作

1.2 编写StudentMapper接口

基础部分需要我们编写Mapper层的实现类,现在不需要了。mybatis会帮我们生成!!
mapper映射文件

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!--MyBatis的DTD约束-->
  3. <!DOCTYPE mapper
  4. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  5. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  6. <!--
  7. mapper:核心根标签
  8. namespace属性:名称空间,这个名字可以随意起,这个以后有用
  9. -->
  10. <mapper namespace="com.gjt.mapper.StudentMapper">
  11. <!--
  12. select:查询功能的标签
  13. id属性:唯一标识
  14. resultType属性:指定结果映射对象类型,映射的全类名
  15. parameterType属性:指定参数映射对象类型
  16. -->
  17. <select id="selectAll" resultType="student">
  18. SELECT * FROM student
  19. </select>
  20. <select id="selectById" resultType="com.gjt.bean.Student" parameterType="java.lang.Integer">
  21. SELECT * FROM student WHERE id = #{11}
  22. </select>
  23. <insert id="insert" parameterType="student">
  24. INSERT INTO student VALUES (#{id},#{name},#{age})
  25. </insert>
  26. <update id="update" parameterType="student">
  27. UPDATE student SET name = #{name},age = #{age} WHERE id = #{id}
  28. </update>
  29. <delete id="delete" parameterType="int">
  30. DELETE FROM student WHERE id = #{id}
  31. </delete>
  32. </mapper>

mapper文件

  1. package com.gjt.mapper;/*
  2. @author gaoJunTao
  3. */
  4. import com.gjt.bean.Student;
  5. import java.util.List;
  6. /*
  7. 持久层接口
  8. */
  9. public interface StudentMapper {
  10. //查询全部
  11. public abstract List<Student> selectAll();
  12. //根据id查询
  13. public abstract Student selectById(Integer id);
  14. //新增数据
  15. public abstract Integer insert(Student stu);
  16. //修改数据
  17. public abstract Integer update(Student stu);
  18. //删除数据
  19. public abstract Integer delete(Integer id);
  20. //多条件查询
  21. public abstract List<Student> selectCondition(Student stu);
  22. //根据多个id查询
  23. public abstract List<Student> selectByIds(List<Integer> ids);
  24. }

1.3 编写Service层和controller层

service层接口省略!

  1. package com.gjt.service;/*
  2. @author gaoJunTao
  3. */
  4. import com.gjt.bean.Student;
  5. import com.gjt.mapper.StudentMapper;
  6. import org.apache.ibatis.io.Resources;
  7. import org.apache.ibatis.session.SqlSession;
  8. import org.apache.ibatis.session.SqlSessionFactory;
  9. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. import java.util.List;
  13. /*
  14. 业务层实现类
  15. */
  16. public class StudentServiceImpl implements StudentService {
  17. @Override
  18. public List<Student> selectAll() {
  19. List<Student> list = null;
  20. SqlSession sqlSession = null;
  21. InputStream is = null;
  22. try {
  23. //1.加载核心配置文件
  24. is = Resources.getResourceAsStream("MyBatisConfig.xml");
  25. //2.获取SqlSession工厂对象
  26. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
  27. //3.通过工厂对象获取SqlSession对象
  28. sqlSession = sqlSessionFactory.openSession(true);
  29. //4.获取StudentMapper接口的实现类对象
  30. StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();
  31. //5.通过实现类对象调用方法,接收结果
  32. list = mapper.selectAll();
  33. } catch (Exception e) {
  34. } finally {
  35. //6.释放资源
  36. if (sqlSession != null) {
  37. sqlSession.close();
  38. }
  39. if (is != null) {
  40. try {
  41. is.close();
  42. } catch (IOException e) {
  43. e.printStackTrace();
  44. }
  45. }
  46. }
  47. //7.返回结果
  48. return list;
  49. }
  50. @Override
  51. public Student selectById(Integer id) {
  52. Student stu = null;
  53. SqlSession sqlSession = null;
  54. InputStream is = null;
  55. try {
  56. //1.加载核心配置文件
  57. is = Resources.getResourceAsStream("MyBatisConfig.xml");
  58. //2.获取SqlSession工厂对象
  59. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
  60. //3.通过工厂对象获取SqlSession对象
  61. sqlSession = sqlSessionFactory.openSession(true);
  62. //4.获取StudentMapper接口的实现类对象
  63. StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();
  64. //5.通过实现类对象调用方法,接收结果
  65. stu = mapper.selectById(id);
  66. } catch (Exception e) {
  67. } finally {
  68. //6.释放资源
  69. if (sqlSession != null) {
  70. sqlSession.close();
  71. }
  72. if (is != null) {
  73. try {
  74. is.close();
  75. } catch (IOException e) {
  76. e.printStackTrace();
  77. }
  78. }
  79. }
  80. //7.返回结果
  81. return stu;
  82. }
  83. @Override
  84. public Integer insert(Student stu) {
  85. Integer result = null;
  86. SqlSession sqlSession = null;
  87. InputStream is = null;
  88. try {
  89. //1.加载核心配置文件
  90. is = Resources.getResourceAsStream("MyBatisConfig.xml");
  91. //2.获取SqlSession工厂对象
  92. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
  93. //3.通过工厂对象获取SqlSession对象
  94. sqlSession = sqlSessionFactory.openSession(true);
  95. //4.获取StudentMapper接口的实现类对象
  96. StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();
  97. //5.通过实现类对象调用方法,接收结果
  98. result = mapper.insert(stu);
  99. } catch (Exception e) {
  100. } finally {
  101. //6.释放资源
  102. if (sqlSession != null) {
  103. sqlSession.close();
  104. }
  105. if (is != null) {
  106. try {
  107. is.close();
  108. } catch (IOException e) {
  109. e.printStackTrace();
  110. }
  111. }
  112. }
  113. //7.返回结果
  114. return result;
  115. }
  116. @Override
  117. public Integer update(Student stu) {
  118. Integer result = null;
  119. SqlSession sqlSession = null;
  120. InputStream is = null;
  121. try {
  122. //1.加载核心配置文件
  123. is = Resources.getResourceAsStream("MyBatisConfig.xml");
  124. //2.获取SqlSession工厂对象
  125. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
  126. //3.通过工厂对象获取SqlSession对象
  127. sqlSession = sqlSessionFactory.openSession(true);
  128. //4.获取StudentMapper接口的实现类对象
  129. StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();
  130. //5.通过实现类对象调用方法,接收结果
  131. result = mapper.update(stu);
  132. } catch (Exception e) {
  133. } finally {
  134. //6.释放资源
  135. if (sqlSession != null) {
  136. sqlSession.close();
  137. }
  138. if (is != null) {
  139. try {
  140. is.close();
  141. } catch (IOException e) {
  142. e.printStackTrace();
  143. }
  144. }
  145. }
  146. //7.返回结果
  147. return result;
  148. }
  149. @Override
  150. public Integer delete(Integer id) {
  151. Integer result = null;
  152. SqlSession sqlSession = null;
  153. InputStream is = null;
  154. try {
  155. //1.加载核心配置文件
  156. is = Resources.getResourceAsStream("MyBatisConfig.xml");
  157. //2.获取SqlSession工厂对象
  158. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
  159. //3.通过工厂对象获取SqlSession对象
  160. sqlSession = sqlSessionFactory.openSession(true);
  161. //4.获取StudentMapper接口的实现类对象
  162. StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();
  163. //5.通过实现类对象调用方法,接收结果
  164. result = mapper.delete(id);
  165. } catch (Exception e) {
  166. } finally {
  167. //6.释放资源
  168. if (sqlSession != null) {
  169. sqlSession.close();
  170. }
  171. if (is != null) {
  172. try {
  173. is.close();
  174. } catch (IOException e) {
  175. e.printStackTrace();
  176. }
  177. }
  178. }
  179. //7.返回结果
  180. return result;
  181. }
  182. }

controller层

  1. package com.gjt.controller;
  2. import com.gjt.bean.Student;
  3. import com.gjt.service.StudentService;
  4. import com.gjt.service.StudentServiceImpl;
  5. import org.junit.Test;
  6. import java.util.List;
  7. /*
  8. 控制层测试类
  9. */
  10. public class StudentController {
  11. //创建业务层对象
  12. private StudentService service = new StudentServiceImpl();
  13. //查询全部功能测试
  14. @Test
  15. public void selectAll() {
  16. List<Student> students = service.selectAll();
  17. for (Student stu : students) {
  18. System.out.println(stu);
  19. }
  20. }
  21. //根据id查询功能测试
  22. @Test
  23. public void selectById() {
  24. Student stu = service.selectById(3);
  25. System.out.println(stu);
  26. }
  27. //新增功能测试
  28. @Test
  29. public void insert() {
  30. Student stu = new Student(4,"赵六",26);
  31. Integer result = service.insert(stu);
  32. System.out.println(result);
  33. }
  34. //修改功能测试
  35. @Test
  36. public void update() {
  37. Student stu = new Student(4,"赵六",16);
  38. Integer result = service.update(stu);
  39. System.out.println(result);
  40. }
  41. //删除功能测试
  42. @Test
  43. public void delete() {
  44. Integer result = service.delete(4);
  45. System.out.println(result);
  46. }
  47. }

其它文件:如三个依赖包;日志配置文件,和MyBatis全局配置文件和jdbc.properties这里省略。

1.4 源码简单分析

  • 分析动态代理对象如何生成的? 通过动态代理开发模式,我们只编写一个接口,不写实现类,我们通过 getMapper() 方法最终获取到 org.apache.ibatis.binding.MapperProxy 代理对象,然后执行功能,而这个代理对象正是 MyBatis 使用了 JDK 的动态代理技术,帮助我们生成了代理实现类对象。从而可以进行相关持久化操作。
  • 分析方法是如何执行的?动态代理实现类对象在执行方法的时候最终调用了 mapperMethod.execute() 方法,这个方法中通过 switch 语句根据操作类型来判断是新增、修改、删除、查询操作,最后一步回到了 MyBatis 最原生的 SqlSession 方式来执行增删改查。

这里的源码分析非常浅显,不管了。只需要知道是通过动态代理方式获得即可。

1.5 知识小结

接口代理方式可以让我们只编写接口即可,而实现类对象由 MyBatis 生成。
实现规则 :

  1. 映射配置文件中的名称空间必须和 Dao 层接口的全类名相同。
  2. 映射配置文件中的增删改查标签的 id 属性必须和 Dao 层接口的方法名相同。
  3. 映射配置文件中的增删改查标签的 parameterType 属性必须和 Dao 层接口方法的参数相同。
  4. 映射配置文件中的增删改查标签的 resultType 属性必须和 Dao 层接口方法的返回值相同。 
  5. 获取动态代理对象 SqlSession 功能类中的 getMapper() 方法。

获取动态代理对象

  • SqlSession功能类中的getMapper()方法。

    2. MyBatis映射配置文件——动态SQL

    2.1 动态sql语句概述

    Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了。
    参考的官方文档,描述如下:
    图片2.png

    2.2 动态 SQL 之

    我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。
    如下图:
    1. <select id="findByCondition" parameterType="student" resultType="student">
    2. select * from student
    3. <where>
    4. <if test="id!=0">
    5. and id=#{id}
    6. </if>
    7. <if test="username!=null">
    8. and username=#{username}
    9. </if>
    10. </where>
    11. </select>
    当查询条件id和username都存在时,控制台打印的sql语句如下:
    1. //获得MyBatis框架生成的StudentMapper接口的实现类
    2. StudentMapper mapper = sqlSession.getMapper( StudentMapper.class);
    3. Student condition = new Student();
    4. condition.setId(1);
    5. condition.setUsername("lucy");
    6. Student student = mapper.findByCondition(condition);
    1590936579118.png
    当查询条件只有id存在时,控制台打印的sql语句如下:
    1. //获得MyBatis框架生成的UserMapper接口的实现类
    2. StudentMapper mapper = sqlSession.getMapper( StudentMapper.class);
    3. Student condition = new Student();
    4. condition.setId(1);
    5. Student student = mapper.findByCondition(condition);
    1590936654661.png
    总结语法:
    1. <where>:条件标签。如果有动态条件,则使用该标签代替 where 关键字。
    2. <if>:条件判断标签。
    3. <if test=“条件判断”>
    4. 查询条件拼接
    5. </if>

    2.3 动态 SQL 之

    循环执行sql的拼接操作,例如:SELECT * FROM student WHERE id IN (1,2,5)。
    1. <select id="findByIds" parameterType="list" resultType="student">
    2. select * from student
    3. <where>
    4. <foreach collection="array" open="id in(" close=")" item="id" separator=",">
    5. #{id}
    6. </foreach>
    7. </where>
    8. </select>
    测试代码片段如下:
    1. //获得MyBatis框架生成的UserMapper接口的实现类
    2. StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    3. int[] ids = new int[]{2,5};
    4. List<Student> sList = mapper.findByIds(ids);
    5. System.out.println(sList);
    总结语法:
    1. <foreach>:循环遍历标签。适用于多个参数或者的关系。
    2. <foreach collection=“”open=“”close=“”item=“”separator=“”>
    3. 获取参数
    4. </foreach>
    属性
    collection:参数容器类型, (list-集合, array-数组)。
    open:开始的 SQL 语句。
    close:结束的 SQL 语句。
    item:参数变量名。
    separator:分隔符。

    2.4 SQL片段抽取

    Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
    1. <!--抽取sql片段简化编写-->
    2. <sql id="selectStudent" select * from student</sql>
    3. <select id="findById" parameterType="int" resultType="student">
    4. <include refid="selectStudent"></include> where id=#{id}
    5. </select>
    6. <select id="findByIds" parameterType="list" resultType="student">
    7. <include refid="selectStudent"></include>
    8. <where>
    9. <foreach collection="array" open="id in(" close=")" item="id" separator=",">
    10. #{id}
    11. </foreach>
    12. </where>
    13. </select>
    总结语法:
    我们可以将一些重复性的 SQL 语句进行抽取,以达到复用的效果。 ```sql
  • :抽取 SQL 语句标签。
  • :引入 SQL 片段标签。 抽取的 SQL 语句 ```

    2.5 知识小结

    MyBatis映射文件配置: ```sql