Mybatis 关联为 one-one 而不是 one-many

Mybatis Association as one-one instead of one-many

为清楚起见进行了编辑 02/07/17

我在工作中使用 Mybatis 3.3,但我 运行 遇到了障碍。我很确定这是我的 resultMapper 的问题,但我很难找到相关的 tutorials/info.

我有一个现有的 Java 模型、Mybatis 映射器和 tables;我正在尝试编写一个尽可能重用的新模块。我现有的模型如下所示:

class Document {
  Header header;
  List<Detail> details;
}

我想用不同的 Mybatis 映射器重用模型以在 Details 和 Headers 之间产生 1-1 关系(即 details.size() 始终为 1)。

我目前只能获取 1 个文档。它提取 table 中的第一个 header,并将每个文档的每个细节附加到它。这是我的结果图和我正在处理的查询。查询 returns 正确的结果,但 Mybatis 将它们包装错误。

<resultMap id="header" type="Header">
    <result property="id" column="ID" />
    <result property="title" column="TITLE" />
</resultMap>

<resultMap id="detail" type="Detail">
    <result property="id" column="ID" />
    <result property="title" column="INFO" />
</resultMap>

<resultMap id="document" type="Document">
  <association property="header" resultMap="header" />
  <association property="details" resultMap="detail" />
</resultMap>

SELECT 
  HEADER.ID,
  DETAIL.ID
FROM HEADER
JOIN DETAIL ON HEADER.ID = DETAIL.HEADER_ID

在文档 resultMap 中使用 collection 而不是 association for 属性 details .

编辑 02-08-17:

根据您的编辑,事情变得更清楚了:轻微的模型更改会更自然地满足您的需求:

class Document {
  Header header;
  Detail detail;
}

但是您想保留 1 个元素的 List<Detail>

我想过。它只需要添加一个 "virtual property" => getter 和 setter 来包装列表。

public void setSingleDetail(Detail detail) {
    this.details = Arrays.asList(detail);
}

public Detail getSingleDetail() {
    return null == this.details ? null : this.details.iterator().next();
}

但这还不够,因为它只是覆盖了详细列表。

如果看看方法org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValuesForNestedResultMap。你会发现Mybatis不允许你想要什么,它总是"group by"容器实体(如果你不指定id属性,它会使用hash)。这也是为什么需要 getter 的原因:与新行中的值进行比较。

你想要做的事情需要一个与 Header 关联的 resultMap Detail(当然还有一个 class 具有匹配结构),然后你会得到一个结果/Detail。

要将行集映射到 object 的树,关键是 object 的身份。考虑这个例子:

header_id | detail_id
---------------------
        1 |        1
        1 |        2

有两种方法可以将其解析为以 Document 作为根实体的 object 树。第一种方法是让一个实体具有 header 和两个 Detail 的 collection。另一种方法是有两个 Document,每个有一个 Detail。这里的区别在于 Document object 的身份。你需要告诉 mybatis 什么列是 Document 实体的标识符。

为了您的目的,这应该是 Detail.

的 ID

您需要更改查询和映射才能实现这一点。 主要目的是在这个映射中指定 detail id 为文档 id:

<resultMap id="document" type="Document">
  <id column="detail_id"/>
  <association property="header" resultMap="header" />
  <association property="details" resultMap="detail"/>
</resultMap> modify the query

您需要更改查询以使详细信息 ID 列具有唯一名称,例如

SELECT 
  HEADER.ID,
  DETAIL.ID DETAIL_ID
FROM HEADER
 JOIN DETAIL ON HEADER.ID = DETAIL.HEADER_ID

但这会破坏 Detail 关联中其他列的映射。 为了将 resultMap 用于 Detail,您需要为 detail table 中的所有列添加相同的前缀,并在映射中指定:

<resultMap id="document" type="Document">
  <id column="detail_id"/>
  <association property="header" resultMap="header" />
  <association property="details" resultMap="detail" prefix="DETAIL_"/>
</resultMap> modify the query

请注意,由于 mybatis 错误,前缀应为大写。