当前位置: 首页 > news >正文

Mybatis技术原理详解之:使用Mapper形式和注解驱动的复杂映射开发

Mybatis技术原理详解之:使用Mapper形式和注解驱动的复杂映射开发

Mapper形式的复杂映射开发

一对一查询

一对一查询的模型

⽤户表和订单表的关系为,⼀个⽤户有多个订单,⼀个订单只从属于⼀个⽤户

⼀对⼀查询的需求:查询⼀个订单,与此同时查询出该订单所属的⽤户

在这里插入图片描述

建表语句
create table user
(`id` int(11),`username` varchar(30),`password` varchar(30),`birthday` varchar(30)
)insert into user values (1, 'lisi', '123', '2023-11-01');
insert into user values (2, 'zhangsan', '123', '2023-11-01');create table `order`
(`id` int(11),`order_time` datetime,`total` float,`uid` int
)insert into `order` values (1, '2023-11-03 12:00:00', 10.0, 1);
insert into `order` values (2, '2023-11-03 12:00:00', 20.0, 1);
insert into `order` values (3, '2023-11-03 12:00:00', 20.0, 2);

构造查询语句

select *, o.id oid from `user` u left join `order` o on u.id = o.uid

查询结果如下所示:

!在这里插入图片描述

创建Order和User实体
@ToString
@Data
public class Order {private int id;private Date orderTime;private double total;// 标识每个订单对应的唯一用户private User user;}@ToString
@Data
public class User {private int id;private String username;private String password;private Date birthday;}
创建OrderMapper接口和OrderMapper.xml

创建OrderMapper接口:

public interface UserMapper {List<Order> oneToOneQuery();
}

创建OrderMapper.xml文件:

<mapper namespace="com.netease.mail.data.dao.UserMapper"><resultMap id="orderMap" type="com.netease.mail.data.pojo.Order"><result column="id" javaType="java.lang.Integer" property="id"></result><result column="order_time" javaType="java.util.Date" property="orderTime"></result><result column="total" javaType="java.lang.Double" property="total"></result><association property="user" javaType="com.netease.mail.data.pojo.User"><result column="uid" javaType="java.lang.Integer" property="id"></result><result column="username" javaType="java.lang.String" property="username"></result><result column="password" javaType="java.lang.String" property="password"></result><result column="birthday" javaType="java.util.Date" property="birthday"></result></association></resultMap><select id="oneToOneQuery" resultMap="orderMap">select *, u.id uid from `order` o left join `user` u on o.uid = u.id</select></mapper>
测试结果
public class MybatisDemo {public static void main(String[] args) throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper userMapper = sqlSession.getMapper(UserMapper.class);System.out.println("---------一对一级联查询-----------");// 一对一级联查询oneToOneMapping(userMapper);}public static void oneToOneMapping(UserMapper userMapper) {// 一对一关联查询List<Order> orders = userMapper.oneToOneQuery();orders.forEach(System.out::println);}}

执行结果如下所示:

在这里插入图片描述

可以看到,结果打印出了每个订单与相应用户的关联信息。

一对多查询

⽤户表和订单表的关系为,⼀个⽤户有多个订单,⼀个订单只从属于⼀个⽤户

⼀对多查询的需求:查询⼀个⽤户,与此同时查询出该⽤户具有的订单

在这里插入图片描述

构造查询语句
select *, o.id oid from `user` u left join `order` o on u.id = o.uid

查询结果如下所示:

在这里插入图片描述

修改User实体
@ToString
@Data
public class Order {private int id;private Date orderTime;private double total;// 标识每个订单对应的唯一用户private User user;}@ToString
@Data
public class User {private int id;private String username;private String password;private Date birthday;// 标识当前用户具有哪些订单private List<Order> orderList;
}
创建UserMapper接口和xml语句

UserMapper.java

public interface UserMapper {List<Order> oneToOneQuery();List<User> oneToMultiQuery();
}

UserMapper.xml

<mapper namespace="com.netease.mail.data.dao.UserMapper"><resultMap id="userMap" type="com.netease.mail.data.pojo.User"><result column="id" javaType="java.lang.Integer" property="id"></result><result column="username" javaType="java.lang.String" property="username"></result><result column="password" javaType="java.lang.String" property="password"></result><result column="birthday" javaType="java.util.Date" property="birthday"></result><collection property="orderList" ofType="com.netease.mail.data.pojo.Order"><result column="oid" javaType="java.lang.Integer" property="id"></result><result column="order_time" javaType="java.util.Date" property="orderTime"></result><result column="total" javaType="java.lang.Double" property="total"></result></collection></resultMap><select id="oneToMultiQuery" resultMap="userMap">select *, o.id oid from `user` u left join `order` o on u.id = o.uid</select></mapper>
测试结果

测试代码如下:

    public static void main(String[] args) throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper userMapper = sqlSession.getMapper(UserMapper.class);System.out.println("---------一对多级联查询-----------");// 一对多级联关系查询oneToMultiMapping(userMapper);}public static void oneToMultiMapping(UserMapper userMapper) {// 一对多级联关系查询List<User> users = userMapper.oneToMultiQuery();users.forEach( user -> {System.out.println(user.getUsername());List<Order> orderList = user.getOrderList();orderList.forEach(System.out::println);});}

代码执行打印信息如下所示:

在这里插入图片描述

从上面的打印信息中可以看到,一对多的映射查询,在UserMapper.xml中设置了<collection>标签后,会将查询结果中<result>标签字段相同的行进行合并,并将合并后的<collection>标签下的<result>字段转换映射到设置的实体类属性List集合中, 从而实现一对多的映射查询。

多对多映射查询

多对多查询的模型

⽤户表和⻆⾊表的关系为,⼀个⽤户有多个⻆⾊,⼀个⻆⾊被多个⽤户使⽤

多对多查询的需求:查询⽤户同时查询出该⽤户的所有⻆⾊

在这里插入图片描述

构造查询语句
select u.*,r.*,r.id rid from user u left join user_role ur on u.id=ur.user_id
left join role r on ur.role_id=r.id

查询结果如下所示:

在这里插入图片描述

创建Role实体、修改User实体
@Data
@ToString
public class Role {private int id;private String roleName;
}@ToString
@Data
public class User {private int id;private String username;private String password;private Date birthday;// 标识当前用户具有哪些订单private List<Order> orderList;// 标识当前用户具有哪些角色private List<Role> roleList;
}
添加UserMapper接口方法和xml语句

UserMapper.java

public interface UserMapper {List<Order> oneToOneQuery();List<User> oneToMultiQuery();List<User> multiToMulti();}

UserMapper.xml

<mapper namespace="com.netease.mail.data.dao.UserMapper"><resultMap id="orderMap" type="com.netease.mail.data.pojo.Order"><result column="id" javaType="java.lang.Integer" property="id"></result><result column="order_time" javaType="java.util.Date" property="orderTime"></result><result column="total" javaType="java.lang.Double" property="total"></result><association property="user" javaType="com.netease.mail.data.pojo.User"><result column="uid" javaType="java.lang.Integer" property="id"></result><result column="username" javaType="java.lang.String" property="username"></result><result column="password" javaType="java.lang.String" property="password"></result><result column="birthday" javaType="java.util.Date" property="birthday"></result></association></resultMap><resultMap id="userMap" type="com.netease.mail.data.pojo.User"><result column="id" javaType="java.lang.Integer" property="id"></result><result column="username" javaType="java.lang.String" property="username"></result><result column="password" javaType="java.lang.String" property="password"></result><result column="birthday" javaType="java.util.Date" property="birthday"></result><collection property="orderList" ofType="com.netease.mail.data.pojo.Order"><result column="oid" javaType="java.lang.Integer" property="id"></result><result column="order_time" javaType="java.util.Date" property="orderTime"></result><result column="total" javaType="java.lang.Double" property="total"></result></collection></resultMap><resultMap id="roleMap" type="com.netease.mail.data.pojo.User"><result column="id" javaType="java.lang.Integer" property="id"></result><result column="username" javaType="java.lang.String" property="username"></result><result column="password" javaType="java.lang.String" property="password"></result><result column="birthday" javaType="java.util.Date" property="birthday"></result><collection property="roleList" ofType="com.netease.mail.data.pojo.Role"><result column="rid" javaType="java.lang.Integer" property="id"></result><result column="role_name" javaType="java.lang.String" property="roleName"></result></collection></resultMap><select id="oneToOneQuery" resultMap="orderMap">select *, u.id uid from `order` o left join `user` u on o.uid = u.id</select><select id="oneToMultiQuery" resultMap="userMap">select *, o.id oid from `user` u left join `order` o on u.id = o.uid</select><select id="multiToMulti" resultMap="roleMap">select u.*,r.*,r.id rid from user u left join user_role ur on u.id=ur.user_idinner join role r on ur.role_id=r.id</select></mapper>
测试结果

测试代码如下所示:

public class MybatisDemo {public static void main(String[] args) throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper userMapper = sqlSession.getMapper(UserMapper.class);System.out.println("---------一对一级联查询-----------");// 一对一级联查询oneToOneMapping(userMapper);System.out.println("---------一对多级联查询-----------");// 一对多级联关系查询oneToMultiMapping(userMapper);System.out.println("---------多对多级联查询-----------");multiToMultiMapping(userMapper);}public static void oneToOneMapping(UserMapper userMapper) {// 一对一关联查询List<Order> orders = userMapper.oneToOneQuery();orders.forEach(System.out::println);}public static void oneToMultiMapping(UserMapper userMapper) {// 一对多级联关系查询List<User> users = userMapper.oneToMultiQuery();users.forEach( user -> {System.out.println(user.getUsername());List<Order> orderList = user.getOrderList();orderList.forEach(System.out::println);});}// 多对多级联关系查询public static void multiToMultiMapping(UserMapper userMapper) {List<User> users = userMapper.multiToMulti();users.forEach(user -> {System.out.println(user.getUsername());List<Role> roleList = user.getRoleList();roleList.forEach(System.out::println);});}}

上面展示了一对一、一对多、多对多的整体测试代码,单独执行多对多的级联关系查询结果如下所示:

在这里插入图片描述

根据上面的查询结果看分析得到,多对多的查询结果封装与一对多的原理类似。因为在关联查询时,无论是left join还是right join,在单次查询中,实际上都是一对多的查询逻辑。比如在上面的查询案例中,以user表出发做left join,此时在上面的多对多模型中,相当于是一个用户对应多个角色,所以查询结果仍然是一对多模型的查询原理。

使用注解的形式完成复杂映射开发

这⼏年来注解开发越来越流⾏,Mybatis也可以使⽤注解开发⽅式,这样我们就可以减少编写Mapper映射⽂件了。常用的Mybatis注解:

@Insert:实现新增

@Update:实现更新

@Delete:实现删除

@Select:实现查询

@Result:实现结果集封装

@Results:可以与@Result ⼀起使⽤,封装多个结果集

@One:实现⼀对⼀结果集封装

@Many:实现⼀对多结果集封装

实现复杂关系映射之前我们可以在映射⽂件中通过配置来实现,使⽤注解开发后,我们可以使⽤@Results注解,

@Result注解,@One注解,@Many注解组合完成复杂关系的配置

在这里插入图片描述

在使用Mybatis注解的方式对上面采用Mapper的方式进行改造之前,由于我们不需要UserMapper.xml文件了,因此,需要修改MyBatis的核⼼配置⽂件,加载使⽤了注解的Mapper接⼝即可。

    <mappers><mapper resource="mapper/UserMapper.xml"/></mappers>替换为<mappers><mapper class="com.netease.mail.data.dao.UserMapper"/></mappers>

或者指定扫描包含映射关系的接⼝所在的包也可以:

    <mappers><package name="com.netease.mail.data.dao"/></mappers>

接下来,我们将使用Mybatis注解的方式来实现上面采用Mapper方式的功能,用实际案例来介绍Mybatis注解开发的基本原理和过程

一对一查询

一对一查询模型和User、Order实体类与上面案例中的一致。需要修改的是创建UserMapper.javaOrderMapper.java两个接口类:

使用注解配置Mapper

编写OrderMapper接口方法查询order表

public interface OrderMapper {@Select("select * from `order`")@Results({@Result(id=true, property = "id", column = "id"),@Result(property = "orderTime", column = "order_time"),@Result(property = "total", column = "total"),@Result(property = "user", column = "uid",javaType = User.class,one=@One(select = "com.netease.mail.data.dao.UserMapper.selectUserById"))})List<Order> findAll();
}

@Results注解代替的标签就相当于是<resultMap>,@Result注解代替的标签就相当于是<result>。需要注意的就是@One注解,该注解代替了<assocation>标签,利用select语句来查询关联表的信息。这里@One注解里的column属性的uid,是作为关联参数传递到UserMapper.selectUserById的sql方法中,用于替换占位符的。

编写UserMapper接口方法,根据传入的uid,查询用户信息并返回

public interface UserMapper {@Select("select * from user where id = #{id}")User selectUserById(@Param("id") int id);
}
编写测试方法
public class MybatisDemo {private static OrderMapper orderMapper;private static UserMapper userMapper;public static void main(String[] args) throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();userMapper = sqlSession.getMapper(UserMapper.class);orderMapper = sqlSession.getMapper(OrderMapper.class);System.out.println("---------注解驱动的一对一级联查询-----------");annotationOneToOneQuery();}public static void annotationOneToOneQuery() {List<Order> orders = orderMapper.findAll();orders.forEach(System.out::println);}}

执行得到输出结果如下所示:

在这里插入图片描述

可以看到,我们在OrderMapper里的findAll方法中,使用注解地方式将需要关联的uid字段传入到了UserMapper的对应方法中,替换了占位符的值。

一对多查询

模型仍然以上面一对多例子中的用户和订单的关联关系为例展开。

⽤户表和订单表的关系为,⼀个⽤户有多个订单,⼀个订单只从属于⼀个⽤户

⼀对多查询的需求:查询⼀个⽤户,与此同时查询出该⽤户具有的订单

User实体与Order实体的配置与上面Mapper形式中的一对多查询的实体类属性保持一致。

使用注解配置Mapper
public interface OrderMapper {@Select("select * from `order` where uid = #{id}")List<Order> findByUid();}public interface UserMapper {@Select("select * from user")@Results(value = {@Result(id=true, property = "id", column = "id"),@Result(property = "username", column = "username"),@Result(property = "password", column = "password"),@Result(property = "birthday", column = "birthday"),@Result(property = "orderList", column = "id",javaType = List.class,many=@Many(select = "com.netease.mail.data.dao.OrderMapper.findByUid"))})List<User> findUserAndOrder();
}
测试结果

在这里插入图片描述

从上图可以看到,同样利用注解的方式,将关联属性在两个方法中进行传递,实现了一对多的级联查询。只是这里用的注解是@Many注解

多对多查询

多对多查询的模型同样体现在,⽤户表和⻆⾊表的关系为,⼀个⽤户有多个⻆⾊,⼀个⻆⾊被多个⽤户使⽤

多对多查询的需求:查询⽤户同时查询出该⽤户的所有⻆⾊

数据表与实体类的修改与上面Mapper形式中的保持一致。

使用注解配置Mapper
public interface UserMapper {@Select("select * from user")@Results(value = {@Result(id=true, property = "id", column = "id"),@Result(property = "username", column = "username"),@Result(property = "password", column = "password"),@Result(property = "birthday", column = "birthday"),@Result(property = "roleList", column = "id",javaType = List.class,many=@Many(select = "com.netease.mail.data.dao.RoleMapper.findByUserId"))})List<User> findUserAndRole();
}public interface RoleMapper {@Select("select * from role r left join user_role ur on r.id = ur.role_id where ur.user_id = #{id}")@Results({@Result(id=true, property = "id", column = "id"),@Result(property = "roleName", column = "role_name")})List<Role> findByUserId();
}
测试结果

在这里插入图片描述

至此,我们实现了利用Mapper.xml文件的方式和注解驱动的两种方式,来实现Mybatis复杂映射开发的过程。利用案例来更加清晰地展示复杂映射开发的原理和过程,便于日后忘记了,可以回过头来借鉴。

相关文章:

  • 数据结构(超详细讲解!!)第二十一节 特殊矩阵的压缩存储
  • 【lvgl】linux开发板搭建环境
  • 类和对象解析
  • Java21-虚拟线程小试牛刀-meethigher
  • Python---字符串中的查找方法--index()--括号里是要获取的字符串
  • Qt5 安装 phonon
  • 力扣:150. 逆波兰表达式求值(Python3)
  • 数据结构和算法的区分和学习
  • Flask蓝图(Blueprint)
  • Pycharm 对容器中的 Python 程序断点远程调试
  • visual basic 6.0软件安装包(永久),适用于Windows各系统附安装教程
  • 旅游业为什么要选择VR全景,VR全景在景区旅游上有哪些应用
  • 路由器基础(二): BGP原理与配置
  • 【计算机组成与设计】Chisel取指和指令译码设计
  • ‘spmatrix‘ has no attribute ‘__div__‘. Did you mean: ‘__dir__‘?
  • 《深入 React 技术栈》
  • canvas绘制圆角头像
  • Docker入门(二) - Dockerfile
  • learning koa2.x
  • Linux中的硬链接与软链接
  • python大佬养成计划----difflib模块
  • scrapy学习之路4(itemloder的使用)
  • Terraform入门 - 3. 变更基础设施
  • VuePress 静态网站生成
  • 从setTimeout-setInterval看JS线程
  • 从零开始在ubuntu上搭建node开发环境
  • 服务器之间,相同帐号,实现免密钥登录
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 思否第一天
  • puppet连载22:define用法
  • ​业务双活的数据切换思路设计(下)
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • $GOPATH/go.mod exists but should not goland
  • (30)数组元素和与数字和的绝对差
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (附源码)计算机毕业设计大学生兼职系统
  • (三)elasticsearch 源码之启动流程分析
  • (十一)手动添加用户和文件的特殊权限
  • (图)IntelliTrace Tools 跟踪云端程序
  • (转)setTimeout 和 setInterval 的区别
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • .mysql secret在哪_MySQL如何使用索引
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。
  • .net FrameWork简介,数组,枚举
  • .NET HttpWebRequest、WebClient、HttpClient
  • .NET 指南:抽象化实现的基类
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • [ C++ ] STL---string类的模拟实现
  • [3D游戏开发实践] Cocos Cyberpunk 源码解读-高中低端机性能适配策略
  • [BZOJ1877][SDOI2009]晨跑[最大流+费用流]
  • [Deepin 15] 编译安装 MySQL-5.6.35
  • [Docker]六.Docker自动部署nodejs以及golang项目
  • [EFI]Dell Inspiron 15 5567 电脑 Hackintosh 黑苹果efi引导文件
  • [Firefly-Linux] RK3568修改控制台DEBUG为普通串口UART