自学内容网 自学内容网

三、MyBatis实践(3):多表映射,动态语句,高级扩展

三、MyBatis多表映射

3.1 多表映射概念

  1. 多表查询结果映射思路

    上面课程中,我全面讲解了单表的mybatis操作!但是开发中更多的是多表查询需求,这种情况我们如何让进行处理?

    MyBatis 思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序查询需求,那就太好了,而 ResultMap 就是 MyBatis 就是完美答案。

    官方例子:我们如何映射下面这个语句?

    <!-- 非常复杂的语句 -->
    <select id="selectBlogDetails" resultMap="detailedBlogResultMap">
      select
           B.id as blog_id,
           B.title as blog_title,
           B.author_id as blog_author_id,
           A.id as author_id,
           A.username as author_username,
           A.password as author_password,
           A.email as author_email,
           A.bio as author_bio,
           A.favourite_section as author_favourite_section,
           P.id as post_id,
           P.blog_id as post_blog_id,
           P.author_id as post_author_id,
           P.created_on as post_created_on,
           P.section as post_section,
           P.subject as post_subject,
           P.draft as draft,
           P.body as post_body,
           C.id as comment_id,
           C.post_id as comment_post_id,
           C.name as comment_name,
           C.comment as comment_text,
           T.id as tag_id,
           T.name as tag_name
      from Blog B
           left outer join Author A on B.author_id = A.id
           left outer join Post P on B.id = P.blog_id
           left outer join Comment C on P.id = C.post_id
           left outer join Post_Tag PT on PT.post_id = P.id
           left outer join Tag T on PT.tag_id = T.id
      where B.id = #{id}
    </select>

    你可能想把它映射到一个智能的对象模型,这个对象表示了一篇博客,它由某位作者所写,有很多的博文,每篇博文有零或多条的评论和标签。 我们先来看看下面这个完整的例子,它是一个非常复杂的结果映射(假设作者,博客,博文,评论和标签都是类型别名)。 虽然它看起来令人望而生畏,但其实非常简单。

    <!-- 非常复杂的结果映射 -->
    <resultMap id="detailedBlogResultMap" type="Blog">
      <constructor>
        <idArg column="blog_id" javaType="int"/>
      </constructor>
      <result property="title" column="blog_title"/>
      <association property="author" javaType="Author">
        <id property="id" column="author_id"/>
        <result property="username" column="author_username"/>
        <result property="password" column="author_password"/>
        <result property="email" column="author_email"/>
        <result property="bio" column="author_bio"/>
        <result property="favouriteSection" column="author_favourite_section"/>
      </association>
      <collection property="posts" ofType="Post">
        <id property="id" column="post_id"/>
        <result property="subject" column="post_subject"/>
        <association property="author" javaType="Author"/>
        <collection property="comments" ofType="Comment">
          <id property="id" column="comment_id"/>
        </collection>
        <collection property="tags" ofType="Tag" >
          <id property="id" column="tag_id"/>
        </collection>
      </collection>
    </resultMap>

    你现在可能看不懂,接下来我们要学习将多表查询结果使用ResultMap标签映射到实体类对象上!

    我们的学习目标:

    多表查询语句使用

    多表结果承接实体类设计

    使用ResultMap完成多表结果映射

  2. 实体类设计方案

    多表关系回顾:(双向查看)

    • 一对一

      夫妻关系,人和身份证号

    • 一对多| 多对一

      用户和用户的订单,锁和钥匙

    • 多对多

      老师和学生,部门和员工 实体类设计关系(查询):(单向查看)

    • 对一 : 夫妻一方对应另一方,订单对应用户都是对一关系

      实体类设计:对一关系下,类中只要包含单个对方对象类型属性即可!

      例如:

      public class Customer {
      ​
        private Integer customerId;
        private String customerName;
      ​
      }
      ​
      public class Order {
      ​
        private Integer orderId;
        private String orderName;
        private Customer customer;// 体现的是对一的关系
      ​
      }  
      ​
    • 对多: 用户对应的订单,讲师对应的学生或者学生对应的讲师都是对多关系:

      实体类设计:对多关系下,类中只要包含对方类型集合属性即可!

      public class Customer {
      ​
        private Integer customerId;
        private String customerName;
        private List<Order> orderList;// 体现的是对多的关系
      }
      ​
      public class Order {
      ​
        private Integer orderId;
        private String orderName;
        private Customer customer;// 体现的是对一的关系
        
      }
      ​
      //查询客户和客户对应的订单集合  不要管!

      多表结果实体类设计小技巧:

    对一,属性中包含对方对象

    对多,属性中包含对方对象集合

    只有真实发生多表查询时,才需要设计和修改实体类,否则不提前设计和修改实体类!

    无论多少张表联查,实体类设计都是两两考虑!

    在查询映射的时候,只需要关注本次查询相关的属性!例如:查询订单和对应的客户,就不要关注客户中的订单集合!

  3. 多表映射案例准备

    数据库:

    CREATE TABLE `t_customer` (`customer_id` INT NOT NULL AUTO_INCREMENT, `customer_name` CHAR(100), PRIMARY KEY (`customer_id`) );
    ​
    CREATE TABLE `t_order` ( `order_id` INT NOT NULL AUTO_INCREMENT, `order_name` CHAR(100), `customer_id` INT, PRIMARY KEY (`order_id`) ); 
    ​
    INSERT INTO `t_customer` (`customer_name`) VALUES ('c01');
    ​
    INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o1', '1');
    INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o2', '1');
    INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o3', '1'); 

    实际开发时,一般在开发过程中,不给数据库表设置外键约束。 原因是避免调试不方便。 一般是功能开发完成,再加外键约束检查是否有bug。

    实体类设计:

    稍后会进行订单关联客户查询,也会进行客户关联订单查询,所以在这先练习设计

    @Data
    public class Customer {
    ​
      private Integer customerId;
      private String customerName;
      private List<Order> orderList;// 体现的是对多的关系
      
    }  
    ​
    @Data
    public class Order {
      private Integer orderId;
      private String orderName;
      private Customer customer;// 体现的是对一的关系
      
    }  
    ​

3.2 对一映射

  1. 需求说明

    根据ID查询订单,以及订单关联的用户的信息!

  2. OrderMapper接口

    public interface OrderMapper {
      Order selectOrderWithCustomer(Integer orderId);
    }
  3. OrderMapper.xml配置文件

    <!-- 创建resultMap实现“对一”关联关系映射 -->
    <!-- id属性:通常设置为这个resultMap所服务的那条SQL语句的id加上“ResultMap” -->
    <!-- type属性:要设置为这个resultMap所服务的那条SQL语句最终要返回的类型 -->
    <resultMap id="selectOrderWithCustomerResultMap" type="order">
    ​
      <!-- 先设置Order自身属性和字段的对应关系 -->
      <id column="order_id" property="orderId"/>
    ​
      <result column="order_name" property="orderName"/>
    ​
      <!-- 使用association标签配置“对一”关联关系 -->
      <!-- property属性:在Order类中对一的一端进行引用时使用的属性名 -->
      <!-- javaType属性:一的一端类的全类名 -->
      <association property="customer" javaType="customer">
    ​
        <!-- 配置Customer类的属性和字段名之间的对应关系 -->
        <id column="customer_id" property="customerId"/>
        <result column="customer_name" property="customerName"/>
    ​
      </association>
    ​
    </resultMap>
    ​
    <!-- Order selectOrderWithCustomer(Integer orderId); -->
    <select id="selectOrderWithCustomer" resultMap="selectOrderWithCustomerResultMap">
    ​
      SELECT order_id,order_name,c.customer_id,customer_name
      FROM t_order o
      LEFT JOIN t_customer c
      ON o.customer_id=c.customer_id
      WHERE o.order_id=#{orderId}
    ​
    </select>

    对应关系可以参考下图:

  4. Mybatis全局注册Mapper文件

    <!-- 注册Mapper配置文件:告诉Mybatis我们的Mapper配置文件的位置 -->
    <mappers>
    ​
      <!-- 在mapper标签的resource属性中指定Mapper配置文件以“类路径根目录”为基准的相对路径 -->
      <mapper resource="mappers/OrderMapper.xml"/>
    ​
    </mappers>
  5. junit测试程序

    @Slf4j
    public class MyBatisTest {
    ​
        private SqlSession session;
        // junit会在每一个@Test方法前执行@BeforeEach方法
    ​
        @BeforeEach
        public void init() throws IOException {
            session = new SqlSessionFactoryBuilder()
                    .build(
                            Resources.getResourceAsStream("mybatis-config.xml"))
                    .openSession();
        }
    ​
        @Test
        public void testRelationshipToOne() {
        
          OrderMapper orderMapper = session.getMapper(OrderMapper.class);
          // 查询Order对象,检查是否同时查询了关联的Customer对象
          Order order = orderMapper.selectOrderWithCustomer(2);
          log.info("order = " + order);
        
        }
    ​
        // junit会在每一个@Test方法后执行@@AfterEach方法
        @AfterEach
        public void clear() {
            session.commit();
            session.close();
        }
    }
  6. 关键词

    在“对一”关联关系中,我们的配置比较多,但是关键词就只有:associationjavaType

3.3 对多映射

  1. 需求说明

    查询客户和客户关联的订单信息!

  2. CustomerMapper接口

    public interface CustomerMapper {
    ​
      Customer selectCustomerWithOrderList(Integer customerId);
    ​
    }
  3. CustomerMapper.xml文件

    <!-- 配置resultMap实现从Customer到OrderList的“对多”关联关系 -->
    <resultMap id="selectCustomerWithOrderListResultMap"
    ​
      type="customer">
    ​
      <!-- 映射Customer本身的属性 -->
      <id column="customer_id" property="customerId"/>
    ​
      <result column="customer_name" property="customerName"/>
    ​
      <!-- collection标签:映射“对多”的关联关系 -->
      <!-- property属性:在Customer类中,关联“多”的一端的属性名 -->
      <!-- ofType属性:集合属性中元素的类型 -->
      <collection property="orderList" ofType="order">
    ​
        <!-- 映射Order的属性 -->
        <id column="order_id" property="orderId"/>
    ​
        <result column="order_name" property="orderName"/>
    ​
      </collection>
    ​
    </resultMap>
    ​
    <!-- Customer selectCustomerWithOrderList(Integer customerId); -->
    <select id="selectCustomerWithOrderList" resultMap="selectCustomerWithOrderListResultMap">
      SELECT c.customer_id,c.customer_name,o.order_id,o.order_name
      FROM t_customer c
      LEFT JOIN t_order o
      ON c.customer_id=o.customer_id
      WHERE c.customer_id=#{customerId}
    </select>

    对应关系可以参考下图:

  4. Mybatis全局注册Mapper文件

    <!-- 注册Mapper配置文件:告诉Mybatis我们的Mapper配置文件的位置 -->
    <mappers>
      <!-- 在mapper标签的resource属性中指定Mapper配置文件以“类路径根目录”为基准的相对路径 -->
      <mapper resource="mappers/OrderMapper.xml"/>
      <mapper resource="mappers/CustomerMapper.xml"/>
    </mappers>
  5. junit测试程序

    @Test
    public void testRelationshipToMulti() {
    ​
      CustomerMapper customerMapper = session.getMapper(CustomerMapper.class);
      // 查询Customer对象同时将关联的Order集合查询出来
      Customer customer = customerMapper.selectCustomerWithOrderList(1);
      log.info("customer.getCustomerId() = " + customer.getCustomerId());
      log.info("customer.getCustomerName() = " + customer.getCustomerName());
      List<Order> orderList = customer.getOrderList();
      for (Order order : orderList) {
        log.info("order = " + order);
      }
    }
  6. 关键词

    在“对多”关联关系中,同样有很多配置,但是提炼出来最关键的就是:“collection”和“ofType”

3.4 多表映射总结

3.4.1 多表映射优化
setting属性属性含义可选值默认值
autoMappingBehavior指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。NONE, PARTIAL, FULLPARTIAL

我们可以将autoMappingBehavior设置为full,进行多表resultMap映射的时候,可以省略符合列和属性命名映射规则(列名=属性名,或者开启驼峰映射也可以自定映射)的result标签!

修改mybati-sconfig.xml:

<!--开启resultMap自动映射 -->
<setting name="autoMappingBehavior" value="FULL"/>

修改teacherMapper.xml

<resultMap id="teacherMap" type="teacher">
    <id property="tId" column="t_id" />
    <!-- 开启自动映射,并且开启驼峰式支持!可以省略 result!-->
<!--        <result property="tName" column="t_name" />-->
    <collection property="students" ofType="student" >
        <id property="sId" column="s_id" />
<!--            <result property="sName" column="s_name" />-->
    </collection>
</resultMap>
3.4.2 多表映射总结
关联关系配置项关键词所在配置文件和具体位置
对一association标签/javaType属性/property属性Mapper配置文件中的resultMap标签内
对多collection标签/ofType属性/property属性Mapper配置文件中的resultMap标签内

四、MyBatis动态语句

4.1 动态语句需求和简介

经常遇到很多按照很多查询条件进行查询的情况,比如智联招聘的职位搜索等。其中经常出现很多条件不取值的情况,在后台应该如何完成最终的SQL语句呢?

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

4.2 if和where标签

使用动态 SQL 最常见情景是根据条件包含 where / if 子句的一部分。比如:

<!-- List<Employee> selectEmployeeByCondition(Employee employee); -->
<select id="selectEmployeeByCondition" resultType="employee">
    select emp_id,emp_name,emp_salary from t_emp
    <!-- where标签会自动去掉“标签体内前面多余的and/or” -->
    <where>
        <!-- 使用if标签,让我们可以有选择的加入SQL语句的片段。这个SQL语句片段是否要加入整个SQL语句,就看if标签判断的结果是否为true -->
        <!-- 在if标签的test属性中,可以访问实体类的属性,不可以访问数据库表的字段 -->
        <if test="empName != null">
            <!-- 在if标签内部,需要访问接口的参数时还是正常写#{} -->
            or emp_name=#{empName}
        </if>
        <if test="empSalary &gt; 2000">
            or emp_salary>#{empSalary}
        </if>
        <!--
         第一种情况:所有条件都满足 WHERE emp_name=? or emp_salary>?
         第二种情况:部分条件满足 WHERE emp_salary>?
         第三种情况:所有条件都不满足 没有where子句
         -->
    </where>
</select>

4.3 set标签

<!-- void updateEmployeeDynamic(Employee employee) -->
<update id="updateEmployeeDynamic">
    update t_emp
    <!-- set emp_name=#{empName},emp_salary=#{empSalary} -->
    <!-- 使用set标签动态管理set子句,并且动态去掉两端多余的逗号 -->
    <set>
        <if test="empName != null">
            emp_name=#{empName},
        </if>
        <if test="empSalary &lt; 3000">
            emp_salary=#{empSalary},
        </if>
    </set>
    where emp_id=#{empId}
    <!--
         第一种情况:所有条件都满足 SET emp_name=?, emp_salary=?
         第二种情况:部分条件满足 SET emp_salary=?
         第三种情况:所有条件都不满足 update t_emp where emp_id=?
            没有set子句的update语句会导致SQL语法错误
     -->
</update>

4.4 trim标签(了解)

使用trim标签控制条件部分两端是否包含某些字符

  • prefix属性:指定要动态添加的前缀

  • suffix属性:指定要动态添加的后缀

  • prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值

  • suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值

<!-- List<Employee> selectEmployeeByConditionByTrim(Employee employee) -->
<select id="selectEmployeeByConditionByTrim" resultType="com.atguigu.mybatis.entity.Employee">
    select emp_id,emp_name,emp_age,emp_salary,emp_gender
    from t_emp
    
    <!-- prefix属性指定要动态添加的前缀 -->
    <!-- suffix属性指定要动态添加的后缀 -->
    <!-- prefixOverrides属性指定要动态去掉的前缀,使用“|”分隔有可能的多个值 -->
    <!-- suffixOverrides属性指定要动态去掉的后缀,使用“|”分隔有可能的多个值 -->
    <!-- 当前例子用where标签实现更简洁,但是trim标签更灵活,可以用在任何有需要的地方 -->
    <trim prefix="where" suffixOverrides="and|or">
        <if test="empName != null">
            emp_name=#{empName} and
        </if>
        <if test="empSalary &gt; 3000">
            emp_salary>#{empSalary} and
        </if>
        <if test="empAge &lt;= 20">
            emp_age=#{empAge} or
        </if>
        <if test="empGender=='male'">
            emp_gender=#{empGender}
        </if>
    </trim>
</select>

4.5 choose/when/otherwise标签

在多个分支条件中,仅执行一个。

  • 从上到下依次执行条件判断

  • 遇到的第一个满足条件的分支会被采纳

  • 被采纳分支后面的分支都将不被考虑

  • 如果所有的when分支都不满足,那么就执行otherwise分支

<!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) -->
<select id="selectEmployeeByConditionByChoose" resultType="com.atguigu.mybatis.entity.Employee">
    select emp_id,emp_name,emp_salary from t_emp
    where
    <choose>
        <when test="empName != null">emp_name=#{empName}</when>
        <when test="empSalary &lt; 3000">emp_salary &lt; 3000</when>
        <otherwise>1=1</otherwise>
    </choose>
    
    <!--
     第一种情况:第一个when满足条件 where emp_name=?
     第二种情况:第二个when满足条件 where emp_salary < 3000
     第三种情况:两个when都不满足 where 1=1 执行了otherwise
     -->
</select>

4.6 foreach标签

基本用法

用批量插入举例

<!--
    collection属性:要遍历的集合
    item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象
    separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符
    open属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串
    close属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串
    index属性:这里起一个名字,便于后面引用
        遍历List集合,这里能够得到List集合的索引值
        遍历Map集合,这里能够得到Map集合的key
 -->
<foreach collection="empList" item="emp" separator="," open="values" index="myIndex">
    <!-- 在foreach标签内部如果需要引用遍历得到的具体的一个对象,需要使用item属性声明的名称 -->
    (#{emp.empName},#{myIndex},#{emp.empSalary},#{emp.empGender})
</foreach>

批量更新时需要注意

上面批量插入的例子本质上是一条SQL语句,而实现批量更新则需要多条SQL语句拼起来,用分号分开。也就是一次性发送多条SQL语句让数据库执行。此时需要在数据库连接信息的URL地址中设置:

atguigu.dev.url=jdbc:mysql:///mybatis-example?allowMultiQueries=true

对应的foreach标签如下:

<!-- int updateEmployeeBatch(@Param("empList") List<Employee> empList) -->
<update id="updateEmployeeBatch">
    <foreach collection="empList" item="emp" separator=";">
        update t_emp set emp_name=#{emp.empName} where emp_id=#{emp.empId}
    </foreach>
</update>

关于foreach标签的collection属性

如果没有给接口中List类型的参数使用@Param注解指定一个具体的名字,那么在collection属性中默认可以使用collection或list来引用这个list集合。这一点可以通过异常信息看出来:

Parameter 'empList' not found. Available parameters are [arg0, collection, list]

在实际开发中,为了避免隐晦的表达造成一定的误会,建议使用@Param注解明确声明变量的名称,然后在foreach标签的collection属性中按照@Param注解指定的名称来引用传入的参数。

4.7 sql片段

抽取重复的SQL片段

<!-- 使用sql标签抽取重复出现的SQL片段 -->
<sql id="mySelectSql">
    select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp
</sql>

引用已抽取的SQL片段

<!-- 使用include标签引用声明的SQL片段 -->
<include refid="mySelectSql"/>

五、MyBatis高级扩展

5.1 Mapper批量映射优化

  1. 需求

    Mapper 配置文件很多时,在全局配置文件中一个一个注册太麻烦,希望有一个办法能够一劳永逸。

  2. 配置方式

    Mybatis 允许在指定 Mapper 映射文件时,只指定其所在的包:

    <mappers>
        <package name="com.atguigu.mapper"/>
    </mappers>

    此时这个包下的所有 Mapper 配置文件将被自动加载、注册,比较方便。

  3. 资源创建要求

  • Mapper 接口和 Mapper 配置文件名称一致

    • Mapper 接口:EmployeeMapper.java

    • Mapper 配置文件:EmployeeMapper.xml

  • Mapper 配置文件放在 Mapper 接口所在的包内

    • 可以将mapperxml文件放在mapper接口所在的包!

    • 可以在sources下创建mapper接口包一致的文件夹结构存放mapperxml文

5.2 插件和分页插件PageHelper

5.2.1 插件机制和PageHelper插件介绍

MyBatis 对插件进行了标准化的设计,并提供了一套可扩展的插件机制。插件可以在用于语句执行过程中进行拦截,并允许通过自定义处理程序来拦截和修改 SQL 语句、映射语句的结果等。

具体来说,MyBatis 的插件机制包括以下三个组件:

  1. Interceptor(拦截器):定义一个拦截方法 intercept,该方法在执行 SQL 语句、执行查询、查询结果的映射时会被调用。

  2. Invocation(调用):实际上是对被拦截的方法的封装,封装了 Object targetMethod methodObject[] args 这三个字段。

  3. InterceptorChain(拦截器链):对所有的拦截器进行管理,包括将所有的 Interceptor 链接成一条链,并在执行 SQL 语句时按顺序调用。

插件的开发非常简单,只需要实现 Interceptor 接口,并使用注解 @Intercepts 来标注需要拦截的对象和方法,然后在 MyBatis 的配置文件中添加插件即可。

PageHelper 是 MyBatis 中比较著名的分页插件,它提供了多种分页方式(例如 MySQL 和 Oracle 分页方式),支持多种数据库,并且使用非常简单。下面就介绍一下 PageHelper 的使用方式。

https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md#如何配置数据库方言

5.2.2 PageHelper插件使用
  1. pom.xml引入依赖

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.1.11</version>
    </dependency>
    ​
  2. mybatis-config.xml配置分页插件

    在 MyBatis 的配置文件中添加 PageHelper 的插件:

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <property name="helperDialect" value="mysql"/>
        </plugin>
    </plugins>
    ​

    其中,com.github.pagehelper.PageInterceptor 是 PageHelper 插件的名称,dialect 属性用于指定数据库类型(支持多种数据库)

  3. 页插件使用

    在查询方法中使用分页:

    @Test
    public void testTeacherRelationshipToMulti() {
    ​
        TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class);
    ​
        PageHelper.startPage(1,2);
        // 查询Customer对象同时将关联的Order集合查询出来
        List<Teacher> allTeachers = teacherMapper.findAllTeachers();
    //
        PageInfo<Teacher> pageInfo = new PageInfo<>(allTeachers);
    ​
        System.out.println("pageInfo = " + pageInfo);
        long total = pageInfo.getTotal(); // 获取总记录数
        System.out.println("total = " + total);
        int pages = pageInfo.getPages();  // 获取总页数
        System.out.println("pages = " + pages);
        int pageNum = pageInfo.getPageNum(); // 获取当前页码
        System.out.println("pageNum = " + pageNum);
        int pageSize = pageInfo.getPageSize(); // 获取每页显示记录数
        System.out.println("pageSize = " + pageSize);
        List<Teacher> teachers = pageInfo.getList(); //获取查询页的数据集合
        System.out.println("teachers = " + teachers);
        teachers.forEach(System.out::println);
    ​
    }

5.3 逆向工程和MybatisX插件

5.3.1 ORM思维介绍

ORM(Object-Relational Mapping,对象-关系映射)是一种将数据库和面向对象编程语言中的对象之间进行转换的技术。它将对象和关系数据库的概念进行映射,最后我们就可以通过方法调用进行数据库操作!!

最终: 让我们可以使用面向对象思维进行数据库操作!!!

ORM 框架通常有半自动和全自动两种方式。

  • 半自动 ORM 通常需要程序员手动编写 SQL 语句或者配置文件,将实体类和数据表进行映射,还需要手动将查询的结果集转换成实体对象。

  • 全自动 ORM 则是将实体类和数据表进行自动映射,使用 API 进行数据库操作时,ORM 框架会自动执行 SQL 语句并将查询结果转换成实体对象,程序员无需再手动编写 SQL 语句和转换代码。

下面是半自动和全自动 ORM 框架的区别:

  1. 映射方式:半自动 ORM 框架需要程序员手动指定实体类和数据表之间的映射关系,通常使用 XML 文件或注解方式来指定;全自动 ORM 框架则可以自动进行实体类和数据表的映射,无需手动干预。

  2. 查询方式:半自动 ORM 框架通常需要程序员手动编写 SQL 语句并将查询结果集转换成实体对象;全自动 ORM 框架可以自动组装 SQL 语句、执行查询操作,并将查询结果转换成实体对象。

  3. 性能:由于半自动 ORM 框架需要手动编写 SQL 语句,因此程序员必须对 SQL 语句和数据库的底层知识有一定的了解,才能编写高效的 SQL 语句;而全自动 ORM 框架通过自动优化生成的 SQL 语句来提高性能,程序员无需进行优化。

  4. 学习成本:半自动 ORM 框架需要程序员手动编写 SQL 语句和映射配置,要求程序员具备较高的数据库和 SQL 知识;全自动 ORM 框架可以自动生成 SQL 语句和映射配置,程序员无需了解过多的数据库和 SQL 知识。

常见的半自动 ORM 框架包括 MyBatis 等;常见的全自动 ORM 框架包括 Hibernate、Spring Data JPA、MyBatis-Plus 等。

5.3.2 逆向工程

MyBatis 的逆向工程是一种自动化生成持久层代码和映射文件的工具,它可以根据数据库表结构和设置的参数生成对应的实体类、Mapper.xml 文件、Mapper 接口等代码文件,简化了开发者手动生成的过程。逆向工程使开发者可以快速地构建起 DAO 层,并快速上手进行业务开发。 MyBatis 的逆向工程有两种方式:通过 MyBatis Generator 插件实现和通过 Maven 插件实现。无论是哪种方式,逆向工程一般需要指定一些配置参数,例如数据库连接 URL、用户名、密码、要生成的表名、生成的文件路径等等。 总的来说,MyBatis 的逆向工程为程序员提供了一种方便快捷的方式,能够快速地生成持久层代码和映射文件,是半自动 ORM 思维像全自动发展的过程,提高程序员的开发效率。

注意:逆向工程只能生成单表crud的操作,多表查询依然需要我们自己编写!

5.3.3 逆向工程插件MyBatisX使用

MyBatisX 是一个 MyBatis 的代码生成插件,可以通过简单的配置和操作快速生成 MyBatis Mapper、pojo 类和 Mapper.xml 文件。下面是使用 MyBatisX 插件实现逆向工程的步骤:

  1. 安装插件:

    在 IntelliJ IDEA 中打开插件市场,搜索 MyBatisX 并安装。

  2. 使用 IntelliJ IDEA连接数据库

  3. 逆向工程案例使用
  4. 正常使用即可,自动生成单表的crud方法!

    package com.atguigu.mapper;
    ​
    import com.atguigu.pojo.User;
    ​
    /**
    * @author Jackiechan
    * @description 针对表【user】的数据库操作Mapper
    * @createDate 2023-06-02 16:55:32
    * @Entity com.atguigu.pojo.User
    */
    public interface UserMapper {
    ​
        int deleteByPrimaryKey(Long id);
    ​
        int insert(User record);
    ​
        int insertSelective(User record);
    ​
        User selectByPrimaryKey(Long id);
    ​
        int updateByPrimaryKeySelective(User record);
    ​
        int updateByPrimaryKey(User record);
    ​
    }
    ​

六、MyBatis总结

核心点掌握目标
mybatis基础使用流程, 参数输入,#{} ${},参数输出
mybatis多表实体类设计,resultMap多表结果映射
mybatis动态语句Mybatis动态语句概念, where , if , foreach标签
mybatis扩展Mapper批量处理,分页插件,逆向工程

原文地址:https://blog.csdn.net/mmmmazhiyuan/article/details/143076591

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!