mybatis-3: record missing in result

MyBatis version

3.5.4

Database vendor and version

H2

Test case or example project

https://github.com/blindpirate/mybatis-bug-reproduction

Steps to reproduce

  • mvn flyway:migrate
  • Run Main class:

The result set should be 3 rows, but MyBatis returns 2 rows.

Expected result

DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 240166646.
DEBUG [main] - Setting autocommit to false on JDBC Connection [conn0: url=jdbc:h2:file:./target/test user=ROOT]
DEBUG [main] - ==>  Preparing: select USER.id,USER.name,t.score_num from (select USER_ID,sum(SCORE) as score_num from `MATCH` group by USER_ID) t inner join USER on t.USER_ID=USER.ID 
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 3
Result size: 3

Actual result

DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 240166646.
DEBUG [main] - Setting autocommit to false on JDBC Connection [conn0: url=jdbc:h2:file:./target/test user=ROOT]
DEBUG [main] - ==>  Preparing: select USER.id,USER.name,t.score_num from (select USER_ID,sum(SCORE) as score_num from `MATCH` group by USER_ID) t inner join USER on t.USER_ID=USER.ID 
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 3
Result size: 2

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 18 (13 by maintainers)

Commits related to this issue

Most upvoted comments

The behavior reported in this issue does not seem like a bug to me.

@Huangxuny1 ,

When using <collection /> or <association />, it is very important to understand how MyBatis identifies the parent object.

Here is your result map:

<resultMap id="rankItem" type="RankItem">
  <result property="score" column="score_num"/>
  <association property="user" javaType="User">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
  </association>
</resultMap>

As there is no <id /> in the root level, MyBatis uses score_num as the ID of the root object (= RankItem) and associates User for each RankItem [1]. The example query result is:

ID NAME SCORE_NUM
1 zhangsan 1300
2 lisi 500
3 wangwu 500

As I explained, score_num is the ID of RankItem, so there will be two RankItems (score=1300 and score=500) instead of 3. This is how it happens.

Now, assuming you actually want one RankItem for each User, you need to tell MyBatis to use the column that identifies USER (i.e USER.id) to identify RankItem. To do this, you just need to add <id /> to your result map.

<resultMap id="rankItem" type="RankItem">
  <id column="id" /><!-- added -->
  <result property="score" column="score_num"/>
  <association property="user" javaType="User">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
  </association>
</resultMap>

It’s pretty much the same with <collection />.

<resultMap id="rankItem" type="RankItem">
  <id column="id" /><!-- added -->
  <result property="score" column="score_num"/>
  <collection property="user" javaType="list" ofType="User">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
  </association>
</resultMap>

Notes:

  • I didn’t specify property in the <id /> element as there is no corresponding property in RankItem.
  • You should specify <id /> in <collection /> or <association /> as well (i.e. <id property="id" column="id"/>). It helps MyBatis work more efficiently and is mandatory in advanced (=complex) mappings. This is mentioned in the doc.

Please try it and let us know if there is any further questions.

[1] When there is no <id /> element, MyBatis uses the combination of all <result /> elements as the ID.


@emacarron , @h3adache I am not so sure, but I think that the line 1054 is for a result map that has <constructor /> in it. I’ll take a deeper look once I find some more time.

Thank you both!

@emacarron

I read the code and agree with you on your following statement.

The rule should be that child objects (associations/collections) do not participate in the cache key and should not matter if they are injected as part of the constructor.

It might break existing code, but it should be possible to fix the breakage by specifying property-less-id elements as I explained above.

OK, thanks

On Thu, Mar 12, 2020 at 11:40 PM Iwao AVE! notifications@github.com wrote:

@Huangxuny1 https://github.com/Huangxuny1 ,

Implementations aside, it would be logically impossible to map complex object graph from joined results without knowing the parent’s ID.

Taking the example result of USER-MATCH tables… ID NAME SCORE_NUM 1 zhangsan 1300 2 lisi 500 3 wangwu 500

You seem to assume that ID is the only possible key of the RankItem, but that is only because you know the result you want. What if you wanted a list of users per score? i.e.

  • Users who scored 500 = [2:lisi] and [3:wongwu]
  • Users who scored 1300 = [1:zhangson]

This is a totally valid result.

My point is that MyBatis cannot read your thoughts, so you need to let MyBatis know which columns to use as the parent key. Makes sense?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/mybatis/mybatis-3/issues/1848#issuecomment-598257925, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFHWTJS5ICWNYJFKIP3U56DRHD67XANCNFSM4LDEU4JA .