The core environment configuration for the start of MyBatis, let's take a look at the Configuration class

Configuration creation

If you like a girl, do you have time to ask where the girl lives? If you only add WeChat, you can only hope for plum blossoms to quench your thirst, and taking the initiative is the last word. Otherwise, even if you rent a Maserati, you don't know where to install B.

To understand Configuration, you must first ask how it was created.

Before that, let me tell you a MyBatis entry class, that is SqlSessionFactoryBuilder, why introduce this class? Because this class can create SqlSession, want children? How can it work without the function of Builder? It's created here

After SqlSessionFactoryBuilder is created XMLConfigBuilder, it will complete the creation of Configuration, that is to say, the creation of Configuration object is completed in XMLConfigBuilder, as shown in the figure below

Seeing this, are you eager to try and hold down the control key to click in? As you wish, see new Configurationwhat is born

This is the work done by initializing Configuration. There is another key class in the figure TypeAliasRegistry, Want to register? You have to know who "I" is first.

TypeAliasRegistry is initialized when Configuration is created

protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();

so? Take a look at what new has done

public TypeAliasRegistry() {
    
    
  registerAlias("string", String.class);

  registerAlias("byte", Byte.class);
  registerAlias("long", Long.class);
  registerAlias("short", Short.class);
  registerAlias("int", Integer.class);
  registerAlias("integer", Integer.class);
  registerAlias("double", Double.class);
  registerAlias("float", Float.class);
  registerAlias("boolean", Boolean.class);

  registerAlias("byte[]", Byte[].class);
  registerAlias("long[]", Long[].class);
  registerAlias("short[]", Short[].class);
  registerAlias("int[]", Integer[].class);
  registerAlias("integer[]", Integer[].class);
  registerAlias("double[]", Double[].class);
  registerAlias("float[]", Float[].class);
  registerAlias("boolean[]", Boolean[].class);

  registerAlias("_byte", byte.class);
  registerAlias("_long", long.class);
  registerAlias("_short", short.class);
  registerAlias("_int", int.class);
  registerAlias("_integer", int.class);
  registerAlias("_double", double.class);
  registerAlias("_float", float.class);
  registerAlias("_boolean", boolean.class);

  registerAlias("_byte[]", byte[].class);
  registerAlias("_long[]", long[].class);
  registerAlias("_short[]", short[].class);
  registerAlias("_int[]", int[].class);
  registerAlias("_integer[]", int[].class);
  registerAlias("_double[]", double[].class);
  registerAlias("_float[]", float[].class);
  registerAlias("_boolean[]", boolean[].class);

  registerAlias("date", Date.class);
  registerAlias("decimal", BigDecimal.class);
  registerAlias("bigdecimal", BigDecimal.class);
  registerAlias("biginteger", BigInteger.class);
  registerAlias("object", Object.class);

  registerAlias("date[]", Date[].class);
  registerAlias("decimal[]", BigDecimal[].class);
  registerAlias("bigdecimal[]", BigDecimal[].class);
  registerAlias("biginteger[]", BigInteger[].class);
  registerAlias("object[]", Object[].class);

  registerAlias("map", Map.class);
  registerAlias("hashmap", HashMap.class);
  registerAlias("list", List.class);
  registerAlias("arraylist", ArrayList.class);
  registerAlias("collection", Collection.class);
  registerAlias("iterator", Iterator.class);

  registerAlias("ResultSet", ResultSet.class);
}

It’s so exciting, such a large piece of code, but it still looks relatively clear. Isn’t this the common type of MyBatis, and they are all saved with their own aliases for parsing.

Configuration labels and usage

After talking about the creation of Configuration, let's not directly cut into the topic of initialization, let's have some dessert first

Remember how you set up a MyBatis project? One of the most critical is not mybatis-config.xmlsuch a configuration called?

This configuration is <configuration>the meaning of the existence of the label.

I wrote a configuration tag on the outermost side, and then the dtd language constraint reminded me that so many attributes can be set, and they all belong to the tags in Configuration, so what are these tags? Don't worry, take your time, master the frequency, rhythm and strength, don't be too aggressive, young people should be calm.

I don't want to come in the order of the labels, please follow my rhythm.

The first two very important properties are propertiesand environments, properties is the external property configuration, you can configure it like this

<properties resource="config.properties" />

Import an external configuration file, config.properties which contains a series of database configurations, let me give you an example, see if you are in a hurry

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/kkb
jdbc.username=root
jdbc.password=123456

After loading the external property configuration, you need to configure environmentsthe tag, which can configure transaction management, data source, read configuration files, etc.

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
      <property name="driver" value="${jdbc.driver}"/>
      <property name="url" value="${jdbc.url}"/>
      <property name="username" value="${jdbc.username}"/>
      <property name="password" value="${jdbc.password}"/>
    </dataSource>
  </environment>
</environments>

Do you understand?

Another key configuration is mapperthe tag, you can understand it as ComponentScan , ComponentScan completes the search of the bean definition, and mapper completes the search of the interface, and the interface must match the corresponding XML namespace. For example

<mappers>
  <package name="com.mybatis.dao"/>
</mappers>

Go deeper and see <setting>what you need. You can set the following. There are a lot of configurations below. You can check (http://www.mybatis.org/mybatis-3/zh/configuration.html#settings) Let's look at these configurations in detail.

<settings>
	// 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。
  <setting name="cacheEnabled" value="true"/>
  
  // 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。
  <setting name="lazyLoadingEnabled" value="true"/>
  
  // 是否允许单一语句返回多结果集(需要驱动支持)。
  <setting name="multipleResultSetsEnabled" value="true"/>
  
  // 使用列标签代替列名。不同的驱动在这方面会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。
  <setting name="useColumnLabel" value="true"/>
  
  // 允许 JDBC 支持自动生成主键,需要驱动支持。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能支持但仍可正常工作(比如 Derby)。
  <setting name="useGeneratedKeys" value="false"/>
  
  // 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  
  // 指定发现自动映射目标未知列(或者未知属性类型)的行为。
  // NONE: 不做任何反应
  // WARNING: 输出提醒日志 ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)
  // FAILING: 映射失败 (抛出 SqlSessionException)
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  
  // 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。
  <setting name="defaultExecutorType" value="SIMPLE"/>
  
  // 设置超时时间,它决定驱动等待数据库响应的秒数。
  <setting name="defaultStatementTimeout" value="25"/>
  
  // 为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。
  <setting name="defaultFetchSize" value="100"/>
  
  // 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false
  <setting name="safeRowBoundsEnabled" value="false"/>
  
  // 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  
  // MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据
  <setting name="localCacheScope" value="SESSION"/>
  
  // 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
  <setting name="jdbcTypeForNull" value="OTHER"/>
  
  // 指定哪个对象的方法触发一次延迟加载。
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

Did you know that both Oracle and MySQL can set aliases for tables and fields? MyBatis can also set an alias, using the typeAliasesattribute, such as

<!-- 为每一个实体类设置一个具体别名 -->
<typeAliases>
  <typeAlias type="com.kaikeba.beans.Dept" alias="Dept"/>
</typeAliases>

<!-- 为当前包下的每一个类设置一个默认别名 -->
<typeAliases>
  <package name="com.mybatis.beans"/>
</typeAliases>

MyBatis can execute different statements according to different database vendors. This multi-vendor support is based on the databaseIdattributes in the mapping statement. MyBatis will load all statements without databaseIdattributes and with attributes matching the current database . databaseIdIf the same statement is found both with databaseIdand without databaseId, the latter is discarded. To support multi-vendor features, just add it to the mybatis-config.xml file as follows databaseIdProvider:

<databaseIdProvider type="DB_VENDOR" />

The databaseIdProvider implementation for DB_VENDOR will set databaseId to DatabaseMetaData#getDatabaseProductName()the returned string. Since these strings are often very long and different versions of the same product return different values, you may want to make them shorter by aliasing the property, like so:

<databaseIdProvider type="DB_VENDOR">
  <property name="SQL Server" value="sqlserver"/>
  <property name="DB2" value="db2"/>
  <property name="Oracle" value="oracle" />
</databaseIdProvider>

Every time MyBatis creates a new instance of a result object, it does so using an ObjectFactory instance. All the default object factory needs to do is instantiate the target class, either via the default constructor, or via the parameterized constructor if the parametermap exists. If you want to override the default behavior of an object factory, you can do so by creating your own object factory. for example:

// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {
    
    
  public Object create(Class type) {
    
    
    return super.create(type);
  }
  public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
    
    
    return super.create(type, constructorArgTypes, constructorArgs);
  }
  public void setProperties(Properties properties) {
    
    
    super.setProperties(properties);
  }
  public <T> boolean isCollection(Class<T> type) {
    
    
    return Collection.class.isAssignableFrom(type);
  }
}
<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
  <property name="someProperty" value="100"/>
</objectFactory>

The role of ObjectFactory is very similar to FactoryBean in Spring. If you don’t know much about FactoryBean, please go to

(https://mp.weixin.qq.com/s/aCFzCopCX1mK6Zg-dT_KgA) to learn more

The back door left by MyBatis to developers can be used for plug-in development. Where is plug-in development reflected? In fact, the four major components of MyBatis will be reflected, and the plug-in development of MyBatis is actually an application of the agent, as shown in the figure

Configuration.java

This is the calling position of the Executor plug-in development, so the calling of StatementHandler, ParameterHandler, and ResultSetHandler is basically the same as that of Executor, as shown in the figure

Through the powerful mechanism provided by MyBatis, it is very simple to use the plug-in, just implement the Interceptor interface and specify the signature of the method you want to intercept. For example, this example on the official website

// ExamplePlugin.java
@Intercepts({
    
    @Signature(
  type= Executor.class,
  method = "update",
  args = {
    
    MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
    
    
  private Properties properties = new Properties();
  public Object intercept(Invocation invocation) throws Throwable {
    
    
    // implement pre processing if need
    Object returnObject = invocation.proceed();
    // implement post processing if need
    return returnObject;
  }
  public void setProperties(Properties properties) {
    
    
    this.properties = properties;
  }
}

Just tell MyBatis about this plug-in, there is a plug-in interceptor here, remember to use Austrian

<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

typeHandlersAlso called a type converter, it is mainly used in the place of parameter conversion, where is the parameter conversion performed? There are actually two points:

  • PreparedStatementHandler needs to convert Java Type to JDBC type when parsing SQL parameters and setting parameters
  • The result set returned by ResultSetHandler needs to convert JDBC type to Java Type

You can write your own type converter, as follows:

// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {
    
    

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

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

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

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

You also need to tell MyBatis that there is a parameter converter, don't forget to convert!

<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>

Analysis of Configuration tags

Now that we have the definitions of the tags above, where should we parse them? Just like the right person in the right position can create the greatest value.

Now it is necessary to continue the third step of SqlSessionFactoryBuilder, the analysis of Configuration

in XMLConfigBuilder

Does this correspond to the label above? The parsing work is carried out here, which is also a good coding habit. A method only does one thing, so we should learn more from this way of writing.

Source code analysis of the Configuration subtag

If you can see this from top to bottom, it means that you have a strong interest in this article. Congratulations, your rank has been upgraded again. I don’t play Glory of Kings, I have been playing Warcraft solo before, solo requires hand speed, and various factors also need to be considered: for example, you are ORC (Orc), your BM (Juvenile) uses W (Wind) Step) The time to hunt monsters should be mastered, the time to harass NE (Night Elf) to collect wood should be mastered, and the time to snatch treasures should be mastered, for example, if you are playing Turtle Rock (Turtle Island), you only need to farm blue fat The time should also be counted, and so on and so on.

You need Sky’s regularity, Moon’s uninhibitedness, fly’s 100% calmness, and TED’s persistence. It also confirms a sentence, children only make choices, adults must!

So you not only need to know the effect, but also the cause.

The first step: Properties analysis

The first method: propertiesElement(root.evalNode("properties")), click in to see its source code, I have made comments here for your convenience

// 其实一个个 <> 的标签就是 一个个的XNode节点
  private void propertiesElement(XNode context) throws Exception {
    
    
    if (context != null) {
    
    
      // 首先判断要解析的属性是否有无子节点
      Properties defaults = context.getChildrenAsProperties();

      // 解析<properties resource=""/> 解析完成就变为配置文件的 SimpleName
      String resource = context.getStringAttribute("resource");

      // 解析<properties url=""/>
      String url = context.getStringAttribute("url");

      // 如果都为空,抛出异常
      if (resource != null && url != null) {
    
    
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }

      // 如果不为空的话,就把配置文件的内容都放到 Resources 类中
      if (resource != null) {
    
    
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
    
    
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      
      // 这块应该是判断有无之前的配置
      Properties vars = configuration.getVariables();
      if (vars != null) {
    
    
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
      // 最后放入 configuration 属性中
      configuration.setVariables(defaults);
    }
  }

The second step: Settings analysis

Here we take the opening of the second-level cache as an example to analyze

<!-- 通知 MyBatis 框架开启二级缓存 -->
<settings>
  <setting name="cacheEnabled" value="true"/>
</settings>

So settingsAsProperties(root.evalNode("settings"))how is it parsed in ?

// XNode 就是一个个的 标签
private Properties settingsAsProperties(XNode context) {
    
    
  if (context == null) {
    
    
    return new Properties();
  }
  // 获取字标签,字标签也就是 <settings> 中的 <setting>
  Properties props = context.getChildrenAsProperties();
  // Check that all settings are known to the configuration class
  // 用反射确保所有的设置都在 Configuration 类中。
  MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
  for (Object key : props.keySet()) {
    
    
    // 如果反射没有确保这个key 在类中,就抛出异常
    if (!metaConfig.hasSetter(String.valueOf(key))) {
    
    
      throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
    }
  }
  return props;
}

After the parsing is completed settings, the bottom layer is to use Hashtable to store entry objects one by one.

Step 3: Analysis of TypeAliases

TypeAliases is used for alias registration, you can specify its alias for the entity class, the source code is as follows

private void typeAliasesElement(XNode parent) {
    
    
  if (parent != null) {
    
    
    // 也是首先判断有无子标签
    for (XNode child : parent.getChildren()) {
    
    
      // 如果有字标签,那么取出字标签的属性名,如果是 package
      if ("package".equals(child.getName())) {
    
    
        // 那么取出 字标签 的name属性
        String typeAliasPackage = child.getStringAttribute("name");
        configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
      } else {
    
    

        // typeAliases 下面有两个标签,一个是 package 一个是 TypeAlias
        String alias = child.getStringAttribute("alias");
        String type = child.getStringAttribute("type");
        try {
    
    
          Class<?> clazz = Resources.classForName(type);
          if (alias == null) {
    
    
            typeAliasRegistry.registerAlias(clazz);
          } else {
    
    
            typeAliasRegistry.registerAlias(alias, clazz);
          }
        } catch (ClassNotFoundException e) {
    
    
          throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
        }
      }
    }
  }
}

Step 4: Plugins analysis

Plug-ins in MyBatis are parsed and registered at this step

private void pluginElement(XNode parent) throws Exception {
    
    
  if (parent != null) {
    
    
    for (XNode child : parent.getChildren()) {
    
    
      // 取出 interceptor 的名称
      String interceptor = child.getStringAttribute("interceptor");
      Properties properties = child.getChildrenAsProperties();
      // 生成新实例,设置属性名
      Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
      interceptorInstance.setProperties(properties);
      // 添加到 configuration 中
      configuration.addInterceptor(interceptorInstance);
    }
  }
}

other steps

In fact, the subsequent source code analysis steps are similar. Generally, it is to judge whether there is such an XNode node, and then judge its child node label, get the attribute of the label, and put it into the Configuration object, thus completing the initialization of the Configuration object. In fact, you It can be seen that the Configuration in MyBatis is also a large container to provide guarantee for subsequent SQL statement parsing and initialization.

Summarize

This article mainly summarizes

  • Configuration creation process

SqlSessionFactoryBuilder creates XMLConfigBuilder, and XMLConfigBuilder creates Configuration again. The creation of Configuration will load some basic attributes, such as transactions, data sources, caches, logs, agents, etc., which are registered by TypeAliasRegistry, and TypeAliasRegistry initialization also registers some basic data types, map, list, collection, etc. Configuration also initializes many other attributes, thus completing the creation of Configuration.

  • Configuration labels and usage

This step analyzes the tags and usage in Configuration. You don’t need to memorize this part. You only need to know which tags are more important, such as: properties, environment, mappers, settings, typeHandler. If you have development needs, you can directly search the official website just fine

(http://www.mybatis.org/mybatis-3/zh/configuration.html)

  • Configuration's parsing of tags

This step analyzes XMLConfigBuilder's parsing work for all tags under the Configuration class, and most of the parsing work patterns are similar

Generally, it judges whether there is such an XNode node, and then judges its child node label, obtains the attribute of the label, and puts it into the Configuration object.

Guess you like

Origin blog.csdn.net/zy_dreamer/article/details/132642041