大厂Java面试题:MyBatis是中如何将结果集映射到Java持久化对象?都有哪些方式?有什么区别?

大家好,我是王有志。今天给大家带来的是一道来自京东的 MyBatis 面试题:MyBatis是中如何将结果集映射到Java持久化对象?都有哪些方式?有什么区别?

MyBatis 提供了两种实现结果集到 Java 持久化对象的映射方式:

  • 自动映射,不显式指定映射关系;
  • 主动映射,使用 resultTyp 或者 resultMap 指定映射关系。

通常在生产应用中,我们可以直接放弃使用自动映射这种方式,在数据库表中的字段与 Java 持久化对象中的字段是一一对应的场景下,且能够使用 MyBatis 插件“mapUnderscoreToCamelCase”实现映射,那么我们可以选择使用 resultType 属性进行配置,如果不能一一对应,或者不能通过 MyBatis 插件“mapUnderscoreToCamelCase”实现映射,以及映射关系较为复杂(例如:一对一关联,一对多关联登)的场景下,我们应该使用 resultMap 元素定义映射关系。

自动映射

自动映射指的是不显式指定 resultType 或 reusltMap,让 MyBatis 尝试自动根据查询结果的列名与 Java 持久化对象进行匹配进行映射,这需要保证数据库表中的字段名与 Java 持久化对象的字段名一致,也可以通过为查询语句中的字段取别名,或者是使用 MyBatis 插件“mapUnderscoreToCamelCase”的方式实现自动映射

下面我们举个例子。

定义数据库表 order_item,代码如下:

create table order_item (
  item_id         int            not null comment '订单商品表主键' primary key,
  order_id        int            not null comment '订单表主键',
  commodity_id    int            not null comment '商品表主键',
  commodity_price decimal(18, 2) not null comment '商品价格',
  commodity_count int            not null comment '商品数量'
) comment '订单明细表';

定义 Java 持久化对象 OrderItemDO,代码如下:

public class OrderItemDO {

  private Integer itemId;

  private Integer orderId;

  private Integer commodityId;

  private BigDecimal commodityPrice;

  private Integer commodityCount;
}

定义 OrderItemMapper 接口中的方法,代码如下:

public interface OrderItemMapper {
    List<OrderItemDO> selectByAutoMap();
}

定义 OrderItemMapper 映射器中的 SQL 语句,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wyz.mapper.OrderItemMapper">
  <select id="selectByAutoMap">
    select item_id as itemId,
           order_id as orderId,
           commodity_id as commodityId,
           commodity_price as commodityPrice,
           commodity_count as commodityCount
    from order_item
  </select>
</mapper>

注意,此时 OrderItemMapper 接口的中方法可能会报错,提示如下:

Result type not match for select id=“selectByAutoMap” srcType: targetType: com.wyz.entity.OrderItemDO

针对于这个报错,我们直接忽略就可以了。

最后来写单元测试,代码如下:

public void testSelectByAutoMap() throws IOException {
Reader mysqlReader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(mysqlReader);
SqlSession sqlSession = sqlSessionFactory.openSession();
OrderItemMapper orderItemMapper = sqlSession.getMapper(OrderItemMapper.class);

List<OrderItemDO> orderItems = orderItemMapper.selectByAutoMap();
System.out.println(JSON.toJSONString(orderItems, JSONWriter.Feature.PrettyFormat));
}

执行单元测试,可以看到控制台正常输出了查询到的数据。

这种方式是在 MyBatis 应用程序构建 MappedStatement 时通过反射获取 OrderItemMapper 接口中的方法,解析方法返回值的方式来获取到结果集映射的 Java 持久化对象的,部分源码如下所示:

图中源码做了大量删减,重点关注下调用链路即可,高亮的部分就是通过反射获取数据集映射的 Java 持久化对象类型的部分。因为需要做额外的工作,并且使用到反射技术,因此在效率上较差,不建议使用这种方式。

主动映射

主动映射可以通过 select 元素中的 resultType 属性定义,也可以通过 resultMap 元素结合 select 元素的 resultMap 属性定义。

使用 reusltType 进行映射

修改 OrderItemMapper 映射器中的 SQL 语句,为其添加 resultType 属性,代码如下:

<select id="selectByAutoMap" resultType="com.wyz.entity.OrderItemDO">
  select item_id as itemId,
         order_id as orderId,
         commodity_id as commodityId,
         commodity_price as commodityPrice,
         commodity_count as commodityCount
  from order_item
</select>

再次执行单元测试,可以看到控制台能够正常输出查询结果。

这种方式与自动映射基本一致,只不过我们指定了 reusltType 的类型,MyBatis 只需要根据 resultType 的配置反射出相应的类型即可,而不需要反射出方法的返回值。同样的,使用 resultType 也需要保证数据库表中的字段名与 Java 持久化对象中的字段名完全一致,当然了,同样也可以通过为查询语句中的字段起别名,或者是使用 MyBatis 的插件“mapUnderscoreToCamelCase”解决。

这种方式 MyBatis 会直接读取 reusltType 的配置,并通过类型别名注册器 TypeAliasRegistry 去解析出配置对应的类名。

使用 resultMap 进行映射

使用 resulttMap 元素可以定义数据库表中的字段名与 Java 持久化对象中的字段名的映射器关系,因为 resultMap 元素强大的功能,它可以处理很多复杂的场景,具体用法可以参考我在掘金专栏中的文章:《MyBatis映射器:一对一关联查询》《MyBatis映射器:一对多关联查询》

下面我们使用 resulttMap 元素定义映射关系,代码如下:

<resultMap id="BaseResultMap" type="com.wyz.entity.OrderItemDO">
  <id property="itemId" column="item_id" jdbcType="INTEGER"/>
  <result property="orderId" column="order_id" jdbcType="INTEGER"/>
  <result property="commodityId" column="commodity_id" jdbcType="INTEGER"/>
  <result property="commodityPrice" column="commodity_price" jdbcType="DECIMAL"/>
  <result property="commodityCount" column="commodity_count" jdbcType="INTEGER"/>
</resultMap>

修改 OrderItemMapper 映射器中的 SQL 语句,为其添加 resultMap 属性,代码如下:

<select id="selectByAutoMap" resultMap="BaseResultMap">
  select item_id,
         order_id,
         commodity_id,
         commodity_price,
         commodity_count
  from order_item
</select>

最后再来执行单元测试,可以看到控制台依旧可以正常输出结果。

使用 reusltMap 元素的优点是,灵活性高,能够适应复杂的场景,并且使用 resultMap 元素的可读性更好,能够清晰直观的看到数据库中表的字段与 Java 持久化对象中字段的映射关系,如果硬要说缺点,也只能是学习成本高(相对来说),配置及相对复杂


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术范王有志

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值