Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MyBatis using Java records with a collection in the constructor gives an error during mapping #3021

Open
mark-smith-nl opened this issue Nov 29, 2023 · 2 comments

Comments

@mark-smith-nl
Copy link

We are moving from using Java Pojo's as DTO's to records with the MyBatis framework.

The following two record types are used:

public record User(Integer cprid, String inlognaam, String upn, Boolean actief, String email, Locatie locatie, Collection<RoleExtra> rolesExtra) {}

public record RoleExtra(Role role) { }

The identifier of a user is the cprid.
Location and Role are simple enums.

Having the following query in myBatis:

<select id="getAllUsers" resultMap="suezUserResult">
    SELECT g.inlognaam, g.cprid, g.upn, g.actief, g.email, g.locatie, grl.rol
    FROM suez.gebruikers g
    LEFT JOIN suez.gebruikers_rollen_link grl USING (cprid)
    WHERE cprid = 107884
    ORDER BY g.naam
</select>

results in the tuples:

     inlognaam | cprid     |             upn                                | actief    |            email                              | locatie.   |   rol
    -----------+--------+-----------------------------+--------+-----------------------------+---------+---------
     msmith      | 107884 | [email protected]   | t            | [email protected]  | AMC        | ROLE_PI
     msmith      | 107884 | [email protected]   | t            | [email protected]  | AMC        | ROLE_PL

The resultmaps used:

<resultMap id="suezUserResult" type="amr.bi.elsalvador.domain.suez.User">
     <constructor>
           <idArg column="cprid" name="cprid"/>
          <arg column="cprid" name="rolesExtra" resultMap="roleExtraResult"/>
          <arg column="inlognaam" name="inlognaam"/>
          <arg column="upn" name="upn"/>
          <arg column="actief" name="actief"/>
          <arg column="email" name="email"/>
          <arg column="locatie" name="locatie"/>
    </constructor>
    <id column="cprid"/>
  </resultMap>

<resultMap type="amr.bi.elsalvador.domain.suez.RoleExtra" id="roleExtraResult">
    <constructor>
        <arg column="rol" javaType="amr.bi.elsalvador.enums.Role" />
    </constructor>
</resultMap>

When invoking getAllUsers I get an exception during instantiation of the User:

Caused by: java.lang.ClassCastException: Cannot cast amr.bi.elsalvador.domain.suez.RoleExtra to java.util.Collection

It seems that MyBatis is trying to make two instances of User for each role ROLE_PI and ROLE_PL (hence the argument type mismatch) instead of one instance using the set of roles.
What am I doing wrong?

@harawata
Copy link
Member

Hello @mark-smith-nl ,

As I explained, in constructor-mapping, collection is supported only via nested select.
In other words, an immutable object whose constructor takes collections is not supported if you use JOIN (it's called "nested result" in our doc, by the way),

It is a known limitation.
The underlying technical difficulty is discussed in #101 if you are interested.

@epochcoder
Copy link
Contributor

epochcoder commented Mar 9, 2024

Would be great if you could test your workload against my branch @mark-smith-nl :) it should fix the issue experienced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants