Mybatis --- 自定義類型轉換

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();

注意事項

  1. 注意:在這3個地方,至少有一處有聲明類型,不然可能會報錯
  2. 查詢沒報錯,但是返回結果爲 null ,先確定是否加載了,然後再看看是否沒有聲明 javaTypejdbcType 導致的
  3. jdbcType / javaType 不匹配或者類似的,看下查詢語句裏是否聲明了 使用哪個轉換類
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章