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

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 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">


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
17天前
|
XML Java 数据库连接
mybatis中在xml文件中通用查询结果列如何使用
mybatis中在xml文件中通用查询结果列如何使用
20 0
|
SQL Java 数据库连接
MyBatis 优秀的持久层框架(一)
MyBatis 优秀的持久层框架
67 0
|
1月前
|
SQL 关系型数据库 Java
Mybatis-Flex框架初体验
Mybatis-Flex框架初体验
|
2月前
|
SQL Java 数据库连接
|
1月前
|
Java 关系型数据库 数据库连接
MyBatis Plus 解决大数据量查询慢问题
MyBatis Plus 解决大数据量查询慢问题
|
15天前
|
SQL Java 数据库连接
什么是MyBatis持久层框架?
MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs (Plain Old Java Objects, 普通的 Java 对象) 映射成数据库中的记录。
26 5
|
30天前
|
SQL XML Java
这样使用MyBatis框架,被攻击了
这样使用MyBatis框架,被攻击了
14 0
|
1月前
|
Java fastjson Apache
Spring Boot+Gradle+ MyBatisPlus3.x搭建企业级的后台分离框架
Spring Boot+Gradle+ MyBatisPlus3.x搭建企业级的后台分离框架
31 1
|
1月前
|
SQL Java 数据库连接
Mybatis查询的时候BigDecimal类型的值查询失效的解决办法
Mybatis查询的时候BigDecimal类型的值查询失效的解决办法