起因:最近一次开会的时候,一位老司机说了下项目中应该使用常量还是枚举来设置参数,然后提到了mybatis中enum需要特殊处理。当时不知道mybatis强大的类型处理器,所以不知道还能用enum来定义对象属性,完成映射就自己研究了一下。
1.ENUM
首先,假设我们有一些特定用户的用户名和密码用枚举来存放(只是假设,真实场景肯定不是这样)
public enum UserEnum {
ZHANGSAN("zhangsan","1235456"),
LISI("lisi","zouni"),
ADMIN("root","root")
;
private String name;
private String password;
private UserEnum(String name, String password) {
this.name = name;
this.password = password;
}
// getter and setter
}
2.POJO
我们需要修改数据库映射的pojo中的属性,将需要设置为枚举的改成对应的枚举类型。提供一个toString方法,方便我们查看返回结果
public class TestEnum {
private Integer id;
private UserEnum name;
private UserEnum password;
// getter and setter
@Override
public String toString() {
return "Testnote [id=" + id + ", name=" + name.getName() + ", password=" + password.getPassword() + "]";
}
}
3.UserEnum的处理器
要使用enum对应的转换器是十分重要的。在mybatis中,有两种对于枚举对象的处理器:
- EnumTypeHandler(通过枚举对象处理)
- EnumOrdinalTypeHandler(通过枚举中常量序号处理)。
这里参考EnumTypeHandler源码,因为测试的时候EnumOrdinalTypeHandler老是失败(希望有成功的伙伴可以指点下)。
public class UserEnumHandler extends BaseTypeHandler<UserEnum> {
private Class<UserEnum> type;
public UserEnumHandler(Class<UserEnum> type) {
if (type == null)
throw new IllegalArgumentException("Type argument cannot be null");
this.type = type;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, UserEnum parameter, JdbcType jdbcType)
throws SQLException {
switch (i) { // i 表示第几个参数
case 1:
if (jdbcType == null) {
ps.setString(i, parameter.getName()); // NameEnum的Name为需要存储的数据
} else {
ps.setObject(i, parameter.getName(), jdbcType.TYPE_CODE);
}
break;
case 2:
if (jdbcType == null) {
ps.setString(i, parameter.getPassword()); // NameEnum的Name为需要存储的数据
} else {
ps.setObject(i, parameter.getPassword(), jdbcType.TYPE_CODE);
}
default:
break;
}
}
@Override
public UserEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
String s = rs.getString(columnName);
switch (columnName) {
case "name":
return s == null ? null : findName(s);
case "password":
return s == null ? null : findPassword(s);
default:
return null;
}
}
@Override
public UserEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String s = rs.getString(columnIndex);
return s == null ? null : findName(s);
}
@Override
public UserEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String s = cs.getString(columnIndex);
return s == null ? null : findName(s);
}
// 遍历UserEnum查找对应name的枚举
private UserEnum findName(String name) {
for (UserEnum tenum : UserEnum.class.getEnumConstants()) {
if (tenum.getName().equals(name)) {
return tenum;
}
}
return null;
}
// 遍历UserEnum查找对应password的枚举
private UserEnum findPassword(String password) {
for (UserEnum tenum : UserEnum.class.getEnumConstants()) {
if (tenum.getPassword().equals(password)) {
return tenum;
}
}
return null;
}
}
4.mapper.java
mapper.java中没有什么特别改动,只是参数应该为枚举类型
public interface TestEnumMapper {
// 根据Enum来更新数据
TestEnum selectByEnum(Integer id);
int updateByEnum(@Param("id") Integer id, @Param("user") UserEnum user);
}
5.重点配置mapper.xml
首先,对于查询返回的resultMap映射,需要配置处理器
<resultMap id="BaseResultMap" type="com.ys.model.TestEnum">
<id column="id" property="id" jdbcType="INTEGER" />
<result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.ys.constant.UserEnumHandler" />
<result column="password" property="password" jdbcType="VARCHAR" typeHandler="com.ys.constant.UserEnumHandler"/>
</resultMap>
通过ID查询,并完成枚举的装配
<select id="selectByEnum" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select id,name,password
from testnote
where id = #{id,jdbcType=INTEGER}
</select>
通过枚举来更新数据
<update id="updateByEnum" parameterType="com.ys.model.TestEnum">
update testnote
<set>
<if test="user != null">
name = #{user,jdbcType=VARCHAR,typeHandler=com.ys.constant.UserEnumHandler},
password = #{user,jdbcType=VARCHAR,typeHandler=com.ys.constant.UserEnumHandler},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
注意:需要使用枚举的地方,用typeHandler来配置即可
6.测试用例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/spring/applicationContext.xml",
"classpath:/spring/applicationContext-mybatis.xml" })
public class MyBatisTest {
@Autowired
private TestEnumMapper testEnumMapper;
@Test
public void testEnum() {
testEnumMapper.updateByEnum(1, UserEnum.ZHANGSAN);
TestEnum note = testEnumMapper.selectByEnum(1);
System.out.println(note.toString());
}
}
输出结果
Preparing: update testnote SET name = ?, password = ? where id = ?
Parameters: zhangsan(String), 1235456(String), 1(Integer)
Updates: 1
Preparing: select id,name,password from testnote where id = ?
Parameters: 1(Integer)
Total: 1
Testnote [id=1, name=zhangsan, password=1235456]