1. MyBatis 入门

  • ssm 框架与三层架构

  • MyBatis 是一个持久层框架,用 Java 语言编写,使用 ORM 思想(Object Relational Mapping) 对象关系映射,就是把数据库表和实体类以及实体类的属性对应起来(实体类中的属性和数据库表的字段名称保持一致),让我们操作实体类就实现操作数据库表。

  • MyBatis 中实体类需要实现 Serializable 接口。

  • MyBatis 的环境搭建

    1. 创建 maven 工程并导入坐标
      <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.4.5</version>
      </dependency>
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.6</version>
      </dependency>
      
    2. 创建实体类(javaBean),和 dao 层的接口
    3. 创建 MyBatis 的主配置文件,名字没要求,在此 SqlMapConifg.xml
      <?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">
      <!-- mybatis的主配置文件 -->
      <configuration>
          <!-- 配置环境 -->
          <environments default="mysql">
              <!-- 配置mysql的环境-->
              <environment id="mysql">
                  <!-- 配置事务的类型-->
                  <transactionManager type="JDBC"></transactionManager>
                  <!-- 配置数据源(连接池) -->
                  <dataSource type="POOLED">
                      <!-- 配置连接数据库的4个基本信息 -->
                      <property name="driver" value="com.mysql.jdbc.Driver"/>
                      <property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/>
                      <property name="username" value="root"/>
                      <property name="password" value="1234"/>
                  </dataSource>
              </environment>
          </environments>
      
          <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
          <!-- 在注册映射文件时使用<mapper resource="org/xx/demo/mapper/xx.xml"/>,不需要映射文件名和接口名一样 -->
          <!-- 如果使用注解来配置,此处的 resources 应该用 class 来指定被注解的 dao 全限定类名 -->
          <mappers>
              <mapper resource="com/itheima/dao/IUserDao.xml"/>
          </mappers>
      </configuration>
      
    4. 创建映射配置文件
      <?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.dao.IUserDao">
          <!--配置查询所有,id与接口中方法名一样-->
          <select id="findAll" resultType="com.itheima.domain.User">
              select * from user
          </select>
      </mapper>
      
  • 环境搭建的注意事项

    • 在 MyBatis 中持久层的操作接口名称也和映射文件也叫做 Mapper(以前为 dao)
    • 在 resources 中创建目录的时候,和包不一样,需一级一级创建
    • MyBatis 映射文件的位置必须和 dao 接口的包结构相同
    • 映射配置文件的 mapper 标签 namespace 属性的取值必须是 dao 接口的全限定类名
    • 映射配置文件的操作配置(select),id 属性的取值必须是 dao 接口的方法名
    • 当我们遵循上述后 3 点之后,在开发中无需再写 dao 的实现类
  • 入门案例

    package com.itheima.test;
    
    import com.itheima.dao.IUserDao;
    import com.itheima.domain.User;
    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.InputStream;
    import java.util.List;
    
    public class MybatisTest {
        public static void main(String[] args)throws Exception {
            //1.读取配置文件
            InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.创建SqlSessionFactory工厂
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(in);
            //3.使用工厂生产SqlSession对象,操作数据库对象
            SqlSession session = factory.openSession();
            //4.使用SqlSession创建Dao接口的代理对象,代理模式
            IUserDao userDao = session.getMapper(IUserDao.class);
            //5.使用代理对象执行方法
            List<User> users = userDao.findAll();
            for(User user : users){
                System.out.println(user);
            }
            //6.释放资源
            session.close();
            in.close();
        }
    }
    

  • 使用注解的时候

    • 不需要映射配置文件
    • 主配置文件中 <mapper class="全限定类型"/>
    • 接口上使用注解 @Select("select * from user")
  • MyBatis 支持写 dao 实现类,一般不用,不简便。

    • 配置映射配置文件
    • 写 dao 的实现类,通过读取配置文件的 SQL 语句来查询
      public class UserDaoImpl implements IUserDao {
          private SqlSessionFactory factory;
      
          public UserDaoImpl(SqlSessionFactory factory) {
              this.factory = factory;
          }
      
          public List<User> findAll() {
              SqlSession session = factory.openSession();
              List<User> users = session.selectList("com.itheima.dao.IUserDao.findAll");
              session.close();
              return users;
          }
      }
      
    • 测试类
      public static void main(String[] args) throws Exception {
              //1.读取配置文件
              InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
              //2.创建SqlSessionFactory工厂
              SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
              SqlSessionFactory factory = builder.build(in);
              //3.使用工厂创建dao对象
              UserDaoImpl userDao = new UserDaoImpl(factory);
              //4.使用代理对象执行方法
              List<User> users = userDao.findAll();
              for (User user : users) {
                  System.out.println(user);
              }
              //5.释放资源
              in.close();
          }
      

2. 自定义 MyBatis 框架

  • MyBatis 在使用代理 dao 的方式实现增删改查时做了什么事情
    1. 创建代理对象
    2. 在代理对象中调用 selectList 方法
  • 执行查询所有分析
  • 创建代理对象的分析
  • 自定义 MyBatis 开发流程图

3. MyBatis 的 CRUD

  • CRUD 使用要求

    • 持久层接口和持久层接口的映射配置必须在相同的包下
    • 持久层映射配置中 mapper 标签的 namespace 属性取值必须是持久层接口的全限定类名
    • SQL 语句的配置标签 <select>,<insert>,<delete>,<update> 的 id 属性必须和持久层接口的方法名相同。
  • MyBatis 动态执行 SQL。

  • MyBatis 默认手动提交事务,需要 sqlSession.commit();

  • insert 操作:

    <insert id="saveUser" parameterType="pers.ylq.domain.User">
        <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
            select last_insert_id();
        </selectKey>
        insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday});
    </insert>
    
    • parameterType 属性:用于指定传入参数的类型,全类名。
    • resultType 属性:用于指定结果集的类型(查询的每条结果要封装的类型)。简单数据类型可以随意写,能识别就好,比如 int、Integer、String、java.liang.String ,如果结果是一个类,则需要写全类名。
    • SQL 语句中使用 #{} 字符 :
      • 它代表占位符,相当于原来 JDBC 部分所学的 ?,都是用于执行语句时替换实际的数据。具体的数据是由 #{} 里面的内容决定的。
      • 语法格式就是使用 #{对象.属性} 的方式,#{user.username} 它会先去找 user 对象,然后在 user 对象中找到 username 属性,并调用 getUsername() 方法把值取出来。但是我们在 parameterType 属性上指定了实体类名称,所以可以省略 user. 而直接写 username。
    • selectKey 的作用:配置保存时获取插入的 id,id 为自增长,keyProperty 表示返回给对象的哪一个属性,keyColumn 指向表中的主键,可省略,order 表示在什么时候执行。
    • select last_insert_id() 查询上一条自增长数据的 id。
  • update 操作

    <update id="updateUser" parameterType="pers.ylq.domain.User">
        update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id};
    </update>
    
    • #{} 中的值代表 User 对象中的属性。
  • delete 操作

    <delete id="deleteUser" parameterType="int">
        delete from user where id=#{id};
    </delete>
    
    • 由于参数类型是基本类型,#{} 中内容可随意填写。
  • select 操作:

    List<User> findAll();
    <select id="findAll" resultType="pers.ylq.domain.User">
        select * from user;
    </select>
    
    • resultType 为查询的结果封装的类型,不是方法的返回值类型。
  • 模糊查询:

    List<User> findByName(String username);
    <select id="findByName" parameterType="String" resultType="pers.ylq.domain.User">
        select * from user where username like #{name};
    </select>
    List<User> users = userDao.findByName("%王%");
    
    • 在传递参数的时候传递 % 可实现动态模糊查询
    <select id="findByName" parameterType="String" resultType="pers.ylq.domain.User">
        select * from user where username like concat('%',#{name},'%');
    </select>
    List<User> users = userDao.findByName("王");
    
    • 使用 concat 函数连接参数。
    List<User> users = userDao.findByName("王");
    <select id="findByName" parameterType="String" resultType="pers.ylq.domain.User">
        select * from user where username like '%${value}%';
    </select>
    
    • 通过 ${} 可以将 parameterType 传入的内容拼接在 SQL 中且不进行 JDBC 类型转换,${}可以接收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值 ${}括号中只能是 value
    public class QueryVo implements Serializable {
        private User user;
        public User getUser() { return user; }
        public void setUser(User user) { this.user = user; }
    }
    
    <select id="findUserByVo" resultType="pers.ylq.domain.User" parameterType="pers.ylq.domain.QueryVo">
        select * from user where username like #{user.username};
    </select>
    
    QueryVo vo = new QueryVo();
    User user = new User();
    vo.setUser(user);
    user.setUsername("%王%");
    List<User> users = userDao.findUserByVo(vo);
    
    • 查询的参数对象中存有对象
  • MyBatis 传递多个参数

    • 传递多个参数的时候,不能使用 parameterType 指定参数类型。
    • 可以通过参数传递顺序取值
      <select id="findByNameAndAddress" resultType="pers.ylq.domain.User">
          select * from user where username = #{arg0} and address = #{arg1};
      </select>
      
      List<User> users = userDao.findByNameAndAddress("老王", "北京");
      
    • 在 dao 层通过注解 @Param 实现多参传递,注解中的属性为参数的名字,在 XML 中通过此名字获取参数。
      List<User> findByNameAndAddress(@Param("username") String username, @Param("address") String address);
      
      <select id="findByNameAndAddress" resultType="pers.ylq.domain.User">
          select * from user where username = #{username} and address = #{address};
      </select>
      
      List<User> users = userDao.findByNameAndAddress("老王", "北京");
      
  • 解决实体类属性和数据库列名不对应的两种方式

    • 第一种:为查询出的数据用 as 关键字起别名,别名跟实体类中名字一样,即可成功封装为对象。
    • 第二种:建立属性名和列名的对应关系,然后使用 resultMap 属性,指向此对应表即可
      <!-- 配置 查询结果的列名和实体类的属性名的对应关系 -->
      <!-- id为对应表的唯一标识,可以随意起名 -->
      <resultMap id="userMap" type="pers.ylq.damain.User">
          <!-- 主键字段的对应 -->
          <id property="userId" column="id"></id>
          <!--非主键字段的对应-->
          <result property="userName" column="username"></result>
          <result property="userAddress" column="address"></result>
          <result property="userSex" column="sex"></result>
          <result property="userBirthday" column="birthday"></result>
      </resultMap>
      
      <select id="findById" parameterType="INT" resultMap="userMap">
          select * from user where id = #{uid}
      </select>
      

4. 主配置文件的配置和顺序

  • properties 标签:配置属性

    • <properties>
          <property name="driver" value="com.mysql.jdbc.Driver"/>
          <property name="url" value="jdbc:mysql://localhost:3306/eesy-mybatis"/>
          <property name="username" value="root"/>
          <property name="password" value="123456"/>
      </properties>
      <environments default="mysql">
          <environment id="mysql">
              <transactionManager type="JDBC"></transactionManager>
              <dataSource type="POOLED">
                  <property name="url" value="${url}"/>
                  <property name="driver" value="${driver}"/>
                  <property name="username" value="${username}"/>
                  <property name="password" value="${password}"/>
              </dataSource>
          </environment>
      </environments>
      

      配置完成之后,可在下面通过 ${name} 取值。

    • resources 属性:用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下。

      <properties resource="jdbcConfig.properties">
      </properties>
      
      <properties resource="pers/ylq/dao/jdbcConfig.properties">
      </properties>
      

      此文件若处于类的根路径下,因此直接写文件名就行。
      下面依旧通过 ${name} 来取值。

    • url 属性

      <properties url="file:///C:/Users/10766/Desktop/jdbcConfig.properties">
      </properties>
      

      URL:Uniform Resource Locator 统一资源定位符。它是可以唯一标识一个资源的位置。
      它的写法:http://localhost:8080/mybatisserver/demo1Servlet 协议 主机 端口 URI
      URI:Uniform Resource Identifier 统一资源标识符。它是在应用中可以唯一定位一个资源的。

  • settings 标签:对 MyBatis 进行一些设置,比如延迟加载。

  • typeAliases 标签:为类型起别名

    <typeAliases>
        <typeAlias type="pers.ylq.domain.User" alias="user"></typeAlias>
    </typeAliases>
    

    type 为实体类类型,alias 为别名,起了别名之后,不再区分大小写,在映射配置文件中可以用别名。
    package 标签:用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写。

    <typeAliases>
        <package name="pers.ylq.domain"></package>
    </typeAliases>
    
  • environments 标签(环境集合属性对象)

    • environment(环境子属性对象)
      • transactionManager(事务管理)
      • dataSource(数据源)
  • mappers 标签

    • mapper:指定映射的接口或者 XML 文件,因为两者路径一样,指定哪个都行。
      • resource:指定映射配置文件
      • class:指定 mapper 接口类路径,此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。
      • package:用于指定 dao 接口所在的包,当指定了之后就不需要再写 mapper 以及 resource 或者 class 了。此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中。

5. 执行过程分析(了解)

  • 分析编写 dao 实现类 MyBatis 的执行过程
  • 分析代理 dao 的执行过程

6. MyBatis 连接池及事务

  • MyBatis 提供了 3 中连接池的配置方式
    • 配置的位置:主配置文件的 DataSource 标签,type 属性。
    • 取值:
      • POOLED 采用传统的 javax.sql.DataSource 规范中的连接池,MyBatis 有针对规范的实现。
      • UNPOOLED 采用传统的获取连接的方式,虽然也实现了 DataSource 接口,但并没有使用池的思想。
      • JNDI 采用服务器提供的 JNDI 技术实现,来获取 DataSource 对象,不同服务器所能拿到的 DataSource 是不一样的。Tomcat 获取的连接池就是 dbcp 连接池。
        注意:如果不是 Web 或者 maven 的 war 工程,是不能使用的。
  • MyBatis 中事务通过 sqlsession 对象的 commit 方法和 rollback 方法实现事务的提交和回滚。
  • SqlSession openSession(boolean var1); 可通过设置参数为 true 来自动提交事务。

7. 动态 SQL

  • if 标签

    <select id="findUserByCondition" parameterType="User" resultMap="userMap">
        select * from user where 1 = 1
        <if test="username!=null">
            and username=#{username}
        </if>
    </select>
    
  • where 标签

    <select id="findUserByCondition" parameterType="User" resultMap="userMap">
        select * from user
        <where>
            <if test="username!=null">
                and username=#{username}
            </if>
            <if test="sex!=null">
                and sex=#{sex}
            </if>
        </where>
    </select>
    
  • foreach 标签

    <select id="findUserByList"  resultMap="userMap">
        select * from user
        <where>
            <foreach collection="list" open="id in(" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>
    
    • 如果传入的是单参数且参数类型是一个 List 的时候,collection 属性值为 list
    • 如果传入的是单参数且参数类型是一个 array 数组的时候,collection 的属性值为 array
  • SQL 标签:声明 SQL 语句,下面可以直接引入

    <sql id="defaultUser">
        select * from user
    </sql>
    <select id="findAll" resultMap="userMap">
        <include refid="defaultUser"></include>
    </select>
    

8. 多表查询

  • 一对一查询(多对一查询)
    • 有两个表,User 表和 Account 表,Account 表中有外键 uid 于 User 中 id 对应。

    • 方式一:定义一个新的类,AccountUser extends Account,还包含用户的信息字段,将返回结果封装到此类中。

    • 方式二:在 Account 类中加入 User 类的对象作为 Account 类的一个属性,定义一个 resultMap

      <resultMap id="accountUserMap" type="Account">
          <id property="id" column="aid"/>
          <result property="uid" column="uid"/>
          <result property="money" column="money"/>
      
          <association property="user" javaType="User">
              <id property="id" column="id"/>
              <result property="username" column="username"/>
              <result property="address" column="address"/>
              <result property="sex" column="sex"/>
              <result property="birthday" column="birthday"/>
          </association>
      </resultMap>
      

      查询结果的 resultMap 指向此映射关系。

  • 一对多查询:
    • User 类中加入 List<Account> accounts;
      <resultMap id="userAccountMap" type="User">
          <id property="id" column="id"/>
          <result property="username" column="username"/>
          <result property="address" column="address"/>
          <result property="sex" column="sex"/>
          <result property="birthday" column="birthday"/>
      
          <collection property="accounts" ofType="Account">
              <id property="id" column="aid"/>
              <result property="uid" column="uid"/>
              <result property="money" column="money"/>
          </collection>
      </resultMap>
      
      <select id="findAll" resultMap="userAccountMap">
          SELECT u.*,a.id aid,uid,money FROM USER u LEFT JOIN account a ON u.id=a.uid;
      </select>
      
  • 多对多查询:在两个表的实体类中分别加入对方实体类的 List 集合,在各自配置文件配置 resultMap 查询即可。

9. MyBatis 延迟加载

  • 延迟加载:需要数据的时候才加载,不需要不加载。
  • 实现:先从单表查询,需要时再去从关联表关联查询。
  • 主配置文件配置:
    <settings>
        <!-- 延迟加载全局开关 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 开启时,任何方法调用都会加载该对象所有属性,否则,按需加载,默认false(true in<=3.4.1)       -->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
    
  • 实体类配置文件:
    • 一对一实现延迟加载,association 实现

      <resultMap id="accountUserMap" type="Account">
          <id property="id" column="id"/>
          <result property="uid" column="uid"/>
          <result property="money" column="money"/>
          <association property="user" column="uid" javaType="User"
                       select="pers.ylq.dao.IUserDao.findById"/>
      </resultMap>
      
      <select id="findAll" resultMap="accountUserMap">
         select * from account
      </select>
      

      select 标签声明延迟加载调用的方法,clomu 为所传递的参数,property 指向实体类中的成员变量

    • 一对多实现延迟加载,collection 实现

      <resultMap id="userMap" type="user">
          <id property="id" column="id"/>
          <result property="username" column="username"/>
          <result property="address" column="address"/>
          <result property="sex" column="sex"/>
          <result property="birthday" column="birthday"/>
          <collection property="accounts" ofType="Account" column="id"
                      select="pers.ylq.dao.IAccountDao.findAccountById"/>
      </resultMap>
      
      <select id="findAll" resultMap="userMap">
          select * from user
      </select>
      

10. 缓存

mybatis 缓存

11. MyBatis 注解开发

  • 在 MyBatis 中,CRUD 共 4 个注解

    • @Select
    • @Insert
    • @Update
    • @Delete
  • 一个接口只能使用注解或 XML 文件,无法共存,即使用注解又使用 XML 会报错。即使 mapper 中使用 class 指向注解也会报错。

  • 当实体类的成员变量名和数据库字段名不对应的时候,可以使用 @Results 来配置对应关系

    @Select("select * from user")
    @Results(id = "userMap", value = {
            @Result(id = true, property = "userId", column = "id"),
            @Result(property = "userName", column = "username"),
            @Result(property = "userAddress", column = "address"),
            @Result(property = "userSex", column = "sex"),
            @Result(property = "userBirthday", column = "birthday"),
    })
    List<User1> findAll();
    
    • id 为该对应关系的唯一标识,供别的方法引用。
    • @Result 中的 id=true,代表其为主键
    • @Result 实现结果集的封装。
    • 别的方法可通过 @ResultMap("id") 引用此映射关系
  • 一对一查询配置(多对一)

    • MyBatis 中没有多对一的概念,多对一是通过一对一的方式来实现的。

    • 首先在 Account 实体类中添加 User 变量,生成 getter 和 setter 方法。

    • 在接口中使用注解配置 @Result

      @Select("select * from account")
      @Results(id = "accountMap", value = {
              @Result(id = true, property = "id", column = "id"),
              @Result(property = "uid", column = "uid"),
              @Result(property = "money", column = "money"),
              @Result(property = "user",column = "uid",one = @One(select = "pers.ylq.dao.IUserDao.findById",fetchType = FetchType.EAGER))
      })
      List<Account> findAll();
      
      • 最后一个 @Result 中,property 代表实体类中的变量,column 代表要传入的数据库的列名
      • @Result 中有:One one() default @One;Many many() default @Many;,one 代表一对一,many 代表一对多,代表此对象对应几个对象。
      • @One 中的属性 select 代表要执行的语句的唯一标识。
      • fetchType 有三个取值:LAZY(延迟加载),EAGER(立即加载),DEFAULT(默认)。
  • 一对多查询配置:

    • 在 User 实体类中添加 private List<Account> accounts;

    • 配置接口的注解

      @Select("select * from user")
      @Results(id = "userMap", value = {
              @Result(id = true, property = "id", column = "id"),
              @Result(property = "username", column = "username"),
              @Result(property = "address", column = "address"),
              @Result(property = "sex", column = "sex"),
              @Result(property = "birthday", column = "birthday"),
              @Result(property = "accounts",column = "id",
                      many = @Many(select = "pers.ylq.dao.IAccountDao.findAccountByUid",
                              fetchType = FetchType.LAZY))
      })
      List<User> findAll();
      
      • many 代表一对多的关系。
      • FetchType.LAZY 代表延迟加载,此属性代替了在主配置文件中 settings 中的配置。
  • 注解使用二级缓存:在 dao 层的接口上配置 @CacheNamespace(blocking=true)

  • 常用注解说明:

    • @Insert:实现新增
    • @Update:实现更新
    • @Delete:实现删除
    • @Select:实现查询
    • @Result:实现结果集封装
    • @Results:可以与 @Result 一起使用,封装多个结果集
    • @ResultMap:实现引用 @Results 定义的封装
    • @One:实现一对一结果集封装
    • @Many:实现一对多结果集封装
    • @SelectProvider: 实现动态 SQL 映射
    • @CacheNamespace:实现注解二级缓存的使用
  • @SelectProvider: 实现动态 SQL 映射

    • @SelectProvider(type = Provider.class,method = "selectProvider") 指定了一个类的 class 和类中的方法。
    • 此方法返回一个字符串,即为所要执行的 SQL 语句。
    • public class Provider {
          public String selectProvider(String name) {
              String sql = "select * from user where 1=1 ";
              if (name != null && !name.equals(" ")) {
                  sql += " and username=#{name} ";
              }
              return sql;
          }
      }
      

12. mapper 层注解

  • @Mapper, mybatis 注解
  • @Repository, sping 注解

相同点:

@Mapper 和 @Repository 都是作用在 dao 层接口,使得其生成代理对象 bean,交给 spring 容器管理

不同点:

@Mapper 不需要配置扫描地址,可以单独使用,如果有多个mapper 文件的话,可以在项目启动类中加入 @MapperScan(“mapper文件所在包”),这样就不需要每个 mapper 文件都加 @Mapper 注解了。

@Repository 需要配置扫描地址

但在 idea 中,使用 @Repository 可以消除在业务层中注入 mapper 对象时的错误

13. MyBatis Generator

MyBatis Generator

14. mybatis plus

mybatis plus