2012-04-18 101 views
13

我知道这已经被问过,但是我没有能够实现一个基于我目前发现的信息的解决方案。所以也许有人可以向我解释。MyBatis枚举用法

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

ISTM setId()在一个枚举是一个可怕的想法... :) – 2017-11-02 10:29:20

回答

18

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

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

一个例子会更好地说明。假设当我传递“Ready”作为参数时,上面的查询返回2"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枚举。

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

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

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

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

6

您可以使用Custom TypeHandler将结果直接转换为ENUM,这样就不需要将所有值都作为UPPER CASE ENUM Names放入数据库中。

这是你的状态枚举自定义处理程序将如何看起来像

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中加入此代码来处理状态。

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

现在让我们来考虑,你必须在你的道以下两个函数的例子,

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两个映射器是状态,myBatis将使用这种类型的CustomTypeHandler,即StatusTypeHandler而不是EnumTypeHandler,它默认使用处理枚举,所以不需要维护正确的Enum数据库中的名称。

+0

如果我需要返回枚举而不传递参数(id,名称等)? – axcdnt 2015-04-15 13:22:26

+2

这就是提供干净解决方案的答案。依靠大写字母很难看。 – Eduardo 2016-09-02 15:04:25