Mybatis — 自定义类型转换
前言
在进行数据库增删改查
的时候,因为实体类类型
和数据库类型
不兼容,或者说Mybatis自带的类型转换
不够用了,这时候就要自己手动操作了,最常见的就是素组
的的操作,根据原有的进行扩展,这样就不用定义一个数组属性,就写一次转换方法。
接口
原生接口org.apache.ibatis.type.TypeHandler<T>
public interface TypeHandler<T> {
/**
用来设置查询参数
*/
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
用来将查询结果转换成指定实体类,依旧是返回结果的列名
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
/**
用来将查询结果转换成指定实体类,依旧是返回结果列的顺序号
*/
T getResult(ResultSet rs, int columnIndex) throws SQLException;
/**
用来将查询结果转换成指定实体类,依旧是返回结果列的顺序号,
只是使用了不同的返回接收接口,写过原生jdbc的应该有印象
*/
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
也可以使用继承的方式扩展,我这里是直接继承 org.apache.ibatis.type.TypeReference<T>
,这个可以省很多事情
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
protected Configuration configuration;
public void setConfiguration(Configuration c) {
this.configuration = c;
}
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
"Cause: " + e, e);
}
} else {
setNonNullParameter(ps, i, parameter, jdbcType);
}
}
public T getResult(ResultSet rs, String columnName) throws SQLException {
T result = getNullableResult(rs, columnName);
if (rs.wasNull()) {
return null;
} else {
return result;
}
}
public T getResult(ResultSet rs, int columnIndex) throws SQLException {
T result = getNullableResult(rs, columnIndex);
if (rs.wasNull()) {
return null;
} else {
return result;
}
}
public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
T result = getNullableResult(cs, columnIndex);
if (cs.wasNull()) {
return null;
} else {
return result;
}
}
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
}
可以看到已经帮我们把结果或者查询参数为空的情况处理好了,我们只需要把 不为空的情况补充完整即可。
代码实现
我这里是实现了一个将'1,2,3,4' 与 ['1','2','3','4']
之间的转换
//这个注解定义的是JdbcType类型,这里的类型不可自己随意定义,必须要是枚举类org.apache.ibatis.type.JdbcType所枚举的数据类型
@MappedJdbcTypes({JdbcType.VARCHAR})
//这里定义的是JavaType的数据类型,描述了哪些Java类型可被拦截
@MappedTypes({List.class})
public class StringListTypeHandler extends BaseTypeHandler<List<String>> {
private final static Logger logger = Logger.getLogger(StringListTypeHandler.class);
public StringListTypeHandler(){
logger.info("initialization StringListTypeHandler ok");
System.out.println("initialization StringListTypeHandler ok");
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
//这个我使用的是 commons.lang3 的包,用来添加 ',' 也可以直接 toString() 后去掉两端的 [] 即可
ps.setString(i, StringUtils.join(parameter,","));
}
@Override
public List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException {
String str = rs.getString(columnName);
if (rs.wasNull())
return null;
//解决增加异常,这样做是因为直接放入时操作的是原素组,导致不能增删操作,所以要重新创建一个
return new ArrayList<String>(Arrays.asList(str.split(",")));
}
@Override
public List<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String str = rs.getString(columnIndex);
if (rs.wasNull())
return null;
return new ArrayList<String>(Arrays.asList(str.split(",")));
}
@Override
public List<String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String str = cs.getString(columnIndex);
if (cs.wasNull())
return null;
return new ArrayList<String>(Arrays.asList(str.split(",")));
}
}
这样一来,工具就完成了。需要注意的是,如果没有添加 MappedJdbcTypes
MappedTypes
的话,要在对应的位置声明相应的类型
配置
先说下我使用的是添加了 MappedJdbcTypes
MappedTypes
注解,所以在spring或者mybatis配置文件
里不做处理,即不额外增加配置也是可以生效的,至少我使用的版本是生效的 mybatis-3.2.3.jar
mybatis-spring-1.3.1.jar
,我猜测,要是没有配置注解的话或许不会生效。都配置上也不会报错,如果报错了,就得优先查看这个,比如没有找到或者加载类失败
在没有mybatis配置文件
的情况下,直接配置在spring
或者其他情况下,把类手动初始化,然后配置到SqlSessionFactory
里
<bean id="intArrayTypeHandler" class="com.ssm.common.model.utils.IntArrayTypeHandler"/>
<bean id="stringArrayTypeHandler" class="com.ssm.common.model.utils.StringArrayTypeHandler"/>
<bean id="stringListTypeHandler" class="com.ssm.common.model.utils.StringListTypeHandler"/>
<!-- 配置 SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--typeHandler注入-->
<!-- 这里是要配置多个的情况下,如果单个的话直接在 property 标签里 ref 即可 -->
<property name="typeHandlers">
<list>
<ref bean="intArrayTypeHandler"/>
<ref bean="stringArrayTypeHandler"/>
<ref bean="stringListTypeHandler"/>
</list>
</property>
</bean>
如果有mybatis配置文件
,那么直接添加一个在 TypeHandleys
标签
<configuration>
<TypeHandleys>
<!-- 如果类里有注释的话这里就可以省去,如果没有建议是加上,即使不报错,也方便自己或者其他人查看 -->
<typeHandler handler="com.ssm.common.model.utils.StringListTypeHandler" javaType="java.util.List" jdbcType="VARCHAR" />
</TypeHandleys>
</configuration>
使用
第一种,在mapper.xml
文件里
<!-- 如果前两出都没有配置 类型的话,这里就要加上对应的类型 -->
<update id="update">
update table set cloum=#{cloum,typeHandler=com.ssm.common.model.utils.StringListTypeHandler} where id=#{id}
</update>
<!-- 也可以配置在查询和结果映射里,另外只有查询参数可以直接写在 sql 语句里,返回结果必须使用映射,同名且没有用到自定义转换类的或许可以不用配置,没有在具体测试 -->
<parameterMap type="" id="">
<parameter property="" javaType="" jdbcType="" typeHandler=""/>
</parameterMap>
<resultMap type="" id="">
<result column="" property="" javaType="" jdbcType="" typeHandler=""/>
</resultMap>
第二种,注解
在 mapper.java
里用注解的方式实现的查询参数直接按#{cloum,typeHandler=com.ssm.common.model.utils.StringListTypeHandler}
这样的形式写上就行,这里要说的都是查询
@Select("select * from table where cloum=#{cloum,typeHandler=com.ssm.common.model.utils.StringListTypeHandler}")
@Results({、
@Result(column="cloum",property="cloum",typeHandler=StringListTypeHandler.class)
})
public List<Role> select();
注意事项
- 注意:在这3个地方,至少有一处有声明类型,不然可能会报错
- 查询没报错,但是返回结果为 null ,先确定是否加载了,然后再看看是否没有声明
javaType
和jdbcType
导致的 - 报
jdbcType / javaType
不匹配或者类似的,看下查询语句里是否声明
了 使用哪个转换类