MyBatis是一个半ORM框架,作用在持久(DAO)层,是对JDBC的一种封装。它支持动态SQL、多级缓存以及高级映射,免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作,通过简单的 XML 或注解来配置和映射原始类型、接口和Pojo类,将数据库中的一条记录对应着一个Pojo类。
MyBatis的前身是iBATIS,iBATIS于2002年由ClintonBegin创建。MyBatis3是iBATIS的全新设计,支持注解和Mapper(映射)。

MyBatis的结构主要包括:

  • 配置文件(mybatis-config.xml):配置MyBatis的数据库环境、映射文件路径等。
    • 数据库环境:可以配置多个环境。有一个默认的调用环境。在调用的时候也可以选择调用具体哪个环境。开发时主要包括5种环境:DEV(开发)、TEST、QA(质量评估)、UAT(用户验收)、PRODUCTION(生产)。每个环境可以配置一个数据源(dataSource),其type属性包括UNPOOLED(非池化)、POOLED(池化)、JNDI(从应用服务器向配置好的JNDI数据源dataSource获取数据库连接,生产环境常用);还可以配置事务管理器(transactionManager),JDBC为开发者管理,MANAGED为配置了管理器。
  • 映射文件(XXXMapping.xml):配置SQL语句映射到哪一个(映射)接口,以及接口的哪一个方法。
  • 映射接口(XXXMapping.java):通过调用映射接口的方法,执行方法对应的SQL语句。

MyBatis的主要步骤:
解析XML配置文件成一个对象,确定调用的数据源,获取数据库链接,通过映射执行SQL语句。这里的映射主要是MyBatis根据接口和映射文件的关系进行反向代理,自动生成接口的实现类。映射关系主要包括以下几个:

  1. 接口(全限定名)——mapper元素namespace属性
  2. 接口方法名——update、insert、delete、select等元素的id属性
  3. 方法参数类型(如pojo类)(全限定名)——update、insert、delete、select等元素parameterType属性
  4. 参数对象(pojo类)的值或属性——SQL语句中的占位符(#{})

MyBatis的几个问题:

  1. 当参数对象(pojo类)的属性也是一个pojo类时,例如Teacher表中拥有一个Telephone对象,MyBatis并不知道该怎样来处理这个类型的对象。MyBatis提供了抽象类BaseTypeHandler,可以继承此类创建自定义类型处理器。
  2. 参数注入的两种方式#{}和${}:#{}会为传入的参数添加引号,能避免SQL注入,但只能用于传入记录的值;${}则不会添加引号,因此可以用于传入表名、字段名等,不能避免SQL注入。

    DML操作

    1、mybatis.properties
    1. driver=com.mysql.jdbc.Driver
    2. url=jdbc:mysql://192.168.110.128:3306/library
    3. username=root
    4. password=root
    2、mybatis-config.xml ```xml <?xml version=”1.0” encoding=”utf-8”?> <!DOCTYPE configuration PUBLIC “-//mybatis.org//DTD Config3.0//EN” “http://mybatis.org/dtd/mybatis-3-config.dtd">

  1. <typeAliases><!-- 为该包下的所有pojo类起别名(为类名) -->
  2. <package name="com.briup.mybatis.bean"/>

  1. <environments default="develop">
  2. <environment id="develop">
  3. <transactionManager type="JDBC"></transactionManager>
  4. <dataSource type="POOLED">
  5. <property name="driver" value="${driver}"/>
  6. <property name="url" value="${url}"/>
  7. <property name="username" value="${username}"/>
  8. <property name="password" value="${password}"/>
  9. </dataSource>
  10. </environment>
  11. </environments>
  12. <mappers>
  13. <mapper resource="com/briup/mybatis/test/StudentMapper.xml"/>
  14. </mappers>

  1. 3StudentMapper.xml
  2. ```xml
  3. <?xml version="1.0" encoding="utf-8"?>
  4. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  5. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  6. <mapper namespace="com.briup.mybatis.test.StudentMapper">
  7. <update id="createTable">
  8. create table student (
  9. id bigint primary key,
  10. score double,
  11. name varchar(20),
  12. birthday datetime
  13. )
  14. </update>
  15. <insert id="insertData" parameterType="com.briup.mybatis.bean.Student">
  16. insert into student values(#{id}, #{score}, #{name}, #{birthday})
  17. </insert>
  18. <insert id="insertDataAutoId" parameterType="com.briup.mybatis.bean.Student">
  19. insert into student(score, name, birthday) values(#{score}, #{name}, #{birthday})
  20. <selectKey order="AFTER" keyColumn="id" keyProperty="id" resultType="java.lang.Long">
  21. SELECT LAST_INSERT_ID()
  22. </selectKey>
  23. </insert>
  24. <update id="updateData" parameterType="com.briup.mybatis.bean.Student">
  25. update student set score = #{score}, name = #{name}, birthday = #{birthday} where id = #{id}
  26. </update>
  27. <delete id="deleteData" parameterType="java.lang.Long">
  28. <!-- 基本数据类型直接写类型名 -->
  29. delete from student where id = #{id}
  30. </delete>
  31. </mapper>

4、StudentMapper.java

  1. package com.briup.mybatis.test;
  2. import com.briup.mybatis.bean.Student;
  3. public interface StudentMapper {
  4. void insertData(Student stu);
  5. void insertDataAutoId(Student stu);
  6. void updateData(Student stu);
  7. void deleteData(Long id);
  8. }

5、执行代码

  1. public class MybatisTest {
  2. @Test
  3. public void testCreateTable() {
  4. try {
  5. InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
  6. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
  7. SqlSession sqlSession = sqlSessionFactory.openSession();
  8. sqlSession.update("linja.createTable");
  9. sqlSession.close();
  10. System.out.println("执行终了");
  11. } catch (IOException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. @Test
  16. public void testInsert() {
  17. try {
  18. InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
  19. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
  20. SqlSession sqlSession = sqlSessionFactory.openSession();
  21. Student student = new Student(2L, 25.5, "3344", new Date(123456666));
  22. sqlSession.insert("linja.insertData", student);
  23. sqlSession.commit();
  24. sqlSession.close();
  25. System.out.println("执行终了");
  26. } catch (IOException e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. @Test
  31. public void testInsertByInterface() {
  32. SqlSession sqlSession = SqlSessionUtil.getSqlSession();
  33. Student student = new Student(null, 25.5, "3344", new Date(723456666));
  34. StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
  35. mapper.insertDataAutoId(student);
  36. sqlSession.commit();
  37. sqlSession.close();
  38. System.out.println(student);
  39. System.out.println("执行终了");
  40. }
  41. }

DQL操作

DQL按查询方式可分为单表查询、多表查询;多表查询又分一对一、一对多和多对多查询。而查询的方法又分为不传参数和传参数的、返回pojo对象或集合的。此外还有查询的列名和pojo对象属性名不一致的问题;动态SQL的问题等。针对以上问题,MyBatis的映射文件提供了一系列的元素供使用:(为了方便,以下注释全部使用Java注释)

  1. <select id="findStudentById" parameterType="int" resultType="Student">
  2. SELECT STUD_ID, NAME, EMAIL, PHONE
  3. FROM STUDENTS
  4. WHERE STUD_ID=#{id}
  5. </select> // 最简单的一种查询
  6. // 如果方法返回类型为List、Set、SortedSet,就会封装成这些集合后返回,resultType是这些集合的泛型
  7. // 如果方法返回为Map,resultType就要填写Map,会将key值设置为列名,value值设置为数据
  1. // 如果列名和pojo对象属性名不一致,就要写一个resultMap,将它们对应起来
  2. <resultMap id="StudentResult" type="com.briup.pojo.Student">
  3. <id property="studId" column="stud_id" /> // 主键使用id标签
  4. <result property="name" column="name" /> // 非主键使用result标签
  5. <result property="email" column="email" />
  6. <result property="phone" column="phone" />
  7. </resultMap>
  8. <select id="findStudentById" parameterType="int" resultMap="StudentResult">
  9. SELECT * FROM STUDENTS WHERE STUD_ID=#{id}
  10. </select>
  1. // resultMap可以拓展
  2. <resultMap type="Student" id="StudentResult">
  3. <id property="stud Id" column="stud_id" />
  4. <result property="name" column="name" />
  5. <result property="email" column="email" />
  6. <result property="phone" column="phone" />
  7. </resultMap>
  8. <resultMap type="Student" id="StudentWithAddressResult" extends="StudentResult">
  9. <result property="address.addrId" column="addr_id" />
  10. <result property="address.street" column="street" />
  11. <result property="address.city" column="city" />
  12. <result property="address.state" column="state" />
  13. <result property="address.zip" column="zip" />
  14. <result property="address.country" column="country" />
  15. </resultMap>
  1. // 可以将需要反复查的语句提取到sql标签中
  2. <sql id="querycolumns">
  3. id,name,age,address
  4. </sql>
  5. <select id="findStudentById"parameterType="int" resultMap="Student">
  6. Select <include refid="querycolumns"/> from students where stud_id = #{studId}
  7. </select>
  1. // 下面是一个地址的普通查询
  2. <resultMap type="Address" id="AddressResult">
  3. <id property="addrId" column="addr_id" />
  4. <result property="street" column="street" />
  5. <result property="city" column="city" />
  6. <result property="state" column="state" />
  7. <result property="zip" column="zip" />
  8. <result property="country" column="country" />
  9. </resultMap>
  10. <select id="findAddressById" parameterType="int" resultMap="AddressResult">
  11. select * from addresses
  12. where addr_id=#{id}
  13. </select>
  14. // 下面是一个查询学生的一对一查询
  15. <resultMap type="Student" id="StudentWithAddressResult">
  16. <id property="studId" column="stud_id" />
  17. <result property="name" column="name" />
  18. <result property="email" column="email" />
  19. <result property="dob" column="dob" />
  20. <result property="phone" column="phone" />
  21. // 在一对一查询中,查Student会查出addr_id,于是在查到的时候也要把addr_id对应的Address查出来并封装
  22. // 使用association标签调用一个嵌套的多表查询
  23. <association property="address" column="addr_id" select="findAddressById" />
  24. </resultMap>
  25. <select id="selectStudentWithAddress" parameterType="int" resultMap="StudentWithAddressResult">
  26. select * from students
  27. where stud_id=#{id}
  28. </select>
  1. // 一对多查询
  2. <!-- 独立的Address封装映射 -->
  3. <resultMap type="Address" id="AddressResult">
  4. <id property="addrId" column="addr_id" />
  5. <result property="street" column="street" />
  6. <result property="city" column="city" />
  7. <result property="state" column="state" />
  8. <result property="zip" column="zip" />
  9. <result property="country" column="country" />
  10. </resultMap>
  11. <!-- 独立的Course封装映射 -->
  12. <resultMap type="Course" id="CourseResult">
  13. <id column="course_id" property="courseId" />
  14. <result column="name" property="name" />
  15. <result column="description" property="description" />
  16. <result column="start_date" property="startDate" />
  17. <result column="end_date" property="endDate" />
  18. </resultMap>
  19. <resultMap type="Tutor" id="TutorResult">
  20. <id column="tutor_id" property="tutorId" />
  21. <result column="tutor_name" property="name" />
  22. <result column="email" property="email" />
  23. <result column="phone" property="phone" />
  24. <!-- 把addr_id列的值当做参数传给findAddressById进行查询 -->
  25. <association property="address" column="addr_id"
  26. select="findAddressById"></association>
  27. <!-- 把tutor_id列的值当做参数传给findCoursesByTutor进行查询 -->
  28. <collection property="courses" column="tutor_id"
  29. select="findCoursesByTutor" />
  30. </resultMap>
  31. <!-- 单独的Tutor查询语句 -->
  32. <select id="findTutorById" parameterType="int"
  33. resultMap="TutorResult">
  34. select *
  35. from tutors
  36. where tutor_id=#{tutor_id}
  37. </select>
  38. <!-- 单独的Address查询语句 -->
  39. <select id="findAddressById" parameterType="int"
  40. resultMap="AddressResult">
  41. select *
  42. from addresses
  43. where addr_id = #{addr_id}
  44. </select>
  45. <!-- 单独的Course查询语句 -->
  46. <select id="findCoursesByTutor" parameterType="int"
  47. resultMap="CourseResult">
  48. select *
  49. from courses
  50. where tutor_id=#{tutor_id}
  51. </select>

几个重要属性:

  • parameterType:用于select、insert等标签,表示输入参数的类型。
  • resultType:用于select、selectKey标签,表示产生的对象的类型,或产生集合的范型。如果多表查询产生多个对象,则不用resultType而用resultMap。
  • type:用于resultMap标签,表示列名和属性名对应的pojo类。
  • javaType:用于resultMap.association标签(即一对一查询),表示property变量的类型。
  • ofType:用于resultMap.collection标签(即一对多查询和多对多查询),表示property变量集合的泛型。
  • jdbcType:association标签和collection标签都有,表示数据在数据库中的类型,一般不写。

MyBatis的多表查询无外乎两种方式:不是嵌套result,就是嵌套resultMap。