mybatis的联合查询与嵌套映射

联合查询与嵌套映射场景

联合查询时指查询语句有left join 或者全表连接等多个表关联的查询,这种查询由于涉及到多个表,且一般都会查出多个表的字段,而相应的resultMap会需要映射成多个对象,如下所示:


  <resultMap id="blogComplexMap" type="com.entity.Blog" autoMapping="true">
        <id column="id" property="id"></id>
        <result column="title" property="title"></result>
        <collection property="comments" ofType="com.entity.Comment" autoMapping="true" columnPrefix="comment_">
        </collection>
    </resultMap>

<!--联合查询-->
    <select id="selectBlogByIdWithComplex" resultMap="blogComplexMap">
        select b.id,b.title,c.id as comment_id ,c.content as comment_content from  blog b left join comment c on b.id=c.blog_id where b.id = #{id}
    </select>

这里需要找blog 和comment两张表的字段,且在结果映射中需要在blog对象里映射出comment集合对象。

映射概念说明

映射是指返回的ResultSet列与Java Bean 属性之间的对应关系。通过ResultMapping进行映射描述,在用ResultMap封装成一个整体。映射分为简单映射与复合嵌套映射。

简单映射:即返回的结果集列与对象属性是1对1的关系,这种情况下ResultHandler 会依次遍历结果集中的行,并给每一行创建一个对象,然后在遍历结果集列填充至对象的映射属性,下图表示user对象与user的resultMap中的属性一一对应的情况:

image-20200624152820045

嵌套映射:但很多时候对象结构, 是树级程现的。即对象中包含对象。与之对应映射也是这种嵌套结构。

image-20200617182311560

在嵌套映射的配置方式上可以直接配置子映射,也以引入外部映射和自动映射。映射共有两类嵌套结构分别是一对一(association) 与一对多(collection) 。

image-20200624155458786

联合查询概念

在配置了映射之后如何获取结果?普通的单表查询是无法获取复合映射所需结果,这就必须用到联合查询。然后在将联合查询返回的数据列,拆分给不同的对象属性。1对1与1对多拆分和创建的方式是一样的。
1对1查询映射

select a.id,
       a.title,
       b.id as user_id,
       b.name as user_name
from blog a
         left join users b on a.author_id=b.id
where a.id = 1;

通过上述语句联合查询语句,可以得出下表中结果。结果中前两字段对应Blog,后两个字段对应User。然后在将User作为author属性填充至Blog对象。
在这里插入图片描述
在这里插入图片描述
在上述两个例子中,每一行都会产生两个对象,一个Blog父对象,一个User子对象。
1对多查询

select a.id,a.title,
       c.id as comment_id,
       c.body as comment_body
from blog a
         left join comment c on a.id=c.blog_id
where a.id = 1;

上述语句可得出三条结果,前两个字段对应Blog,后两个字段对应Comment(评论)。与1对1不同的是,三行指向的是同一Blog。因为它ID都是一样的。
在这里插入图片描述
在这里插入图片描述
上述结果中,相同的三行Blog将会创建一个Blog,同时分别创建三个不同的Comment组成一个集合,并填充至comments对象。那么如何判断数据是否相同需不需要合并呢,实际上mybatis是基于RowKey来断定两行数据是否相同的 。RowKey一般基于的配置来指定,但实际上有时并不会指定,这时将会采用其它映射字段创建RowKey具体规则如下:
在这里插入图片描述
相应的源码:

 private CacheKey createRowKey(ResultMap resultMap, ResultSetWrapper rsw, String columnPrefix) throws SQLException {
        CacheKey cacheKey = new CacheKey();
        cacheKey.update(resultMap.getId());
        List<ResultMapping> resultMappings = this.getResultMappingsForRowKey(resultMap);
        if (resultMappings.isEmpty()) {
            if (Map.class.isAssignableFrom(resultMap.getType())) {
                this.createRowKeyForMap(rsw, cacheKey);
            } else {
                this.createRowKeyForUnmappedProperties(resultMap, rsw, cacheKey, columnPrefix);
            }
        } else {
            this.createRowKeyForMappedProperties(resultMap, rsw, cacheKey, resultMappings, columnPrefix);
        }

        return cacheKey.getUpdateCount() < 2 ? CacheKey.NULL_CACHE_KEY : cacheKey;
    }

结果集解析流程

这里直接采用1对多的情况进行解析,因为1对1就是1对多的简化版。查询的结果如下表:
在这里插入图片描述
其整个解析流程如下图:
在这里插入图片描述
所有映射流程的解析都是在DefaultResultSetHandler当中完成。主要方法如下:
handleRowValuesForNestedResultMap()

嵌套结果集解析入口,在这里会遍历结果集中所有行。并为每一行创建一个RowKey对象。然后调用getRowValue()获取解析结果对象。最后保存至ResultHandler中,对应的源码如下:
在这里插入图片描述

注:调用getRowValue前会基于RowKey获取已解析的对象,然后作为partialObject参数发给getRowValue

getRowValue()

该方法最终会基于当前行生成一个解析好的对象。具体职责包括
1.创建对象、
2.填充普通属性
3.填充嵌套属性。
在解析嵌套属性时会以递归的方式在调用getRowValue获取子对象。
4.最后基于RowKey 暂存当前解析对象。

如果partialObject参数不为空 只会执行 第3步。因为1、2已经执行过了。
相应的源码如下:
在这里插入图片描述

applyNestedResultMappings()

解析并填充嵌套结果集映射,遍历所有嵌套映射,然后获取其嵌套ResultMap。接着创建RowKey 去获取暂存区的值。然后调用getRowValue 获取属性对象。最后填充至父对象。

​ 如果通过RowKey能获取到属性对象,它还是会去调用getRowsValue,因为有可能属下还存在未解析的属性。
相应的源码如下:
在这里插入图片描述

本文参考的文档为:www.coderead.cn

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
MyBatis是一个开源的持久层框架,它可以帮助我们简化JavaEE应用程序中的数据库访问操作。在MyBatis中,一对多嵌套查询是指在查询结果中包含了一个对象和该对象所关联的多个子对象。 在JavaEE中使用MyBatis进行一对多嵌套查询的步骤如下: 1. 定义实体类:首先需要定义两个实体类,一个是主对象的实体类,另一个是子对象的实体类。主对象实体类中需要包含一个子对象的合属性。 2. 编写Mapper接口:创建一个Mapper接口,其中定义了查询方法,使用@Results和@Result注解来映射查询结果到实体类中。 3. 编写Mapper XML文件:在Mapper XML文件中编写SQL语句,使用嵌套查询的方式来获取主对象和子对象的数据,并通过resultMap将查询结果映射到实体类中。 4. 调用Mapper接口:在Java代码中调用Mapper接口的方法来执行查询操作,获取一对多嵌套查询的结果。 下面是一个示例代码,演示了如何使用MyBatis进行一对多嵌套查询: 1. 定义主对象实体类: ```java public class Order { private int id; private String orderNo; private List<OrderItem> orderItems; // 省略getter和setter方法 } ``` 2. 定义子对象实体类: ```java public class OrderItem { private int id; private String productName; // 省略getter和setter方法 } ``` 3. 编写Mapper接口: ```java public interface OrderMapper { @Results(id = "orderResultMap", value = { @Result(property = "id", column = "id"), @Result(property = "orderNo", column = "order_no"), @Result(property = "orderItems", column = "id", javaType = List.class, many = @Many(select = "com.example.mapper.OrderItemMapper.findByOrderId")) }) @Select("SELECT * FROM orders WHERE id = #{id}") Order findById(int id); } ``` 4. 编写Mapper XML文件: ```xml <mapper namespace="com.example.mapper.OrderMapper"> <resultMap id="orderResultMap" type="com.example.entity.Order"> <id property="id" column="id"/> <result property="orderNo" column="order_no"/> <collection property="orderItems" ofType="com.example.entity.OrderItem"> <id property="id" column="id"/> <result property="productName" column="product_name"/> </collection> </resultMap> <select id="findById" resultMap="orderResultMap"> SELECT * FROM orders WHERE id = #{id} </select> </mapper> ``` 5. 调用Mapper接口: ```java public class Main { public static void main(String[] args) { SqlSessionFactory sqlSessionFactory = ...; // 初始化SqlSessionFactory try (SqlSession sqlSession = sqlSessionFactory.openSession()) { OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); Order order = orderMapper.findById(1); System.out.println("订单号:" + order.getOrderNo()); System.out.println("订单项:"); for (OrderItem orderItem : order.getOrderItems()) { System.out.println("商品名称:" + orderItem.getProductName()); } } } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值