Mybatis配置文件详解-TypeHandler配置分析

        我们知道java有java的数据类型,数据库有数据库的数据类型,那么我们在往数据库中插入数据的时候是如何把java类型当做数据库类型插入数据库,在从数据库读取数据的时候又是如何把数据库类型当做java类型来处理呢?这中间必然要经过一个类型转换。在Mybatis中我们可以定义一个叫做TypeHandler类型处理器的东西,通过它可以实现Java类型跟数据库类型的相互转换。

        使用TypeHandler标签配置一个自己定义的类型处理一般有三个步骤:编写类型处理器类,在Mybatis全局配置文件中配置该类型处理器,在SQL映射配置文件中使用。

        编写类型处理器类一般要实现TypeHandler接口,接口的泛型指定要转换的Java参数类型(若不指定则默认为Object类)。在TypeHandler中定义了四个方法:

public interface TypeHandler {  
   
    /** 
     * 用于定义在Mybatis设置参数时该如何把Java类型的参数转换为对应的数据库类型 
     * @param ps 当前的PreparedStatement对象 
     * @param i 当前参数的位置 
     * @param parameter 当前参数的Java对象 
     * @param jdbcType 当前参数的数据库类型 
     * @throws SQLException 
     */  
    void setParameter(PreparedStatement ps, int i, T parameter,  
           JdbcType jdbcType) throws SQLException;  
   
    /** 
     * 用于在Mybatis获取数据结果集时如何把数据库类型转换为对应的Java类型 
     * @param rs 当前的结果集 
     * @param columnName 当前的字段名称 
     * @return 转换后的Java对象 
     * @throws SQLException 
     */  
    T getResult(ResultSet rs, String columnName) throws SQLException;  
   
    /** 
     * 用于在Mybatis通过字段位置获取字段数据时把数据库类型转换为对应的Java类型 
     * @param rs 当前的结果集 
     * @param columnIndex 当前字段的位置 
     * @return 转换后的Java对象 
     * @throws SQLException 
     */  
    T getResult(ResultSet rs, int columnIndex) throws SQLException;  
   
    /** 
     * 用于Mybatis在调用存储过程后把数据库类型的数据转换为对应的Java类型 
     * @param cs 当前的CallableStatement执行后的CallableStatement 
     * @param columnIndex 当前输出参数的位置 
     * @return 
     * @throws SQLException 
     */  
    T getResult(CallableStatement cs, int columnIndex) throws SQLException;  
   
} 

创建实体类:

package com.ghgcn.mybatis.entity;

public class PhoneNumber {

    private String countryCode;
    private String stateCode;
    private String number;

    public PhoneNumber() {
    }

    public PhoneNumber(String countryCode, String stateCode, String number) {
        super();
        this.countryCode = countryCode;
        this.stateCode = stateCode;
        this.number = number;
    }

    public PhoneNumber(String string) {
        if (string != null) {
            String[] parts = string.split("-");
            if (parts.length > 0)
                this.countryCode = parts[0];
            if (parts.length > 1)
                this.stateCode = parts[1];
            if (parts.length > 2)
                this.number = parts[2];
        }
    }

    public String getAsString() {
        return countryCode + "-" + stateCode + "-" + number;
    }

    public String getCountryCode() {
        return countryCode;
    }

    public void setCountryCode(String countryCode) {
        this.countryCode = countryCode;
    }

    public String getStateCode() {
        return stateCode;
    }

    public void setStateCode(String stateCode) {
        this.stateCode = stateCode;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return "PhoneNumber [countryCode=" + countryCode + ", stateCode=" + stateCode + ", number=" + number + "]";
    }

}

package com.ghgcn.mybatis.entity;

import java.io.Serializable;
import java.util.Date;

public class Student implements Serializable {
    
    private static final long serialVersionUID = -7301987836735803448L;
    private Integer studId;
    private String name;
    private String email;
    private Date dob;
    
    private  PhoneNumber phone;
    
    public Integer getStudId() {
        return studId;
    }
    
    public void setStudId(Integer studId) {
        this.studId = studId;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public String getEmail() {
        return email;
    }
    
    public void setEmail(String email) {
        this.email = email;
    }
    
    public Date getDob() {
        return dob;
    }
    
    public void setDob(Date dob) {
        this.dob = dob;
    }

    
    public PhoneNumber getPhone() {
        return phone;
    }

    
    public void setPhone(PhoneNumber phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "Student [studId=" + studId + ", name=" + name + ", email=" + email + ", dob=" + dob + ", phone=" + phone
                + "]";
    }

    
    
    
    
}

新建自定义TypeHandler

public class PhoneTypeHandler extends BaseTypeHandler {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, PhoneNumber parameter, JdbcType jdbcType)
            throws SQLException {
        
        ps.setString(i, parameter.getAsString());
        
    }

    @Override
    public PhoneNumber getNullableResult(ResultSet rs, String columnName) throws SQLException {
       
        return new PhoneNumber(rs.getString(columnName));
    }

    @Override
    public PhoneNumber getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        
        return new PhoneNumber(rs.getString(columnIndex));
    }

    @Override
    public PhoneNumber getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return new PhoneNumber(cs.getString(columnIndex));
    }

}

在config.xml中配置自定义TypeHandler


        

在mapper.xml映射文件中使用自定义TypeHandler

    
        
        
        
        
        
        

    

 

BaseTypeHandler抽象类

        在实现自己的TypeHandler时,除了上面提到的实现最原始的接口之外,Mybatis还为我们提供了一个实现了TypeHandler接口的抽象类BaseTypeHandler。所以我们也可以通过继承BaseTypeHandler来实现自己的TypeHandler。
我们先来看一下BaseTypeHandler类的定义:

public abstract class BaseTypeHandler extends TypeReference implements TypeHandler {  
   
  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;  
   
}  

        我们可以看到BaseTypeHandler对TypeHandler接口的四个方法做了一个简单的选择,把null值的情况都做了一个过滤,核心的取值和设值的方法还是抽象出来了供子类来实现。使用BaseTypeHandler还有一个好处是它继承了另外一个叫做TypeReference的抽象类,通过TypeReference的getRawType()方法可以获取到当前TypeHandler所使用泛型的原始类型。这对Mybatis在注册TypeHandler的时候是非常有好处的。在没有指定javaType的情况下,Mybatis在注册TypeHandler时可以通过它来获取当前TypeHandler所使用泛型的原始类型作为要注册的TypeHandler的javaType类型。

你可能感兴趣的:(MyBatis)