MyBatis 里 association的三种读取方式

本文介绍了在MyBatis中实现一对多关联查询的方法,包括直接指定列名、使用ResultMap以及嵌套查询三种方式,并提供了详细的XML配置示例。

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

在数据库的查询中,我们经常会遇到一对一的连接查询,比如一条商品购买明细能唯一对应一条商品信息。如下面两个类:

商品类(均省略getset函数):
public class Seckill {


    private long seckillId;
    private String name;
    private int number;
    private Date startTime;
    private Date endTime;
    private Date createTime;

商品购买明细类:
public class SuccessKilled {
    private long seckillId;
    private long userPhone;
    private short state;
    private Date createTime;
    //多对一
    private Seckill seckill;

在使用Mybatis时,我们可以很轻易的获得商品信息的一条记录,对应xml文件如下:

<mapper namespace="edu.seckill.dao.SeckillDao">


    <resultMap id="seckill" type="edu.seckill.entity.Seckill">
        <id column="seckill_id" jdbcType="INTEGER" property="seckillId"></id>
        <result column="name" jdbcType="VARCHAR" property="name"></result>
        <result column="number" jdbcType="INTEGER" property="number"></result>
        <result column="start_time" jdbcType="TIMESTAMP" property="startTime"></result>
        <result column="end_time" jdbcType="TIMESTAMP" property="endTime"></result>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"></result>
    </resultMap>

    <select id="queryById" parameterType="edu.seckill.entity.Seckill" resultMap="seckill">
        SELECT seckill_id, name, number, start_time, end_time, create_time
        FROM seckill
        WHERE seckill_id = #{seckillId};
    </select>

但是在读取商品明细类时,seckill却不止一个字段,这时就可以使用association来进行读取操作。

第一种方式:直接在association标签里写对应的列名

 <resultMap id="successKilled" type="edu.seckill.entity.SuccessKilled">
        <id column="seckill_id" jdbcType="INTEGER" property="seckillId"></id>
        <id column="user_phone" jdbcType="INTEGER" property="userPhone"></id>
        <result column="state" jdbcType="INTEGER" property="state"></result>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"></result>


        <association property="seckill" javaType="edu.seckill.entity.Seckill">
            <id column="seckill.seckill_id" jdbcType="INTEGER" property="seckillId"></id>
            <result column="seckill.name" jdbcType="VARCHAR" property="name"></result>
            <result column="seckill.number" jdbcType="INTEGER" property="number"></result>
            <result column="seckill.start_time" jdbcType="TIMESTAMP" property="startTime"></result>
            <result column="seckill.end_time" jdbcType="TIMESTAMP" property="endTime"></result>
            <result column="seckill.create_time" jdbcType="TIMESTAMP" property="createTime"></result>
        </association>-->


    </resultMap>

对应的sql语句应写全,要注意sql语句列名与ResultMap里column的值的对应。
注:Mybatis会忽略表名或者表的别名。

 <select id="queryByIdWithSeckill" resultMap="successKilled">
       SELECT
        sk.seckill_id,
        sk.user_phone,
        sk.create_time,
        sk.state,
        s.seckill_id "seckill.seckill_id",
        s.name "seckill.name",
        s.number "seckill.number",
        s.start_time "seckill.start_time",
        s.end_time "seckill.end_time",
        s.create_time "seckill.create_time"
        FROM success_killed sk INNER JOIN seckill s
        ON sk.seckill_id = s.seckill_id
        WHERE sk.seckill_id = #{seckillId}
        AND user_phone = #{userPhone}
    </select>

第二种方式:给association传入一个ResultMap

    <resultMap id="successKilled" type="edu.seckill.entity.SuccessKilled">
        <id column="seckill_id" jdbcType="INTEGER" property="seckillId"></id>
        <id column="user_phone" jdbcType="INTEGER" property="userPhone"></id>
        <result column="state" jdbcType="INTEGER" property="state"></result>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"></result>
        <association property="seckill" javaType="edu.seckill.entity.Seckill" resultMap="edu.seckill.dao.SeckillDao.seckill"></association>


    </resultMap>

这种方式显得代码简洁,但是要注意的是association里的ResultMap与自己所处的ResultMap里的column列名有没有重复,如果重复的列名对应的值是相同的就无所谓,但如果取值不同的话则不建议使用。
这里的sql语句与第一种大致相同,需要注意的是列别名。

第三种方式:给association传入一个select

    <resultMap id="successKilled" type="edu.seckill.entity.SuccessKilled">
        <id column="seckill_id" jdbcType="INTEGER" property="seckillId"></id>
        <id column="user_phone" jdbcType="INTEGER" property="userPhone"></id>
        <result column="state" jdbcType="INTEGER" property="state"></result>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"></result>


        <association column="seckill_id" property="seckill" javaType="edu.seckill.entity.Seckill" select="edu.seckill.dao.SeckillDao.queryById"></association>
    </resultMap>

这种方式本质上相当于两次select,但是使用得当显得代码简洁,sql语句也不用去进行连接查询。

此处的sql语句:

<select id="queryByIdWithSeckill" resultMap="successKilled">
        SELECT * FROM success_killed
        WHERE seckill_id = #{seckillId}
        AND user_phone = #{userPhone}
    </select>

注:此处的association标签传入的column参数只有一个,如果需要多个的话可以使用column= “{prop1=col1,prop2=col2}”这样的语法,设置多个列名传入到嵌套查询语句。

### MyBatis 中关联查询映射的两种处理方式 MyBatis 提供了两种主要的方式来处理关联查询映射,分别是嵌套查询和嵌套结果查询。这两种方式各有优缺点,在实际开发中可以根据需求选择合适的方案。 #### 1. 嵌套查询 (Nested Query) 嵌套查询是指通过执行额外的 SQL 查询来获关联对象的数据。这种方式通常会涉及多次数据库访问,因此可能会带来性能开销。然而,它的灵活性较高,适合复杂场景下的动态查询。 - **实现原理**: 在主查询的结果集中,针对每一条记录再次发起新的 SQL 请求以加载关联数据。 - **适用场景**: 当关联的对象较少或者需要频繁更新时,可以考虑使用此方法。 - **优点**: - 实现简单直观。 - 可以灵活定义子查询逻辑。 - **缺点**: - 性能较低,可能引发 N+1 查询问题[^2]。 示例代码展示如何配置嵌套查询: ```xml <!-- 用户表 --> <select id="selectUser" resultType="User"> SELECT * FROM user WHERE id = #{id} </select> <!-- 订单表 --> <select id="selectOrdersByUserId" resultType="Order"> SELECT * FROM orders WHERE userId = #{userId} </select> ``` 在 Java 层面调用上述两个查询并手动组装对象即可完成嵌套查询的操作。 --- #### 2. 嵌套结果查询 (Nested Result Mapping) 嵌套结果查询是一种更高效的解决方案,它通过单次联合查询直接获所需的所有数据,并将其映射到目标对象上。这种方法减少了对数据库的重复请求次数,从而提高了整体性能。 - **实现原理**: 使用 `JOIN` 或其他形式的组合查询一次性提多个表的相关信息,再利用 `<association>` 和 `<collection>` 节点指定具体的映射规则。 - **适用场景**: 数据量较大且读取频率较高的情况下推荐采用该策略。 - **优点**: - 减少了不必要的网络交互成本。 - 避免潜在的 N+1 查询风险。 - **缺点**: - 对于复杂的多层嵌套关系来说,SQL 编写难度增加。 - 如果某些字段仅用于辅助过滤而不参与最终输出,则可能导致冗余计算资源浪费。 以下是基于 XML 的嵌套结果查询配置实例: ```xml <resultMap type="Order" id="orderResultMap"> <id property="orderId" column="order_id"/> <result property="amount" column="total_amount"/> <!-- 定义关联的一对一或一对多属性 --> <association property="user" javaType="User"> <id property="userId" column="user_id"/> <result property="name" column="username"/> </association> <collection property="products" ofType="Product"> <id property="productId" column="product_id"/> <result property="productName" column="product_name"/> </collection> </resultMap> <select id="findOrdersWithDetails" resultMap="orderResultMap"> SELECT o.order_id, o.total_amount, u.user_id, u.username, p.product_id, p.product_name FROM orders o LEFT JOIN users u ON o.user_id = u.id LEFT JOIN order_products op ON o.order_id = op.order_id LEFT JOIN products p ON op.product_id = p.id; </select> ``` 以上片段展示了如何在一个查询中同时获订单详情及其对应的用户信息以及商品列表。 --- ### 解决映射失败的问题 如果遇到部分字段被映射为 null 的情况,请确认是否开启了 MyBatis 的驼峰命名转换功能。默认情况下,MyBatis 不支持自动匹配带有下划线分隔符的列名与实体类中的驼峰风格属性名称。可以通过以下配置解决这一问题[^3]: ```yaml mybatis.configuration.map-underscore-to-camel-case: true ``` 启用后,例如数据库中的 `create_time` 列会被正确映射至 Java Bean 的 `createTime` 字段。 ---
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值