导读:本期聚焦于北京GEO公司创作的《一对多关系的XML数据如何映射?详解JAXB与MyBatis两种实现方案》,敬请观看详情。解析XML文档时遇到嵌套的一对多结构该怎么处理?比如一个订单包含多个商品、一个部门包含多个员工,这类数据想要顺利映射到Java对象,就需要掌握正确的配置方法。本文围绕一对多关系的XML数据映射展开,先分析XML中一对多结构的常见形态,再介绍JAXB注解方式如何用@XmlElement把重复节点绑定到List集合,接着讲解MyBatis解析XML时的collection映射技巧,最后对比两种方案的适用场景与优缺点,帮助你根据项目实际情况选择合适的处理方式,附完整代码示例可直接参考。

在数据交互场景中,XML格式至今仍被大量系统使用,比如银行接口、企业级报文、配置文件等。这些XML文档往往不是扁平结构,而是存在明显的层级关系:一个订单节点下面挂着多个商品节点,一个班级节点下面包含多个学生节点。当我们需要把这类一对多结构的XML解析成Java对象时,就需要用到专门的映射技术。本文将分别介绍JAXB注解映射和MyBatis的collection映射两种主流方案,并给出完整的代码演示。

一对多关系的XML数据如何映射?详解JAXB与MyBatis两种实现方案

XML中一对多结构的常见形态

先来看一个典型的例子,假设有下面这样一份订单XML文档:

<order>
    <orderId>1001</orderId>
    <customer>张三</customer>
    <items>
        <item>
            <name>机械键盘</name>
            <price>399.00</price>
        </item>
        <item>
            <name>无线鼠标</name>
            <price>129.00</price>
        </item>
    </items>
</order>

这份文档里,<order>是"一"的一方,<item>是"多"的一方,一个订单对应多个商品。这种结构有两种常见的表现形式:一种是像上面这样,多个重复节点被包裹在一个父容器节点<items>里;另一种是没有容器节点,多个<item>直接作为<order>的子节点重复出现。这两种形态在映射时的配置方式略有差别,后面会分别说明。

对应到Java对象层面,我们需要设计两个类:Order类持有一个List<Item>类型的属性,Item类则包含自己的字段。一对多映射的核心,就是告诉解析框架如何把XML中重复出现的节点逐一收集到这个List集合中。

使用JAXB注解完成一对多映射

JAXB是JDK自带的XML与Java对象互转框架(JDK 8及以前内置,JDK 9之后需要单独引入依赖),通过注解声明映射规则,无需手写解析逻辑。针对上面带容器节点的结构,实体类的写法如下:

import javax.xml.bind.annotation.*;
import java.util.List;

@XmlRootElement(name = "order")
@XmlAccessorType(XmlAccessType.FIELD)
public class Order {
    @XmlElement(name = "orderId")
    private Integer orderId;

    @XmlElement(name = "customer")
    private String customer;

    @XmlElement(name = "items")
    private Items items;

    // 省略getter和setter
}

@XmlAccessorType(XmlAccessType.FIELD)
public class Items {
    @XmlElement(name = "item")
    private List<Item> itemList;

    // 省略getter和setter
}

@XmlAccessorType(XmlAccessType.FIELD)
public class Item {
    @XmlElement(name = "name")
    private String name;

    @XmlElement(name = "price")
    private BigDecimal price;

    // 省略getter和setter
}

注意这里引入了一个中间类Items来对应容器节点,这是处理带包裹节点结构的标准做法。如果XML中没有<items>容器,多个<item>直接挂在<order>下面,那配置就简化了,直接在Order类中这样写:

@XmlElement(name = "item")
private List<Item> itemList;

反序列化的代码非常简洁,几行就能把XML转成对象:

JAXBContext context = JAXBContext.newInstance(Order.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
StringReader reader = new StringReader(xmlString);
Order order = (Order) unmarshaller.unmarshal(reader);
// 遍历一对多集合
order.getItems().getItemList().forEach(item ->
    System.out.println(item.getName() + ":" + item.getPrice()));

除了@XmlElement,还有一种@XmlElementWrapper注解可以省掉中间类,它的作用正是为集合元素声明一个包裹节点:

@XmlElementWrapper(name = "items")
@XmlElement(name = "item")
private List<Item> itemList;

这样写就不需要单独定义Items类了,代码更加紧凑。JAXB同样支持把Java对象序列化回XML,调用Marshaller即可,双向转换能力是它的一大优势。缺点是注解配置相对繁琐,遇到层级很深的文档时,实体类的数量会随之膨胀。

MyBatis中collection标签处理一对多映射

另一种常见需求是把XML数据直接映射到MyBatis的查询结果中,或者理解MyBatis结果映射中一对多的处理思想。在MyBatis的Mapper XML里,<collection>标签专门负责一对多关系,它告诉框架某个属性是集合类型,需要按指定规则组装数据:

<resultMap id="orderResultMap" type="com.example.Order">
    <id property="orderId" column="order_id"/>
    <result property="customer" column="customer"/>
    <collection property="items" ofType="com.example.Item">
        <id property="name" column="name"/>
        <result property="price" column="price"/>
    </collection>
</resultMap>

property指定Java类中的集合属性名,ofType指定集合中元素的类型,内层的<id><result>则完成元素内部字段与列的对应。需要注意<id>标签的作用很关键,MyBatis用它来区分不同的一对多子记录,如果漏写或者写错,可能出现集合元素重复或丢失的问题,这是实际开发中高频踩坑点。

当关联数据来自另一张表时,还可以用嵌套select的方式,为collection指定select属性指向一条子查询,框架会自动按外层主键去查询子记录并组装成集合。这种方式写起来简单,但存在N+1查询问题:外层查N条记录,就会触发N次子查询。数据量大时建议改用嵌套resultMap配合join查询,一次SQL把数据查全,性能差距会非常明显。

两种方案如何选择

JAXB适合处理独立的XML文档解析场景,比如接收第三方系统的XML报文、读取XML配置文件,它不依赖数据库,纯内存操作,转换速度快,而且支持双向转换。MyBatis的collection则面向数据库查询结果的组装,本质是ORM层面的关系映射,与XML数据解析属于不同的应用层面。

如果项目只是解析XML文件,首选JAXB或者更轻量的Dom4j手动解析;如果涉及分页读取超大XML文件,则可以考虑SAX或StAX流式解析,避免内存溢出。而当你需要在查询结果中体现一对多业务关系时,MyBatis的collection是标准答案。两者并不冲突,实际项目中经常同时使用:接口层用JAXB解析外部XML报文,落库和查询则交给MyBatis处理。

最后提醒一点,无论采用哪种方案,都要注意集合属性的初始化问题。JAXB在XML中对应节点不存在时会把集合置为null,直接遍历会抛空指针异常,建议在实体类中初始化为空的ArrayList,或者在业务代码里做好判空处理,养成这个习惯能避免不少线上问题。

XML映射一对多关系JAXB修改时间:2026-09-05 04:02:31

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260905/50663.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。