Mybatis - 从 SELECT 返回包含 Hashmap 的对象

Yuv*_*val 4 java mysql hashmap mybatis

我正在使用Mybatis-3,我有以下情况:

我有类 - 用户,如下所示:

public class User extends GeneralDto {

    private String userId;
    private String email;
    private String firstName;
    private String lastName;
    private long creationTimestamp;
    private long updateTimestamp;
    private List<String> tags;
    private HashMap<String, String> attributes;
    private HashMap<String, String> accounts;

    get and set to all + equals + hashcode.
}
Run Code Online (Sandbox Code Playgroud)

这两个哈希图包含字符串类型的未知键和值(它们给我带来了很多麻烦)。

我已经使用插入方法将这个类映射到表中。它被映射到下表方案:

User (userIdentity, userId, email, firstName, lastName, creationTimestamp, updateTimestamp)

UserAttribute (userIdentity, attributeName, attributeValue, creationTimestamp, updateTimestamp)

UserTag (userIdentity, tagName, creationTimestamp, updateTimestamp)

UserAccount (userIdentity, accountIdentity, role, creationTimestamp, updateTimestamp)
Run Code Online (Sandbox Code Playgroud)

我需要创建一个 GET 方法。该方法接收 UserKey 对象,该对象包含作为用户密钥的 userId。并返回用户类的一个实例。

这是连接所有表并从每个表中获取相关数据的 SELECT 语句:

<select id="getUser" parameterType="com.intel.aa.iot.mybatis.UserResultHandler" resultMap="userResultMap" resultOrdered="true">
    SELECT
        U.userId as userId,
        U.email as email,
        U.firstName as firstName,
        U.lastName as lastName,
        U.creationTimestamp as creationTimestamp,
        U.updateTimestamp as updateTimestamp,
        UT.tagName as tagName,
        UAT.attributeName as attributeName,
        UAT.attributeValue as attributeValue,
        A.accountId as accountId,
        UAC.role as role
    FROM User U
        LEFT OUTER JOIN UserTag UT ON U.userIdentity = UT.userIdentity
        LEFT OUTER JOIN UserAttribute UAT ON U.userIdentity = UAT.userIdentity
        LEFT OUTER JOIN UserAccount UAC ON U.userIdentity = UAC.userIdentity
        LEFT OUTER JOIN ACCOUNTS A ON UAC.accountIdentity = A.accountIdentity
    WHERE U.userId = #{userKey.userId}
Run Code Online (Sandbox Code Playgroud)

由于连接的原因,此查询可能返回不止一行,但所有行都属于给定的 userId。

我的问题是如何将其映射到一个结果中,该结果是用户类的一个实例。我尝试使用结果映射,但在映射哈希映射时遇到了问题。然后我尝试使用 ResultHandler - 返回名为 UserProperties 的类,它包含每个哈希图作为两个列表(见下面的代码),但不幸的是它并没有工作 - 最终每个列表只保存一个值。

用户属性类:

public static class UserProperties {
    private User user;
    private List<String> attributeNames;
    private List<String> attributeValues;
    private List<String> accountIds;
    private List<String> accountRoles;

    get and set to all
}
Run Code Online (Sandbox Code Playgroud)

结果图:

<resultMap id="userResultMap" type="com.intel.aa.iot.mybatis.UserMapper$UserProperties">
    <association property="user" javaType="com.intel.aa.iot.dto.User">
        <id property="userId" column="userId"/>
        <result property="email" column="email"/>
        <result property="firstName" column="firstName"/>
        <result property="lastName" column="lastName"/>
        <result property="creationTimestamp" column="creationTimestamp"/>
        <result property="updateTimestamp" column="updateTimestamp"/>
        <collection property="tags" javaType="java.util.ArrayList" column="tagName" ofType="java.lang.String"/>
    </association>
    <collection property="attributeNames"  javaType="java.util.ArrayList" column="attributeName" ofType="java.lang.String"/>
    <collection property="attributeValues" javaType="java.util.ArrayList" column="attributeValue" ofType="java.lang.String"/>
    <collection property="accountIds" javaType="java.util.ArrayList" column="accountId" ofType="java.lang.String"/>
    <collection property="accountRoles" javaType="java.util.ArrayList" column="role" ofType="java.lang.String"/>
</resultMap>
Run Code Online (Sandbox Code Playgroud)

我处理这些 Hasmap 的方法是什么?

谢谢!

小智 5

在过去的几年里,我自己也遇到过同样的问题,但我还没有找到理想的解决方案。最终的问题是 mybatis 不允许你指定 hashmap 的键——它使用结果属性名称作为键。

我为解决此问题而采取的两种方法远非完美,但我将在此处概述它们,以防它们对您有用:

  1. 使用结果处理程序。我看到你说你已经尝试过这个,但只得到第一个值 - 你有机会分享你的代码吗?查询中返回的每一行都会调用一次结果处理程序,因此您不想每次都创建一个新对象。仅当您还没有与该行中的 ID 匹配的对象时,您才想创建一个对象,然后根据以下行迭代构建它。

  2. 将名称和值构建为哈希映射列表,然后使用选择拦截器(mybatis 插件)调用对象内部的方法来构建真正的哈希映射。为此,在您的结果映射中,您需要在 hashmap 类型的关联下指定您的名称和值结果。然后 Mybatis 会把它变成一个看起来像这样的列表:

    [列表 [Hashmap {"attributeName"=name1,"attributeValue"=value1}], [Hashmap {"attributeName"=name2,"attributeValue"=value2}]]

然后你可以调用拦截器中的一个方法来迭代这个hashmap来构建你想要的hashmap。

这两种解决方案都不漂亮。第一个解决方案失去了结果映射的优雅,第二个解决方案弄脏了您的域对象。但是如果您对更多细节感兴趣,请告诉我,我将添加一些代码示例。