From f80471c586cbae07d87db704ae9eea388506cf20 Mon Sep 17 00:00:00 2001 From: AmyliaY <471816751@qq.com> Date: Tue, 3 Dec 2019 21:50:22 +0800 Subject: [PATCH] =?UTF-8?q?mybatis=E5=9F=BA=E7=A1=80=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=B1=82=E6=BA=90=E7=A0=81=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...1、反射工具箱和TypeHandler系列.md | 382 ++++++++++++++++++ ...分.md => 2、DataSource及Transaction.md} | 0 .../3、binding模块和缓存模块.md | 382 ++++++++++++++++++ 3 files changed, 764 insertions(+) create mode 100644 docs/Mybatis/基础支持层/1、反射工具箱和TypeHandler系列.md rename docs/Mybatis/基础支持层/{基础支持部分.md => 2、DataSource及Transaction.md} (100%) create mode 100644 docs/Mybatis/基础支持层/3、binding模块和缓存模块.md diff --git a/docs/Mybatis/基础支持层/1、反射工具箱和TypeHandler系列.md b/docs/Mybatis/基础支持层/1、反射工具箱和TypeHandler系列.md new file mode 100644 index 0000000..e1cfa60 --- /dev/null +++ b/docs/Mybatis/基础支持层/1、反射工具箱和TypeHandler系列.md @@ -0,0 +1,382 @@ +基础支持层主要看一下mybatis实现ORM的底层代码实现。 +## 反射工具包 +### Reflector +Reflector类主要实现了对JavaBean的元数据属性的封装,比如:可读属性列表,可写属性列表;及反射操作的封装,如:属性对应的setter方法,getter方法的反射调用。源码实现如下: +```java +public class Reflector { + + /** JavaBean的Class类型,在调用Reflector的构造方法时初始化该值 */ + private final Class type; + + /** 可读的属性列表 */ + private final String[] readablePropertyNames; + private final String[] writablePropertyNames; + + /** key属性名,value该属性名对应的setter方法调用器 */ + private final Map setMethods = new HashMap<>(); + private final Map getMethods = new HashMap<>(); + + /** key属性名称,value该属性setter方法的返回值类型 */ + private final Map> setTypes = new HashMap<>(); + private final Map> getTypes = new HashMap<>(); + + /** type的默认构造方法 */ + private Constructor defaultConstructor; + + /** 所有属性名称的集合 */ + private Map caseInsensitivePropertyMap = new HashMap<>(); + + /** + * 里面的大部分方法都是通过简单的JDK反射操作实现的 + * @param clazz + */ + public Reflector(Class clazz) { + type = clazz; + addDefaultConstructor(clazz); + + // 处理clazz中的所有getter方法,填充getMethods集合和getTypes集合 + addGetMethods(clazz); + addSetMethods(clazz); + + // 处理没有getter、setter方法的字段 + addFields(clazz); + + // 根据getMethods、setMethods集合初始化可读、可写的属性 + readablePropertyNames = getMethods.keySet().toArray(new String[0]); + writablePropertyNames = setMethods.keySet().toArray(new String[0]); + + // 初始化caseInsensitivePropertyMap集合,key属性名的大写,value属性名 + for (String propName : readablePropertyNames) { + caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); + } + for (String propName : writablePropertyNames) { + caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); + } + } +} +``` +### ReflectorFactory +顾名思义,Reflector的工厂模式,跟大部分工厂类一样,里面肯定有通过标识获取对象的方法。类的设计也遵照了 接口,实现类的模式,虽然本接口只有一个默认实现。 +```java +public interface ReflectorFactory { + + boolean isClassCacheEnabled(); + + void setClassCacheEnabled(boolean classCacheEnabled); + + /** + * 主要看一下这个方法,通过JavaBean的clazz获取该JavaBean对应的Reflector + */ + Reflector findForClass(Class type); +} + +public class DefaultReflectorFactory implements ReflectorFactory { + private boolean classCacheEnabled = true; + + /** 大部分容器及工厂设计模式的管用伎俩,key:JavaBean的clazz,value:JavaBean对应的Reflector实例 */ + private final ConcurrentMap, Reflector> reflectorMap = new ConcurrentHashMap<>(); + + /** + * 实例化一个ConcurrentMap全局变量,然后暴露一个方法从map中获取目标对象,这种设计是很多框架都会用的 + */ + @Override + public Reflector findForClass(Class type) { + if (classCacheEnabled) { + // synchronized (type) removed see issue #461 + return reflectorMap.computeIfAbsent(type, Reflector::new); + } else { + return new Reflector(type); + } + } + + public DefaultReflectorFactory() { + } + + @Override + public boolean isClassCacheEnabled() { + return classCacheEnabled; + } + + @Override + public void setClassCacheEnabled(boolean classCacheEnabled) { + this.classCacheEnabled = classCacheEnabled; + } +} + +/** + * 支持定制化ReflectorFactory + */ +public class CustomReflectorFactory extends DefaultReflectorFactory { + +} +``` +### ObjectFactory +改类也是接口+一个默认实现类,并且支持自定义扩展。 +```java +/** + * MyBatis uses an ObjectFactory to create all needed new Objects. + */ +public interface ObjectFactory { + + /** + * Sets configuration properties. + */ + default void setProperties(Properties properties) { + // NOP + } + + /** + * Creates a new object with default constructor. + */ + T create(Class type); + + /** + * Creates a new object with the specified constructor and params. + */ + T create(Class type, List> constructorArgTypes, List constructorArgs); + + /** + * Returns true if this object can have a set of other objects. + * It's main purpose is to support non-java.util.Collection objects like Scala collections. + */ + boolean isCollection(Class type); + +} + +/** + * ObjectFactory接口的唯一直接实现,反射工厂,根据传入的参数列表,选择 + * 合适的构造函数实例化对象,不传参数,则直接调用其午餐构造方法 + */ +public class DefaultObjectFactory implements ObjectFactory, Serializable { + + private static final long serialVersionUID = -8855120656740914948L; + + @Override + public T create(Class type) { + return create(type, null, null); + } + + @SuppressWarnings("unchecked") + @Override + public T create(Class type, List> constructorArgTypes, List constructorArgs) { + Class classToCreate = resolveInterface(type); + // we know types are assignable + return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs); + } + + /** + * 通过反射来实例化给定的类,如果调用无参构造方法,则直接constructor.newInstance() + * 如果有参,则根据参数类型和参数值进行调用 + */ + private T instantiateClass(Class type, List> constructorArgTypes, List constructorArgs) { + try { + Constructor constructor; + if (constructorArgTypes == null || constructorArgs == null) { + constructor = type.getDeclaredConstructor(); + try { + return constructor.newInstance(); + } catch (IllegalAccessException e) { + if (Reflector.canControlMemberAccessible()) { + constructor.setAccessible(true); + return constructor.newInstance(); + } else { + throw e; + } + } + } + constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()])); + try { + return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); + } catch (IllegalAccessException e) { + if (Reflector.canControlMemberAccessible()) { + constructor.setAccessible(true); + return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); + } else { + throw e; + } + } + } catch (Exception e) { + String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList) + .stream().map(Class::getSimpleName).collect(Collectors.joining(",")); + String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList) + .stream().map(String::valueOf).collect(Collectors.joining(",")); + throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e); + } + } +} +``` +## 类型转换 +类型转换是实现ORM的重要一环,由于 数据库中的数据类型与Java语言的数据类型并不对等,所以在PrepareStatement为sql语句绑定参数时,需要从Java类型转换成JDBC类型,而从结果集获取数据时,又要将JDBC类型转换成Java类型,mybatis使用TypeHandler完成了上述的双向转换。 +### JdbcType +mybatis通过JdbcType这个枚举类型代表了JDBC中的数据类型 +```java +/** + * 该枚举类描述了JDBC中的数据类型 + */ +public enum JdbcType { + /* + * This is added to enable basic support for the + * ARRAY data type - but a custom type handler is still required + */ + ARRAY(Types.ARRAY), + BIT(Types.BIT), + TINYINT(Types.TINYINT), + SMALLINT(Types.SMALLINT), + INTEGER(Types.INTEGER), + BIGINT(Types.BIGINT), + FLOAT(Types.FLOAT), + REAL(Types.REAL), + DOUBLE(Types.DOUBLE), + NUMERIC(Types.NUMERIC), + DECIMAL(Types.DECIMAL), + CHAR(Types.CHAR), + VARCHAR(Types.VARCHAR), + LONGVARCHAR(Types.LONGVARCHAR), + DATE(Types.DATE), + TIME(Types.TIME), + TIMESTAMP(Types.TIMESTAMP), + BINARY(Types.BINARY), + VARBINARY(Types.VARBINARY), + LONGVARBINARY(Types.LONGVARBINARY), + NULL(Types.NULL), + OTHER(Types.OTHER), + BLOB(Types.BLOB), + CLOB(Types.CLOB), + BOOLEAN(Types.BOOLEAN), + CURSOR(-10), // Oracle + UNDEFINED(Integer.MIN_VALUE + 1000), + NVARCHAR(Types.NVARCHAR), // JDK6 + NCHAR(Types.NCHAR), // JDK6 + NCLOB(Types.NCLOB), // JDK6 + STRUCT(Types.STRUCT), + JAVA_OBJECT(Types.JAVA_OBJECT), + DISTINCT(Types.DISTINCT), + REF(Types.REF), + DATALINK(Types.DATALINK), + ROWID(Types.ROWID), // JDK6 + LONGNVARCHAR(Types.LONGNVARCHAR), // JDK6 + SQLXML(Types.SQLXML), // JDK6 + DATETIMEOFFSET(-155), // SQL Server 2008 + TIME_WITH_TIMEZONE(Types.TIME_WITH_TIMEZONE), // JDBC 4.2 JDK8 + TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE); // JDBC 4.2 JDK8 + + public final int TYPE_CODE; + + /** 该静态集合维护了 常量编码 与 JdbcType之间的关系 */ + private static Map codeLookup = new HashMap<>(); + + static { + for (JdbcType type : JdbcType.values()) { + codeLookup.put(type.TYPE_CODE, type); + } + } + + JdbcType(int code) { + this.TYPE_CODE = code; + } + + public static JdbcType forCode(int code) { + return codeLookup.get(code); + } + +} +``` +### TypeHandler +TypeHandler是mybatis中所有类型转换器的顶层接口,主要用于 数据从Java类型到JdbcType类型的相互转换。 +```java +public interface TypeHandler { + + /** 通过PreparedStatement为SQL语句绑定参数时,将数据从Java类型转换为JDBC类型 */ + void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; + + /** 从结果集获取数据时,将数据由JDBC类型转换成Java类型 */ + T getResult(ResultSet rs, String columnName) throws SQLException; + + T getResult(ResultSet rs, int columnIndex) throws SQLException; + + T getResult(CallableStatement cs, int columnIndex) throws SQLException; + +} + +/** + * 可用于实现自定义的TypeHandler + */ +public abstract class BaseTypeHandler extends TypeReference implements TypeHandler { + + /** + * 只是处理了一些数据为空的特殊情况,非空数据的处理都交给子类去处理 + */ + @Override + 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 { + try { + setNonNullParameter(ps, i, parameter, jdbcType); + } catch (Exception e) { + throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " + + "Try setting a different JdbcType for this parameter or a different configuration property. " + + "Cause: " + e, e); + } + } + } + + @Override + public T getResult(ResultSet rs, String columnName) throws SQLException { + try { + return getNullableResult(rs, columnName); + } catch (Exception e) { + throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e); + } + } +} + + +public class IntegerTypeHandler extends BaseTypeHandler { + + /** + * NonNull就是NoneNull,非空的意思 + */ + @Override + public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType) + throws SQLException { + // IntegerTypeHandler就调用PreparedStatement的setInt方法 + // BooleanTypeHandler就调用PreparedStatement的setBoolean方法 + // 其它的基本数据类型,以此类推 + ps.setInt(i, parameter); + } + + @Override + public Integer getNullableResult(ResultSet rs, String columnName) + throws SQLException { + int result = rs.getInt(columnName); + return result == 0 && rs.wasNull() ? null : result; + } + + @Override + public Integer getNullableResult(ResultSet rs, int columnIndex) + throws SQLException { + int result = rs.getInt(columnIndex); + return result == 0 && rs.wasNull() ? null : result; + } + + @Override + public Integer getNullableResult(CallableStatement cs, int columnIndex) + throws SQLException { + int result = cs.getInt(columnIndex); + return result == 0 && cs.wasNull() ? null : result; + } +} +``` +TypeHandler主要用于单个参数的类型转换,如果多列值转换成一个Java对象,可以在映射文件中定义合适的映射规则 + + diff --git a/docs/Mybatis/基础支持层/基础支持部分.md b/docs/Mybatis/基础支持层/2、DataSource及Transaction.md similarity index 100% rename from docs/Mybatis/基础支持层/基础支持部分.md rename to docs/Mybatis/基础支持层/2、DataSource及Transaction.md diff --git a/docs/Mybatis/基础支持层/3、binding模块和缓存模块.md b/docs/Mybatis/基础支持层/3、binding模块和缓存模块.md new file mode 100644 index 0000000..04185f7 --- /dev/null +++ b/docs/Mybatis/基础支持层/3、binding模块和缓存模块.md @@ -0,0 +1,382 @@ +基础支持层主要看一下mybatis实现ORM的基础代码实现。 +## 反射工具包 +### Reflector +Reflector类主要实现了对JavaBean的元数据属性的封装,比如:可读属性列表,可写属性列表;及反射操作的封装,如:属性对应的setter方法,getter方法的反射调用。源码实现如下: +```java +public class Reflector { + + /** JavaBean的Class类型,在调用Reflector的构造方法时初始化该值 */ + private final Class type; + + /** 可读的属性列表 */ + private final String[] readablePropertyNames; + private final String[] writablePropertyNames; + + /** key属性名,value该属性名对应的setter方法调用器 */ + private final Map setMethods = new HashMap<>(); + private final Map getMethods = new HashMap<>(); + + /** key属性名称,value该属性setter方法的返回值类型 */ + private final Map> setTypes = new HashMap<>(); + private final Map> getTypes = new HashMap<>(); + + /** type的默认构造方法 */ + private Constructor defaultConstructor; + + /** 所有属性名称的集合 */ + private Map caseInsensitivePropertyMap = new HashMap<>(); + + /** + * 里面的大部分方法都是通过简单的JDK反射操作实现的 + * @param clazz + */ + public Reflector(Class clazz) { + type = clazz; + addDefaultConstructor(clazz); + + // 处理clazz中的所有getter方法,填充getMethods集合和getTypes集合 + addGetMethods(clazz); + addSetMethods(clazz); + + // 处理没有getter、setter方法的字段 + addFields(clazz); + + // 根据getMethods、setMethods集合初始化可读、可写的属性 + readablePropertyNames = getMethods.keySet().toArray(new String[0]); + writablePropertyNames = setMethods.keySet().toArray(new String[0]); + + // 初始化caseInsensitivePropertyMap集合,key属性名的大写,value属性名 + for (String propName : readablePropertyNames) { + caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); + } + for (String propName : writablePropertyNames) { + caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); + } + } +} +``` +### ReflectorFactory +顾名思义,Reflector的工厂模式,跟大部分工厂类一样,里面肯定有通过标识获取对象的方法。类的设计也遵照了 接口,实现类的模式,虽然本接口只有一个默认实现。 +```java +public interface ReflectorFactory { + + boolean isClassCacheEnabled(); + + void setClassCacheEnabled(boolean classCacheEnabled); + + /** + * 主要看一下这个方法,通过JavaBean的clazz获取该JavaBean对应的Reflector + */ + Reflector findForClass(Class type); +} + +public class DefaultReflectorFactory implements ReflectorFactory { + private boolean classCacheEnabled = true; + + /** 大部分容器及工厂设计模式的管用伎俩,key:JavaBean的clazz,value:JavaBean对应的Reflector实例 */ + private final ConcurrentMap, Reflector> reflectorMap = new ConcurrentHashMap<>(); + + /** + * 实例化一个ConcurrentMap全局变量,然后暴露一个方法从map中获取目标对象,这种设计是很多框架都会用的 + */ + @Override + public Reflector findForClass(Class type) { + if (classCacheEnabled) { + // synchronized (type) removed see issue #461 + return reflectorMap.computeIfAbsent(type, Reflector::new); + } else { + return new Reflector(type); + } + } + + public DefaultReflectorFactory() { + } + + @Override + public boolean isClassCacheEnabled() { + return classCacheEnabled; + } + + @Override + public void setClassCacheEnabled(boolean classCacheEnabled) { + this.classCacheEnabled = classCacheEnabled; + } +} + +/** + * 支持定制化ReflectorFactory + */ +public class CustomReflectorFactory extends DefaultReflectorFactory { + +} +``` +### ObjectFactory +改类也是接口+一个默认实现类,并且支持自定义扩展。 +```java +/** + * MyBatis uses an ObjectFactory to create all needed new Objects. + */ +public interface ObjectFactory { + + /** + * Sets configuration properties. + */ + default void setProperties(Properties properties) { + // NOP + } + + /** + * Creates a new object with default constructor. + */ + T create(Class type); + + /** + * Creates a new object with the specified constructor and params. + */ + T create(Class type, List> constructorArgTypes, List constructorArgs); + + /** + * Returns true if this object can have a set of other objects. + * It's main purpose is to support non-java.util.Collection objects like Scala collections. + */ + boolean isCollection(Class type); + +} + +/** + * ObjectFactory接口的唯一直接实现,反射工厂,根据传入的参数列表,选择 + * 合适的构造函数实例化对象,不传参数,则直接调用其午餐构造方法 + */ +public class DefaultObjectFactory implements ObjectFactory, Serializable { + + private static final long serialVersionUID = -8855120656740914948L; + + @Override + public T create(Class type) { + return create(type, null, null); + } + + @SuppressWarnings("unchecked") + @Override + public T create(Class type, List> constructorArgTypes, List constructorArgs) { + Class classToCreate = resolveInterface(type); + // we know types are assignable + return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs); + } + + /** + * 通过反射来实例化给定的类,如果调用无参构造方法,则直接constructor.newInstance() + * 如果有参,则根据参数类型和参数值进行调用 + */ + private T instantiateClass(Class type, List> constructorArgTypes, List constructorArgs) { + try { + Constructor constructor; + if (constructorArgTypes == null || constructorArgs == null) { + constructor = type.getDeclaredConstructor(); + try { + return constructor.newInstance(); + } catch (IllegalAccessException e) { + if (Reflector.canControlMemberAccessible()) { + constructor.setAccessible(true); + return constructor.newInstance(); + } else { + throw e; + } + } + } + constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()])); + try { + return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); + } catch (IllegalAccessException e) { + if (Reflector.canControlMemberAccessible()) { + constructor.setAccessible(true); + return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); + } else { + throw e; + } + } + } catch (Exception e) { + String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList) + .stream().map(Class::getSimpleName).collect(Collectors.joining(",")); + String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList) + .stream().map(String::valueOf).collect(Collectors.joining(",")); + throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e); + } + } +} +``` +## 类型转换 +类型转换是实现ORM的重要一环,由于 数据库中的数据类型与Java语言的数据类型并不对等,所以在PrepareStatement为sql语句绑定参数时,需要从Java类型转换成JDBC类型,而从结果集获取数据时,又要将JDBC类型转换成Java类型,mybatis使用TypeHandler完成了上述的双向转换。 +### JdbcType +mybatis通过JdbcType这个枚举类型代表了JDBC中的数据类型 +```java +/** + * 该枚举类描述了JDBC中的数据类型 + */ +public enum JdbcType { + /* + * This is added to enable basic support for the + * ARRAY data type - but a custom type handler is still required + */ + ARRAY(Types.ARRAY), + BIT(Types.BIT), + TINYINT(Types.TINYINT), + SMALLINT(Types.SMALLINT), + INTEGER(Types.INTEGER), + BIGINT(Types.BIGINT), + FLOAT(Types.FLOAT), + REAL(Types.REAL), + DOUBLE(Types.DOUBLE), + NUMERIC(Types.NUMERIC), + DECIMAL(Types.DECIMAL), + CHAR(Types.CHAR), + VARCHAR(Types.VARCHAR), + LONGVARCHAR(Types.LONGVARCHAR), + DATE(Types.DATE), + TIME(Types.TIME), + TIMESTAMP(Types.TIMESTAMP), + BINARY(Types.BINARY), + VARBINARY(Types.VARBINARY), + LONGVARBINARY(Types.LONGVARBINARY), + NULL(Types.NULL), + OTHER(Types.OTHER), + BLOB(Types.BLOB), + CLOB(Types.CLOB), + BOOLEAN(Types.BOOLEAN), + CURSOR(-10), // Oracle + UNDEFINED(Integer.MIN_VALUE + 1000), + NVARCHAR(Types.NVARCHAR), // JDK6 + NCHAR(Types.NCHAR), // JDK6 + NCLOB(Types.NCLOB), // JDK6 + STRUCT(Types.STRUCT), + JAVA_OBJECT(Types.JAVA_OBJECT), + DISTINCT(Types.DISTINCT), + REF(Types.REF), + DATALINK(Types.DATALINK), + ROWID(Types.ROWID), // JDK6 + LONGNVARCHAR(Types.LONGNVARCHAR), // JDK6 + SQLXML(Types.SQLXML), // JDK6 + DATETIMEOFFSET(-155), // SQL Server 2008 + TIME_WITH_TIMEZONE(Types.TIME_WITH_TIMEZONE), // JDBC 4.2 JDK8 + TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE); // JDBC 4.2 JDK8 + + public final int TYPE_CODE; + + /** 该静态集合维护了 常量编码 与 JdbcType之间的关系 */ + private static Map codeLookup = new HashMap<>(); + + static { + for (JdbcType type : JdbcType.values()) { + codeLookup.put(type.TYPE_CODE, type); + } + } + + JdbcType(int code) { + this.TYPE_CODE = code; + } + + public static JdbcType forCode(int code) { + return codeLookup.get(code); + } + +} +``` +### TypeHandler +TypeHandler是mybatis中所有类型转换器的顶层接口,主要用于 数据从Java类型到JdbcType类型的相互转换。 +```java +public interface TypeHandler { + + /** 通过PreparedStatement为SQL语句绑定参数时,将数据从Java类型转换为JDBC类型 */ + void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; + + /** 从结果集获取数据时,将数据由JDBC类型转换成Java类型 */ + T getResult(ResultSet rs, String columnName) throws SQLException; + + T getResult(ResultSet rs, int columnIndex) throws SQLException; + + T getResult(CallableStatement cs, int columnIndex) throws SQLException; + +} + +/** + * 可用于实现自定义的TypeHandler + */ +public abstract class BaseTypeHandler extends TypeReference implements TypeHandler { + + /** + * 只是处理了一些数据为空的特殊情况,非空数据的处理都交给子类去处理 + */ + @Override + 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 { + try { + setNonNullParameter(ps, i, parameter, jdbcType); + } catch (Exception e) { + throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " + + "Try setting a different JdbcType for this parameter or a different configuration property. " + + "Cause: " + e, e); + } + } + } + + @Override + public T getResult(ResultSet rs, String columnName) throws SQLException { + try { + return getNullableResult(rs, columnName); + } catch (Exception e) { + throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e); + } + } +} + + +public class IntegerTypeHandler extends BaseTypeHandler { + + /** + * NonNull就是NoneNull,非空的意思 + */ + @Override + public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType) + throws SQLException { + // IntegerTypeHandler就调用PreparedStatement的setInt方法 + // BooleanTypeHandler就调用PreparedStatement的setBoolean方法 + // 其它的基本数据类型,以此类推 + ps.setInt(i, parameter); + } + + @Override + public Integer getNullableResult(ResultSet rs, String columnName) + throws SQLException { + int result = rs.getInt(columnName); + return result == 0 && rs.wasNull() ? null : result; + } + + @Override + public Integer getNullableResult(ResultSet rs, int columnIndex) + throws SQLException { + int result = rs.getInt(columnIndex); + return result == 0 && rs.wasNull() ? null : result; + } + + @Override + public Integer getNullableResult(CallableStatement cs, int columnIndex) + throws SQLException { + int result = cs.getInt(columnIndex); + return result == 0 && cs.wasNull() ? null : result; + } +} +``` +TypeHandler主要用于单个参数的类型转换,如果多列值转换成一个Java对象,可以在映射文件中定义合适的映射规则 + +