MyBatis enum usage

2022-09-02 20:50:25

我知道以前有人问过这个问题,但是我无法根据到目前为止找到的信息实现解决方案。所以也许有人可以向我解释。

我有一个表“状态”。它有两列:id 和 name。id 是一个 PK。

我不想使用 POJO 状态,而是使用枚举。我创建了这样一个枚举,如下所示:

public enum Status {
    NEW(1), READY(2), CLOSED(3);

    private int id;

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return this.id;
    }

    Status(int id) {
        this.id = id;
    }
}

这是我的映射器

     <select id="getStatusByName" resultType="Status" parameterType="String">       
        SELECT  ls.id, ls.name
        FROM status AS ls
        WHERE ls.name = #{name}
    </select>

但是由于某种原因,当我尝试检索枚举时,某些内容会中断,但没有抛出异常。


答案 1

我从几个角度研究了这个问题,这是我的发现。警告:我使用MyBatis-3.1.1进行了所有这些调查,因此在早期版本中,事情可能表现得不同。

首先,MyBatis有一个内置的.缺省情况下,每当您将 Java 枚举指定为 resultType 或 parameterType 时,这将处理该类型。对于查询,当尝试将数据库记录转换为 Java 枚举时,EnumTypeHandler 只采用一个参数并尝试查找与该值对应的 Java 枚举值。EnumTypeHandler

一个例子可以更好地说明。假设您上面的查询返回,当我传入“Ready”作为参数时。在这种情况下,我收到错误消息 。如果我颠倒您的 SELECT 语句的顺序2"Ready"No enum constant com.foo.Status.2

SELECT ls.name, ls.id

则错误消息为 。我想你可以推断出MyBatis在做什么。请注意,EnumTypeHandler 忽略了从查询返回的第二个值。No enum constant com.foo.Status.Ready

将查询更改为

SELECT UPPER(ls.name)

使其工作:返回 Status.READY 枚举。

因此,接下来,我尝试为 Status 枚举定义自己的 TypeHandler。不幸的是,与默认值一样,我只能获取其中一个值(id或name)以引用正确的枚举,而不是两者。因此,如果数据库 ID 与您上面硬编码的值不匹配,则会出现不匹配的情况。如果确保数据库 ID 始终与在枚举中指定的 id 匹配,则数据库中所需的只是名称(转换为大写)。EnumTypeHandler

然后我想我会变得聪明并实现一个MyBatis ObjectFactory,获取int id和String名称,并确保它们在我传回的Java枚举中匹配,但这不起作用,因为MyBatis不调用Java枚举类型的ObjectFactory(至少我无法让它工作)。

所以我的结论是,MyBatis中的Java枚举很容易,只要你只需要将数据库中的名称与枚举常量名称相匹配 - 要么使用内置的EnumTypeHandler,要么定义你自己的,如果在SQL中执行UPPER(name)不足以匹配Java枚举名称。在许多情况下,这就足够了,因为枚举值可能只是列上的检查约束,并且它只有单个值,而没有 id。如果还需要匹配 int id 和名称,请在设置 Java 枚举和/或数据库条目时手动匹配 ID。

最后,如果你想看到一个可行的例子,请在这里看到我的MyBatis koans的koan 23:https://github.com/midpeter444/mybatis-koans。如果您只想查看我的解决方案,请查看 complete-koans/koan23 目录。我还有一个通过Java枚举将记录插入数据库的示例。


答案 2

您可以使用自定义类型处理程序将结果直接转换为 ENUM,这样就无需将所有值都作为大写 ENUM 名称放入数据库中。

这就是状态枚举自定义处理程序的外观

public class StatusTypeHandler implements TypeHandler<Status> {

public Status getResult(ResultSet rs, String param) throws SQLException {
    return Status.getEnum(rs.getInt(param));
}

public Status getResult(CallableStatement cs, int col) throws SQLException {
    return Status.getEnum(cs.getInt(col));
}

public void setParameter(PreparedStatement ps, int paramInt, Status paramType, JdbcType jdbctype)
        throws SQLException {
    ps.setInt(paramInt, paramType.getId());
}
}

定义 TypeHandler 以在 mybatis-config 中默认处理状态.xml添加此代码。

    <typeHandlers> 
            <typeHandler javaType='Status' handler='StatusTypeHandler' /> 
    </typeHandlers>

现在让我们考虑一个例子,在你的 Dao 中有以下两个函数,

Status getStatusById(int code);
Status getStatusByName(String name);

您的映射器将如下所示

<select id="getStatusById" resultType="Status" parameterType="int">       
    SELECT  ls.id
    FROM status AS ls
    WHERE ls.id = #{id}
</select>

<select id="getStatusByName" resultType="Status" parameterType="String">       
    SELECT  ls.id
    FROM status AS ls
    WHERE ls.name = #{name}
</select>

现在,由于两个映射器的结果类型都是 Status,myBatis 将对此类型使用 CustomTypeHandler,即 StatusTypeHandler,而不是它默认用于处理枚举的 EnumTypeHandler,因此无需在数据库中维护正确的 Enum 名称。