Mybatis系列第7篇:各种查询详解

DROP TABLE IF EXISTS t_order_detail;

CREATE TABLE t_order_detail(

id int AUTO_INCREMENT PRIMARY KEY COMMENT ‘订单明细id’,

order_id INT NOT NULL DEFAULT 0 COMMENT ‘订单id,来源于t_order.id’,

goods_id INT NOT NULL DEFAULT 0 COMMENT ‘商品id,来源于t_goods.id’,

num INT NOT NULL DEFAULT 0 COMMENT ‘商品数量’,

total_price DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT ‘商品总金额’

) COMMENT ‘订单表’;

INSERT INTO t_order_detail VALUES (1,1,1,2,17.76),(2,1,1,1,16.66),(3,2,1,1,8.88);

select * from t_user;

select * from t_goods;

select * from t_order;

select * from t_order_detail;

单表查询(3种方式)

需求

需要按照订单id查询订单信息。

方式1
创建每个表对应的Model

db中表的字段是采用下划线分割的,model中我们是采用骆驼命名法来命名的,如OrderModel:

package com.javacode2018.chat05.demo1.model;

import lombok.*;

import java.util.List;

@Getter

@Setter

@Builder

@ToString

@NoArgsConstructor

@AllArgsConstructor

public class OrderModel {

private Integer id;

private Integer userId;

private Long createTime;

private Long upTime;

}

其他几个Model也类似。

Mapper xml
<![CDATA[ SELECT a.id,a.user_id as userId,a.create_time createTime,a.up_time upTime FROM t_order a WHERE a.id = #{value} ]]>

注意上面的resultType,标识结果的类型。

Mapper接口方法

OrderModel getById(int id);

mybatis全局配置文件
<?xml version="1.0" encoding="UTF-8" ?>
测试用例

com.javacode2018.chat05.demo1.Demo1Test#getById

@Before

public void before() throws IOException {

//指定mybatis全局配置文件

String resource = “demo1/mybatis-config.xml”;

//读取全局配置文件

InputStream inputStream = Resources.getResourceAsStream(resource);

//构建SqlSessionFactory对象

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

this.sqlSessionFactory = sqlSessionFactory;

}

@Test

public void getById() {

try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {

OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

OrderModel orderModel = mapper.getById(1);

log.info(“{}”, orderModel);

}

}

运行输出

35:59.211 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById - ==>  Preparing: SELECT a.id,a.user_id as userId,a.create_time createTime,a.up_time upTime FROM t_order a WHERE a.id = ?

35:59.239 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById - ==> Parameters: 1(Integer)

35:59.258 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById - <==      Total: 1

35:59.258 [main] INFO  c.j.chat05.demo1.Demo1Test - OrderModel(id=1, userId=2, createTime=1577947790, upTime=1577947790)

原理

sql中我们使用了别名,将t_order中的字段转换成了和OrderModel中字段一样的名称,最后mybatis内部会通过反射,将查询结果按照名称到OrderModel中查找同名的字段,然后进行赋值。

方式2

若我们项目中表对应的Model中的字段都是采用骆驼命名法,mybatis中可以进行一些配置,可以使表中的字段和对应Model中骆驼命名法的字段进行自动映射。

需要在mybatis全局配置文件中加入下面配置:

Mapper xml
<![CDATA[ SELECT a.id,a.user_id,a.create_time,a.up_time FROM t_order a WHERE a.id = #{value} ]]>

注意上面的sql,我们没有写别名了,由于我们开启了自动骆驼命名映射,所以查询结果会按照下面的关系进行自动映射:

| sql对应的字段 | OrderModel中的字段 |

| — | — |

| id | id |

| user_id | userId |

| create_time | createTime |

| up_time | upTime |

Mapper接口

OrderModel getById1(int id);

测试用例

com.javacode2018.chat05.demo1.Demo1Test#getById1

@Test

public void getById1() {

try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {

OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

OrderModel orderModel = mapper.getById1(1);

log.info(“{}”, orderModel);

}

}

运行输出

59:44.884 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==>  Preparing: SELECT a.id,a.user_id,a.create_time,a.up_time FROM t_order a WHERE a.id = ?

59:44.917 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==> Parameters: 1(Integer)

59:44.935 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - <==      Total: 1

59:44.935 [main] INFO  c.j.chat05.demo1.Demo1Test - OrderModel(id=1, userId=2, createTime=1577947790, upTime=1577947790)

输出中可以看出,sql中的字段是下划线的方式,OrderModel中的字段是骆驼命名法,结果也自动装配成功,这个就是开启mapUnderscoreToCamelCase产生的效果。

方式3

mapper xml中有个更强大的元素resultMap,通过这个元素可以定义查询结果的映射关系。

Mapper xml
<![CDATA[ SELECT a.id,a.user_id,a.create_time,a.up_time FROM t_order a WHERE a.id = #{value} ]]>

上面resultMap有2个元素需要指定:

  • id:resultMap标识

  • type:将结果封装成什么类型,此处我们需要将结果分装为OrderModel

注意上面的select元素,有个resultMap,标识查询结果使用哪个resultMap进行映射,此处我们使用的是orderModelMap2,所以查询结果会按照orderModelMap2关联的resultMap进行映射。

Mapper接口

OrderModel getById2(int id);

测试用例

com.javacode2018.chat05.demo1.Demo1Test#getById2

@Test

public void getById2() {

try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {

OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

OrderModel orderModel = mapper.getById2(1);

log.info(“{}”, orderModel);

}

}

运行输出

14:12.518 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - ==>  Preparing: SELECT a.id,a.user_id,a.create_time,a.up_time FROM t_order a WHERE a.id = ?

14:12.546 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - ==> Parameters: 1(Integer)

14:12.564 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - <==      Total: 1

14:12.564 [main] INFO  c.j.chat05.demo1.Demo1Test - OrderModel(id=1, userId=2, createTime=1577947790, upTime=1577947790)

一对一关联查询(4种方式)

需求

通过订单id查询订单的时候,将订单关联的用户信息也返回。

我们修改一下OrderModel代码,内部添加一个UserModel,如下:

package com.javacode2018.chat05.demo2.model;

import lombok.*;

import java.util.List;

@Getter

@Setter

@Builder

@ToString

@NoArgsConstructor

@AllArgsConstructor

public class OrderModel {

private Integer id;

private Integer userId;

private Long createTime;

private Long upTime;

//下单用户信息

private UserModel userModel;

}

UserModel内容:

package com.javacode2018.chat05.demo2.model;

import lombok.*;

@Getter

@Setter

@Builder

@ToString

@NoArgsConstructor

@AllArgsConstructor

public class UserModel {

private Integer id;

private String name;

}

方式1
Mapper xml
<![CDATA[ SELECT a.id, a.user_id, a.create_time, a.up_time, b.name FROM t_order a, t_user b WHERE a.user_id = b.id AND a.id = #{value} ]]>

注意重点在于上面的这两行:

这个地方使用到了级联赋值,多级之间用.进行引用,此处我们只有一级,可以有很多级。

Mapper 接口

OrderModel getById1(int id);

测试用例

com.javacode2018.chat05.demo2.Demo2Test#getById1

@Before

public void before() throws IOException {

//指定mybatis全局配置文件

String resource = “demo2/mybatis-config.xml”;

//读取全局配置文件

InputStream inputStream = Resources.getResourceAsStream(resource);

//构建SqlSessionFactory对象

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

this.sqlSessionFactory = sqlSessionFactory;

}

@Test

public void getById1() {

try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {

OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

OrderModel orderModel = mapper.getById1(1);

log.info(“{}”, orderModel);

}

}

运行输出

24:20.811 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==>  Preparing: SELECT a.id, a.user_id, a.create_time, a.up_time, b.name FROM t_order a, t_user b WHERE a.user_id = b.id AND a.id = ?

24:20.843 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==> Parameters: 1(Integer)

24:20.861 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - <==      Total: 1

24:20.861 [main] INFO  c.j.chat05.demo2.Demo2Test - OrderModel(id=1, userId=2, createTime=1577947790, upTime=1577947790, userModel=UserModel(id=2, name=路人甲Java))

方式2

这次我们需要使用mapper xml中另外一个元素association,这个元素可以配置关联对象的映射关系,看示例。

Mapper xml
<![CDATA[ SELECT a.id, a.user_id, a.create_time, a.up_time, b.name FROM t_order a, t_user b WHERE a.user_id = b.id AND a.id = #{value} ]]>

注意上面下面这部分代码:

注意上面的property属性,这个就是配置sql查询结果和OrderModel.userModel对象的映射关系,将user_iduserModel中的id进行映射,name和userModel中的name进行映射

Mapper接口

OrderModel getById2(int id);

测试用例

@Test

public void getById2() {

try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {

OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

OrderModel orderModel = mapper.getById2(1);

log.info(“{}”, orderModel);

}

}

运行结果

51:44.896 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - ==>  Preparing: SELECT a.id, a.user_id, a.create_time, a.up_time, b.name FROM t_order a, t_user b WHERE a.user_id = b.id AND a.id = ?

51:44.925 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - ==> Parameters: 1(Integer)

51:44.941 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - <==      Total: 1

51:44.942 [main] INFO  c.j.chat05.demo2.Demo2Test - OrderModel(id=1, userId=2, createTime=1577947790, upTime=1577947790, userModel=UserModel(id=2, name=路人甲Java))

从结果的最后一行可以看出,所有字段的值映射都是ok的。

方式3

先按照订单id查询订单数据,然后在通过订单中user_id去用户表查询用户数据,通过两次查询,组合成目标结果,mybatis已经内置了这种操作,如下。

UserMapper.xml

我们先定义一个通过用户id查询用户信息的select元素,如下

<![CDATA[ SELECT id,name FROM t_user where id = #{value} ]]>
OrderModel.xml
<![CDATA[ SELECT a.id, a.user_id, a.create_time, a.up_time FROM t_order a WHERE a.id = #{value} ]]>

OrderModel.userModel属性的值来在于另外一个查询,这个查询是通过association元素的select属性指定的,此处使用的是

com.javacode2018.chat05.demo2.mapper.UserMapper.getById

这个查询是有条件的,条件通过associationcolumn进行传递的,此处传递的是getById3查询结果中的user_id字段。

Mapper接口

OrderModel getById3(int id);

测试用例

com.javacode2018.chat05.demo2.Demo2Test#getById3

@Test

public void getById3() {

try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {

OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

OrderModel orderModel = mapper.getById3(1);

log.info(“{}”, orderModel);

}

}

运行输出

07:12.569 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById3 - ==>  Preparing: SELECT a.id, a.user_id, a.create_time, a.up_time FROM t_order a WHERE a.id = ?

07:12.600 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById3 - ==> Parameters: 1(Integer)

07:12.619 [main] DEBUG c.j.c.d.mapper.UserMapper.getById - ====>  Preparing: SELECT id,name FROM t_user where id = ?

07:12.620 [main] DEBUG c.j.c.d.mapper.UserMapper.getById - ====> Parameters: 2(Integer)

07:12.625 [main] DEBUG c.j.c.d.mapper.UserMapper.getById - <====      Total: 1

07:12.625 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById3 - <==      Total: 1

07:12.625 [main] INFO  c.j.chat05.demo2.Demo2Test - OrderModel(id=1, userId=2, createTime=1577947790, upTime=1577947790, userModel=UserModel(id=2, name=路人甲Java))

从输出中可以看出有2次查询,先按照订单id查询订单,然后通过订单记录中用户id去用户表查询用户信息,最终执行了2次查询。

方式4

方式3中给第二个查询传递了一个参数,如果需要给第二个查询传递多个参数怎么办呢?可以这么写

 

这种相当于给子查询传递了一个map,子查询中 需要用过map的key获取对应的条件,看案例:

OrderMapper.xml

<![CDATA[ SELECT a.id, a.user_id, a.create_time, a.up_time FROM t_order a WHERE a.id = #{value} ]]>

UserMapper.xml

<![CDATA[ SELECT id,name FROM t_user where id = #{uid1} and id = #{uid2} ]]>

Mapper接口

OrderModel getById4(int id);

测试用例

com.javacode2018.chat05.demo2.Demo2Test#getById4

@Test

public void getById4() {

try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true)😉 {

OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);

OrderModel orderModel = mapper.getById4(1);

log.info(“{}”, orderModel);

}

}

运行输出

19:59.881 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById4 - ==>  Preparing: SELECT a.id, a.user_id, a.create_time, a.up_time FROM t_order a WHERE a.id = ?

19:59.914 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById4 - ==> Parameters: 1(Integer)

19:59.934 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ====>  Preparing: SELECT id,name FROM t_user where id = ? and id = ?

19:59.934 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ====> Parameters: 2(Integer), 1577947790(Long)

19:59.939 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - <====      Total: 0

19:59.939 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById4 - <==      Total: 1

19:59.939 [main] INFO  c.j.chat05.demo2.Demo2Test - OrderModel(id=1, userId=2, createTime=1577947790, upTime=1577947790, userModel=null)

输出中看一下第二个查询的条件,传过来的是第一个查询的user_id和create_time

一对多查询(2种方式)

需求

根据订单id查询出订单信息,并且查询出订单明细列表。

先修改一下OrderModel代码,如下:

package com.javacode2018.chat05.demo3.model;

import lombok.*;

import java.util.List;

@Getter

@Setter

@Builder

@ToString

@NoArgsConstructor

@AllArgsConstructor

public class OrderModel {

private Integer id;

private Integer userId;

private Long createTime;

private Long upTime;

//订单详情列表

private List orderDetailModelList;

}

OrderModel中添加了一个集合orderDetailModelList用来存放订单详情列表。

方式1
OrderMapper.xml

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

我个人认为,如果你想靠着背面试题来获得心仪的offer,用癞蛤蟆想吃天鹅肉形容完全不过分。想必大家能感受到面试越来越难,想找到心仪的工作也是越来越难,高薪工作羡慕不来,却又对自己目前的薪资不太满意,工作几年甚至连一个应届生的薪资都比不上,终究是错付了,错付了自己没有去提升技术。

这些面试题分享给大家的目的,其实是希望大家通过大厂面试题分析自己的技术栈,给自己梳理一个更加明确的学习方向,当你准备好去面试大厂,你心里有底,大概知道面试官会问多广,多深,避免面试的时候一问三不知。

大家可以把Java基础,JVM,并发编程,MySQL,Redis,Spring,Spring cloud等等做一个知识总结以及延伸,再去进行操作,不然光记是学不会的,这里我也提供一些脑图分享给大家:

希望你看完这篇文章后,不要犹豫,抓紧学习,复习知识,准备在明年的金三银四拿到心仪的offer,加油,打工人!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
"/>

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-lLN4W9oX-1713500975511)]

[外链图片转存中…(img-Z5Q48Z3j-1713500975514)]

[外链图片转存中…(img-5iASooPd-1713500975516)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

我个人认为,如果你想靠着背面试题来获得心仪的offer,用癞蛤蟆想吃天鹅肉形容完全不过分。想必大家能感受到面试越来越难,想找到心仪的工作也是越来越难,高薪工作羡慕不来,却又对自己目前的薪资不太满意,工作几年甚至连一个应届生的薪资都比不上,终究是错付了,错付了自己没有去提升技术。

这些面试题分享给大家的目的,其实是希望大家通过大厂面试题分析自己的技术栈,给自己梳理一个更加明确的学习方向,当你准备好去面试大厂,你心里有底,大概知道面试官会问多广,多深,避免面试的时候一问三不知。

大家可以把Java基础,JVM,并发编程,MySQL,Redis,Spring,Spring cloud等等做一个知识总结以及延伸,再去进行操作,不然光记是学不会的,这里我也提供一些脑图分享给大家:

[外链图片转存中…(img-htnRLfBN-1713500975519)]

[外链图片转存中…(img-NLV5suGc-1713500975520)]

[外链图片转存中…(img-UKsfRw9u-1713500975522)]

希望你看完这篇文章后,不要犹豫,抓紧学习,复习知识,准备在明年的金三银四拿到心仪的offer,加油,打工人!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值