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

本文涉及的产品
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
简介: 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">


相关实践学习
自建数据库迁移到云数据库
本场景将引导您将网站的自建数据库平滑迁移至云数据库RDS。通过使用RDS,您可以获得稳定、可靠和安全的企业级数据库服务,可以更加专注于发展核心业务,无需过多担心数据库的管理和维护。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
SQL XML Java
273 0
|
7月前
|
SQL Java 数据库连接
区分iBatis与MyBatis:两个Java数据库框架的比较
总结起来:虽然从技术角度看,iBATIS已经停止更新但仍然可用;然而考虑到长期项目健康度及未来可能需求变化情况下MYBATISS无疑会是一个更佳选择因其具备良好生命周期管理机制同时也因为社区力量背书确保问题修复新特征添加速度快捷有效.
574 12
|
8月前
|
SQL XML Java
MyBatis框架如何处理字符串相等的判断条件。
总的来说,MyBatis框架提供了灵活而强大的机制来处理SQL语句中的字符串相等判断条件。无论是简单的等值判断,还是复杂的条件逻辑,MyBatis都能通过其标签和属性来实现,使得动态SQL的编写既安全又高效。
641 0
|
9月前
|
SQL XML Java
MyBatis Mapper中使用limit参数的查询问题
总结而言,MyBatis中使用 `limit`参数的查询可以高度定制并且灵活,基于方法签名和XML映射文件的组合来达成多样化的查询需求。通过参数化查询和动态SQL,MyBatis可以有效地处理各种复杂情境下的数据库操作,并且将SQL语句的维护与业务代码的编写相分离,提升代码的可维护性和可阅读性。
751 13
|
10月前
|
SQL Java 数据库
解决Java Spring Boot应用中MyBatis-Plus查询问题的策略。
保持技能更新是侦探的重要素质。定期回顾最佳实践和新技术。比如,定期查看MyBatis-Plus的更新和社区的最佳做法,这样才能不断提升查询效率和性能。
546 1
|
Oracle 关系型数据库 Java
|
SQL 缓存 Java
框架源码私享笔记(02)Mybatis核心框架原理 | 一条SQL透析核心组件功能特性
本文详细解构了MyBatis的工作机制,包括解析配置、创建连接、执行SQL、结果封装和关闭连接等步骤。文章还介绍了MyBatis的五大核心功能特性:支持动态SQL、缓存机制(一级和二级缓存)、插件扩展、延迟加载和SQL注解,帮助读者深入了解其高效灵活的设计理念。
|
SQL XML Java
一、MyBatis简介:MyBatis历史、MyBatis特性、和其它持久化层技术对比、Mybatis下载依赖包流程
一、MyBatis简介:MyBatis历史、MyBatis特性、和其它持久化层技术对比、Mybatis下载依赖包流程
414 69
|
10月前
|
Java 数据库连接 数据库
Spring boot 使用mybatis generator 自动生成代码插件
本文介绍了在Spring Boot项目中使用MyBatis Generator插件自动生成代码的详细步骤。首先创建一个新的Spring Boot项目,接着引入MyBatis Generator插件并配置`pom.xml`文件。然后删除默认的`application.properties`文件,创建`application.yml`进行相关配置,如设置Mapper路径和实体类包名。重点在于配置`generatorConfig.xml`文件,包括数据库驱动、连接信息、生成模型、映射文件及DAO的包名和位置。最后通过IDE配置运行插件生成代码,并在主类添加`@MapperScan`注解完成整合
1549 1
Spring boot 使用mybatis generator 自动生成代码插件
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于注解的整合
本文介绍了Spring Boot集成MyBatis的两种方式:基于XML和注解的形式。重点讲解了注解方式,包括@Select、@Insert、@Update、@Delete等常用注解的使用方法,以及多参数时@Param注解的应用。同时,针对字段映射不一致的问题,提供了@Results和@ResultMap的解决方案。文章还提到实际项目中常结合XML与注解的优点,灵活使用两者以提高开发效率,并附带课程源码供下载学习。
967 0