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
不匹配或者類似的,看下查詢語句裏是否聲明
了 使用哪個轉換類