JAVAEE框架技术之9-myBatis高级查询技术文档

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: JAVAEE框架技术之9-myBatis高级查询技术文档

高级查询

Mybatis作为一个ORM框架,也对sql的高级查询作了支持,下面我来学习mybatis中的一对一,一对多, 多对多

案例说明

此案例中的业务关系是用户,订单,订单详情,商品之间的关系,其中
    一个订单属性于一个人
    一个订单可以有多个订单详情
    一个订单详情中包含一个商品信息
它们的关系是:
    订单和人是一对一关系
    订单和订单详情是一对多的关系
    订单和商品是多对多的关系

表分析

导入课程资料中的数据库及实体类

业务需求

一对一查询: 查询订单,并且查询出下单人信息。

一对多查询:查询订单,查询出下单人信息并且查询出订单详情。

多对多查询:查询订单,查询出下单人信息并且查询出订单详情中的商品数据。

一对一查询

需求:查询订单,并且查询出下单人信息

sql分析

-- 查询订单,并且查询出下单人的信息
SELECT
  * 
FROM
  tb_user as u
  left join tb_order AS o ON u.id = o.user_id 
WHERE
  o.order_number = 20200921001

代码实现

@Data
public class Order {
    private Integer id;
    private Long userId;
    private String orderNumber;
    //添加User对象
    private User user;
}
public interface UserMapper {
    /**
     *  一对一:查询订单,并且查询出下单人信息
     * @param orderNumber
     * @return
     */
    public Order oneToOne(@Param("orderNumber") String orderNumber);
}
<!--一对一-->
    <resultMap id="oneToOneResultMap" type="Order" autoMapping="true">
        <!--order的id-->
        <id property="id" column="id"/>
        <!--一对一映射
            association: 一对一映射
            property: 表示子对象的属性名
            javaType: 指定子对象的类型
            autoMapping: 完成子对象属性自动映射
        -->
        <association property="user" javaType="User" autoMapping="true">
            <!--user的id-->
            <id property="id" column="id"/>
            <result property="userName" column="user_name"/>
        </association>
    </resultMap>
    <!--一对一:查询订单,并且查询出下单人信息-->
    <select id="oneToOne" resultMap="oneToOneResultMap">
        SELECT   *  FROM
             tb_user as u left join tb_order as o on u.id = o.user_id
        WHERE
             o.order_number = #{orderNumber}
    </select>
public class UserDaoTest {
    private UserMapper userMapper;
    private SqlSession sqlSession;
    SqlSessionFactory sqlSessionFactory;
    @Before
    public void setUp() throws Exception {
        //指定核心配置文件的位置
        String resource = "mybatis-config.xml";
        //加载核心配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //构建sqlSessionFactory对象
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取SqlSession对象,SqlSession可以操作crud
        sqlSession = sqlSessionFactory.openSession();
        //动态代理
        userMapper = sqlSession.getMapper(UserMapper.class);
    }
    //一对一查询
    @Test
    public void oneToOne(){
        Order order = this.userMapper.oneToOne("20200921001");
        System.out.println(order);
    }
}

一对多查询

**一对多查询:**查询订单,查询出下单人信息并且查询出订单详情

sql分析

-- 查询订单,查询出下单人信息并且查询出订单详情。
 SELECT
      o.id as o_id,
      u.id as u_id,
      d.id as d_id,
      u.user_name,
      o.order_number,
      d.total_price
  FROM
      tb_order AS o
      left join tb_user AS u ON o.user_id = u.id
      left join tb_orderdetail as d on d.order_id = o.id
  WHERE
      o.order_number = 20200921001;

代码实现

/**
 * 订单表
 */
@Data
public class Order {
    private Integer id;
    private Long userId;
    private String orderNumber;
    //一对一:添加User对象
    private User user;
    //一对多:添加Orderdetail
    private List<Orderdetail> orderdetails;
}
/**
     * 一对多:查询订单,查询出下单人信息并且查询出订单详情
     * @param orderNumber
     * @return
     */
    public Order oneToMany(@Param("orderNumber") String orderNumber);
<!--一对多-->
    <resultMap id="oneToManyResultMap" type="Order" autoMapping="true">
        <!--映射order本身-->
        <id property="id" column="o_id"/>
        <!--映射user子对象-->
        <association property="user" javaType="User" autoMapping="true">
            <id property="id" column="u_id"/>
        </association>
        <!--映射Orderdetail-->
        <collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">
            <id property="id" column="d_id"/>
        </collection>
    </resultMap>
    <!--一对多:查询订单,查询出下单人信息并且查询出订单详情-->
    <select id="oneToMany" resultMap="oneToManyResultMap">
        SELECT
               o.id as o_id,
               u.id as u_id,
               d.id as d_id,
               u.user_name,
               o.order_number,
               d.total_price
        FROM
             tb_order AS o
                 left join tb_user AS u ON o.user_id = u.id
                 left join tb_orderdetail as d on d.order_id = o.id
        WHERE
                o.order_number = #{orderNumber};
    </select>
//一对多
    @Test
    public void oneToMany(){
        Order order = this.userMapper.oneToMany("20200921001");
        System.out.println(order);
    }

多对多查询

**多对多查询:**查询订单,查询出下单人信息并且查询出订单详情中的商品数据

定单和商品表 是多对多的对应关系

sql分析

代码实现

@Data
public class Orderdetail {
    private Integer id;
    private Double totalPrice;
    private Integer status;    
    /*商品信息*/
    private Item item;
}
/**
     * 多对多查询:查询订单,查询出下单人信息并且查询出订单详情中的商品数据。
     * @param orderNumber
     * @return
     */
    public Order manyToMany(@Param("orderNumber") String orderNumber)
<!--多对多-->
    <resultMap id="manyToManyResultMap" type="Order" autoMapping="true">
        <!--映射order本身-->
        <id property="id" column="o_id"/>
        <!--映射user子对象-->
        <association property="user" javaType="User" autoMapping="true">
            <id property="id" column="u_id"/>
        </association>
        <!--映射Orderdetail-->
        <collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">
            <id property="id" column="d_id"/>
            <!--映射item子对象-->
            <association property="item" javaType="Item" autoMapping="true">
                <id property="id" column="i_id"/>
            </association>
        </collection>
    </resultMap>
    <!--多对多查询:查询订单,查询出下单人信息并且查询出订单详情中的商品数据。-->
    <select id="manyToMany" resultMap="manyToManyResultMap">
        SELECT
               o.id as o_id,
               u.id as u_id,
               d.id as d_id,
               i.id as i_id,
               u.user_name,
               o.order_number,
               d.total_price,
               i.item_name
        FROM
             tb_order AS o
                 left join tb_user AS u ON o.user_id = u.id
                 left join tb_orderdetail as d on d.order_id = o.id
                 left join tb_item as i on d.item_id = i.id
        WHERE
                o.order_number = #{orderNumber}
    </select>
//多对多
    @Test
    public void manyToMany(){
        Order order = this.userMapper.manyToMany("20200921001");
        System.out.println(order);
    }

ResultMap的继承

回顾以上多表映射中resultMap映射中其实有 一对一,一对多,多对多中都有一对一对映射很重复的,每一次都需要写,不好,其实我们可以把相同的一对一映射进行抽取,然后再继承过来。

代码实现

分页插件

PageHelper分页插件

Mybatis的plugin实现原理

添加依赖

<!--分页-->
<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper</artifactId>
  <version>3.7.5</version>
</dependency>
<dependency>
  <groupId>com.github.jsqlparser</groupId>
  <artifactId>jsqlparser</artifactId>
  <version>0.9.1</version>
</dependency>

配置插件

<!--分页插件-->
<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <!--数据库方言-->
        <property name="dialect" value="mysql"/>
        <!-- 设置为true时,使用RowBounds分页会进行count查询 -->
        <property name="rowBoundsWithCount" value="true"/>
    </plugin>
</plugins>
5.x后续方案
<plugin interceptor="com.github.pagehelper.PageInterceptor">
        <property name="helperDialect" value="mysql"/>
        <property name="reasonable" value="true"/>
 </plugin>

实现分页

/**
 * 分页查询
 * @return
 */
public List<User> queryAll();
<!--分页查询-->
<select id="queryAll" resultType="User">
    select * from tb_user
</select>
//分页查询
    @Test
    public void queryAll(){
        //实现分页   参数1:当前页(从1开始)  参数2:显示多少条
        PageHelper.startPage(2, 2);
        //你只管查询所有,如何分页交给 PageHelper.startPage(2, 2);完成
        List<User> users = this.userMapper.queryAll();
        for (User user : users) {
            System.out.println(user);
        }
        //获取更多的分页信息
        PageInfo<User> pageInfo = new PageInfo<User>(users);
        System.out.println("当前页:" + pageInfo.getPageNum());
        System.out.println("当前页显示的条数:" + pageInfo.getPageSize());
        System.out.println("总页码:" + pageInfo.getPages());
        System.out.println("最后一页:" + pageInfo.getLastPage());
        System.out.println("分页相关的信息:" + pageInfo.getList());
        System.out.println("分页总条数:" + pageInfo.getTotal());
    }

懒加载(延迟加载)

思考问题?

一对多:我们查询用户时,要不要把关联的订单查询出来

多对一:我们查询订单时,要不要把关联的用户查询出来

什么是懒加载?

就是在需要它的时候才加载,不需要的话就不加载

使用场景

一对多,多对多 通常采用延迟加载

一对一,多对一 通常采用立即加载

配置懒加载

<settings>
    <!-- lazyLoadingEnabled:延迟加载启动,默认是false 相当于是否开启延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true" />
    <!--aggressiveLazyLoading:积极的懒加载,falsed话按需加载,默认是true -->
    <setting name="aggressiveLazyLoading" value="false"></setting>
</settings>

一对多 collection延迟加载

一对多:我们查询用户时,要不要把关联的订单查询出来

@Data
public class User {
    private Long id;
    private String userName;
    private String password;
    private String name;
    private Integer age;
    private Integer sex;
    private Date birthday;
    private Date created;
    private Date updated;
    /*在一方添加多方对象*/
    private List<Order> orders;
}
public List<User> queryAllUser(Integer id);
public Order      queryOrderById(Integer id);
<resultMap id="userByIdMap" type="User" autoMapping="true">
<!--一对多,延迟加载-->
<collection property="orders" column="id"             select="cn.yanqi.mapper.UserMapper.queryOrderById" ofType="Order" autoMapping="true"/>
</resultMap>
<select id="queryAllUser" resultMap="userByIdMap">
  select * from tb_user where id = #{id}
</select>
<select id="queryOrderById" resultType="Order">
  select * from tb_order where id = #{id}
</select>
@Test
    public void queryUserById(){
        List<User> list = this.userMapper.queryAllUser(2);
        // list.forEach(System.out::println); 需要时才会加载order
    }

多对一 association 延迟加载

多对一:我们查询订单时,要不要把关联的用户查询出来

@Data
public class Order {
    private Integer id;
    private Long userId;
    private String orderNumber;
    //一对一:添加User对象
    private User user;
    //一对多:添加Orderdetail
    private List<Orderdetail> orderdetails;
}
public Order queryOrderById2(Integer id);
<resultMap id="OrderResultMap" type="Order" autoMapping="true">
<id column="id" property="id"/>
<!--多对一:延迟加载 -->
<association property="user" javaType="User" column="id" select="queryUserById" fetchType="lazy" autoMapping="true"/>
</resultMap>
<!--order查询-->
<select id="queryOrderById2" resultMap="OrderResultMap">
  select * from tb_order where id = #{id}
</select>
<!--user查询-->
<select id="queryUserById" resultType="User">
  select * from tb_user where id = #{id}
</select>
@Test
    public void queryOrderById2(){
        Order order = this.userMapper.queryOrderById2(2);
        User user = order.getUser();//需要时才加载user
        System.out.println(user);
    }

逆向工程

我们之前都是根据数据库中表的字段来完成实体类的书写,再mapper接口和对应的sql映射文件,这此常规的操作是不是很重复,其实这些东西可以自动生成。以下就是告拆大家如何快速实现CRUD操作:

mybatis-generator

添加插件

  • pom.xml
<build>
    <plugins>
        <!-- mybatis代码生成插件 -->
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.2</version>
            <configuration>
                <verbose>true</verbose>
                <overwrite>true</overwrite>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>5.1.16</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

配置文件

  • datasource.properties
db.driverClassName=com.mysql.jdbc.Driver
db.url=jdbc:mysql:///mybatis?characterEncoding=utf-8
db.username=root
db.password=root
  • 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>
   <!--导入属性配置-->
   <properties resource="datasource.properties"></properties>
   <!-- 指定数据库驱动的jdbc驱动jar包的位置 -->
   <!--<classPathEntry location="${db.driverLocation}" />-->
   <!-- context 是逆向工程的主要配置信息 -->
   <!-- id:起个名字 -->
   <!-- targetRuntime:设置生成的文件适用于那个 mybatis 版本 -->
   <context id="default" targetRuntime="MyBatis3">
      <!--optional,旨在创建class时,对注释进行控制-->
      <commentGenerator>
         <property name="suppressDate" value="true" />
         <!-- 是否去除自动生成的注释 true:是 : false:否 -->
         <property name="suppressAllComments" value="true" />
      </commentGenerator>
      <!--jdbc的数据库连接-->
      <jdbcConnection driverClass="${db.driverClassName}"
                  connectionURL="${db.url}"
                  userId="${db.username}"
                  password="${db.password}">
      </jdbcConnection>
      <!--非必须,类型处理器,在数据库类型和java类型之间的转换控制-->
      <javaTypeResolver>
         <!-- 默认情况下数据库中的 decimal,bigInt 在 Java 对应是 sql 下的 BigDecimal 类 -->
         <!-- 不是 double 和 long 类型 -->
         <!-- 使用常用的基本类型代替 sql 包下的引用类型 -->
         <property name="forceBigDecimals" value="false" />
      </javaTypeResolver>
      <!-- targetPackage:生成的实体类所在的包 -->
      <!-- targetProject:生成的实体类所在的硬盘位置 -->
      <javaModelGenerator targetPackage="cn.yanqi.pojo"
                     targetProject=".\src\main\java">
         <!-- 是否允许子包 -->
         <property name="enableSubPackages" value="false" />
         <!-- 是否对modal添加构造函数 -->
         <property name="constructorBased" value="true" />
         <!-- 是否清理从数据库中查询出的字符串左右两边的空白字符 -->
         <property name="trimStrings" value="true" />
         <!-- 建立modal对象是否不可改变 即生成的modal对象不会有setter方法,只有构造方法 -->
         <property name="immutable" value="false" />
      </javaModelGenerator>
      <!-- targetPackage 和 targetProject:生成的 mapper 文件的包和位置 -->
      <sqlMapGenerator targetPackage="mappers"
                   targetProject=".\src\main\resources">
         <!-- 针对数据库的一个配置,是否把 schema 作为字包名 -->
         <property name="enableSubPackages" value="false" />
      </sqlMapGenerator>
      <!-- targetPackage 和 targetProject:生成的 interface 文件的包和位置 -->
      <javaClientGenerator type="XMLMAPPER"
                      targetPackage="cn.yanqi.dao" targetProject=".\src\main\java">
         <!-- 针对 oracle 数据库的一个配置,是否把 schema 作为字包名 -->
         <property name="enableSubPackages" value="false" />
      </javaClientGenerator>
      <table tableName="tb_user" domainObjectName="User"
            enableCountByExample="false" enableUpdateByExample="false"
            enableDeleteByExample="false" enableSelectByExample="false"
            selectByExampleQueryId="false">
      </table>
      <table tableName="tb_order" domainObjectName="Order"
            enableCountByExample="false" enableUpdateByExample="false"
            enableDeleteByExample="false" enableSelectByExample="false"
            selectByExampleQueryId="false">
      </table>
      <table tableName="tb_orderdetail" domainObjectName="Orderdetail"
            enableCountByExample="false" enableUpdateByExample="false"
            enableDeleteByExample="false" enableSelectByExample="false"
            selectByExampleQueryId="false">
      </table>
      <table tableName="tb_item" domainObjectName="Item"
            enableCountByExample="false" enableUpdateByExample="false"
            enableDeleteByExample="false" enableSelectByExample="false"
            selectByExampleQueryId="false">
      </table>
   </context>
</generatorConfiguration>

IDEA插件代码生成器

Easy Code的代码生成器,从实体类到controller service dao sql都给生成好,很方便

IDEA连接数据库

下载插件

生成代码



false">


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
Java 数据库连接 数据库
mybatis查询数据,返回的对象少了一个字段
mybatis查询数据,返回的对象少了一个字段
140 8
|
4天前
|
SQL 安全 Java
MyBatis-Plus条件构造器:构建安全、高效的数据库查询
MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。
9 1
MyBatis-Plus条件构造器:构建安全、高效的数据库查询
|
10天前
|
SQL Java 数据库连接
mybatis如何仅仅查询某个表的几个字段
【10月更文挑战第19天】mybatis如何仅仅查询某个表的几个字段
15 1
|
2月前
|
SQL XML Java
mybatis复习04高级查询 一对多,多对一的映射处理,collection和association标签的使用
文章介绍了MyBatis中高级查询的一对多和多对一映射处理,包括创建数据库表、抽象对应的实体类、使用resultMap中的association和collection标签进行映射处理,以及如何实现级联查询和分步查询。此外,还补充了延迟加载的设置和用法。
mybatis复习04高级查询 一对多,多对一的映射处理,collection和association标签的使用
|
12天前
|
缓存 Cloud Native 安全
探索阿里巴巴新型ORM框架:超越MybatisPlus?
【10月更文挑战第9天】在Java开发领域,Mybatis及其增强工具MybatisPlus长期占据着ORM(对象关系映射)技术的主导地位。然而,随着技术的发展,阿里巴巴集团推出了一种新型ORM框架,旨在提供更高效、更简洁的开发体验。本文将对这一新型ORM框架进行探索,分析其特性,并与MybatisPlus进行比较。
20 0
|
3月前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
|
3月前
|
SQL Java 关系型数据库
MyBatis-Plus 分页魅力绽放!紧跟技术热点,带你领略数据分页的高效与便捷
【8月更文挑战第29天】在 Java 开发中,数据处理至关重要,尤其在大量数据查询与展示时,分页功能尤为重要。MyBatis-Plus 作为一款强大的持久层框架,提供了便捷高效的分页解决方案。通过封装数据库分页查询语句,开发者能轻松实现分页功能。在实际应用中,只需创建 `Page` 对象并设置页码和每页条数,再通过 `QueryWrapper` 构建查询条件,调用 `selectPage` 方法即可完成分页查询。MyBatis-Plus 不仅生成分页 SQL 语句,还自动处理参数合法性检查,并支持条件查询和排序等功能,极大地提升了系统性能和稳定性。
51 0
|
3月前
|
SQL Java 数据库连接
【Java 第十三篇章】MyBatis 框架介绍
MyBatis 原名 iBATIS,2001 年由 Clinton Begin 创建,以其简易灵活著称。2010 年更名以重塑品牌形象。MyBatis 通过 SQL 映射文件将 SQL 语句与 Java 代码分离,支持编写原生 SQL 并与方法映射。具备对象关系映射功能,简化数据库记录处理。支持动态 SQL 构建,灵活应对不同查询条件。内置缓存机制,提升查询效率。相比全功能 ORM,MyBatis 提供更高 SQL 控制度和更好的维护性,并易于与 Spring 等框架集成,广泛应用于 Java 数据访问层。
31 0
|
3月前
|
druid Java 数据库连接
SpringBoot项目整合MybatisPlus持久层框架+Druid数据库连接池,以及实现增删改查功能
SpringBoot项目整合MybatisPlus和Druid数据库连接池,实现基本的增删改查功能。
309 0
|
3月前
|
SQL Java 数据库连接
后端框架的学习----mybatis框架(5、分页)
这篇文章介绍了如何在MyBatis框架中实现分页功能,包括使用SQL的`limit`语句进行分页和利用MyBatis的`RowBounds`对象进行分页的方法。