ToB企服应用市场:ToB评测及商务社交产业平台

标题: 《深入理解Mybatis原理》MyBatis配置解析过程 [打印本页]

作者: 刘俊凯    时间: 2025-1-2 07:34
标题: 《深入理解Mybatis原理》MyBatis配置解析过程
配置解析主体方法
  1. public Configuration parse() {  
  2.     if (parsed) {  
  3.         throw new BuilderException("Each XMLConfigBuilder can only be used once.");  
  4.     }  
  5.     parsed = true;  
  6.     //源码中没有这一句,只有 parseConfiguration(parser.evalNode("/configuration"));  
  7.     //为了让读者看得更明晰,源码拆分为以下两句  
  8.     XNode configurationNode = parser.evalNode("/configuration");  
  9.     parseConfiguration(configurationNode);  
  10.     return configuration;  
  11. }  
  12. /**
  13. * 解析 "/configuration"节点下的子节点信息,然后将解析的结果设置到Configuration对象中
  14. */  
  15. private void parseConfiguration(XNode root) {  
  16.     try {  
  17.         //1.首先处理properties 节点     
  18.         propertiesElement(root.evalNode("properties")); //issue #117 read properties first  
  19.         //2.处理typeAliases  
  20.         typeAliasesElement(root.evalNode("typeAliases"));  
  21.         //3.处理插件  
  22.         pluginElement(root.evalNode("plugins"));  
  23.         //4.处理objectFactory  
  24.         objectFactoryElement(root.evalNode("objectFactory"));  
  25.         //5.objectWrapperFactory  
  26.         objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));  
  27.         //6.settings  
  28.         settingsElement(root.evalNode("settings"));  
  29.         //7.处理environments  
  30.         environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631  
  31.         //8.database  
  32.         databaseIdProviderElement(root.evalNode("databaseIdProvider"));  
  33.         //9.typeHandlers  
  34.         typeHandlerElement(root.evalNode("typeHandlers"));  
  35.         //10.mappers  
  36.         mapperElement(root.evalNode("mappers"));  
  37.     } catch (Exception e) {  
  38.         throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);  
  39.     }  
  40. }
复制代码
通过以上源码,就能看出,在mybatis的配置文件中:
配置文件元素

properties
  1. <configuration>
  2.    
  3.    
  4.     <properties>
  5.         <property name="driver" value="com.mysql.jdbc.Driver"/>
  6.         <property name="url" value="jdbc:mysql://localhost:3306/test1"/>
  7.         <property name="username" value="root"/>
  8.         <property name="password" value="root"/>
  9.     </properties>
复制代码
那么,要是两种方法都同时配置了,那么最终会采用什么样的配置呢?
这是由于配置是存放在Properties,它继承自HashTable类,当依次将上述几种配置源put进去时,后加载的配置会覆盖先加载的配置。所以,最终应用配置时Configuration配置优先级最高,其次是另外两种中的一种。具体可以参考接下来的源码分析。
envirements
  1. <configuration>
  2.    
  3.    
  4.     <properties>
  5.         <property name="driver" value="com.mysql.jdbc.Driver"/>
  6.         <property name="url" value="jdbc:mysql://localhost:3306/test1"/>
  7.         <property name="username" value="root"/>
  8.         <property name="password" value="root"/>
  9.     </properties><configuration>
  10.    
  11.    
  12.     <properties>
  13.         <property name="driver" value="com.mysql.jdbc.Driver"/>
  14.         <property name="url" value="jdbc:mysql://localhost:3306/test1"/>
  15.         <property name="username" value="root"/>
  16.         <property name="password" value="root"/>
  17.     </properties><configuration>
  18.    
  19.    
  20.     <properties>
  21.         <property name="driver" value="com.mysql.jdbc.Driver"/>
  22.         <property name="url" value="jdbc:mysql://localhost:3306/test1"/>
  23.         <property name="username" value="root"/>
  24.         <property name="password" value="root"/>
  25.     </properties><configuration>
  26.    
  27.    
  28.     <properties>
  29.         <property name="driver" value="com.mysql.jdbc.Driver"/>
  30.         <property name="url" value="jdbc:mysql://localhost:3306/test1"/>
  31.         <property name="username" value="root"/>
  32.         <property name="password" value="root"/>
  33.     </properties>                                       
复制代码
environments元素节点可以配置多个environment子节点, 怎么理解呢?
假如我们系统的开发情况和正式情况所用的数据库不一样(这是肯定的), 那么可以设置两个environment, 两个id分别对应开发情况(dev)和正式情况(final),那么通过配置environments的default属性就能选择对应的environment了, 比方,我将environments的deault属性的值配置为dev, 那么就会选择dev的environment。 那么这个是怎么实现的呢?
看源码: mybatis 是通过XMLConfigBuilder这个类在解析mybatis配置文件的,XMLConfigBuilder对于environments的解析:
  1. public class XMLConfigBuilder extends BaseBuilder {
  2.     private boolean parsed;
  3.     // xml解析器
  4.     private XPathParser parser;
  5.     private String environment;
  6.   
  7.    
  8.     // 看看解析enviroments元素节点的方法
  9.     private void environmentsElement(XNode context) throws Exception {
  10.         if (context != null) {
  11.             if (environment == null) {
  12.                 //解析environments节点的default属性的值
  13.                 //例如: <environments default="development">
  14.                 environment = context.getStringAttribute("default");
  15.             }
  16.             //递归解析environments子节点
  17.             for (XNode child : context.getChildren()) {
  18.                 //<environment id="development">, 只有enviroment节点有id属性,那么这个属性有何作用?
  19.                 //environments 节点下可以拥有多个 environment子节点
  20.                 //类似于这样: <environments default="development"><environment id="development">...</environment><environment id="test">...</environments>
  21.                 //意思就是可以对应多个环境,比如开发环境,测试环境等, 由environments的default属性去选择对应的enviroment
  22.                 String id = child.getStringAttribute("id");
  23.                 //isSpecial就是根据由environments的default属性去选择对应的enviroment
  24.                 if (isSpecifiedEnvironment(id)) {
  25.                     //事务, mybatis有两种:JDBC 和 MANAGED, 配置为JDBC则直接使用JDBC的事务,配置为MANAGED则是将事务托管给容器,
  26.                     TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
  27.                     //enviroment节点下面就是dataSource节点了,解析dataSource节点(下面会贴出解析dataSource的具体方法)
  28.                     DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
  29.                     DataSource dataSource = dsFactory.getDataSource();
  30.                     Environment.Builder environmentBuilder = new Environment.Builder(id)
  31.                           .transactionFactory(txFactory)
  32.                           .dataSource(dataSource);
  33.                     //将dataSource设置进configuration对象
  34.                     configuration.setEnvironment(environmentBuilder.build());
  35.                 }
  36.             }
  37.         }
  38.     }
  39.    
  40.     //dataSource的解析方法
  41.     private DataSourceFactory dataSourceElement(XNode context) throws Exception {
  42.         if (context != null) {
  43.             //dataSource的连接池
  44.             String type = context.getStringAttribute("type");
  45.             //子节点 name, value属性set进一个properties对象
  46.             Properties props = context.getChildrenAsProperties();
  47.             //创建dataSourceFactory
  48.             DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
  49.             factory.setProperties(props);
  50.             return factory;
  51.         }
  52.         throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  53.     }
  54. }
复制代码
另有一个问题, 在配置dataSource的时候使用了 ${driver} 这种表达式, 那么这种形式是怎么解析的?着实,是通过PropertyParser这个类解析:
  1. /**
  2. * 这个类解析${}这种形式的表达式
  3. */
  4. public class PropertyParser {
  5.     public static String parse(String string, Properties variables) {
  6.         VariableTokenHandler handler = new VariableTokenHandler(variables);
  7.         GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
  8.         return parser.parse(string);
  9.     }
  10.     private static class VariableTokenHandler implements TokenHandler {
  11.         private Properties variables;
  12.         public VariableTokenHandler(Properties variables) {
  13.             this.variables = variables;
  14.         }
  15.         public String handleToken(String content) {
  16.             if (variables != null && variables.containsKey(content)) {
  17.                 return variables.getProperty(content);
  18.             }
  19.             return "${" + content + "}";
  20.         }
  21.     }
  22. }
复制代码
以上就是对于properties 和 environments元素节点的分析,比力紧张的都在对于源码的注释中标出。
typeAliases

typeAliases节点紧张用来设置别名,着实这是挺好用的一个功能, 通过配置别名,我们不用再指定完整的包名,并且还能取别名。
比方: 我们在使用 com.demo.entity. UserEntity 的时候,我们可以直接配置一个别名user, 如许以后在配置文件中要使用到com.demo.entity.UserEntity的时候,直接使用User即可。
就以上例为例,我们来实现一下,看看typeAliases的配置方法:
  1. <configuration>
  2.     <typeAliases>
  3.         
  4.         <typeAlias alias="UserEntity" type="com.dy.entity.User"/>
  5.     </typeAliases>
  6.   
  7.     ......
  8.   
  9. </configuration>
复制代码
再写一段测试代码,看看有没生效:(我只写一段伪代码)
  1. Configuration con = sqlSessionFactory.getConfiguration();
  2. Map<String, Class<?>> typeMap = con.getTypeAliasRegistry().getTypeAliases();
  3. for(Entry<String, Class<?>> entry: typeMap.entrySet()) {
  4.     System.out.println(entry.getKey() + " ================> " + entry.getValue().getSimpleName());
  5. }
复制代码
typeAliasesElement:
  1. /**
  2. * 解析typeAliases节点
  3. */
  4. private void typeAliasesElement(XNode parent) {
  5.     if (parent != null) {
  6.         for (XNode child : parent.getChildren()) {
  7.             //如果子节点是package, 那么就获取package节点的name属性, mybatis会扫描指定的package
  8.             if ("package".equals(child.getName())) {
  9.                 String typeAliasPackage = child.getStringAttribute("name");
  10.                 //TypeAliasRegistry 负责管理别名, 这儿就是通过TypeAliasRegistry 进行别名注册, 下面就会看看TypeAliasRegistry源码
  11.                 configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
  12.             } else {
  13.                 //如果子节点是typeAlias节点,那么就获取alias属性和type的属性值
  14.                 String alias = child.getStringAttribute("alias");
  15.                 String type = child.getStringAttribute("type");
  16.                 try {
  17.                     Class<?> clazz = Resources.classForName(type);
  18.                     if (alias == null) {
  19.                         typeAliasRegistry.registerAlias(clazz);
  20.                     } else {
  21.                         typeAliasRegistry.registerAlias(alias, clazz);
  22.                     }
  23.                 } catch (ClassNotFoundException e) {
  24.                     throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
  25.                 }
  26.             }
  27.         }
  28.     }
  29. }
复制代码
紧张的源码在这儿:TypeAliasRegistry.java
  1. public class TypeAliasRegistry {
  2.   
  3.   //这就是核心所在啊, 原来别名就仅仅通过一个HashMap来实现, key为别名, value就是别名对应的类型(class对象)
  4.   private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
  5.   /**
  6.    * 以下就是mybatis默认为我们注册的别名
  7.    */
  8.   public TypeAliasRegistry() {
  9.     registerAlias("string", String.class);
  10.     registerAlias("byte", Byte.class);
  11.     registerAlias("long", Long.class);
  12.     registerAlias("short", Short.class);
  13.     registerAlias("int", Integer.class);
  14.     registerAlias("integer", Integer.class);
  15.     registerAlias("double", Double.class);
  16.     registerAlias("float", Float.class);
  17.     registerAlias("boolean", Boolean.class);
  18.     registerAlias("byte[]", Byte[].class);
  19.     registerAlias("long[]", Long[].class);
  20.     registerAlias("short[]", Short[].class);
  21.     registerAlias("int[]", Integer[].class);
  22.     registerAlias("integer[]", Integer[].class);
  23.     registerAlias("double[]", Double[].class);
  24.     registerAlias("float[]", Float[].class);
  25.     registerAlias("boolean[]", Boolean[].class);
  26.     registerAlias("_byte", byte.class);
  27.     registerAlias("_long", long.class);
  28.     registerAlias("_short", short.class);
  29.     registerAlias("_int", int.class);
  30.     registerAlias("_integer", int.class);
  31.     registerAlias("_double", double.class);
  32.     registerAlias("_float", float.class);
  33.     registerAlias("_boolean", boolean.class);
  34.     registerAlias("_byte[]", byte[].class);
  35.     registerAlias("_long[]", long[].class);
  36.     registerAlias("_short[]", short[].class);
  37.     registerAlias("_int[]", int[].class);
  38.     registerAlias("_integer[]", int[].class);
  39.     registerAlias("_double[]", double[].class);
  40.     registerAlias("_float[]", float[].class);
  41.     registerAlias("_boolean[]", boolean[].class);
  42.     registerAlias("date", Date.class);
  43.     registerAlias("decimal", BigDecimal.class);
  44.     registerAlias("bigdecimal", BigDecimal.class);
  45.     registerAlias("biginteger", BigInteger.class);
  46.     registerAlias("object", Object.class);
  47.     registerAlias("date[]", Date[].class);
  48.     registerAlias("decimal[]", BigDecimal[].class);
  49.     registerAlias("bigdecimal[]", BigDecimal[].class);
  50.     registerAlias("biginteger[]", BigInteger[].class);
  51.     registerAlias("object[]", Object[].class);
  52.     registerAlias("map", Map.class);
  53.     registerAlias("hashmap", HashMap.class);
  54.     registerAlias("list", List.class);
  55.     registerAlias("arraylist", ArrayList.class);
  56.     registerAlias("collection", Collection.class);
  57.     registerAlias("iterator", Iterator.class);
  58.     registerAlias("ResultSet", ResultSet.class);
  59.   }
  60.   
  61.   /**
  62.    * 处理别名, 直接从保存有别名的hashMap中取出即可
  63.    */
  64.   @SuppressWarnings("unchecked")
  65.   public <T> Class<T> resolveAlias(String string) {
  66.     try {
  67.       if (string == null) return null;
  68.       String key = string.toLowerCase(Locale.ENGLISH); // issue #748
  69.       Class<T> value;
  70.       if (TYPE_ALIASES.containsKey(key)) {
  71.         value = (Class<T>) TYPE_ALIASES.get(key);
  72.       } else {
  73.         value = (Class<T>) Resources.classForName(string);
  74.       }
  75.       return value;
  76.     } catch (ClassNotFoundException e) {
  77.       throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
  78.     }
  79.   }
  80.   
  81.   /**
  82.    * 配置文件中配置为package的时候, 会调用此方法,根据配置的报名去扫描javabean ,然后自动注册别名
  83.    * 默认会使用 Bean 的首字母小写的非限定类名来作为它的别名
  84.    * 也可在javabean 加上注解@Alias 来自定义别名, 例如: @Alias(user)
  85.    */
  86.   public void registerAliases(String packageName){
  87.     registerAliases(packageName, Object.class);
  88.   }
  89.   public void registerAliases(String packageName, Class<?> superType){
  90.     ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
  91.     resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  92.     Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
  93.     for(Class<?> type : typeSet){
  94.       // Ignore inner classes and interfaces (including package-info.java)
  95.       // Skip also inner classes. See issue #6
  96.       if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
  97.         registerAlias(type);
  98.       }
  99.     }
  100.   }
  101.   public void registerAlias(Class<?> type) {
  102.     String alias = type.getSimpleName();
  103.     Alias aliasAnnotation = type.getAnnotation(Alias.class);
  104.     if (aliasAnnotation != null) {
  105.       alias = aliasAnnotation.value();
  106.     }
  107.     registerAlias(alias, type);
  108.   }
  109.   //这就是注册别名的本质方法, 其实就是向保存别名的hashMap新增值而已, 呵呵, 别名的实现太简单了,对吧
  110.   public void registerAlias(String alias, Class<?> value) {
  111.     if (alias == null) throw new TypeException("The parameter alias cannot be null");
  112.     String key = alias.toLowerCase(Locale.ENGLISH); // issue #748
  113.     if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
  114.       throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
  115.     }
  116.     TYPE_ALIASES.put(key, value);
  117.   }
  118.   public void registerAlias(String alias, String value) {
  119.     try {
  120.       registerAlias(alias, Resources.classForName(value));
  121.     } catch (ClassNotFoundException e) {
  122.       throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e);
  123.     }
  124.   }
  125.   
  126.   /**
  127.    * 获取保存别名的HashMap, Configuration对象持有对TypeAliasRegistry的引用,因此,如果需要,我们可以通过Configuration对象获取
  128.    */
  129.   public Map<String, Class<?>> getTypeAliases() {
  130.     return Collections.unmodifiableMap(TYPE_ALIASES);
  131.   }
  132. }
复制代码
由源码可见,设置别名的原理就这么简单,Mybatis默认给我们设置了不少别名,在上面代码中都可以见到。
TypeHandler

Mybatis中的TypeHandler是什么?
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。Mybatis默认为我们实现了许多TypeHandler, 当我们没有配置指定TypeHandler时,Mybatis会根据参数或者返回结果的不同,默认为我们选择合适的TypeHandler处理。
那么,Mybatis为我们实现了哪些TypeHandler呢? 我们怎么自定义实现一个TypeHandler ? 这些都会在接下来的mybatis的源码中看到。
先看看配置:
  1. <configuration>
  2.    
  3.    
  4.     <properties>
  5.         <property name="driver" value="com.mysql.jdbc.Driver"/>
  6.         <property name="url" value="jdbc:mysql://localhost:3306/test1"/>
  7.         <property name="username" value="root"/>
  8.         <property name="password" value="root"/>
  9.     </properties>                <configuration>
  10.     <typeAliases>
  11.         
  12.         <typeAlias alias="UserEntity" type="com.dy.entity.User"/>
  13.     </typeAliases>
  14.   
  15.     ......
  16.   
  17. </configuration>
复制代码
typeHandlerElement
老规矩,先从对xml的解析讲起
  1. /**
  2. * 解析typeHandlers节点
  3. */
  4. private void typeHandlerElement(XNode parent) throws Exception {
  5.     if (parent != null) {
  6.       for (XNode child : parent.getChildren()) {
  7.         //子节点为package时,获取其name属性的值,然后自动扫描package下的自定义typeHandler
  8.         if ("package".equals(child.getName())) {
  9.           String typeHandlerPackage = child.getStringAttribute("name");
  10.           typeHandlerRegistry.register(typeHandlerPackage);
  11.         } else {
  12.           //子节点为typeHandler时, 可以指定javaType属性, 也可以指定jdbcType, 也可两者都指定
  13.           //javaType 是指定java类型
  14.           //jdbcType 是指定jdbc类型(数据库类型: 如varchar)
  15.           String javaTypeName = child.getStringAttribute("javaType");
  16.           String jdbcTypeName = child.getStringAttribute("jdbcType");
  17.           //handler就是我们配置的typeHandler
  18.           String handlerTypeName = child.getStringAttribute("handler");
  19.           //resolveClass方法就是我们上篇文章所讲的TypeAliasRegistry里面处理别名的方法
  20.           Class<?> javaTypeClass = resolveClass(javaTypeName);
  21.           //JdbcType是一个枚举类型,resolveJdbcType方法是在获取枚举类型的值
  22.           JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
  23.           Class<?> typeHandlerClass = resolveClass(handlerTypeName);
  24.           //注册typeHandler, typeHandler通过TypeHandlerRegistry这个类管理
  25.           if (javaTypeClass != null) {
  26.             if (jdbcType == null) {
  27.               typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
  28.             } else {
  29.               typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
  30.             }
  31.           } else {
  32.             typeHandlerRegistry.register(typeHandlerClass);
  33.           }
  34.         }
  35.       }
  36.     }
  37. }
复制代码
接下来看看TypeHandler的管理注册类:TypeHandlerRegistry.java
  1. /**
  2. * typeHandler注册管理类
  3. */
  4. public final class TypeHandlerRegistry {
  5.   //源码一上来,二话不说,几个大大的HashMap就出现,这不又跟上次讲的typeAliases的注册类似么
  6.   //基本数据类型与其包装类
  7.   private static final Map<Class<?>, Class<?>> reversePrimitiveMap = new HashMap<Class<?>, Class<?>>() {
  8.     private static final long serialVersionUID = 1L;
  9.     {
  10.       put(Byte.class, byte.class);
  11.       put(Short.class, short.class);
  12.       put(Integer.class, int.class);
  13.       put(Long.class, long.class);
  14.       put(Float.class, float.class);
  15.       put(Double.class, double.class);
  16.       put(Boolean.class, boolean.class);
  17.       put(Character.class, char.class);
  18.     }
  19.   };
  20.   //这几个MAP不用说就知道存的是什么东西吧,命名的好处
  21.   private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
  22.   private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
  23.   private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
  24.   private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
  25.   //就像上篇文章讲的typeAliases一样,mybatis也默认给我们注册了不少的typeHandler
  26.   //具体如下
  27.   public TypeHandlerRegistry() {
  28.     register(Boolean.class, new BooleanTypeHandler());
  29.     register(boolean.class, new BooleanTypeHandler());
  30.     register(JdbcType.BOOLEAN, new BooleanTypeHandler());
  31.     register(JdbcType.BIT, new BooleanTypeHandler());
  32.     register(Byte.class, new ByteTypeHandler());
  33.     register(byte.class, new ByteTypeHandler());
  34.     register(JdbcType.TINYINT, new ByteTypeHandler());
  35.     register(Short.class, new ShortTypeHandler());
  36.     register(short.class, new ShortTypeHandler());
  37.     register(JdbcType.SMALLINT, new ShortTypeHandler());
  38.     register(Integer.class, new IntegerTypeHandler());
  39.     register(int.class, new IntegerTypeHandler());
  40.     register(JdbcType.INTEGER, new IntegerTypeHandler());
  41.     register(Long.class, new LongTypeHandler());
  42.     register(long.class, new LongTypeHandler());
  43.     register(Float.class, new FloatTypeHandler());
  44.     register(float.class, new FloatTypeHandler());
  45.     register(JdbcType.FLOAT, new FloatTypeHandler());
  46.     register(Double.class, new DoubleTypeHandler());
  47.     register(double.class, new DoubleTypeHandler());
  48.     register(JdbcType.DOUBLE, new DoubleTypeHandler());
  49.     register(String.class, new StringTypeHandler());
  50.     register(String.class, JdbcType.CHAR, new StringTypeHandler());
  51.     register(String.class, JdbcType.CLOB, new ClobTypeHandler());
  52.     register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
  53.     register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
  54.     register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
  55.     register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
  56.     register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
  57.     register(JdbcType.CHAR, new StringTypeHandler());
  58.     register(JdbcType.VARCHAR, new StringTypeHandler());
  59.     register(JdbcType.CLOB, new ClobTypeHandler());
  60.     register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
  61.     register(JdbcType.NVARCHAR, new NStringTypeHandler());
  62.     register(JdbcType.NCHAR, new NStringTypeHandler());
  63.     register(JdbcType.NCLOB, new NClobTypeHandler());
  64.     register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
  65.     register(JdbcType.ARRAY, new ArrayTypeHandler());
  66.     register(BigInteger.class, new BigIntegerTypeHandler());
  67.     register(JdbcType.BIGINT, new LongTypeHandler());
  68.     register(BigDecimal.class, new BigDecimalTypeHandler());
  69.     register(JdbcType.REAL, new BigDecimalTypeHandler());
  70.     register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
  71.     register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
  72.     register(Byte[].class, new ByteObjectArrayTypeHandler());
  73.     register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
  74.     register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
  75.     register(byte[].class, new ByteArrayTypeHandler());
  76.     register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
  77.     register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
  78.     register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
  79.     register(JdbcType.BLOB, new BlobTypeHandler());
  80.     register(Object.class, UNKNOWN_TYPE_HANDLER);
  81.     register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
  82.     register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
  83.     register(Date.class, new DateTypeHandler());
  84.     register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
  85.     register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
  86.     register(JdbcType.TIMESTAMP, new DateTypeHandler());
  87.     register(JdbcType.DATE, new DateOnlyTypeHandler());
  88.     register(JdbcType.TIME, new TimeOnlyTypeHandler());
  89.     register(java.sql.Date.class, new SqlDateTypeHandler());
  90.     register(java.sql.Time.class, new SqlTimeTypeHandler());
  91.     register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
  92.     // issue #273
  93.     register(Character.class, new CharacterTypeHandler());
  94.     register(char.class, new CharacterTypeHandler());
  95.   }
  96.   public boolean hasTypeHandler(Class<?> javaType) {
  97.     return hasTypeHandler(javaType, null);
  98.   }
  99.   public boolean hasTypeHandler(TypeReference<?> javaTypeReference) {
  100.     return hasTypeHandler(javaTypeReference, null);
  101.   }
  102.   public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
  103.     return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
  104.   }
  105.   public boolean hasTypeHandler(TypeReference<?> javaTypeReference, JdbcType jdbcType) {
  106.     return javaTypeReference != null && getTypeHandler(javaTypeReference, jdbcType) != null;
  107.   }
  108.   public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
  109.     return ALL_TYPE_HANDLERS_MAP.get(handlerType);
  110.   }
  111.   public <T> TypeHandler<T> getTypeHandler(Class<T> type) {
  112.     return getTypeHandler((Type) type, null);
  113.   }
  114.   public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference) {
  115.     return getTypeHandler(javaTypeReference, null);
  116.   }
  117.   public TypeHandler<?> getTypeHandler(JdbcType jdbcType) {
  118.     return JDBC_TYPE_HANDLER_MAP.get(jdbcType);
  119.   }
  120.   public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
  121.     return getTypeHandler((Type) type, jdbcType);
  122.   }
  123.   public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference, JdbcType jdbcType) {
  124.     return getTypeHandler(javaTypeReference.getRawType(), jdbcType);
  125.   }
  126.   private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
  127.     Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
  128.     TypeHandler<?> handler = null;
  129.     if (jdbcHandlerMap != null) {
  130.       handler = jdbcHandlerMap.get(jdbcType);
  131.       if (handler == null) {
  132.         handler = jdbcHandlerMap.get(null);
  133.       }
  134.     }
  135.     if (handler == null && type != null && type instanceof Class && Enum.class.isAssignableFrom((Class<?>) type)) {
  136.       handler = new EnumTypeHandler((Class<?>) type);
  137.     }
  138.     @SuppressWarnings("unchecked")
  139.     // type drives generics here
  140.     TypeHandler<T> returned = (TypeHandler<T>) handler;
  141.     return returned;
  142.   }
  143.   public TypeHandler<Object> getUnknownTypeHandler() {
  144.     return UNKNOWN_TYPE_HANDLER;
  145.   }
  146.   public void register(JdbcType jdbcType, TypeHandler<?> handler) {
  147.     JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler);
  148.   }
  149.   //
  150.   // REGISTER INSTANCE
  151.   //
  152.   /**
  153.    * 只配置了typeHandler, 没有配置jdbcType 或者javaType
  154.    */
  155.   @SuppressWarnings("unchecked")
  156.   public <T> void register(TypeHandler<T> typeHandler) {
  157.     boolean mappedTypeFound = false;
  158.     //在自定义typeHandler的时候,可以加上注解MappedTypes 去指定关联的javaType
  159.     //因此,此处需要扫描MappedTypes注解
  160.     MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
  161.     if (mappedTypes != null) {
  162.       for (Class<?> handledType : mappedTypes.value()) {
  163.         register(handledType, typeHandler);
  164.         mappedTypeFound = true;
  165.       }
  166.     }
  167.     // @since 3.1.0 - try to auto-discover the mapped type
  168.     if (!mappedTypeFound && typeHandler instanceof TypeReference) {
  169.       try {
  170.         TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
  171.         register(typeReference.getRawType(), typeHandler);
  172.         mappedTypeFound = true;
  173.       } catch (Throwable t) {
  174.         // maybe users define the TypeReference with a different type and are not assignable, so just ignore it
  175.       }
  176.     }
  177.     if (!mappedTypeFound) {
  178.       register((Class<T>) null, typeHandler);
  179.     }
  180.   }
  181.   /**
  182.    * 配置了typeHandlerhe和javaType
  183.    */
  184.   public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
  185.     register((Type) javaType, typeHandler);
  186.   }
  187.   private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
  188.     //扫描注解MappedJdbcTypes
  189.     MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
  190.     if (mappedJdbcTypes != null) {
  191.       for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
  192.         register(javaType, handledJdbcType, typeHandler);
  193.       }
  194.       if (mappedJdbcTypes.includeNullJdbcType()) {
  195.         register(javaType, null, typeHandler);
  196.       }
  197.     } else {
  198.       register(javaType, null, typeHandler);
  199.     }
  200.   }
  201.   public <T> void register(TypeReference<T> javaTypeReference, TypeHandler<? extends T> handler) {
  202.     register(javaTypeReference.getRawType(), handler);
  203.   }
  204.   /**
  205.    * typeHandlerhe、javaType、jdbcType都配置了
  206.    */
  207.   public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler) {
  208.     register((Type) type, jdbcType, handler);
  209.   }
  210.   /**
  211.    * 注册typeHandler的核心方法
  212.    * 就是向Map新增数据而已
  213.    */
  214.   private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
  215.     if (javaType != null) {
  216.       Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
  217.       if (map == null) {
  218.         map = new HashMap<JdbcType, TypeHandler<?>>();
  219.         TYPE_HANDLER_MAP.put(javaType, map);
  220.       }
  221.       map.put(jdbcType, handler);
  222.       if (reversePrimitiveMap.containsKey(javaType)) {
  223.         register(reversePrimitiveMap.get(javaType), jdbcType, handler);
  224.       }
  225.     }
  226.     ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
  227.   }
  228.   //
  229.   // REGISTER CLASS
  230.   //
  231.   // Only handler type
  232.   public void register(Class<?> typeHandlerClass) {
  233.     boolean mappedTypeFound = false;
  234.     MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
  235.     if (mappedTypes != null) {
  236.       for (Class<?> javaTypeClass : mappedTypes.value()) {
  237.         register(javaTypeClass, typeHandlerClass);
  238.         mappedTypeFound = true;
  239.       }
  240.     }
  241.     if (!mappedTypeFound) {
  242.       register(getInstance(null, typeHandlerClass));
  243.     }
  244.   }
  245.   // java type + handler type
  246.   public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
  247.     register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
  248.   }
  249.   // java type + jdbc type + handler type
  250.   public void register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass) {
  251.     register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
  252.   }
  253.   // Construct a handler (used also from Builders)
  254.   @SuppressWarnings("unchecked")
  255.   public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
  256.     if (javaTypeClass != null) {
  257.       try {
  258.         Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
  259.         return (TypeHandler<T>) c.newInstance(javaTypeClass);
  260.       } catch (NoSuchMethodException ignored) {
  261.         // ignored
  262.       } catch (Exception e) {
  263.         throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
  264.       }
  265.     }
  266.     try {
  267.       Constructor<?> c = typeHandlerClass.getConstructor();
  268.       return (TypeHandler<T>) c.newInstance();
  269.     } catch (Exception e) {
  270.       throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
  271.     }
  272.   }
  273.   /**
  274.    * 根据指定的pacakge去扫描自定义的typeHander,然后注册
  275.    */
  276.   public void register(String packageName) {
  277.     ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
  278.     resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
  279.     Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
  280.     for (Class<?> type : handlerSet) {
  281.       //Ignore inner classes and interfaces (including package-info.java) and abstract classes
  282.       if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
  283.         register(type);
  284.       }
  285.     }
  286.   }
  287.   
  288.   // get information
  289.   
  290.   /**
  291.    * 通过configuration对象可以获取已注册的所有typeHandler
  292.    */
  293.   public Collection<TypeHandler<?>> getTypeHandlers() {
  294.     return Collections.unmodifiableCollection(ALL_TYPE_HANDLERS_MAP.values());
  295.   }
  296.   
  297. }
复制代码
由源码可以看到, mybatis为我们实现了那么多TypeHandler, 恣意打开一个TypeHandler,看其源码,都可以看到,它继承自一个抽象类:BaseTypeHandler, 那么我们是不是也能通过继承BaseTypeHandler,从而实现自定义的TypeHandler ? 答案是肯定的,
演示自定义TypeHandler:
  1. @MappedJdbcTypes(JdbcType.VARCHAR)  
  2. //此处如果不用注解指定jdbcType, 那么,就可以在配置文件中通过"jdbcType"属性指定, 同理, javaType 也可通过 @MappedTypes指定
  3. public class ExampleTypeHandler extends BaseTypeHandler<String> {
  4.   @Override
  5.   public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
  6.     ps.setString(i, parameter);
  7.   }
  8.   @Override
  9.   public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
  10.     return rs.getString(columnName);
  11.   }
  12.   @Override
  13.   public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
  14.     return rs.getString(columnIndex);
  15.   }
  16.   @Override
  17.   public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
  18.     return cs.getString(columnIndex);
  19.   }
  20. }
复制代码
然后,就该配置自定义TypeHandler了:
  1. <configuration>
  2.   <typeHandlers>
  3.       
  4.       <typeHandler handler="ExampleTypeHandler"/>
  5.   </typeHandlers>
  6.   
  7.   ......
  8.   
  9. </configuration>
复制代码
也就是说,我们在自定义TypeHandler的时候,可以在TypeHandler通过@MappedJdbcTypes指定jdbcType, 通过 @MappedTypes 指定javaType, 如果没有使用注解指定,那么我们就需要在配置文件中配置。
objectFactory

objectFactory是干什么的? 需要配置吗?
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。默认的对象工厂需要做的仅仅是实例化目的类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。默认情况下,我们不需要配置,mybatis会调用默认实现的objectFactory。 除非我们要自定义ObjectFactory的实现, 那么我们才需要去手动配置。
那么怎么自定义实现ObjectFactory? 怎么配置呢?自定义ObjectFactory只需要去继承DefaultObjectFactory(是ObjectFactory接口的实现类),并重写其方法即可。具体的,本处不多说,背面再具体讲解。
写好了ObjectFactory, 仅需做如下配置:
  1. <configuration>
  2.     ......
  3.     <objectFactory type="org.mybatis.example.ExampleObjectFactory">
  4.         <property name="someProperty" value="100"/>
  5.     </objectFactory>
  6.     ......
  7. </configuration>
复制代码
objectFactoryElement源码:
  1. /**
  2. * objectFactory 节点解析
  3. */
  4. private void objectFactoryElement(XNode context) throws Exception {
  5.     if (context != null) {
  6.       //读取type属性的值, 接下来进行实例化ObjectFactory, 并set进 configuration
  7.       //到此,简单讲一下configuration这个对象,其实它里面主要保存的都是mybatis的配置
  8.       String type = context.getStringAttribute("type");
  9.       //读取propertie的值, 根据需要可以配置, mybatis默认实现的objectFactory没有使用properties
  10.       Properties properties = context.getChildrenAsProperties();
  11.       
  12.       ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
  13.       factory.setProperties(properties);
  14.       configuration.setObjectFactory(factory);
  15.     }
  16. }
复制代码
plugins

plugin有何作用? 需要配置吗?
plugins 是一个可选配置。mybatis中的plugin着实就是个interceptor, 它可以拦截Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler 的部门方法,处理我们自己的逻辑。Executor就是真正执行sql语句的东西, ParameterHandler 是处理我们传入参数的,还记得前面讲TypeHandler的时候提到过,mybatis默认帮我们实现了不少的typeHandler, 当我们不表现配置typeHandler的时候,mybatis会根据参数类型自动选择合适的typeHandler执行,着实就是ParameterHandler 在选择。ResultSetHandler 就是处理返回结果的。
怎么自定义plugin ? 怎么配置?要自定义一个plugin, 需要去实现Interceptor接口,这儿不细说,背面实战部门会具体讲解。定义好之后,配置如下:
  1. <configuration>
  2.     ......
  3.     <plugins>
  4.       <plugin interceptor="org.mybatis.example.ExamplePlugin">
  5.         <property name="someProperty" value="100"/>
  6.       </plugin>
  7.     </plugins>
  8.     ......
  9. </configuration>
复制代码
pluginElement源码:
  1. /**
  2.    * plugins 节点解析
  3.    */
  4.   private void pluginElement(XNode parent) throws Exception {
  5.     if (parent != null) {
  6.       for (XNode child : parent.getChildren()) {
  7.         String interceptor = child.getStringAttribute("interceptor");
  8.         Properties properties = child.getChildrenAsProperties();
  9.         //由此可见,我们在定义一个interceptor的时候,需要去实现Interceptor, 这儿先不具体讲,以后会详细讲解
  10.         Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
  11.         interceptorInstance.setProperties(properties);
  12.         configuration.addInterceptor(interceptorInstance);
  13.       }
  14.     }
  15.   }
复制代码
mappers

mappers, 这下引出mybatis的核心之一了,mappers作用 ? 需要配置吗?
mappers 节点下,配置我们的mapper映射文件, 所谓的mapper映射文件,就是让mybatis 用来创建数据表和javabean映射的一个桥梁。在我们现实开发中,通常一个mapper文件对应一个dao接口, 这个mapper可以看做是dao的实现。所以,mappers必须配置。
  1.     ......<configuration>
  2.    
  3.    
  4.     <properties>
  5.         <property name="driver" value="com.mysql.jdbc.Driver"/>
  6.         <property name="url" value="jdbc:mysql://localhost:3306/test1"/>
  7.         <property name="username" value="root"/>
  8.         <property name="password" value="root"/>
  9.     </properties>              ......
复制代码
mapperElement源码:
  1. /**
  2.    * mappers 节点解析
  3.    * 这是mybatis的核心之一
  4.    */
  5.   private void mapperElement(XNode parent) throws Exception {
  6.     if (parent != null) {
  7.       for (XNode child : parent.getChildren()) {
  8.         if ("package".equals(child.getName())) {
  9.           //如果mappers节点的子节点是package, 那么就扫描package下的文件, 注入进configuration
  10.           String mapperPackage = child.getStringAttribute("name");
  11.           configuration.addMappers(mapperPackage);
  12.         } else {
  13.           String resource = child.getStringAttribute("resource");
  14.           String url = child.getStringAttribute("url");
  15.           String mapperClass = child.getStringAttribute("class");
  16.           //resource, url, class 三选一
  17.          
  18.           if (resource != null && url == null && mapperClass == null) {
  19.             ErrorContext.instance().resource(resource);
  20.             InputStream inputStream = Resources.getResourceAsStream(resource);
  21.             //mapper映射文件都是通过XMLMapperBuilder解析
  22.             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
  23.             mapperParser.parse();
  24.           } else if (resource == null && url != null && mapperClass == null) {
  25.             ErrorContext.instance().resource(url);
  26.             InputStream inputStream = Resources.getUrlAsStream(url);
  27.             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
  28.             mapperParser.parse();
  29.           } else if (resource == null && url == null && mapperClass != null) {
  30.             Class<?> mapperInterface = Resources.classForName(mapperClass);
  31.             configuration.addMapper(mapperInterface);
  32.           } else {
  33.             throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
  34.           }
  35.         }
  36.       }
  37.     }
  38.   }
复制代码
settings
  1. <settings>
  2.     <setting name="cacheEnabled" value="true"/>
  3.     <setting name="lazyLoadingEnabled" value="true"/>
  4.     <setting name="multipleResultSetsEnabled" value="true"/>
  5.     <setting name="useColumnLabel" value="true"/>
  6.     <setting name="useGeneratedKeys" value="false"/>
  7.     <setting name="enhancementEnabled" value="false"/>
  8.     <setting name="defaultExecutorType" value="SIMPLE"/>
  9.     <setting name="defaultStatementTimeout" value="25000"/>
  10. </settings>
复制代码
setting节点里配置的值会直接改写Configuration对应的变量值,这些变量形貌的是Mybatis的全局运行方式,如果对这些属性的寄义不熟悉的话发起不要配置,使用默认值即可。
settingsElement:
  1. private void settingsElement(XNode context) throws Exception {
  2.     if (context != null) {
  3.       Properties props = context.getChildrenAsProperties();
  4.       // Check that all settings are known to the configuration class
  5.       MetaClass metaConfig = MetaClass.forClass(Configuration.class);
  6.       for (Object key : props.keySet()) {
  7.         if (!metaConfig.hasSetter(String.valueOf(key))) {
  8.           throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
  9.         }
  10.       }
  11.       configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
  12.       configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
  13.       configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
  14.       configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
  15.       configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
  16.       configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
  17.       configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
  18.       configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
  19.       configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
  20.       configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
  21.       configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
  22.       configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
  23.       configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
  24.       configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
  25.       configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
  26.       configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
  27.       configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
  28.       configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
  29.       configuration.setLogPrefix(props.getProperty("logPrefix"));
  30.       configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
  31.       configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
  32.     }
  33. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4