17

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

我有一个表“状态”。它有两列: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>

但由于某种原因,当我尝试检索枚举时,有些东西会中断,但不会引发异常。

4

2 回答 2

17

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

首先,MyBatis 有一个内置的EnumTypeHandler. 默认情况下,只要将 Java 枚举指定为 resultType 或 parameterType,就会处理该类型。对于查询,当尝试将数据库记录转换为 Java 枚举时,EnumTypeHandler 只接受一个参数并尝试查找与该值对应的 Java 枚举值。

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

SELECT ls.name, ls.id

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

将您的查询更改为

SELECT UPPER(ls.name)

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

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

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

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

最后,如果您想查看一个工作示例,请在此处查看我的 MyBatis koans 的 koan 23:https ://github.com/midpeter444/mybatis-koans 。如果您只想查看我的解决方案,请查看已完成的 koans/koan23 目录。我还有一个通过 Java 枚举将记录插入数据库的示例。

于 2012-06-08T00:53:03.370 回答
13

您可以使用自定义类型处理程序将结果直接转换为 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());
}
}

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

    <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>

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

于 2013-09-03T16:20:33.907 回答