目录
一、MyBatis参数
SqlSessiong工具类
1、映射文件配置-入参
1.1 parameterType入参
1.2 单个入参,变量名任意定义:
1.3 多个入参,解决方案:
1.4 pojo类入参:
1.5 Map入参:
1.6 自增主键回填
2、参数值的获取
2.1 获取参数的方式
2.2 ${}取值的应用场景
3、结果集映射
3.1 简单结果映射resultType
3.2 resultType测例
3.3 结果映射resultMap
3.3 resultMap测例
3.4 resultMap标签映射流程
4、SQL片段
4.1 引用当前文件中的SQL片段
二、动态sql(根据不同条件拼接SQL语句)
1、OGNL表达式介绍
1.1 动态SQL中使用到的运算符: ognl表达式
2、动态SQL标签if、choose、where、set、foreach
2.1 if标签
2.2 if测例
2.3 choose标签:分支选择(多选一,遇到成立的条件即停止)
2.4 choose测例
2.5 where标签:拼接条件查询,去除多余and、or
2.6 where测例
2.7 set标签:update语句中,添加set关键字,会将动态sql最后多余的逗号去除
2.8 set测例
2.9 foreach标签:遍历集合或者数组,拼接成字符串用于sql中in关键字
2.10 foreach测例
2.11 特殊字符处理
三、高级查询
1、一对一查询
1.1 一对一映射语法格式
1.2 一对一测例:
1.3 一对一查询映射规律
2、一对多查询
1.1 一对多映射语法:
1.2 一对多测例
1.3 一对多映射流程
1.4 一对多映小结
3、多对多查询
1.1 多对多映射
1.2 多对多测例
4、多对多查询扩展
5、ResultMap继承extend
public class SessionFactoryUtils {
//声明一个工厂对象
private static SqlSessionFactory factory;
//在静态代码块中创建会话工厂
static {
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//得到输入流
try(InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml")) {
factory = builder.build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
静态方法得到会话工厂
*/
public static SqlSessionFactory getSessionFactory() {
return factory;
}
/**
得到会话对象
*/
public static SqlSession getSession() {
return factory.openSession();//使用的是默认事务:手动提交
}
}
CRUD标签都有属性parameterType,底层的statement通过它指定接收的参数类型。入参数据有以下几种类型:HashMap,基本数据类型(包装类),实体类;
在mybatis中入参的数据类型分为2大类:
基本数据类型:int,string,long,Date等;
复杂数据类型:类(pojo)和Map;
说明:如果传递参数是数组或者集合,底层都会封装到Map集合中。
mapper.java
根据id查询用户信息,入参是单个参数:用户id
User queryById(Integer id); //接口方法传入一个参数
mapper.xml配置:
在xml中通过#{任意变量名}可接收到参数,若接口传入单个参数,可在xml中使用任意变量名接收。
mapper.java
多个入参,根据用户名和性别查询用户信息
User queryByNameAndSex(String name ,String sex);
错误演示:
报参数绑定异常!
需使用参数索引(arg0,arg1)、参数位置(param1,param2)、或在接口处明确指定传入参数名。
方式1:使用参数索引获取:arg0,arg1 (了解即可)
方式2:使用参数位置获取:param1,param2 (了解即可)
掌握:命名参数获取,在接口处明确指定传入参数名称。
mapper.java接口
List queryByNameAndSex(@Param("name") String name,@Param("sex") String sex);
mapper.xml,变量名与接口中明确的@Param("name")参数名相同,接收参数时通过指定的名称获取参数值。
mapper.java接口
保存用户,pojo对象作为入参
int saveUser (User user);
mapper.xml,#{变量},变量名与User类中属性名对应
insert into user values (null ,#{username},#{birthday},#{sex},#{address})
mapper.java接口
以map为参数插入用户信息
int saveUser2 (Map map);
SqlSession sqlSession = SessionFactoryUtils.getSession();
//获取接口代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map map = new HashMap<>();
map.put("username","鞋垫老板");
map.put("sex","男");
map.put("address","鞋店");
mapper.saveUser2(map);
sqlSession.commit();
sqlSession.close();
mapper.xml
insert into user values (null ,#{username},null,#{sex},#{address})
1)使用insert标签的子标签selectKey+last_insert_id()函数实现实现
insert into user values(null,#{username},#{birthday},#{sex},#{address})
select last_insert_id()
2)使用insert标签的属性useGeneratedKeys,keyProperty,keyColumn实现
说明:直接在insert标签中增加属性的方式,只适合于支持自动增长主键类型的数据库。
insert into user values(null,#{username},#{birthday},#{sex},#{address})
在mybatis中获取传入参数值的方式有两种:#{} 和 ${}
#{}: 使用#{}的sql是进行预编译的,防止sql注入。
${}:参数与sql直接拼接,有sql注入的风险。${id} 获取id值时,接口必须使用命名参数取值@param,若取单个值,也可使用${value}获取。
mapper.java接口
User findById2(@Param("id") Integer id);
mapper.xml
如果需要设置到SQL中的不是查询的条件,只能使用${}拼接;
SELECT COUNT(*) FROM ${tableName}
例:传入参数:没有指定参数名称
User selectUserById(Integer id);
获取参数通过${value}获取
mybatis框架提供了resultType和resultMap来对结果集进行封装; 只要一个方法有返回值需要处理,那么 resultType和resultMap必须有一个;
从sql语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。 可以使用 resultType 或 resultMap,但不能同时使用。
① 返回值是基本类型
1)基本类型 int short double ... 别名: _基本类型名称
2)包装类 类 String ArrayList .... 别名: 类名首字母小写
3)自定义类 扫包取别名 类首字母小写(大写也可以)
② 返回值为一个pojo(User)对象时
1)表中列明要与pojo中属性名称一致
2)若表中列名与pojo中属性名称不一致,可使用as取别名使其一致即可
3)如果满足驼峰映射,也可以开启驼峰映射设置
③返回值为一个List
当返回值为List集合时,resultType需要设置成集合中存储的具体的pojo数据类型
1)返回一条数据,封装到map中
接口:
Map findMapById(@Param("id") Integer id);
xml:
2)返回多条数据,封装到map中
获取所有用户,value为user对象:@MapKey("id"),指定key为id值
接口:
@MapKey("id")
Map findAllToMap();
xml:
正常开发中,数据库字段名称与Pojo类属性名称不一致时,一般通过驼峰映射或者AS关键字取别名可以搞定,但是对于复杂的orm映射,上述的2种方式就不能适用了。
resultMap是mybatis中最重要最强大的元素,使用ResultMap可以解决ORM复杂映射问题: 1. POJO属性名和表结构字段名不一致的问题(字段名:user_name,属性名:name) 2. 完成高级查询,比如说,一对一、一对多、多对多
之前使用驼峰匹配,方便封装用户。现取消驼峰映射,使用ResultMap。
resultMap标签自定义结果集,自行设置结果集的封装方式。
id属性:resultMap标签的唯一标识,不能重复,一般是用来被引用的
type属性:结果集的封装类型
autoMapping属性:操作单表时,不配置默认为true,如果pojo对象中的属性名称和表中字段名称相同,则自动映射。
SqlSession sqlSession = SessionFactoryUtils.getSession();
//获取接口代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//调用方法根据id查询单个数据
User user = mapper.findUserById(2);
System.out.println("user = " + user);
sqlSession.close();
注意:若xml中sql只查询数据库部分字段,需在pojo类加入无参构造,不然报索引超出边界异常。
提高了代码的复用性,结果集映射的;(将查询的结果映射到pojo类下)
sql标签可定义sql片段,在需使用该sql片段的地方,通过
定义sql片段
id,user_name,birthday,sex,address
使用SQL片段,在SQL语句中通过``标签引入SQL片段
若每个映射文件都编写一个相同的sql就比较麻烦,因此可将通用的sql片段都定义在一个专门存放sql片段的映射文件中,由其他映射文件引用即可。
在src目录下新增commonSql.xml映射文件
id,user_name,birthday,sex,address
test1,test2,test3
把该映射文件引入到mybatis的全局配置文件中(mybatis-config.xml):
引用sql片段
OGNL( Object Graph Navigation Language )对象图导航语言,这是一种强大的表达式语言,通过它可以非常方便的来操作对象属性。
1. e1 or e2 满足一个即可
2. e1 and e2 都得满足
3. e1 == e2 , e1 eq e2 判断是否相等
4. e1 != e2 , e1 neq e2 不相等
5. e1 lt e2:小于 lt表示less than
6. e1 lte e2:小于等于,其他gt(大于),gte(大于等于) gt 表示greater than
7. e1 in e2 e2包含e1
8. e1 not in e2 e2不包含e1
9. e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2
10. !e,not e:非,求反
11. e.method(args)调用对象方法
12. e.property对象属性值 user.userName
13. e1[ e2 ]按索引取值,List,数组和Map
14. @class@method(args)调用类的静态方法
15. @class@field调用类的静态字段值
满足条件sql加入拼接
说明:
1)if标签:判断语句,用于进行逻辑判断的。如果判断条件为true,则执行if标签的文本内容
2)test属性:用来编写表达式,支持ognl
查询男性用户,若输入用户名,按用户名模糊查询,若没输入用户名,查询所有男性用户
//接口
List findUsersByName(@Param("name") String name);
测试:
@Test
public void t8() {
SqlSession sqlSession = SessionFactoryUtils.getSession();
//获取接口代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List usersByName = mapper.findUsersByName("%道%");
System.out.println("usersByName = " + usersByName);
sqlSession.commit();
sqlSession.close();
}
//usersByName = [
// User{id=2, username='樱木花道', birthday=1992-11-12, sex='男', address='湘北高中一年'},
// User{id=14, username='仙道彰', birthday=1997-02-10, sex='男', address='陵南高中篮球队'}]
choose标签:分支选择(多选一,遇到成立的条件即停止)
when子标签:编写条件,不管有多少个when条件,一旦其中一个条件成立,后面的when条件都不执行
test属性:编写ognl表达式
otherwise子标签:当所有条件都不满足时,才会执行该条件。
select from user where sex = '男'
and user_name like #{name}
and address like #{address}
and user_name = '樱木花道'
编写一个查询方法,设置两个参数,一个是用户名,一个是住址
根据用户名或者住址查询所有男性用户:
如果输入了用户名则按照用户名模糊查找,
否则就按照住址查找,两个条件只能成立一个,
如果都不输入就查找用户名为“樱木花道”的用户。
接口:
//根据用户名或者住址查询所有男性用户
List findUsersByNameOrAddress(@Param("name") String name ,@Param("address") String address);
mapper.xml
测试:
@Test
public void t9() {
SqlSession sqlSession = SessionFactoryUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// List usersByName = mapper.findUsersByNameOrAddress("%道%",null);
// List usersByName = mapper.findUsersByNameOrAddress(null,"%湘北高中%");
//所有条件都不满足,查询名字为‘樱木花道’
List usersByName = mapper.findUsersByNameOrAddress(null,null);
System.out.println("usersByName = " + usersByName);
sqlSession.commit();
sqlSession.close();
}
格式:
select from user
user_name = #{name}
and address = #{address}
若只输入用户名按照用户名查询;若只输入住址按住址查询;若两者都输入,则按照两个条件查询;若两者都未输入,全表查询;
//接口
List findUsersByNameAndAddress(@Param("name") String name ,@Param("address") String address);
mapper.xml:
测试:
//where标签:拼接条件查询,去除多余and、or
// 若只输入用户名按照用户名查询;若只输入住址按住址查询;若两者都输入,则按照两个条件查询;若两者都未输入,全表查询;
@Test
public void t10() {
SqlSession sqlSession = SessionFactoryUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//无条件查全表
// List usersByName = mapper.findUsersByNameAndAddress(null,null);
//有名字查名字,有地址查地址,全有就全查
// List usersByName = mapper.findUsersByNameAndAddress("%道%",null);
// List usersByName = mapper.findUsersByNameAndAddress(null,"%湘北高中%");
List usersByName = mapper.findUsersByNameAndAddress("%道%","%湘北高中%");
System.out.println("usersByName = " + usersByName);
sqlSession.commit();
sqlSession.close();
}
格式:
update t
字段名1=值1,
字段名2=值2,
where 条件
修改用户信息,若参数user中的某个属性为null,则不修改。
//接口
int updateUserById(User user);
set标签内循环,如条件不匹配自动省略”,“.
update user
user_name=#{username},
birthday=#{birthday},
sex=#{sex},
address=#{address}
where id = #{id}
//set标签:去除被set标签包裹的多余的逗号','
@Test
public void t11() {
SqlSession sqlSession = SessionFactoryUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User(1,"赤木刚宪",null,null,null);
int i = mapper.updateUserById(user);
System.out.println("更新结果 = " + i);
sqlSession.commit();
sqlSession.close();
}
语法格式:
#{元素}
说明:
collection属性:接收的集合名或者数组名(接口中定义的名字)
item属性:集合或者数组中的每个元素别名 ,用于#{}中
separator属性:标签分隔符
open属性:以什么开始
close属性:以什么结束
按照id值是1,2,3来查询(删除)用户数据
//接口
List findByIds(@Param("ids") List ids);
//foreach标签:按照id值是1,2,3来查询(删除)用户数据,遍历集合或者数组,拼接成字符串用于sql中in关键字
@Test
public void t12() {
SqlSession sqlSession = SessionFactoryUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List ids = new ArrayList<>();
List list = Stream.of(1, 2,3).collect(Collectors.toList());
List byIds = mapper.findByIds(list);
System.out.println("查询结果 = " + byIds);
sqlSession.commit();
sqlSession.close();
}
在编写Mapper映射文件时,有时需用到一些诸如:>,<之类的特殊字符,不能直接书写在xml中,需对其处理。处理方式:使用转义字符代替特殊字符
转义字符 |
sql符号 |
说明 |
< |
< |
小于 |
> |
> |
大于 |
& |
& |
和号 |
' |
' |
单引号 |
" |
" |
双引号 |
举例:批量将id小于3的用户查询出来,在映射文件中直接写<号,xml约束会报错!
表与表的关系:
数据准备
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for tb_item
-- ----------------------------
DROP TABLE IF EXISTS `tb_item`;
CREATE TABLE `tb_item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`item_name` varchar(32) NOT NULL COMMENT '商品名称',
`item_price` float(6,1) NOT NULL COMMENT '商品价格',
`item_detail` text COMMENT '商品描述',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of tb_item
-- ----------------------------
INSERT INTO `tb_item` VALUES ('1', 'iPhone 6', '5288.0', '苹果公司新发布的手机产品。');
INSERT INTO `tb_item` VALUES ('2', 'iPhone 6 plus', '6288.0', '苹果公司发布的新大屏手机。');
-- ----------------------------
-- Table structure for tb_order
-- ----------------------------
DROP TABLE IF EXISTS `tb_order`;
CREATE TABLE `tb_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL,
`order_number` varchar(20) NOT NULL COMMENT '订单号',
PRIMARY KEY (`id`),
KEY `FK_orders_1` (`user_id`),
CONSTRAINT `FK_orders_1` FOREIGN KEY (`user_id`) REFERENCES `tb_user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of tb_order
-- ----------------------------
INSERT INTO `tb_order` VALUES ('1', '1', '20140921001');
INSERT INTO `tb_order` VALUES ('2', '2', '20140921002');
INSERT INTO `tb_order` VALUES ('3', '1', '20140921003');
-- ----------------------------
-- Table structure for tb_orderdetail
-- ----------------------------
DROP TABLE IF EXISTS `tb_orderdetail`;
CREATE TABLE `tb_orderdetail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_id` int(32) DEFAULT NULL COMMENT '订单号',
`item_id` int(32) DEFAULT NULL COMMENT '商品id',
`total_price` double(20,0) DEFAULT NULL COMMENT '商品总价',
`status` int(11) DEFAULT NULL COMMENT '状态',
PRIMARY KEY (`id`),
KEY `FK_orderdetail_1` (`order_id`),
KEY `FK_orderdetail_2` (`item_id`),
CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`order_id`) REFERENCES `tb_order` (`id`),
CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`item_id`) REFERENCES `tb_item` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of tb_orderdetail
-- ----------------------------
INSERT INTO `tb_orderdetail` VALUES ('1', '1', '1', '5288', '1');
INSERT INTO `tb_orderdetail` VALUES ('2', '1', '2', '6288', '1');
INSERT INTO `tb_orderdetail` VALUES ('3', '2', '2', '6288', '1');
INSERT INTO `tb_orderdetail` VALUES ('4', '3', '1', '5288', '1');
-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(100) DEFAULT NULL COMMENT '用户名',
`password` varchar(100) DEFAULT NULL COMMENT '密码',
`name` varchar(100) DEFAULT NULL COMMENT '姓名',
`age` int(10) DEFAULT NULL COMMENT '年龄',
`sex` int(11) DEFAULT NULL COMMENT '0-女 1-男',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`user_name`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO `tb_user` VALUES ('1', 'zhangsan', '123456', '樱木花道', '30', '1');
INSERT INTO `tb_user` VALUES ('2', 'lisi', '123456', '赤木刚宪', '21', '0');
INSERT INTO `tb_user` VALUES ('3', 'wangwu', '123456', '流川枫', '22', '1');
INSERT INTO `tb_user` VALUES ('4', 'zhangwei', '123456', '宫城良田', '20', '1');
INSERT INTO `tb_user` VALUES ('5', 'lina', '123456', '三井寿', '28', '0');
order订单类:
public class Order {
private Integer id;
private String orderNumber;
//实现一对一映射 加入类型为User的order属性
private User2 orderUser;
getter、setter、toString...
}
user用户类:
public class User2 implements Serializable{
private Long id;
// 用户名
private String userName;
// 密码
private String password;
// 姓名
private String name;
// 年龄
private Integer age;
//0-女 1-男
private Integer sex;
getter、setter、toString...
}
item商品类:
public class Item {
private Integer id;
private String itemName;
private Float itemPrice;
private String itemDetail;
getter、setter、toString...
}
orderdetail 订单明细类:
public class Orderdetail {
private Integer id;
private Double totalPrice;
private Integer status;
getter、setter、toString...
}
......
若使用as取别名,则column="别名"。
通过订单编号20140921003查询出订单信息,并查询出下单人信息
//高级查询 一对一 通过订单编号20140921003查询出订单信息,并查询出下单人信息
Order findUserByOrderNum(@Param("orderNumber") String orderNumber);
若使用as取别名,则column="别名"。
//通过订单编号20140921003查询出订单信息(以订单为返回)
//高级查询 一对一 通过订单编号20140921003查询出订单信息,并查询出下单人信息
@Test
public void t2() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final Order userByOrderNum = mapper.findUserByOrderNum("20140921003");
System.out.println("userByOrderNum = " + userByOrderNum);
session.close();
}
实现步骤:
1) 维护pojo对象之间一对一的关系:
Order 包含属性:User类型的变量
2) 配置一对一的映射关系:
若使用as取别名,则column="别名"。
//查询id为1的用户及其订单信息(以用户为返回)
//高级查询 一对一 通过订单编号20140921003查询出订单信息,并查询出下单人信息
User2 findUserAndOrderById(@Param("userId") Long userId);
public class User implements Serializable{
private List orders;
private Long id;
private String userName; // 用户名
private String password; // 密码
private String name; // 姓名
private Integer age; // 年龄
private Integer sex; //0-女 1-男
// getter and setter and toString
}
//高级查询 一对多 查询id为1的用户及其订单信息(以用户为返回) 一个用户可以有多个订单,一个订单只能属于一个用户,用户(1)-订单(n)
@Test
public void t3() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final User2 userAndOrderById = mapper.findUserAndOrderById(1L);
System.out.println("userAndOrderById = " + userAndOrderById);
session.close();
}
/*User2{userOrder=[
Order{id=1, orderNumber='20140921001', orderUser=null},
Order{id=3, orderNumber='20140921003', orderUser=null}],
id=1, userName='zhangsan', password='123456', name='樱木花道', age=30, sex=1}*/
在一对多的场景中,一般主表中通过创建一个集合属性来包含从表的数据
user类包含List
多对多查询本质是一对多和一对一查询的组合,核心标签用法如下:
查询订单号为20140921001的订单,包含订单详情信息以及订单中的商品信息
select orders.id order_id, orders.order_number,
detail.id detail_id, detail.status, detail.total_price,
item.id, item.item_name, item.item_price, item.item_detail
from tb_order orders, tb_orderdetail detail, tb_item item
where orders.id = detail.order_id
and detail.item_id = item.id
and orders.order_number = '20140921001';
//‘订单’类关联‘订单详情’属性
public class Order {
private Integer id;
private String orderNumber;
private User user;
private List orderdetailList;
...
}
// 每一条订单详情记录中都包含了一条商品信息
//‘订单详情’类关联‘商品’属性
public class Orderdetail {
private Integer id;
private Double totalPrice;
private Integer status;
private Item item;
......
}
//高级查询 多对多 返回订单 查询订单号为20140921001的订单,包含订单详情信息以及订单中的商品信息
Order findDetailAndItem(@Param("orderNumber")String orderNumber);
//高级查询 多对多 返回订单 查询订单号为20140921001的订单,包含订单详情信息以及订单中的商品信息
@Test
public void t4() {
final SqlSession session = SessionFactoryUtils.getSession();
final UserMapper mapper = session.getMapper(UserMapper.class);
final Order detailAndItem = mapper.findDetailAndItem("20140921001");
System.out.println("多对多 = " + detailAndItem);
session.close();
}
查询订单号为20140921001的订单信息、订单所属用户信息、订单中的详细商品信息
SELECT
tb_user.id as user_id, tb_user.age, tb_user.`password`, tb_user.sex,
tb_user.user_name, tb_user.`name`, tb_order.id AS order_id, tb_order.order_number,
tb_orderdetail.id AS detail_id, tb_orderdetail.total_price, tb_orderdetail.`status`,
tb_item.*
FROM
tb_order, tb_orderdetail, tb_item, tb_user
WHERE
tb_order.id = tb_orderdetail.order_id
AND tb_orderdetail.item_id = tb_item.id
AND tb_order.user_id=tb_user.id
AND tb_order.order_number = ?
//根据订单编号查询订单信息,用户信息和订单详情信息以及关联的商品信息
Order findByOrderNumber3(@Param("orderNumber") String orderNumber);
若两个ResultMap结果集有重叠的部分,可以通过extend属性继承简化;
将上述扩展通过extends继承后简化映射文件配置: