Mybatis

基础环境搭建

  • 是什么

    mybatis 是一个sql 映射框架,提供的数据库的操作能力,增强的JDBC,使用mybatis 让开发人员集中精神写sql 就行了,不必关心

    Connection、Statement、ResuletSet 的创建,销毁,sql 的执行

  1. 新建表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    -- 创建数据库
    create database springdb CHARACTER SET utf8 COLLATE utf8_general_ci;
    -- 使用数据库
    use springdb;
    -- 创建表
    create table Student
    (
    userId int not null primary key comment '用户ID',
    userName varchar(80) not null comment '用户名',
    email varchar(20) not null comment '邮箱',
    userAge int default 18
    ) character set utf8;
  2. 加入maven 依赖坐标,mysql 驱动坐标

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.1</version>
    </dependency>

    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.9</version>
    </dependency>
  3. 创建实体类, –保存表中的一行数据的

  4. 创建持久层的dao 接口,定义操作数据库的方法

  5. 创建一个mybatis 使用的配置文件

    • 该配置文件叫做sql 映射文件: 写sql 语句的。一般一个表对应一个sql 映射文件
    • 文件类型为xxx.xml
    • 文件创建在dao 所在接口目录中
    • 文件名和接口保持一致
  6. 创建mybatis 的主配置文件

    • 一个项目就一个主配置文件
    • 主配置文件提供了数据库的连接信息和sql 映射文件的位置信息
  7. 创建使用mybatis

    • 通过mybatis 访问数据库
  • jar 下载

    1
    https://mvnrepository.com/artifact/org.mybatis/mybatis
    mybatis 下载
    mybatis下载
  • 映射文件解释

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <?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="org.mybatis.example.BlogMapper">
    <select id="" resultType="">

    </select>
    </mapper>

    <!--
    sql映射文件: 写sql语句的,mybatis会执行这些sql
    1. 指定约束文件
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

    mybatis-3-mapper.dtd 是约束文件的名称,扩展名是 dtd

    2. 约束文件作用: 限制和检查在当前文件中出现的标签,属性必须符合`mybatis`要求

    3. mapper 是当前文件的根标签,必须的
    namespace: 叫做命名空间,唯一值的,可以是自定义的字符串,要求你使用 dao 接口的全限定名称

    4. 在当前文件中,可以使用特定的标签,表示数据库的特定操作

    : 表示更新数据库的操作,就是在标签中 写 update sql 语句
    : 表示插入,放的是 insert 语句
    : 表示删除,执行的是 delete 语句


    <select id="" resultType=""> </select>
    id: 你要执行的 sql 语句的唯一标识,mybatis会使用这个 id 值来找到要执行的 sql 语句,可以自定义,但是要求你使用接口中的方法名称

    resultType: 表示结果类型,是 sql 语句执行后得到 ResultSet,遍历这个 ResultSet得到 Java对象的类型.值写的类型的全限定名称
    -->
  • 主配置文件详解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>

    <!--
    环境配置: 数据库的连接信息
    default: 必须和某个 environment 的 id 值一样
    告诉 mybatis 使用那个数据库的连接信息,也就是访问那个数据库
    -->
    <environments default="development">

    <!--
    environment: 一个数据库信息的配置,环境
    id: 一个唯一的值,可以自定义,表示环境的名称
    -->
    <environment id="development">

    <!--
    transactionManager: mybatis 的事物类型
    type: JDBC(表示使用 jdbc中的Connection对象的commit,rollback做事务处理)
    -->
    <transactionManager type="JDBC"/>
    <!--
    dataSource: 表示数据源,连接数据库的
    type: 表述数据源的类型, POOLED 表示使用连接池
    -->
    <dataSource type="POOLED">
    <!--
    driver、user、username、password 是固定的,不能自定义
    -->

    <property name="driver" value="${driver}"/>
    <!-- 连接数据库的 url 字符串 : value="jdbc:mysql://localhost:3306/springdb" -->
    <property name="url" value="${url}"/>
    <!-- 访问数据库的用户名:value="root" -->
    <property name="username" value="${username}"/>
    <!-- 密码: value="root" -->
    <property name="password" value="${password}"/>
    </dataSource>
    </environment>
    </environments>
    <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    </mappers>
    </configuration>
  • 如果项目为手动创建,注意设置文件目录类型

    | 设置目录类型 |

| :———————————————————-: |
| |

  • 配置pom.xml 添加插件【重要】

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <build>
    <resources>

    <resource>
    <!-- 所在目录 -->
    <directory>src/main/java</directory>
    <includes>
    <!-- 包括目录下的 下的 properties,xml 过滤 -->
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>false</filtering>
    </resource>
    <!-- resource 下的 properties,xml 过滤 -->
    <resource>
    <directory>src/main/resources</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>true</filtering>
    </resource>
    </resources>
    </build>
  • 注意使用的namespac、id、resultSet 等的值信息

  • mybatis 主配置文件中关于url 连接的参数问题等

    1
    2
    3
    4
    5
    5.1+:
    <property name="url" value="jdbc:mysql://localhost:3306/springdbautoReconnect=true&amp;useSSL=false"/>

    8.0+
    <property name="url" value="jdbc:mysql://localhost:3306/springdbuseUnicode=true&amp;serverTimezone=GMT&amp;useSSL=false"/>
  • 如果运行过程中文件位置不能正确解析,则可以进行项目重新构建、清理IDEA 缓存或执行mvn clean,mvn compile 等进行解决

  • sql 映射文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?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.coderitl.dao.StudentDao">
    <select id="selectStudents" resultType="com.coderitl.domain.Student">
    select userId, userName, email, userAge from Student
    </select>
    </mapper>

  • 主配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>

    <settings>
    <!-- 设置 mybatis 输出日志 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <environments default="development">
    <environment id="development">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url"
    value="jdbc:mysql://localhost:3306/springdb?autoReconnect=true&amp;useSSL=false"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
    </dataSource>
    </environment>
    </environments>
    <!-- 映射文件路径 -->
    <mappers>
    <mapper resource="com/coderitl/dao/StudentDao.xml"/>
    </mappers>
    </configuration>
  • entity

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    mysql> use springdb;
    Database changed
    mysql> desc student;
    +----------+-------------+------+-----+---------+-------+
    | Field | Type | Null | Key | Default | Extra |
    +----------+-------------+------+-----+---------+-------+
    | userId | int(11) | NO | PRI | NULL | |
    | userName | varchar(80) | NO | | NULL | |
    | email | varchar(20) | NO | | NULL | |
    | userAge | int(11) | YES | | 18 | |
    +----------+-------------+------+-----+---------+-------+
    4 rows in set (0.01 sec)


    public class Student {
    private Integer userId;
    private String userName;
    private String email;
    private Integer userAge;

    // 需提供为 JavaBean...
    }
  • dao 接口

    1
    2
    3
    4
    5
    6
    7
    8
    package com.coderitl.dao;

    import java.util.List;

    public interface StudentDao {
    public List<Student> selectStudents();
    }

  • 测试mybatis 连接,注意包资源问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    package com.coderitl.mybatis;


    import com.coderitl.domain.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 java.io.IOException;
    import java.io.InputStream;
    import java.util.List;

    public class DoMainAppFirst {
    public static void main(String[] args) throws IOException {
    // TODO: 访问 mybatis 读取student 数据

    // 1.定义 mybatis 主配置文件名称,从类路径的根路径开始(target/class)
    String config = "mybatis.xml";
    // 2. 读取这个 config 表示的文件
    InputStream in = Resources.getResourceAsStream(config);
    // 3. 创建 SqlSessionFactoryBuilder 对象
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    // 4. 创建SqlSessionFactory 对象
    SqlSessionFactory factory = builder.build(in);
    // 5. [重要] 获取SqlSession 对象,从 SqlSessionFactory 中获取sqlSession
    SqlSession sqlSession = factory.openSession();
    // 6. [重要] 指定要执行的 sql 语句的标识,sql 映射文件中的 namespace+"."+标签的id
    String sqlId = "com.coderitl.dao.StudentDao" + "." + "selectStudents";
    // 7. 执行sql 语句,通过 sqlId 找到语句
    List<Student> studentList = sqlSession.selectList(sqlId);
    // 8. 输出结果
    studentList.forEach(student -> System.out.println(student));
    // 9. 关闭 sqlSession 对象
    sqlSession.close();
    }
    }

  • 查询结果

    结果映射
    结果映射

主要类的介绍

  • 主要类

    Resources mybatis 中的一个类,负责读取主配置文件

    1
    InputStream in = Resources.getResourceAsStream("mybatis.xml");

    SqlSessionFactoryBuilder : 创建SqlSessionFactory 对象

    1
    2
    3
    4
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    // 创建
    SqlSessionFactory factory = builder.build(in);

    SqlSessionFactory 重量级对象,程序创建一个对象耗时比较长,使用资源比较多,在整个项目中,有一个就够用了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    SqlSessionFactory: 是接口,接口实现类为 DefaultSqlSessionFactory
    SqlSessionFactory 作用: 获取 SqlSession 对象。
    SqlSession sqlSession = factory.openSession();

    openSession()方法说明:
    1. openSession(): 无参数的,获取是非自动提交事物的SqlSession对象
    2. openSession(bollean autoCimmit):
    openSession(true): 获取自动提交事物的 SqlSession 对象
    openSession(false): 非自动提交事物的 SqlSession 对象

    SqlSession 接口: 定义了操作数据的方法,例如selectOne()、selectList()、insert()、update()、delete()、commit()、rollback()

    SqlSession 接口的实现类DefaultSqlSession

    使用要求: SqlSession 对象不是线程安全的,执行SqlSession.close() 这样能保证它的使用是线程安全的

    • 封装工具类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      package com.coderitl.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;

      import java.io.IOException;
      import java.io.InputStream;

      public class MybatisUtils {
      private static SqlSessionFactory factory = null;

      static {
      // 项目的 mybatis 主配置文件
      try {
      String config = "mybatis.xml";
      InputStream in = Resources.getResourceAsStream(config);
      // 创建SqlSessionFactory,使用 SqlSessionFactoryBuild
      factory = new SqlSessionFactoryBuilder().build(in);
      } catch (IOException e) {
      e.printStackTrace();
      }
      }

      // 获取SqlSession的方法
      public static SqlSession getSqlSession() {
      SqlSession sqlSession = null;
      if (factory != null) {
      // 自动提交事物
      sqlSession = factory.openSession(true);
      }
      return sqlSession;
      }
      }

    • 使用工具类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      // 测试工具类使用
      @Test
      public void testMybatisUtils() {
      // 获取 SqlSession 对象,从 SqlSessionFactory中获取SqlSession
      SqlSession sqlSession = MybatisUtils.getSqlSession();
      // 指定要执行的 sql 语句标识,sql 映射文件 namespace+"."+标签id的值
      // 6. [重要] 指定要执行的 sql 语句的标识,sql 映射文件中的 namespace+"."+标签的id
      String sqlId = "com.coderitl.dao.StudentDao" + "." + "selectStudents";
      // 7. 执行sql 语句,通过 sqlId 找到语句
      List<Student> studentList = sqlSession.selectList(sqlId);
      // 8. 输出结果
      studentList.forEach(student -> System.out.println(student));
      // 9. 关闭 sqlSession 对象
      sqlSession.close();
      }

    • 输出

      使用mybatis工具类

动态代理和参数

  • 动态代理

    List<Student> studentList = dao.selectStudents();

    1. dao 对象,类型是 studentDao,全限定名称是: com.coderitl.dao.StudentDao 全限定名称 和 namespace 是一样的

    2. 方法名称:selectStudents(传统dao 使用=> 接口和实现类),这个方法就是 mapper 文件中的 id selectStudents

    3. 通过 dao 中方法的返回值也可以确定 Mybatis 要调用的是 SqlSession 的方法
      如果返回值是 List,调用的是SqlSession.selectList() 方法
      如果返回值是 int 或者是List 的,mapper 文件中的标签是 insertupdate 就会调用SqlSession insertupdate 等方法

  • 是什么?

    mybatis 根据dao 的方法调用,获取执行sql 的语句信息,mybatis 根据你的dao 接口,创建出一个dao 接口的实现类,并创建这个类的对象,完成SqlSession 调用方法,访问数据库

  • 实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    package com.coderitl.mybatis;

    import com.coderitl.dao.StudentDao;
    import com.coderitl.entity.Student;
    import com.coderitl.utils.MybatisUtils;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.junit.jupiter.api.Test;

    import java.util.List;

    public class TestMybatis {
    @Test
    public void testProxySelect() {
    /**
    * 使用 mybatis 的动态代理机制,使用 SqlSession.getMapper(dao接口)
    * getMapper 能获取 dao 接口对于的实现类对象
    */
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    // 调用 dao 方法 执行数据库的操作
    List<Student> students = dao.selectStudents();
    for (Student stu : students) {
    System.out.println(stu);
    }
    sqlSession.close();
    }

    @Test
    public void testProxyInsert() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    // 调用 dao 方法 执行数据库的操作
    Student student = new Student(1003, "day02", "day02@qq.com", 2);
    int res = dao.insertStudent(student);
    if (res > 0) {
    System.out.println("数据添加成功!");
    } else {
    System.out.println("数据添加失败");
    }
    sqlSession.close();
    }


    }

  • 输出

    动态代理实现结果输出
    动态代理实现结果输出
  • 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    interface:
    /**
    * 一个简单类型的参数
    * 简单类型: mybatis 把 java 的基本数据类型和string都叫简单类型
    *
    * 在 mapper 文件中获取简单类型的一个参数的值,使用 #{任意字符}
    * @param userId
    * @return
    */
    public Student selectStudentById(Integer userId);
    1
    2
    3
    4
    5
    6

    <select id="selectStudentById" parameterType="java.lang.Integer" resultType="com.coderitl.entity.Student">
    select *
    from Student
    where userId = #{userId}
    </select>
    1
    2
    3
    4
    5
    6
    7
    8
    @Test
    public void testSelectStudentById(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentDao mapper = sqlSession.getMapper(StudentDao.class);
    Student student = mapper.selectStudentById(1001);
    System.out.println(student);
    sqlSession.close();
    }

    parameterTypedao 接口中方法参数的数据类型

    parameterType 它的值是Java 的数据类型全限定名称或者是mybatis 定义的别名

    1
    2
    parameterType="java.lang.Integer" 
    parameterType="int"

    注意: parameterType 不是强制的,mybatis 通过反射机制能够发现接口参数的数据类型,所以可以没有,一般不写

    1. 使用#{} 之后,mybatis 执行sql 是使用的jdbc 中的PrepareStatement 对象

      1
      2
      3
      String sql = "select * from Studnet where id=?";
      PrepareStatement pst = conn.prepareStatement(sql);
      pst.setInt(1,1003);
    2. 执行sql 封装为resultType=”com.coderitl.eneity.Student“对象

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      ResultSet rs = ps.executeQuery();
      Student student = null;
      while(rs.next()){
      // 从数据库中取表的一行数据,存放到一个 Java 对象属性中
      student = new Student();
      student.setUserId(rs.getUserId("userId"));
      ...
      }
      rerurn student; // 给了 dao 方法调用的返回值

      => Student student = mapper.selectStudentById(1001);

      日志分析
      日志分析
  • 映射文件

    1
    2
    3
    4
    5
    6
    7
    <!--  自定义参数名称使用 -->
    <select id="selectMuliParam" resultType="com.coderitl.entity.Student">
    select *
    from Student
    where userName = #{myname}
    or userAge = #{myage}
    </select>
  • 接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /**
    * 多参数: 命名参数,在形参的前面加入 @Param("自定义参数名称")
    *
    * @param userName
    * @param userAge
    * @return
    */
    List<Student> selectMuliParam(@Param("myname") String userName, @Param("myage") Integer userAge);

  • 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Test
    public void testSelectMuliParam() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentDao mapper = sqlSession.getMapper(StudentDao.class);
    List<Student> students = mapper.selectMuliParam("coder-itl", 1001);
    for (Student stu : students) {
    System.out.println(stu);
    }
    sqlSession.close();
    }
  • 输出

    SelectMuliParam
    SelectMuliParam
  • 定义对象

    1
    2
    3
    4
    5
    public class QueryParam {
    private String paramName;
    private Integer paramAge;
    // JavaBean...
    }
  • 定义接口

    1
    2
    3
    4
    5
    6
    7
    /**
    * 多个参数: 使用 java 对象作为接口中方法的参数
    *
    * @param param
    * @return
    */
    List<Student> selectMuliObject(QueryParam param);
  • 映射文件sql

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!--
    多个参数: 使用 java 对象的属性值,作为参数的的实际值
    使用对象语法: #{属性名,javaType=类型名称(java.lang.String),jdbcType=varchar} 该方法不常用
    javaType: 指的是 java 中的属性的数据类型
    jdbcType: 指的是 在数据库中的数据类型

    常用: #{属性名} javaType、jdbcType 的值 mybatis 反射能获取
    -->
    <select id="selectMuliObject" resultType="com.coderitl.entity.Student">
    select *
    from Student
    where userName = #{paramName}
    or userAge = #{paramAge}
    </select>
  • 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Test
    public void testSelectMuliObject() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentDao mapper = sqlSession.getMapper(StudentDao.class);

    QueryParam param = new QueryParam("coder-itl", 4);

    List<Student> students = mapper.selectMuliObject(param);
    for (Student stu : students) {
    System.out.println(stu);
    }
    sqlSession.close();
    }
  • 输出

    对象参数使用
    对象参数使用
  • #占位符

    告诉mybatis 使用实际的参数值代替,并使用PrepareStatement 对象执行sql 语句,#{} 代替sql 语句的? 这样做更安全,通常也是首选方法

  • $ 字符串替换

    告诉mybatis 使用$ 包含的字符串替换所在位置.使用Statement sql 欲绝的${} 的内容连接起来,主要是用在替换表名、列名、不同列排序等操作

  • 复现

    • 接口

      1
      2
      3
      4
      public Student selectStudentById(Integer userId);

      public Student selectStudentById_$(@Param("userId") Integer userId);

    • 映射文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      #(占位符)

      <select id="selectStudentById" parameterType="java.lang.Integer" resultType="com.coderitl.entity.Student">
      select *
      from Student
      where userId = #{userId}
      </select>


      $(字符串拼接):

      <select id="selectStudentById_$" parameterType="java.lang.Integer" resultType="com.coderitl.entity.Student">
      select *
      from Student
      where userId = ${userId}
      </select>
    • 测试

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #:
      @Test
      public void testSelectStudentById() {
      SqlSession sqlSession = MybatisUtils.getSqlSession();
      StudentDao mapper = sqlSession.getMapper(StudentDao.class);
      // ==> Preparing: select * from Student where userId = ?
      Student student = mapper.selectStudentById(1001);
      System.out.println(student);
      sqlSession.close();
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      $:
      @Test
      public void testSelectStudentById_$() {
      SqlSession sqlSession = MybatisUtils.getSqlSession();
      StudentDao mapper = sqlSession.getMapper(StudentDao.class);
      // Preparing: select * from Student where userId = 1001
      Student student = mapper.selectStudentById_$(1001);
      System.out.println(student);
      sqlSession.close();
      }

    • 输出

      1
      2
      3
      #: Preparing: select * from Student where userId = ? # 占位符

      $: Preparing: select * from Student where userId = 1001 # 字符串替换

Mybatis获取参数值的各种情况

  • mapper 接口方法的参数为单个的字面量类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!--
    可以通过 ${} 和 #{} 以任意的名称获取参数值,但是需要注意 ${} 的单引号问题
    -->
    <select id="getUserByUserName" resultType="com.coderitl.domain.User" parameterType="string">
    select *
    from user
    where userName = #{username}
    </select>


    <select id="getUserByUserName" resultType="com.coderitl.domain.User" parameterType="string">
    select *
    from user
    where userName = '${username}'
    </select>

    #{} ${} => 单引号包裹
  • mapper 接口方法的参数为多个时

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!--
    此时 mybatis 会将这些参数放在一个 map 集合中,以两种方式进行存储
    1. 以 arg0,arg1 为键,以参数为值
    2. 以 param1,param2 为键,以参数为值
    因此只需要通过 #{} 和 ${} 以键的方式访问值即可,但是需要注意 ${} 的单引号问题
    -->

    <!-- User checkLoginByMap(Map<String,Object> map); -->
    <select id="checkLoginByMap" resultType="user">
    select *
    from user
    where userName = #{username}
    and password = #{password}
    </select>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 测试 map 参数
    @Test
    public void testArgsByMap() {
    // 获取 SqlSession 对象,从 SqlSessionFactory中获取SqlSession
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    // 获取接口的字节码文件
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String,Object> map = new HashMap<>();
    map.put("username","root");
    map.put("password","root");
    // 调用接口方法
    User user = mapper.checkLoginByMap(map);
    System.out.println("user = " + user);

    }
    map 参数
  • mapper 接口方法的参数是实体类类型

    1
    2
    3
    4
    <!--  int insertUser(User user); 通过访问实体类属性值即可  -->
    <insert id="insertUser" parameterType="user">
    insert into user (userName, password) values (#{userName}, #{password});
    </insert>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 测试 实体类 参数
    @Test
    public void testArgsByEntity() {
    // 获取接口的字节码文件
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User("entity", "entity");
    // 调用接口方法
    int userInfo = mapper.insertUser(user);
    System.out.println("user = " + userInfo);
    }
    实体类参数
  • 命名参数

    1
    2
    3
    4
    5
    6
    7
    <!-- User checkLoginByParam(@Param("userName") String username,@Param("password") String password); -->
    <select id="checkLoginByParam" resultType="user">
    select *
    from user
    where userName = #{userName}
    and password = #{password}
    </select>
    @Param("userName")

Mybatis的各种非查询功能

  • 查询一个实体类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!--
    1. 若查询的数据只有一条,可以通过实体类对象或者集合接受
    2. 若查询出的结果数据有多条,可以通过List集合接受,一定不能通过实体类对象接受,此时会抛出异常 TooManyResultException

    dao 接口的数据类型,而不是 resultType
    -->

    <!-- User getUserById(@Param("id") int id); -->
    <select id="getUserById" resultType="user">
    select * from user where id=#{id}
    </select>

  • 单行单列查询

    1
    2
    3
    4
    5
    <!--  Integer getUserCount() -->
    <select id="getUserCount" resultType="integer">
    select count(*)
    from user
    </select>
  • Map*

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!-- 根据用户 id 查询用户信息为一个 map 集合 -->

    <!-- Map<String, Object> getUserByIdToMap(@Param("id") int id) -->
    <select id="getUserByIdToMap" resultType="map">
    select *
    from user
    where id = #{id}
    </select>

    1
    2
    3
    4
    5
    6
    7
    @Test
    public void testGetUserByIdToMap() {
    // 获取接口的字节码文件
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    // 调用接口方法
    System.out.println(mapper.getUserByIdToMap(1));
    }
    返回结果为Map
  • 所有数据转换为Map

    1
    2
    3
    4
    5
    <!--  List<Map<String, Object>> getAllUserToMap(); -->
    <select id="getAllUserToMap" resultType="map">
    select *
    from user
    </select>
    1
    2
    3
    4
    5
    6
    7
    8
    @Test
    public void testGetAllUserByIdToMap() {
    // 获取接口的字节码文件
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<Map<String, Object>> allUserToMap = mapper.getAllUserToMap();
    System.out.println("allUserToMap = " + allUserToMap);
    }

    List<Map<String, Object>> getAllUserToMap();
    1
    2
    3
    4
    5
    // 如果不是用 List> ,可以使用注解 @MapKey(key) 添加在借口方法上,key需要具有唯一标识(不能重复)

    @MapKey("id")
    Map<String, Object> getAllUserToMap();

    @MapKey(key)

特殊的SQL执行

  • 根据用户名模糊查询用户信息

    1
    2
    3
    4
    5
    6
    7
    <!-- List<User> getUserByLike(@Param("username") String username); -->
    <select id="getUserByLike" resultType="user">
    select *
    from user
    where userName like '%${username}%'
    </select>

    '%${username}%' resultType 不匹配出现的异常
    1
    2
    3
    4
    5
    6

    <select id="getUserByLike" resultType="user">
    select *
    from user
    where userName like concat('%', #{username}, '%')
    </select>
    concat('%', #{username}, '%')
    1
    2
    3
    4
    5
    6

    <select id="getUserByLike" resultType="user">
    select *
    from user
    where userName like "%"#{username}"%"
    </select>
    单引号匹配的结果非正常内容信息 双引号为正常解析内容
  • 批量删除

    1
    2
    3
    4
    5
    6
    <!-- int deleteAll(String ids); -->
    <delete id="deleteAll">
    delete
    from user
    where id in (${ids})
    </delete>
    1
    2
    3
    4
    5
    6
    7
    @Test
    public void testDeleteAll() {
    // 获取接口的字节码文件
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int deleteAll = mapper.deleteAll("6,7,8");
    System.out.println("deleteAll = " + deleteAll);
    }
    SQL 条件只能使用 ${ },因为#{} 会添加单引号
  • 动态设置表名(只能使用${ })

    1
    2
    3
    4
    5
    <!-- List<User> getUserByTableName(@Param("tableName") String tableName); -->
    <select id="getUserByTableName" resultType="user">
    select *
    from ${tableName}
    </select>
  • 添加功能获取自增的主键

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!--
    void insertUserInfo(User user); 添加之后,获取自增主键
    useGeneratedKeys="true" 表示标签中的 SQL 使用了自增主键
    keyProperty="id" 获取的属性放在了传输过来的对象的 id 上
    -->
    <insert id="insertUserInfo" useGeneratedKeys="true" keyProperty="id">
    insert into user (userName, password)
    values (#{userName}, #{password});
    </insert>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Test
    public void testInsertUserInfo() {
    // 获取接口的字节码文件
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User("user", "user");
    // 输出信息包含主键
    mapper.insertUserInfo(user);
    System.out.println(user);
    }
    输出主键

自定义映射ResultMap

  • ResultMap 处理字段和属性的映射关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    -- <!-- 若字段名和实体类中的属性名不一致,则可以通过 ResultMap 设置自定义映射  -->

    create table t_emp
    (
    eid int not null primary key auto_increment comment '员工id',
    emp_name varchar(20),
    age int,
    sex char,
    email varchar(20)
    );
    -- 添加字段 did 关联
    alter table t_emp add column did int;

    -- 添加测试数据
    insert into t_emp(emp_name, age, sex, email, did) values ('root1',18,'男','123@qq.com',1);
    insert into t_emp(emp_name, age, sex, email, did) values ('root2',28,'男','123@qq.com',2);
    insert into t_emp(emp_name, age, sex, email, did) values ('root3',38,'男','123@qq.com',3);
    insert into t_emp(emp_name, age, sex, email, did) values ('root4',48,'男','123@qq.com',1);
    insert into t_emp(emp_name, age, sex, email, did) values ('root5',58,'男','123@qq.com',2);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    -- 部门表
    create table t_dept
    (
    did int not null primary key auto_increment,
    dept_name varchar(20)
    );

    -- 测试数据
    insert into t_dept(dept_name) values('A');
    insert into t_dept(dept_name) values('B');
    insert into t_dept(dept_name) values('C');

  • 解决mysql 属性与实体类属性不一致问题

    1
    // TODO: mysql: emp_name entity: empName
    • 解决方法一起别名

      1
      2
      3
      4
      5

      <select id="getAllEmp" resultType="emp">
      select *, emp_name as empName
      from t_emp
      </select>
    • 全局配置下划线自动映射为驼峰

      1
      2
      3
      4
      5

      <settings>
      <!-- 将下划线自动映射为驼峰 -->
      <setting name="mapUnderscoreToCamelCase" value="true"/>
      </settings>
    • 方式三:ResultMap

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      <!--
      id="empResultMap" => 唯一标识
      type="emp" => 要处理的实体类
      -->
      <resultMap id="empResultMap" type="emp">
      <!-- 主键的映射关系 -->
      <id property="eid" column="eid"/>
      <!--
      使用了 resultMap 尽可能设置所有属性、无论匹配与否
      property: 实体类对应的属性名
      column: 数据库对应的属性名
      -->
      <result property="empName" column="emp_name"/>
      <result property="age" column="age"/>
      <result property="sex" column="sex"/>
      <result property="email" column="email"/>
      </resultMap>

      <!-- List<Emp> getAllEmp(); -->
      <select id="getAllEmp" resultType="emp" resultMap="empResultMap">
      select *
      from t_emp
      </select>
      1
      2
      3
      4
      5
      6
      <resultMap>
      <!-- 处理多对一的映射关系 -->
      <association property=""></association>
      <!-- 处理一多对的映射关系 -->
      <collection property=""></collection>
      </resultMap>
      ResultMap
  • 多对一映射 => 多的实体类里面创建一的对象 ->

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @ToString
    public class Emp implements Serializable {
    private int eid;
    private String empName;
    private int age;
    private String sex;
    private String email;
    private int did;
    // 多的实体类里面创建一的对象
    private Dept dept;
    }


    • 方式一级联映射

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      <resultMap id="getEmpAndDeptResultMap" type="Emp">
      <id property="eid" column="eid"/>
      <result property="empName" column="emp_name"/>
      <result property="age" column="age"/>
      <result property="sex" column="sex"/>
      <result property="email" column="email"/>
      <!-- dept: 级联映射 -->
      <result property="dept.did" column="did"/>
      <result property="dept.deptName" column="dept_name"/>
      </resultMap>

      <select id="getEmpAndDept" resultMap="getEmpAndDeptResultMap">
      select *
      from t_emp
      left join t_dept te on t_emp.eid = te.did
      where eid = #{eid};
      </select>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      /**
      * 查询员工信息以及对应的部门信息
      */
      @Test
      public void testGetEmpAndDept() {
      // 方式一: 级联映射解决
      Emp empAndDept = mapper.getEmpAndDept(1);
      System.out.println("empAndDept = " + empAndDept);
      }
      级联属性赋值
    • 方式二association

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      <!--
      association: 处理多对一的映射关系
      <association property="dept" javaType="Dept">
      property="dept" 需要处理多对一的映射关系的映射名 => private Dept dept;
      javaType="Dept" 该属性的类型(实体类名)
      -->

      <resultMap id="getEmpAndDeptResultMap" type="Emp">
      <id property="eid" column="eid"/>
      <result property="empName" column="emp_name"/>
      <result property="age" column="age"/>
      <result property="sex" column="sex"/>
      <result property="email" column="email"/>
      <!-- dept: association 处理多对一映射 -->
      <association property="dept" javaType="Dept">
      <id property="did" column="did"/>
      <result property="deptName" column="dept_name"/>
      </association>
      </resultMap>

      <select id="getEmpAndDept" resultMap="getEmpAndDeptResultMap">
      select *
      from t_emp
      left join t_dept te on t_emp.eid = te.did
      where eid = #{eid};
      </select>

    • 方式三分步查询 *推荐*

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      <!-- EmpMapper.xml -->
      <!--
      <association property="dept"
      column="did"
      select="com.coderitl.mapper.DeptMapper.getEmpAndDeptByStepTwo"/>
      property="dept": 需要处理多对一的映射关系的映射名 => private Dept dept;
      column="did": 设置分布查询的条件
      select="com.coderitl.mapper.DeptMapper.getEmpAndDeptByStepTwo": 设置分步查询的 sql 的唯一标识(namespace.sqlId)
      -->

      <resultMap id="getEmpAndDeptByStepOneResultMap" type="emp">
      <id property="eid" column="eid"/>
      <result property="empName" column="emp_name"/>
      <result property="age" column="age"/>
      <result property="sex" column="sex"/>
      <result property="email" column="email"/>

      <association property="dept"
      column="did"
      select="com.coderitl.mapper.DeptMapper.getEmpAndDeptByStepTwo"/>
      </resultMap>
      <!-- 通过分步查询员工以及员工所对应的部门信息: Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid); -->
      <select id="getEmpAndDeptByStepOne" resultType="emp" resultMap="getEmpAndDeptByStepOneResultMap">
      select *
      from t_emp
      where eid = #{eid}
      </select>
      1
      2
      3
      4
      5
      6
      <!-- Dept getEmpAndDeptByStepTwo(@Param("did") Integer did); -->
      <select id="getEmpAndDeptByStepTwo" resultType="dept">
      select *
      from t_dept
      where did = #{did}
      </select>
      1
      2
      3
      4
      5
      @Test
      public void testGetEmpAndDeptByStepOne_Two() {
      Emp empAndDeptByStepOne = mapper.getEmpAndDeptByStepOne(1);
      System.out.println("empAndDeptByStepOne = " + empAndDeptByStepOne);
      }
      分步查询
    • 推荐原因

      • 分布查询的优点

        可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息

        lazyLoadingEnabled: 延迟加载的全局开关,当开启时,所有关联对象都会延迟加载(需要开启。两个默认都是 false)

        aggressiveLazyLoading: 当开启时,任何方法的调用都会加载对象的所有属性,否则,每个属性会按需加载(需要关闭)

        此时就可以实现按需加载,获取的数据是什么,就会执行相应的sql,此时可以通过association collection 中的fetchType 属性设置当前的分布查询是否使用延迟加载,fetchType=lazy(延迟加载)|eage(立即加载)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <!-- 主配置文件内配置 -->
      <settings>
      <!-- 开启延迟加载 -->
      <setting name="lazyLoadingEnabled" value="true"/>
      </settings>


      <association property="dept"
      column="did"
      fetchType="eager"
      select="com.coderitl.mapper.DeptMapper.getEmpAndDeptByStepTwo"/>
      延迟加载的效果
  • 一对多的映射关系

    • collection

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @ToString
      public class Dept implements Serializable {

      private int did;
      private String deptName;

      private List<Emp> emps;
      }

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      <resultMap id="deptAndEmpResultMap" type="Dept">
      <id property="did" column="did"/>
      <result property="deptName" column="dept_name"/>
      <collection property="emps" ofType="emp">
      <id property="eid" column="eid"/>
      <result property="empName" column="emp_name"/>
      <result property="age" column="age"/>
      <result property="sex" column="sex"/>
      <result property="email" column="email"/>
      </collection>
      </resultMap>
      <!-- Dept getDeptAndEmp(@Param("did") Integer did); -->
      <select id="getDeptAndEmp" resultMap="deptAndEmpResultMap">
      -- 获取部门以及部门中所有的员工信息
      select te.eid,
      te.emp_name,
      te.age,
      te.sex,
      te.email,
      te.did,
      dept_name
      from t_dept
      left join t_emp te on t_dept.did = te.did
      where te.did = #{did};
      </select>
      collection 处理一对多映射
    • 通过分布查询实现

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      <resultMap id="getDeptAndEmpStepOneResultMap" type="dept">
      <id property="did" column="did"/>
      <result property="deptName" column="dept_name"/>
      <collection property="emps"
      column="did"
      select="com.coderitl.mapper.EmpMapper.getDeptAndEmpByStepTwo"/>
      </resultMap>
      <!-- Dept getDeptAndEmpStepOne(@Param("did") Integer did); -->
      <select id="getDeptAndEmpStepOne" resultMap="getDeptAndEmpStepOneResultMap">
      select *
      from t_dept
      where did = #{did}
      </select>

      1
      2
      3
      4
      5
      6
      <!-- Emp getDeptAndEmpByStepTwo(@Param("did") Integer did); -->
      <select id="getDeptAndEmpByStepTwo" resultType="emp">
      select *
      from t_emp
      where eid = #{did}
      </select>
      分步查询

动态SQL

  • 注意

    mybatis 主配置文件中关于url 的连接参数中需要添加characterEncoding=UTF-8,否则中文内容无法查出结果信息,后续数据库配置中会列出完整配置 注意实体符号使用 Eg: > is &gt;

  • 接口

    1
    public List<Student> selectLike(@Param("userName") String userName);
  • 映射文件

    1
    2
    3
    4
    5
    <select id="selectLike" resultType="com.coderitl.entity.Student">
    select *
    from Student
    where userName like #{userName}
    </select>
  • 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Test
    public void testSelectLike() {
    /**
    * 使用 mybatis 的动态代理机制,使用 SqlSession.getMapper(dao接口)
    * getMapper 能获取 dao 接口对于的实现类对象
    */
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    // 调用 dao 方法 执行数据库的操作
    String userName = "%%";
    List<Student> students = dao.selectLike(userName);

    for (Student student : students) {
    System.out.println(student);
    }
    sqlSession.close();

    }
  • 输出

    url 参数影响查询结果
    url参数影响查询结果
  • 动态sql

    同一个dao 的方法,根据不同的条件可以表示不同的sql 语句,主要是where 部分的变化

  • 语法

    test: 使用对象的属性值作为条件

  • 接口

    1
    public List<Student> selectIf(Student student);
  • 映射

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <select id="selectIf" resultType="com.coderitl.entity.Student">
    select *
    from Student
    where
    <if test="userName!=null and userName!='' ">
    userName = #{userName}
    </if>

    </select>
  • 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Test
    public void testSelectIf() {
    /**
    * 使用 mybatis 的动态代理机制,使用 SqlSession.getMapper(dao接口)
    * getMapper 能获取 dao 接口对于的实现类对象
    */
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    // 调用 dao 方法 执行数据库的操作
    Student student = new Student();
    student.setUserName("coder-itl");
    List<Student> students = dao.selectIf(student);

    for (Student stu : students) {
    System.out.println(stu);
    }
    sqlSession.close();

    }
  • 语法

    1
    2
    3
    <where>
    ... 可以有多个 if
    </where>
  • 映射

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <select id="selectWhere" resultType="com.coderitl.entity.Student">
    select *
    from Student
    <where>
    <if test="userName!=null and userName!='' ">
    userName = #{userName}
    </if>
    </where>
    </select>
  • 语法

    分析
    分析
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <foreach collection="集合类型" 
    open="开始的字符"
    close="结束的字符"
    item="集合中的成员"
    separator="集合成员之间的分隔符">
    #{item 的值}
    </foreach>

    <!--
    collection: 表示循环的对象是数组,还是 list 集合.如果 dao 接口方法的形参是数组,collection="array",如果 dao接口形参是 List,collection="list"

    open: 循环开始时的字符 sql.append("(")
    close: 循环结束时的字符 sql.append(")")
    item: 集合成员,自定义的变量 Interger item = listInfo.get(i) // item 是集合成员
    separator: 集合成员之间的分隔符 sql.append(".")
    #{item 的值}: 获取集合成员的值
    -->
  • 接口

    1
    public List<Student> selectForeach1(List<Integer> listId);
  • 映射

    1
    2
    3
    4
    5
    6
    7
    8
    <select id="selectForeach1" resultType="com.coderitl.entity.Student">
    select *
    from Student
    where userId in
    <foreach collection="list" open="(" close=")" separator="," item="myId">
    #{myId}
    </foreach>
    </select>
  • 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Test
    public void testSelectForeach1() {
    /**
    * 使用 mybatis 的动态代理机制,使用 SqlSession.getMapper(dao接口)
    * getMapper 能获取 dao 接口对于的实现类对象
    */
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    List<Integer> listInfo = new ArrayList<>();
    // 实现 => (1001,1004)
    listInfo.add(1001);
    listInfo.add(1004);
    List<Student> students = dao.selectForeach1(listInfo);
    for (Student student : students) {
    System.out.println(student);
    }
    sqlSession.close();

    }
  • 输出

    小括号的内容封装在list里面
    小括号的内容封装在list里面
  • 接口

    1
    public List<Student> selectForeach2(List<Student> studentList);
  • 映射

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!--
    循环的 List<Student>
    -->
    <select id="selectForeach2" resultType="com.coderitl.entity.Student">
    select *
    from Student
    <if test="list !=null and list.size>0">
    where userId in
    <foreach collection="list" open="(" close=")" separator="," item="stu">
    #{stu.userId}
    </foreach>
    </if>
    </select>
  • 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Test
    public void testSelectForeach2() {
    /**
    * 使用 mybatis 的动态代理机制,使用 SqlSession.getMapper(dao接口)
    * getMapper 能获取 dao 接口对于的实现类对象
    */
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    List<Student> list = new ArrayList<>();
    Student student1 = new Student();
    student1.setUserId(1001);
    Student student2 = new Student();
    student2.setUserId(1004);

    list.add(student1);
    list.add(student2);

    List<Student> stu = dao.selectForeach2(list);
    stu.forEach(student -> System.out.println(student));
    sqlSession.close();

    }
  • 是什么?

    sql 标签是一段代码,可以是表名,几个字段,where 条件都可以,可以在其他地方复用的sql 标签内容

  • 接口

    1
    public List<Student> testSql();
  • 映射文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 定义
    <sql id="studentFileList">
    userId,userName,email,userAge
    </sql>

    // 使用
    <select id="testSql" resultType="stu">
    select
    <include refid="studentFileList"/>
    from Student
    </select>
  • 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Test
    public void testSql() {
    /**
    * 使用 mybatis 的动态代理机制,使用 SqlSession.getMapper(dao接口)
    * getMapper 能获取 dao 接口对于的实现类对象
    */
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    List<Student> students = dao.testSql();
    for (Student student : students) {
    System.out.println(student);
    }
    sqlSession.close();


    }

  • 注意idea 会报错关于 sql标签,按下快捷键alt + Enter 找到sql|select|insert|update|delete 删除sql 即可(解决之后不能复现错误,只记得大概)

  • 输出

    sql 标签
    sql标签

数据库配置

  • mybatis 主配置文件添加日志配置(方式一: 配置简单)

    1
    2
    3
    4
    5
    <!-- settings: 控制 mybatis 全局行为 -->
    <settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

  • 外部文件配置方式二: 内容更加清晰

    • 添加maven 依赖

      1
      2
      3
      4
      5
      <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
      </dependency>
    • resources 目录下,新建文件log4j.properties

    • 填充以下内容

      1
      2
      3
      4
      5
      6
      7
      8
      # Global logging configuration
      log4j.rootLogger=ERROR, stdout
      # MyBatis logging configuration...
      log4j.logger.org.mybatis.example.BlogMapper=TRACE
      # Console output...
      log4j.appender.stdout=org.apache.log4j.ConsoleAppender
      log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
      log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!--
第一种语法格式:
type: java类型的全限定名称(自定义类型)
alias: 自定义别名

优点: 别名可以自己定义
缺点: 每个类型必须单独定义
-->

<typeAliases>
<typeAlias type="com.coderitl.entity.Student" alias="stu"/>
</typeAliases>


<!--
第二种方式(推荐):
name: 包名 mybatis 会把这个包中所有的类名作为别名(不用区分大小写)
优点: 使用方便,一次多个类定义
缺点: 别名不能自己定义 必须是类名
-->
<typeAliases>
<package name="com.coderitl.entity"/>
</typeAliases>

需要把数据库的配置信息放到一个单独文件中,独立管理,这个文件扩展名是properties,在这个文件中,使用自定义的key=value 的格式表示数据

  • 使用步骤

    1. resources 目录中,创建xxx.properties

    2. 在文件中,使用key=value 的格式定义数据

      1
      2
      3
      4
      5
      6
      jdbc.driver=com.mysql.jdbc.Driver

      jdbc.url=jdbc:mysql://localhost:3306/database-name?autoReconnect=true&amp;useSSL=false&amp;characterEncoding=UTF-8

      jdbc.username=root
      jdbc.password=root
    3. mybatis 主配置文件,使用property 标签引用外部的属性配置文件

      1
      2
      3
      4
      5
      6
      7
      <!--
      使用外部属性配置文件
      resource: 指定类路径下的某个属性配置文件

      注意书写位置:
      -->
      <properties resource="jdbc.properties"/>
    4. 在使用值的位置,使用${key} 获取对应的value(等号右侧的值)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <environments default="development">
      <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
      <property name="driver" value="${jdbc.driver}"/>
      <property name="url"
      value="${jdbc.url}"/>
      <property name="username" value="${jdbc.username}"/>
      <property name="password" value="${jdbc.password}"/>
      </dataSource>
      </environment>
      </environments>
  • 配置方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!--
    第一种方式: resources="mapper文件的路径"
    优点: 文件清晰,加载文件是明确的 文件的位置比较灵活
    缺点: 文件比较多,代码量会比较大,管理难度大
    -->

    <mapper resource="com/coderitl/dao/StudentDao.xml" />

    <!--
    第二种方式: package"(推荐)
    name: mapper文件所在的包名(如果在 resources 路径下,需要创建出与 dao|mapper 接口所在包路径一致,创建方式: resources: Eg: com/coderitl/,apper)
    特点: 把这个包中所有的 mapper 文件,一次加载
    -->
    <package name="com.coderitl.dao" />


    <!-- 注解配置 -->
    <mapper class="" />

    package 细节
  • 注解使用时机

    1
    SQL语句比较简单的时候,使用注解绑定就可以了,当SQL语句比较复杂的话,使用xml方式绑定,一般用xml方式绑定比较多
  • MyBatis 接口绑定的几种方式

    • 使用注解,在接口的方法上面添加@Select @Update 等注解,里面写上对应的SQL 语句进行SQL 语句的绑定。
    • 通过映射文件xml 方式进行绑定,指定xml 映射文件中的namespace 对应的接口的全路径名
  • 主配置文件完整配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    <!--
    使用外部属性配置文件
    resource: 指定类路径下的某个属性配置文件
    -->
    <properties resource="jdbc.properties"/>


    <settings>
    <!-- 设置 mybatis 输出日期 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!-- 别名 -->
    <typeAliases>
    <package name=""/>
    </typeAliases>

    <!-- 连接 -->
    <environments default="development">
    <environment id="development">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
    <property name="driver" value="${jdbc.driver}"/>
    <property name="url"
    value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
    </dataSource>
    </environment>
    </environments>

    <!-- 两种方式 选择其一 -->
    <mappers>
    <package name="com.coderitl.dao" />
    </mappers>
    </configuration>
  • 映射文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?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="">


    </mapper>

分页PageHelper

  • 分页

    PageHelper 做数据分页,在你的select 语句后面加入分页的sql 内容,如果使用的是mysql 数据库,select * from student 后面加入limit 语句

  1. 加入pagehelper 依赖

    1
    2
    3
    4
    5
    6
    <!--  PageHelper -->
    <dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.10</version>
    </dependency>
  2. mybatis 主配置文件,加入plugin 声明

    1
    2
    3
    4

    <plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
    </plugins>
  3. select 语句之前,调用PageHelper.startPage(页码,每页大小)

  • 数据准备

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    MySQL [springdb]> select * from Student;
    +--------+-----------+-----------------+---------+
    | userId | userName | email | userAge |
    +--------+-----------+-----------------+---------+
    | 1001 | coder-itl | coderitl@qq.com | 18 |
    | 1002 | mybatis | mybatis@qq.com | 4 |
    | 1003 | day02 | day02@qq.com | 2 |
    | 1004 | day02 | day02@qq.com | 2 |
    | 1005 | 张三 | zhangsan@qq.com | 12 |
    | 1006 | 1006 | 1006@qq.com | 12 |
    | 1007 | 1007 | 1007@qq.com | 12 |
    | 1008 | 1008 | 1008@qq.com | 12 |
    | 1009 | 1009 | 1009@qq.com | 12 |
    | 1010 | 1010 | 1010@qq.com | 12 |
    | 1011 | 1011 | 1011@qq.com | 12 |
    +--------+-----------+-----------------+---------+
  • 接口

    1
    2
    // PageHelper
    public List<Student> selectLimitPage();
  • 映射

    1
    2
    3
    4
    5
    <!-- 使用 PageHelper -->
    <select id="selectLimitPage" resultType="stu">
    select *
    from Student
    </select>
  • 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Test
    public void testPageHelper() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentDao mapper = sqlSession.getMapper(StudentDao.class);

    // 调用 PageHelper 的方法
    PageHelper.startPage(2, 3);
    List<Student> students = mapper.selectLimitPage();
    students.forEach(stu -> System.out.println(stu));
    sqlSession.close();
    }
  • 输出

    PageHelper
    PageHelper

缓存

  • 缓存验证

    缓存验证
    缓存验证
  • 一级缓存

    一级缓存也叫做sqlSession 级别缓存,为每个sqlSession 单独分配的缓存内存,无需动手开启可直接使用,多个sqlSession 的缓存时不共享的

    • 特性

      1. 如果多次查询使用的是同一个sqlSession 对象,则第一次查询后数据会存放到缓存,后续的查询则直接访问缓存中存储的数据

        同一个sqlSession 对象
      2. 如果第一次查询完成后,对查询出的对象进行修改(此修改会影响到缓存),第二次查询会直接访问缓存,造成第二次查询的结果与数据库据不一致

      3. 当我们进行在查询时想要跳过缓存直接查询数据库,则可以通过sqlSession.clearCache(),来清除当前sqlSession 缓存

      4. 如果第一次查询之后第二次查询之前,使用当前的sqlSession 执行了修改操作,此修改操作会使第一次查询并缓存的数据失效,因此第二次查询会再次访问数据库

    • 一级缓存失效的情况

      • 不同的SqlSession 对应不同的一级缓存
      • 同一个SqlSession 但是查询条件不同
      • 同一个SqlSession 两次查询期间执行了任何一次增删改操作
      • 同一个SqlSession 两次查询期间手动清空了缓存
  • 二级缓存

    二级缓存是SqlSessionFacaory 级别的,通过同一个SqlSessionFactory 创建的SqlSession 查询的结果会被缓存,此后若再次执行相同的查询语句,结果会从缓存中获取

    • 二级缓存开启的条件

      • 在核心配置文件中,设置全局配置属性cacheEnabled=true,默认为true,不需要设置

      • 在映射文件中设置标签<cache/>

        设置标签<cache/>
        在这里插入图片描述
      • 二级缓存必须在SqlSession 关闭或提交之后有效

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        // 缓存命中率 0.0
        Cache Hit Ratio [com.coderitl.mapper.EmpMapper]: 0.0

        // SqlSession 提交
        @Test
        public void testCacheTwo() {
        try {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
        EmpMapper mapper1 = sqlSession1.getMapper(EmpMapper.class);
        Emp empbyEid1 = mapper1.getEmpbyEid(1);
        System.out.println("empbyEid1 = " + empbyEid1);
        sqlSession1.commit();

        SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
        EmpMapper mapper2 = sqlSession2.getMapper(EmpMapper.class);
        Emp empbyEid2 = mapper1.getEmpbyEid(1);
        System.out.println("empbyEid2 = " + empbyEid2);
        sqlSession2.commit();

        } catch (IOException e) {
        throw new RuntimeException(e);
        }

        | 未生效: 必须在 SqlSession 关闭或提交之后有效 | 关闭SqlSession |

      | :———————————————————-: | :———————————————————-: |
      | | |

      • 查询的数据转换的实体类类型必须实现序列化接口

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        @ToString
        public class Emp implements Serializable {
        private int eid;
        private String empName;
        private int age;
        private String sex;
        private String email;
        private int did;

        private Dept dept;
        }

    • 二级缓存失效的情况

      两次查询之间执行了任意的增删改,会使一级二级缓存同时失效

  • 二级缓存的相关配置

    mapper 配置文件中添加的cache 标签可以设置一些属性

    • eviction 属性: 缓存回收策略

      • LRU: 最近最少使用: 移除最长时间不被使用的对象
    • FIFO: 先进先出: 按对象进入缓存的顺序来移除他们

      • SOFT: 软引用: 移除基于垃圾回收器状态和软引用规则的对象
    • WEAK: 弱引用: 更积极地移除基于垃圾回收器状态和弱引用规则的对象

    • flushInterval:刷新间隔,单位毫秒

      默认情况下不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

    • size:引用数目,正整数

      代表缓存最多可以存储多少个对象,太大容易导致内存溢出

    • readOnly:只读:true/false

    • true:只读缓存,会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改,这提供了很重要的性能优势

      • false: 读写缓存,会返回缓存对象的拷贝(通过序列化),这会慢一些,但是安全,因此默认为false
  • Mybatis 缓存查询的顺序

    • 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来数据,可以拿来直接使用
    • 如果二级缓存没有命中,在查询一级缓存
    • 如果一级缓存也没有命中,则查询数据库
    • SqlSession 关闭之后,一级缓存中的数据会写入二级缓存

整合第三方缓存

  • 添加依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.2</version>
    </dependency>
    <!-- slf4j 日志门面的一个具体实现 -->
    <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.11</version>
    </dependency>

  • 创建配置文件(固定名称 ehcache.xml)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    <!-- resources/ehcache.xml -->
    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
    updateCheck="false">
    <!--
    diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
    user.home – 用户主目录
    user.dir – 用户当前工作目录
    java.io.tmpdir – 默认临时文件路径
    -->
    <diskStore path="E:\mybatis-review\ehcache"/>
    <!--
    defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
    -->
    <!--
    name:缓存名称。
    maxElementsInMemory:缓存最大数目
    maxElementsOnDisk:硬盘最大缓存个数。
    eternal:对象是否永久有效,一但设置了,timeout将不起作用。
    overflowToDisk:是否保存到磁盘,当系统当机时
    timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
    timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
    diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
    diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
    diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
    memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
    clearOnFlush:内存数量最大时是否清除。
    memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
    FIFO,first in first out,这个是大家最熟的,先进先出。
    LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
    LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
    -->
    <defaultCache
    eternal="false"
    maxElementsInMemory="10000"
    overflowToDisk="false"
    diskPersistent="false"
    timeToIdleSeconds="1800"
    timeToLiveSeconds="259200"
    memoryStoreEvictionPolicy="LRU"/>

    <cache
    name="cloud_user"
    eternal="false"
    maxElementsInMemory="5000"
    overflowToDisk="false"
    diskPersistent="false"
    timeToIdleSeconds="1800"
    timeToLiveSeconds="1800"
    memoryStoreEvictionPolicy="LRU"/>

    </ehcache>
  • 设置二级缓存类型

    1
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
  • 添加logback.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration debug="false">

    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="/home"/>

    <!--控制台日志, 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">

    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
    </appender>

    <!--文件日志, 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <!--日志文件输出的文件名-->
    <FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
    <!--日志文件保留天数-->
    <MaxHistory>30</MaxHistory>
    </rollingPolicy>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">

    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
    <!--日志文件最大的大小-->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    <MaxFileSize>10MB</MaxFileSize>
    </triggeringPolicy>
    </appender>

    <!-- show parameters for hibernate sql 专为 Hibernate 定制 -->
    <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>
    <logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG"/>
    <logger name="org.hibernate.SQL" level="DEBUG"/>
    <logger name="org.hibernate.engine.QueryParameters" level="DEBUG"/>
    <logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG"/>

    <!-- myibatis log configure-->
    <logger name="com.apache.ibatis" level="DEBUG"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日志输出级别 -->
    <root level="DEBUG">
    <appender-ref ref="STDOUT"/>
    <appender-ref ref="FILE"/>
    </root>
    <logger name="com.coderitl.mapper" level="DEBUG"/>
    </configuration>

Mybatis的逆向工程

  • 正向工程: 先创建Java 实体类,由框架负责根据实体类生成数据表,hibernate 支持正向工程

  • 逆向工程: 先创建数据库表,由框架负责根据数据库表,反向生成如下资源

    • Java 实体类
    • Mapper 接口
    • Mapper 映射文件
  • 创建逆向工程的步骤

    • 添加依赖

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      <dependencies>
      <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
      </dependency>

      <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.9</version>
      </dependency>
      <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
      </dependency>
      </dependencies>

      <build>
      <plugins>

      <plugin>
      <groupId>org.mybatis.generator</groupId>
      <artifactId>mybatis-generator-maven-plugin</artifactId>
      <version>1.4.1</version>
      <!-- 插件的依赖 -->
      <dependencies>
      <!-- 逆向工程的核心依赖 -->
      <dependency>
      <groupId>org.mybatis.generator</groupId>
      <artifactId>mybatis-generator-core</artifactId>
      <version>1.4.1</version>
      </dependency>
      <!-- 数据库连接池 -->
      <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.11</version>
      </dependency>
      <!-- mysql 驱动 -->
      <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
      </dependency>
      </dependencies>
      </plugin>
      </plugins>
      </build>
    • 创建逆向工程的配置文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      <!-- generatorConfig.xml 固定名称 -->
      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE generatorConfiguration PUBLIC
      "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
      "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
      <generatorConfiguration>
      <!--
      targetRuntime: 执行生成的逆向工程
      MyBatis3: 生成带条件的 CRUD
      MyBatis3Simple: 生成基本的 CRUD
      -->
      <context id="context" targetRuntime="MyBatis3">

      <property name="javaFileEncoding" value="UTF-8"/>

      <!-- suppressAllComments 设置为 true 则不再生成注释 -->
      <commentGenerator>
      <property name="suppressAllComments" value="true"/>
      </commentGenerator>

      <!-- 数据库的相关配置 -->
      <jdbcConnection
      driverClass="com.mysql.jdbc.Driver"
      connectionURL="jdbc:mysql://localhost:3306/score_system?autoReconnect=true&amp;useSSL=false&amp;characterEncoding=UTF-8"
      userId="root"
      password="root"/>

      <javaTypeResolver>
      <property name="forceBigDecimals" value="false"/>
      </javaTypeResolver>

      <!-- 实体类生成的策略 -->
      <javaModelGenerator targetPackage="com.coderitl.domain" targetProject="src/main/java">
      <property name="enableSubPackages" value="true"/>
      <property name="trimStrings" value="true"/>
      </javaModelGenerator>

      <!-- Mapper.xml 文件的生成策略 -->
      <sqlMapGenerator targetPackage="com.coderitl.mapper" targetProject="src/main/resources">
      <property name="enableSubPackages" value="true"/>
      </sqlMapGenerator>

      <!-- Mapper 接口文件的生成策略 -->
      <javaClientGenerator targetPackage="com.coderitl.mapper" targetProject="src/main/java" type="XMLMAPPER">
      <property name="enableSubPackages" value="true"/>
      </javaClientGenerator>

      <!--
      table指定每个生成表的生成策略 表名 和 model实体类名
      tableName="*" 可以对应所有表,此时不写 domainObjectName
      domainObjectName 属性指定生成出来的实体类类名
      -->
      <table tableName="t_emp" domainObjectName="Emp"></table>
      <table tableName="t_dept" domainObjectName="Dept"></table>
      </context>
      </generatorConfiguration>

    • 执行

      执行如下插件