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分析

-- 查询订单,查询出下单人信息并且查询出订单详情中的商品数据。
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 = 20200921001

代码实现

@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>

实现分页

/**
 * 分页查询
 * @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连接数据库

下载插件

生成代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值