Chapter 1 global configuration file of Mybatis framework

Keywords: Mybatis

1.mybatis-config.xml template

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  
</configuration>

2. properties

Use example

These properties can be set in the child elements of the properties element, or can be configured externally, introduced through resource, and dynamically replaced. For example:

// Introducing attributes through resource
<properties resource="org/mybatis/example/config.properties">
  // Set in child elements
  <property name="username" value="root"/>
  <property name="password" value="123456"/>
</properties>

priority

If a property is configured in multiple places, Mybatis will load it in the following order:

  • First read the properties specified in the properties element body.
  • Then read the property file under the classpath according to the resource attribute in the properties element, or read the property file according to the path specified by the url attribute, and overwrite the previously read property with the same name.
  • Finally, read the property passed as a method parameter and overwrite the previously read property with the same name.

We can see that the above order is overwritten every time, because the last order is in positive order. The attributes passed through the method parameters have the highest priority, followed by the configuration file specified in the resource/url attribute, and the attributes specified in the properties element have the lowest priority.

3. settings

Common configurations. See the official website for all configurations

Set namedescribeEffective valueDefault value
cacheEnabledGlobally turn on or off any cache configured in all mapper profiles.true | falsetrue
lazyLoadingEnabledGlobal switch for delayed loading. When on, all associated objects are loaded late. In a specific association, the switch state of the item can be overridden by setting the fetchType property.true | falsefalse
useColumnLabelUse column labels instead of column names. The actual performance depends on the database driver. For details, please refer to the relevant documents of the database driver or observe through comparative test.true | falsetrue
defaultStatementTimeoutSet the timeout, which determines the number of seconds the database driver waits for a database response.Any positive integerNot set (null)
mapUnderscoreToCamelCaseWhether to enable automatic hump naming mapping, that is, from the classic database column name A_COLUMN maps to the classic Java property name aColumn.true | falseFalse

Use example

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

4. Type aliases

Manual assignment

Type alias sets an abbreviated name for a Java type. It is only used for XML configuration and is intended to reduce redundant fully qualified class name writing. For example:

<typeAliases>
  // Set the alias of an entity class separately
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
</typeAliases>

You can specify a package name and set all aliases to lowercase class names

<typeAliases>
  <package name="com.jianan.entity"/>
</typeAliases>

In the Java Bean of each package, the Alias defaults to the initial lowercase unqualified class name of the Bean, but we can modify it through the @ Alias annotation

@Alias("author")
public class Author {
    ...
}

Provided by default

aliasType of mapping
_bytebyte
_shortshort
_intint
_longlong
_floatfloat
_doubledouble
_booleanboolean
_integerint
stringString
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean
dateDate
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection

5. Type handlers

When MyBatis sets the parameters in the PreparedStatement or takes a value from the result set, it will use the type processor to convert the obtained value into Java type in an appropriate way.

Provided by default

Some are listed below. For more information, please see the official website

Type processorJava typeJDBC type
BooleanTypeHandlerjava.lang.Boolean, booleanDatabase compatible BOOLEAN
ByteTypeHandlerjava.lang.Byte, byteDatabase compatible NUMERIC or BYTE
ShortTypeHandlerjava.lang.Short, shortDatabase compatible NUMERIC or SMALLINT
IntegerTypeHandlerjava.lang.Integer, intDatabase compatible NUMERIC or INTEGER
LongTypeHandlerjava.lang.Long, longDatabase compatible NUMERIC or BIGINT
FloatTypeHandlerjava.lang.Float, floatDatabase compatible NUMERIC or FLOAT
DoubleTypeHandlerjava.lang.Double, doubleDatabase compatible NUMERIC or DOUBLE
DateTypeHandlerjava.util.DateTIMESTAMP
StringTypeHandlerjava.lang.StringCHAR, VARCHAR
...

Custom processor

You can override an existing type processor or create your own type processor to handle unsupported or nonstandard types. The specific methods are as follows:

  1. Implement the org.apache.ibatis.type.TypeHandler interface, or inherit a convenient class org.apache.ibatis.type.BaseTypeHandler,
  2. Specifies that it can (optionally) map it to a JDBC type
  3. Register in Mybatis global configuration file

TypeHandler interface

public interface TypeHandler<T> {

    /**
     * It is used to define how to convert Java types to corresponding database types when mybatis sets parameters
     * @param preparedStatement Current PreparedStatement object
     * @param i The location of the current parameter
     * @param s Java object for the current parameter
     * @param jdbcType Database object for the current parameter
     * @throws SQLException
     */
    void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

    /**
     * It is used to define how to convert the database type to the corresponding Java type when Mybatis obtains the dataset
     * @param resultSet Current dataset
     * @param s Converted Java object
     * @return
     * @throws SQLException
     */
    T getResult(ResultSet rs, String columnName) throws SQLException;

    /**
     * It is used to define how to convert the database type to the corresponding Java type when Mybatis obtains field data through field location
     * @param resultSet Current result set
     * @param i The location of the current field
     * @return
     * @throws SQLException
     */
    T getResult(ResultSet rs, int columnIndex) throws SQLException;

    /**
     * It is used for Mybatis to convert the data of database type to the corresponding Java type after calling the stored procedure
     * @param callableStatement CallableStatement after the current CallableStatement is executed
     * @param i The location of the current output parameter
     * @return
     * @throws SQLException
     */
    T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

BaseTypeHandler class (recommended)

We can see that the BaseTypeHandler class implements the TypeHandler interface and inherits TypeReference

  • BaseTypeHandler is an abstract class that requires subclasses to implement the four abstract methods defined by it, and it itself implements the four methods of the typeHandler interface.
  • The BaseTypeHandler class adds non null judgment, and subclasses are omitted. After the subclass rewrites the method, it only focuses on logic
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {

  protected Configuration configuration;

  public void setConfiguration(Configuration c) {
    this.configuration = c;
  }

  @Override
  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
    if (parameter == null) {
      if (jdbcType == null) {
        // Both are null, setting exception
        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
      }
      try {
        // Make a null setting
        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 {
        // Set parameters normally. This method is overridden and customized by subclasses
        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 {
    T result;
    try {
      // Gets the result set. This method is overridden and customized by subclasses
      result = getNullableResult(rs, columnName);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
    }
    if (rs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }

  @Override
  public T getResult(ResultSet rs, int columnIndex) throws SQLException {
    T result;
    try {
      // Gets the result set. This method is overridden and customized by subclasses
      result = getNullableResult(rs, columnIndex);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from result set.  Cause: " + e, e);
    }
    if (rs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }

  @Override
  public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
    T result;
    try {
      // Gets the result set. This method is overridden and customized by subclasses
      result = getNullableResult(cs, columnIndex);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from callable statement.  Cause: " + e, e);
    }
    if (cs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }

  // Abstract methods that require subclass inheritance implementation
  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;
}

test case

We customize a type processor to realize the function of splicing the array into the database, and then converting it into an array when taking out the data

a> Custom TypeHandler
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(Array.class)
//Here, if you do not specify the JDBC type with annotations, you can specify it in the configuration file through the "JDBC type" attribute. Similarly, the javaType can also be specified through @ MappedTypes
public class Example2TypeHandler extends BaseTypeHandler<String[]> {

    // Java -- > database splices arrays and stores them in the database
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String[] parameter, JdbcType jdbcType) throws SQLException {
        StringBuffer result = new StringBuffer();
        for (String value : parameter) {
            result.append(value).append(",");
        }
        ps.setString(i, result.toString());
    }

    // Database -- > java intercepts data into an array and stores it in
    @Override
    public String[] getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String columnValue = rs.getString(columnName);
        return this.getStringArray(columnValue);
    }

    @Override
    public String[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String columnValue = rs.getString(columnIndex);
        return this.getStringArray(columnValue);
    }

    @Override
    public String[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String columnValue = cs.getString(columnIndex);
        return this.getStringArray(columnValue);
    }

    // Convert string to array
    private String[] getStringArray(String columnValue) {
        if (columnValue == null) {
            return null;
        }
        return columnValue.split(",");
    }
}
b> Register TypeHandler with global profile
<typeHandlers>
    <typeHandler handler="com.jianan.config.Example2TypeHandler" />
</typeHandlers>
c> Specify mapping type
	<resultMap id="users" type="com.jianan.entity.User">
        <result column="id" property="id" />
        <result column="name" property="names" typeHandler="com.jianan.config.Example2TypeHandler"  />
    </resultMap>

    <select id="listAll" resultMap="users">
        SELECT id,name FROM `user`
    </select>	
d> Test code
	static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = factory.openSession();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static SqlSession sqlSession;

    @Test
    public void m1() {
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = userMapper.listAll();
        System.out.println(list);
    }
e> Results
[User{id=1, name='null', names=[Jialin 1, Jialin 2]}, User{id=2, name='null', names=[Canaan 1, Canaan 2]}]

6. Object factory

Every time mybatis creates a new instance of the result object, it uses an object factory instance to complete the instantiation. The default object factory only needs to instantiate the target class, either through the default parameterless construction method or through parameters to call the parameterless construction. If we want to override the default behavior of the object factory, we can customize it.

1. Class diagram

From the class diagram below, we can easily see that ObjectFactory is the interface that provides the basic method of the object factory, while DefaultObjectFactory is the default object factory. If we want to customize, we can inherit the DefaultObjectFactory class and override the method

2.ObjectFactory interface

public interface ObjectFactory {

  // Set properties to configure the object factory
  void setProperties(Properties properties);

  // Processing nonparametric construction method
  <T> T create(Class<T> type);
 
  // Processing parametric construction method 
  <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
  
  // Determine whether it is a collection
  <T> boolean isCollection(Class<T> type);

}

3.DefaultObjectFactory interface

public class DefaultObjectFactory implements ObjectFactory, Serializable {

  private static final long serialVersionUID = -8855120656740914948L;
  
  // The default processing is parameterless construction
  @Override
  public <T> T create(Class<T> type) {
    return create(type, null, null);
  }
  
  // The default process is parametric construction
  @SuppressWarnings("unchecked")
  @Override
  public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    // Type judgment if the type is an interface, convert it to a subclass because the interface cannot be created
    Class<?> classToCreate = resolveInterface(type);
    // we know types are assignable
    // create object
    return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
  }
  
  // set a property
  @Override
  public void setProperties(Properties properties) {
    // no props for default
  }

  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    try {
      Constructor<T> constructor;
      // Parameter type or parameter judgment
      if (constructorArgTypes == null || constructorArgs == null) {
        // Get parameterless constructor
        constructor = type.getDeclaredConstructor();
        if (!constructor.isAccessible()) {
          constructor.setAccessible(true);
        }
        // create object
        return constructor.newInstance();
      }
      // If you execute here, you need to use a parametric construct
      constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
      if (!constructor.isAccessible()) {
        constructor.setAccessible(true);
      }
      return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
    } catch (Exception e) {
      // The following is to splice the exception information and throw an exception
      StringBuilder argTypes = new StringBuilder();
      if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
        for (Class<?> argType : constructorArgTypes) {
          argTypes.append(argType.getSimpleName());
          argTypes.append(",");
        }
        argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
      }
      StringBuilder argValues = new StringBuilder();
      if (constructorArgs != null && !constructorArgs.isEmpty()) {
        for (Object argValue : constructorArgs) {
          argValues.append(String.valueOf(argValue));
          argValues.append(",");
        }
        argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
      }
      throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
    }
  }
  
  // Processing type when the created object is an interface, we type convert it because the interface cannot be created. We convert it into a subclass
  // At this time, we are thinking about isCollection(), which mainly provides collection judgment, but we rarely use it 
  protected Class<?> resolveInterface(Class<?> type) {
    Class<?> classToCreate;
    if (type == List.class || type == Collection.class || type == Iterable.class) {
      classToCreate = ArrayList.class;
    } else if (type == Map.class) {
      classToCreate = HashMap.class;
    } else if (type == SortedSet.class) { // issue #510 Collections Support
      classToCreate = TreeSet.class;
    } else if (type == Set.class) {
      classToCreate = HashSet.class;
    } else {
      classToCreate = type;
    }
    return classToCreate;
  }
  
  // Is it a collection
  @Override
  public <T> boolean isCollection(Class<T> type) {
    return Collection.class.isAssignableFrom(type);
  }
}

4. Example of official website

// 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">
  <!-- set a property -->
  <property name="someProperty" value="100"/>
</objectFactory>

7. Environment configuration

MyBatis can be configured to adapt to a variety of environments. For example, development, testing and production environments need different configurations; Or you want to use the same SQL mapping in multiple production databases with the same Schema.

However, remember: Although multiple environments can be configured, only one environment can be selected for each SqlSessionFactory instance, and each database corresponds to one SqlSessionFactory instance.

Configuration example

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

matters needing attention

1.id configuration

  • The default environment ID (for example, default = "development") must match one of the environment IDs.
  • The environment ID defined by each environment element (for example: id = "development").

2. Transaction manager

There are two types of transaction managers in MyBatis, that is, type = "[JDBC managed]", which can also be customized

  • JDBC – this configuration directly uses JDBC's commit and rollback facilities, which rely on connections obtained from data sources to manage transaction scopes.
  • It never commits or rolls back a connection, but lets the container manage the entire life cycle of the transaction

Tip: if you are using Spring + MyBatis, there is no need to configure the transaction manager, because the Spring module will use its own manager to override the previous configuration.

3. Data source

The dataSource element uses the standard JDBC data source interface to configure the resources of the JDBC connection object. There are three built-in data source types, that is, type="[UNPOOLED|POOLED|JNDI],

UNPOOLED: do not use connection pool

  • driver – this is the fully qualified name of a JDBC driven Java class (not a data source class that may be included in a JDBC driver).
  • url – this is the JDBC URL address of the database.
  • username – the user name to log in to the database.
  • Password – password to log in to the database.
  • defaultTransactionIsolationLevel – the default connection transaction isolation level.
  • defaultNetworkTimeout – the default network timeout in milliseconds to wait for the database operation to complete. Check the API documentation for java.sql.Connection#setNetworkTimeout() for more information.

POOLED: use connection pool

In addition to the above mentioned attributes under UNPOOLED, there are more attributes to configure the data source of POOLED:

  • poolMaximumActiveConnections – the number of active (in use) connections that can exist at any time. Default: 10
  • poolMaximumIdleConnections – the number of idle connections that may exist at any time.
  • poolMaximumCheckoutTime – the time that connections in the pool are checked out before being forcibly returned. The default value is 20000 milliseconds (i.e. 20 seconds)
  • poolTimeToWait – this is an underlying setting. If it takes a long time to obtain a connection, the connection pool will print the status log and try to obtain a connection again (to avoid continuous failure in case of misconfiguration and no log printing). The default value is 20000 milliseconds (i.e. 20 seconds).
  • poolMaximumLocalBadConnectionTolerance – this is a low-level setting for bad connection tolerance that applies to every thread trying to get a connection from the cache pool. If the thread gets a bad connection, the data source allows the thread to try to get a new connection again, but the number of retries should not exceed the sum of poolMaximumIdleConnections and poolMaximumLocalBadConnectionTolerance. Default value: 3 (added to 3.4.5)
  • poolPingQuery – a probe query sent to the database to verify that the connection is working properly and ready to accept the request. The default is "NO PING QUERY SET", which will cause most database drivers to return appropriate error messages when errors occur.
  • poolPingEnabled – whether to enable detection query. If enabled, you need to set the poolPingQuery property to an executable SQL statement (preferably a very fast SQL statement). The default value is false.
  • poolPingConnectionsNotUsedFor – configure the frequency of poolPingQuery. It can be set as the database connection timeout to avoid unnecessary detection. The default value is 0 (that is, all connections are detected at every moment - of course, it is only applicable when poolPingEnabled is true).

JNDI - this data source implementation is designed to be used in containers such as EJB s or application servers,

8. Database vendor ID (databaseIdProvider)

Mybatis can execute different statements according to different database vendors

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

MyBatis loads statements with and without the databaseId attribute that match the current database. If the same statement with and without databaseId is found at the same time, the latter will be discarded.

<select id="selectTime" resultType="String" databaseId="mysql">
   SELECT * FROM `user`
</select>

9. mappers

<!-- Use resource references relative to Classpaths -->
<mappers>
     <mapper resource="com.jianan.mapper.UserMapper" />
</mappers>
<!-- Register all the mapper interface implementations in the package as mappers -->
<mappers>
  <package name="com.jianan.mapper"/>
</mappers>
<!-- Use the mapper interface to implement the fully qualified class name of the class -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- Use fully qualified resource locators( URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>

Posted by programming_passion on Fri, 22 Oct 2021 19:03:55 -0700