mybaties之关联查询与延迟加载

本文详细介绍MyBatis中的一对一、一对多和多对多关联查询方式,包括使用resultType和resultMap进行结果映射的方法,并演示如何实现延迟加载以提高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mybatis专栏 https://blog.youkuaiyun.com/worn_xiao/category_6530299.html?spm=1001.2014.3001.5482

一Mybaties关联查询与缓存

基本数据模型:

如上所示是我们电商系统的基本数据模型

1.1 一对一映射

模拟一个订单只能属于一个用户的,一对一映射。

SELECT
 orders.`id`,
 orders.`user_id`,
 orders.`number`,
 user.`username`,
 user.`sex`
FROM
 orders,
 USER
WHERE orders.`user_id` = user.`id`

1.1.1 以resultType作为扩展类

创建可扩展的PO类,扩展订单实体:

publicclass OrdersExt extends Orders{

   

    //添加用户属性

    /*USER.username,

      USER.address */

   

    private String username;

    private String address;

    public String getUsername() {

       returnusername;

    }

    publicvoid setUsername(String username) {

       this.username = username;

    }

    public String getAddress() {

       returnaddress;

    }

    publicvoid setAddress(String address) {

       this.address = address;

    }

   

}

创建mapper映射接口:

public List<OrdersExt> findOrdersUser();

创建映射文件:

<mapper namespace="cn.itcast.mybatis.mapper.OrdersMapper">

 

    <!-- 定义查询订单表列名的SQL片段 -->

    <sql id="select_orders">

       Orders.id,

       Orders.user_id,

       orders.number,

       orders.createtime,

       orders.note

    </sql>

    <!-- 定义查询用户表列名的SQL片段 -->

    <sql id="select_user">

       user.username,

       user.address

    </sql>

    <!-- 进行订单信息查询,包括用户的名称和地址信息 -->

    <select id="findOrdersUser" resultType="OrdersExt">

       Select

       <include refid="select_orders"/>

       <include refid="select_user"></include>

       from orders,user

       where orders.user_id = user.id

    </select>

</mapper>

加载映射文件

<!-- 批量加载mapper文件,需要mapper接口文件和mapper映射文件名称相同且在同一个包下 -->
<package name="cn.itcast.mybatis.mapper"/>

测试代码

@Test

publicvoid testFindOrdersUser(){

    // 创建sqlSession

    SqlSession sqlSession = sqlSessionFactory.openSession();

 

    // 通过SqlSession构造usermapper的代理对象

    OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);

    // 调用usermapper的方法

    List<OrdersExt> list = ordersMapper.findOrdersUser();

 

    System.out.println(list);

 

    // 释放SqlSession

    sqlSession.close();

}

1.1.2 以resultMap作为扩展

 vo实体类

publicclass Orders {

    private Integer id;

    private Integer userId;

    private String number;

    private Date createtime;

private String note;

private User user;

mapper接口:

public List<OrdersExt> findOrdersUserRstMap();

<!-- 进行订单信息查询,包括用户的名称和地址信息 (ResultMap)-->

    <select id="findOrdersUserRstMap" resultMap="OrdersUserRstMap">

       Select

       <include refid="select_orders"/>

       ,

        <include refid="select_user"></include>

       from orders,user

       where orders.user_id = user.id

xml映射文件

</select>

 

    <!-- 定义orderUserResultMap -->

    <resultMap type=" cn.itcast.mybatis.po.Orders" id="OrdersUserRstMap">

       <id column="id"property="id" />

       <result column="user_id"property="userId" />

       <result column="number"property="number" />

       <result column="createtime"property="createtime" />

       <result column="note"property="note" />

       <!-- 映射一对一关联关系的用户对象-->

       <!--

           property:指定关联对象要映射到Orders的哪个属性上

           javaType:指定关联对象所要映射的java类型

         -->

       <!-- id标签:指定关联对象结果集的唯一标识,很重要,不写不会报错,但是会影响性能 -->

       <association property="user"javaType="cn.itcast.mybatis.po.User">

           <id column="user_id"property="id" />

           <result column="username"property="username" />

           <result column="address"property="address" />

       </association>

    </resultMap>

测试代码

@Test

publicvoid testFindOrdersUserRstMap() {

    // 创建sqlSession

    SqlSession sqlSession = sqlSessionFactory.openSession();

 

    // 通过SqlSession构造usermapper的代理对象

    OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);

    // 调用usermapper的方法

    List<Orders> list = ordersMapper.findOrdersUserRstMap();

      

    //此处我们采用debug模式来跟踪代码,然后验证结果集是否正确

    System.out.println(list);

    // 释放SqlSession

    sqlSession.close

 

resultType:使用resultType实现较为简单,如果pojo中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。

如果没有查询结果的特殊要求建议使用resultType。

resultMap:需要单独定义resultMap,实现有点麻烦,如果对查询结果有特殊的要求,使用resultMap可以完成将关联查询映射pojo的对象属性中。

resultMap可以实现延迟加载,resultType无法实现延迟加载。

1.2 一对多映射

业务场景描述:一个订单有多个订单明细。

Select

         Orders.id,

         Orders.user_id,

         orders.number,

orders.createtime,

orders.note,

user.username,

user.address,

orderdetail.iddetail_id,

orderdetail.items_id,

orderdetail.items_num

from orders,user,orderdetail

where orders.user_id = user.id

         andorders.id = orderdetail.orders_id

 

修改订单实体

在Orders类中添加以下属性,并提供get/set方法:

//订单明细

private List<Orderdetail> detailList;

mapper接口

// 查询订单信息及订单明细信息(一对多映射之使用resultMap)

public List<Orders> findOrdersAndOrderdetailRstMap();

编写映射文件Mapper.xml

<!-- 定义OrdersAndOrderdetailRstMap -->

<!-- extends:继承已有的ResultMap,值为继承的ResultMap的唯一标示 -->

<resultMap type="Orders"id="OrdersAndOrderdetailRstMap"

    extends="OrdersUserRstMap">

       <!-- 映射关联关系(一对多) -->

       <!-- collection标签:定义一个一对多关系

           ofType:指定该集合参数所映射的类型

        -->

       <collection property="detailList"ofType="Orderdetail">

           <id column="detail_id"property="id" />

           <result column="items_id"property="itemsId" />

           <result column="items_num"property="itemsNum" />

       </collection>

    </resultMap>

 

<!-- 查询订单信息,包括用户名称、用户地址,订单商品信息(嵌套结果) -->

<select id="findOrdersAndOrderdetailRstMap"resultMap="OrdersAndOrderdetailRstMap">

 

       Select

       <include refid="select_orders"/>,

       <include refid="select_user"/>,

       orderdetail.id detail_id,

       orderdetail.items_id,

       orderdetail.items_num

       from orders,user,orderdetail

       where orders.user_id = user.id

       and

       orders.id = orderdetail.orders_id

 

    </select>

resultMap的extends属性:可以用此属性来继承一个已有的resultmap。但是它继承的resultMap的type和它本身的type要保持一致。通过resultmap的继承,可以减少mapper的冗余映射。

测试代码

@Test

publicvoid testFindOrdersAndOrderdetailRstMap() {

    // 创建sqlSession

    SqlSession sqlSession = sqlSessionFactory.openSession();

 

    // 通过SqlSession构造usermapper的代理对象

    OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);

    // 调用usermapper的方法

    List<Orders> list = ordersMapper.findOrdersAndOrderdetailRstMap();

      

    //此处我们采用debug模式来跟踪代码,然后验证结果集是否正确

    System.out.println(list);

    // 释放SqlSession

    sqlSession.close();

mybatis使用resultMap的collection对关联查询的多条记录映射到一个list集合属性中。

1.3 多对多映射

用户信息与商品信息的多对多关系

select

         Orders.id,

         Orders.user_id,

         orders.number,

orders.createtime,

orders.note,

user.username,

user.address,

orderdetail.iddetail_id,

orderdetail.items_id,

orderdetail.items_num

 items.name items_name,

 items.detail items_detail 

FROM

 orders,

USER,

 orderdetail,

 items

WHERE user.`id` = orders.`user_id`

  ANDorders.`id` = orderdetail.`orders_id`

  ANDorderdetail.`items_id` = items.`id`

修改实体信息,分别在用户中添加订单信息,订单中添加订单明细,订单明细中添加订单项

在user类中添加List<Orders> ordersList 属性

// 订单信息

private List<Orders> ordersList;

在Orders类中添加List<Orderdetail>属性

//订单明细

private List<Orderdetail> detailList;

在Orderdetail类中添加Items属性

//商品信息

private Items items;

接口定义

//查询用户及用户购买商品信息(多对多映射之使用resultMap)

public List<User> findUserAndItemsRstMap();

编写映射文件

<!-- 定义UserAndItemsRstMap -->

    <resultMap type="User" id="UserAndItemsRstMap">

       <!-- 用户信息 -->

       <!-- id:关联查询用户的唯一标示 -->

       <id column="user_id"property="id" />

       <result column="username"property="username" />

       <result column="address"property="address" />

       <!-- 订单信息(一个用户有多个订单) -->

       <collection property="ordersList"ofType="orders">

           <id column="id"property="id" />

           <result column="user_id"property="userId" />

           <result column="number"property="number" />

           <result column="createtime"property="createtime" />

           <result column="note"property="note" />

           <!-- 订单明细信息(一个订单有多个订单明细) -->

           <collection property="detailList"ofType="orderdetail">

              <id column="detail_id"property="id" />

              <result column="items_id"property="itemsId" />

              <result column="items_num"property="itemsNum" />

              <!-- 商品信息(一个订单明细对应一个商品) -->

              <association property="items"javaType="cn.itcast.mybatis.po.Items">

                  <id column="items_id"property="id" />

                  <result column="items_name"property="name" />

                  <result column="items_detail"property="detail" />

              </association>

           </collection>

       </collection>

    </resultMap>

 

    <!-- 查询用户及用户购买商品信息(多对多映射之使用resultMap) -->

    <select id="findUserAndItemsRstMap" resultMap="UserAndItemsRstMap">

       Select

       <include refid="select_orders"/>

       ,

       <include refid="select_user"/>

       ,

       <include refid="select_orderdetail"></include>

       ,

       items.name items_name,

       items.detail items_detail

       from

       orders,user,orderdetail,items

       where orders.user_id = user.id

       and

       orders.id = orderdetail.orders_id

       and orderdetail.items_id = items.id

    </select>

编写测试代码:

@Test

    publicvoid testFindUserAndItemsRstMap() {

       // 创建sqlSession

       SqlSession sqlSession = sqlSessionFactory.openSession();

       // 通过SqlSession构造usermapper的代理对象

       OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);

       // 调用usermapper的方法

       List<User> list = ordersMapper.findUserAndItemsRstMap();

       // 此处我们采用debug模式来跟踪代码,然后验证结果集是否正确

        System.out.println(list);

       // 释放SqlSession

       sqlSession.close();

    }

1.3.1 关联映射总结

resultType:

作用:将查询结果按照sql列名pojo属性名一致性映射到pojo中。

场合:常见一些明细记录的展示,比如用户购买商品明细,将关联查询信息全部展示在页面时,此时可直接使用resultType将每一条记录映射到pojo中,在前端页面遍历list(list中是pojo)即可。

resultMap:使用association和collection完成一对一和一对多高级映射(对结果有特殊的映射要求)。

association:

作用:将关联查询信息映射到一个pojo对象中。

场合:为了方便查询关联信息可以使用association将关联订单信息映射为用户对象的pojo属性中,比如:查询订单及关联用户信息。

使用resultType无法将查询结果映射到pojo对象的pojo属性中,根据对结果集查询遍历的需要选择使用resultType还是resultMap。

        

collection:

作用:将关联查询信息映射到一个list集合中。

场合:为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中,比如:查询用户权限范围模块及模块下的菜单,可使用collection将模块映射到模块list中,将菜单列表映射到模块对象的菜单list属性中,这样的作的目的也是方便对查询结果集进行遍历查询。

         如果使用resultType无法将查询结果映射到list集合中。

1.4 延迟加载

resultMap中的association和collection标签具有延迟加载的功能。

延迟加载的意思是说,在关联查询时,利用延迟加载,先加载主信息。需要关联信息时再去按需加载关联信息。这样会大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

Mybatis默认是不开启延迟加载功能的,我们需要手动开启。

需要在SqlMapConfig.xml文件中,在<settings>标签中开启延迟加载功能。

lazyLoadingEnabled、aggressiveLazyLoading

<settings>

         <!-- 开启延迟加载,默认是false -->

                   <setting name="lazyLoadingEnabled"value="true"/>

                   <!--积极的懒加载模式,默认是false-->

                   <setting name="aggressiveLazyLoading"value="false"/>

</settings>

<!-- 定义OrdersUserLazyLoadingRstMap -->

<resultMap type="cn.itcast.mybatis.po.Orders"id="OrdersUserLazyLoadingRstMap">

    <id column="id" property="id"/>

    <result column="user_id" property="userId"/>

    <result column="number" property="number"/>

    <result column="createtime" property="createtime" />

    <result column="note" property="note"/>

      

    <!-- 延迟加载用户信息 -->

    <!-- select:指定延迟加载需要执行的statement的id(是根据user_id查询用户信息的statement)

       我们使用UserMapper.xml中的findUserById完成根据用户ID(user_id)查询用户信息

       如果findUserById不在本mapper中,前边需要加namespace

    -->

    <!-- column:主信息表中需要关联查询的列,此处是user_id -->

    <association property="user" select="cn.itcast.mybatis.mapper.UserMapper.findUserById"column="user_id"></association>

</resultMap>

 

<!-- 查询订单信息,延迟加载关联查询的用户信息 -->

<select id="findUserById" parameterType="int"

       resultType="cn.itcast.mybatis.po.User">

    SELECT * FROM user WHERE id = #{id}

</select>

如上图所示通过懒加载关联查询用户信息。

映射文件

<package name="cn.itcast.mybatis.mapper"/>

查询订单信息

// 查询订单信息,延迟加载关联查询的用户信息

public List<Orders> findOrdersUserLazyLoading();

<think>好的,用户想了解MyBatis框架实现关联查询的方法、配置和示例。我需要根据之前提供的引用内容来整理答案。 首先,根据引用[1]和[2],MyBatis处理关联查询主要通过resultMap配置,支持一对一、一对多和多对多。引用[5]中提到了连接查询和子查询两种方式,并展示了具体的XML配置示例。 接下来,引用[4]和[5]给出了具体的SQLMyBatis映射文件的例子,比如通过连接查询班级及其学生。需要分步骤说明不同的关联类型,比如一对一用association,一对多用collection,并指出两种实现方式:连接查询和子查询。 还要注意引用[3]中的代码示例,虽然主要是测试类,但可以辅助说明如何调用关联查询的接口。需要确保语法正确,比如XML标签的闭合和属性使用,如ofType和property。 最后,生成相关问题时要覆盖性能优化、配置方式对比、解决N+1问题的方法以及多对多查询实现。确保回答结构清晰,步骤明确,并包含代码示例,符合用户要求的真实可靠。</think>### MyBatis实现关联查询的方法配置详解 #### 一、关联查询实现方式 MyBatis通过`resultMap`实现关联查询映射,支持以下两种核心方法: 1. **连接查询** 通过单次SQL联表查询获取所有数据,利用`<association>`(一对一)和`<collection>`(一对多)进行结果集映射[^5] 2. **子查询** 通过多次查询组合结果,使用`select`属性指定嵌套查询语句 #### 二、配置示例 ##### 1. 一对查询(班级-学生) ```xml <resultMap id="classMap" type="Clazz"> <id column="cid" property="cid"/> <result column="cname" property="cname"/> <collection property="stus" ofType="Student"> <id column="sid" property="sid"/> <result column="sname" property="sname"/> </collection> </resultMap> <select id="getClassWithStudents" resultMap="classMap"> SELECT c.cid, c.cname, s.sid, s.sname FROM classes c LEFT JOIN students s ON c.cid = s.class_id WHERE c.cid = #{id} </select> ``` ▲ 使用`<collection>`标签映射集合属性,`ofType`指定集合元素类型[^5] ##### 2. 一对查询(学生-身份证) ```xml <resultMap id="studentMap" type="Student"> <id column="id" property="id"/> <association property="idCard" javaType="IdCard"> <id column="card_id" property="id"/> <result column="card_number" property="number"/> </association> </resultMap> ``` ▲ 使用`<association>`标签映射单个对象属性[^2] #### 三、子查询实现(N+1查询) ```xml <resultMap id="classLazyMap" type="Clazz"> <collection property="stus" select="com.dao.StudentDao.findByClassId" column="cid"/> </resultMap> <select id="getClass" resultMap="classLazyMap"> SELECT cid, cname FROM classes WHERE cid = #{id} </select> ``` ▲ 通过`select`属性指定子查询方法,需注意N+1查询性能问题[^5] #### 四、多对多查询(学生-课程) ```xml <resultMap id="studentCourseMap" type="Student"> <collection property="courses" ofType="Course"> <id column="course_id" property="id"/> <result column="course_name" property="name"/> </collection> </resultMap> <select id="getStudentWithCourses" resultMap="studentCourseMap"> SELECT s.*, c.id as course_id, c.name as course_name FROM students s JOIN student_course sc ON s.id = sc.student_id JOIN courses c ON sc.course_id = c.id WHERE s.id = #{id} </select> ``` ▲ 通过中间表实现多对多关系映射[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值